Impl OCR. Close #46

This commit is contained in:
Changhua 2023-12-06 23:58:33 +08:00
parent 45826ec8be
commit 4ef96205e6
9 changed files with 217 additions and 55 deletions

View File

@ -56,3 +56,8 @@ typedef struct {
string mobile;
string home;
} UserInfo_t;
typedef struct {
int32_t status;
string result;
} OcrResult_t;

View File

@ -30,6 +30,7 @@ enum Functions {
FUNC_GET_CONTACT_INFO = 0x55;
FUNC_REVOKE_MSG = 0x56;
FUNC_DECRYPT_IMAGE = 0x60;
FUNC_EXEC_OCR = 0x61;
FUNC_ADD_ROOM_MEMBERS = 0x70;
FUNC_DEL_ROOM_MEMBERS = 0x71;
FUNC_INV_ROOM_MEMBERS = 0x72;
@ -73,6 +74,7 @@ message Response
DbTables tables = 8; //
DbRows rows = 9; //
UserInfo ui = 10; //
OcrMsg ocr = 11; // OCR
};
}
@ -217,3 +219,9 @@ message PatMsg
string roomid = 1; // id
string wxid = 2; // wxid
}
message OcrMsg
{
int32 status = 1; //
string result = 2; //
}

View File

@ -50,6 +50,10 @@ static string get_key(uint8_t header1, uint8_t header2, uint8_t *key)
string DecryptImage(string src, string dir)
{
if (!fs::exists(src)) {
return "";
}
ifstream in(src.c_str(), ios::binary);
if (!in.is_open()) {
LOG_ERROR("Failed to read file {}", src);
@ -335,3 +339,72 @@ string GetAudio(uint64_t id, string dir)
return mp3path;
}
OcrResult_t GetOcrResult(string path)
{
OcrResult_t ret = { -1, "" };
if (!fs::exists(path)) {
LOG_ERROR("Can not find: {}", path);
return ret;
}
// 路径分隔符有要求,必须为 `\`
wstring wsPath = String2Wstring(fs::path(path).make_preferred().string());
WxString wxPath(wsPath);
WxString nullObj;
WxString ocrBuffer;
DWORD ocrCall1 = g_WeChatWinDllAddr + g_WxCalls.ocr.call1;
DWORD ocrCall2 = g_WeChatWinDllAddr + g_WxCalls.ocr.call2;
DWORD ocrCall3 = g_WeChatWinDllAddr + g_WxCalls.ocr.call3;
DWORD tmp = 0;
int status = -1;
__asm {
pushad;
pushfd;
lea ecx, ocrBuffer;
call ocrCall1;
call ocrCall2;
lea ecx, nullObj;
push ecx;
lea ecx, tmp;
push ecx;
lea ecx, ocrBuffer;
push ecx;
push 0x0;
lea ecx, wxPath;
push ecx;
mov ecx, eax;
call ocrCall3;
mov status, eax;
popfd;
popad;
}
if (status != 0)
{
LOG_ERROR("OCR status: {}", to_string(status));
return ret; // 识别出错
}
ret.status = status;
DWORD addr = (DWORD)&ocrBuffer;
DWORD header = GET_DWORD(addr);
DWORD num = GET_DWORD(addr + 0x4);
if (num <= 0) {
return ret; // 识别内容为空
}
for (uint32_t i = 0; i < num; i++) {
DWORD content = GET_DWORD(header);
ret.result += Wstring2String(GET_WSTRING(content + 0x14));
ret.result += "\n";
header = content;
}
return ret;
}

View File

@ -8,3 +8,4 @@ std::string DecryptImage(std::string src, std::string dst);
int RefreshPyq(uint64_t id);
int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
int RevokeMsg(uint64_t id);
OcrResult_t GetOcrResult(std::string path);

View File

@ -37,7 +37,9 @@ WxCalls_t wxCalls = {
/* call1, call2, call3 */
{0x931730, 0x1D58751, 0x1421940},
/* call1, call2, call3, call4, call5, call6, call7, call8*/
{0x78CB40, 0x7F99D0, 0x78CF20, 0x78CEF0, 0xF59E40, 0xBD1A00, 0x7FA980, 0x755060}
{0x78CB40, 0x7F99D0, 0x78CF20, 0x78CEF0, 0xF59E40, 0xBD1A00, 0x7FA980, 0x755060},
/* call1, call2, call3 */
{0x80A800, 0x80F270, 0x13DA3E0}
};
int LoadCalls(const wchar_t *version, WxCalls_t *calls)

View File

@ -688,6 +688,31 @@ bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len)
return true;
}
bool func_exec_ocr(char *path, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
rsp.func = Functions_FUNC_EXEC_OCR;
rsp.which_msg = Response_ocr_tag;
OcrResult_t ret = { -1, "" };
if (path == NULL) {
LOG_ERROR("Empty path.");
} else {
ret = GetOcrResult(path);
}
rsp.msg.ocr.status = ret.status;
rsp.msg.ocr.result = (char *)ret.result.c_str();
pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
if (!pb_encode(&stream, Response_fields, &rsp)) {
LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
return false;
}
*len = stream.bytes_written;
return true;
}
bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len)
{
Response rsp = Response_init_default;
@ -901,6 +926,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_decrypt_image(req.msg.dec, out, out_len);
break;
}
case Functions_FUNC_EXEC_OCR: {
LOG_DEBUG("[Functions_FUNC_EXEC_OCR]");
ret = func_exec_ocr(req.msg.str, out, out_len);
break;
}
case Functions_FUNC_ADD_ROOM_MEMBERS: {
LOG_DEBUG("[Functions_FUNC_ADD_ROOM_MEMBERS]");
ret = func_add_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len);

View File

@ -145,6 +145,12 @@ typedef struct CallInviteCM {
DWORD call8;
} CallInviteCM_t;
typedef struct CallOcr {
DWORD call1;
DWORD call2;
DWORD call3;
} CallOcr_t;
typedef struct WxCalls {
DWORD login; // 登录状态
UserInfoCall_t ui; // 用户信息
@ -166,6 +172,7 @@ typedef struct WxCalls {
CallRichText_t rt; // 发送消息卡片
CallPatMsg_t pm; // 发送拍一拍消息
CallInviteCM_t irm; // 邀请群成员
CallOcr_t ocr; // OCR
} WxCalls_t;
struct WxString {

View File

@ -755,6 +755,40 @@ class Wcf():
rsp = self._send_request(req)
return rsp.str
def get_ocr_result(self, extra: str, timeout: int = 2) -> str:
"""获取 OCR 结果。鸡肋,需要图片能自动下载;通过下载接口下载的图片无法识别。
Args:
extra (str): 待识别的图片路径消息里的 extra
Returns:
str: OCR 结果
"""
def _inner(extra):
req = wcf_pb2.Request()
req.func = wcf_pb2.FUNC_EXEC_OCR # FUNC_EXEC_OCR
req.str = extra
rsp = self._send_request(req)
ocr = json_format.MessageToDict(rsp.ocr)
return ocr.get("status", 0), ocr.get("result", "")
cnt = 0
while True:
status, result = _inner(extra)
if status == 0:
break
cnt += 1
if cnt > timeout:
break
sleep(1)
if status != 0:
self.LOG.error(f"OCR failed, status: {status}")
return result
def download_image(self, id: int, extra: str, dir: str, timeout: int = 30) -> str:
"""下载图片

File diff suppressed because one or more lines are too long