From fa31de81e033ceb35c93b5ff260746f53e73c4f3 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Thu, 10 Aug 2023 17:13:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E6=96=B0=E5=A2=9E=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=85=AC=E4=BC=97=E5=8F=B7=E6=B6=88=E6=81=AF=EF=BC=8C=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E9=99=84=E4=BB=B6=EF=BC=8C=E5=9B=BE=E7=89=87=E8=A7=A3?= =?UTF-8?q?=E7=A0=81=EF=BC=8C=E8=8E=B7=E5=8F=96=E8=AF=AD=E9=9F=B3=EF=BC=8C?= =?UTF-8?q?=E5=8F=91=E9=80=81=E8=87=AA=E5=AE=9A=E4=B9=89=E8=A1=A8=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 315 +++++++++++++++++++++++++++++++++ python/3.9.5.81/http_client.py | 94 +++++++++- src/hooks.cc | 13 +- src/http_client.cc | 2 +- src/http_server_callback.cc | 87 +++++++-- src/manager.cc | 312 ++++++++++++++++++++++++++++++++ src/manager.h | 13 ++ src/utils.cc | 79 +++++++++ src/utils.h | 2 + src/wechat_function.h | 22 ++- 10 files changed, 917 insertions(+), 22 deletions(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index c3f8143..b3718a9 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -1365,4 +1365,319 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 }, "msg": "success" } +``` + +#### 27.发送公众号消息** +###### 接口功能 +> 自定义发送公众号消息 + +###### 接口地址 +> [/api/forwardPublicMsg](/api/forwardPublicMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|appName|string|公众号id,消息内容里的appname| +|userName|string|公众号昵称,消息内容里的username| +|title|string|链接地址,消息内容里的title| +|url|string|链接地址,消息内容里的url| +|thumbUrl|string|缩略图地址,消息内容里的thumburl| +|digest|string|摘要,消息内容里的digest| +|wxid|string|wxid| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "appName": "快手", + "userName": "gh_271633", + "title": "PC硬件、数码产品彻底反转", + "url": "http://mp.weixin.qq.com/s?__biz=Mzg3MzYg==&mid=22440&idx=1&sn=bd8e8b0d9f2753f3c340&chksm=ced16f2ff9a6e639cc9bb76631ff03487f86486f0f29fcf9f8bed754354cb20eda31cc894a56&scene=0&xtrack=1#rd", + "thumbUrl": "https://mmbiz.qpic.cn/sz__jpg/tpzwaqMCicQyEkpxmpmmP9KgoBHiciamYhqZ0ff4kNlozxgRq4AtEzibo4iaw/640?wxtype=jpeg&wxfrom=0", + "digest": "这谁顶得住?", + "wxid": "filehelper" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + +#### 28.转发公众号消息** +###### 接口功能 +> 转发公众号消息 + +###### 接口地址 +> [/api/forwardPublicMsgByMsgId](/api/forwardPublicMsgByMsgId) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId|number|msgId| +|wxid|string|wxid| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "msgId": 8871595889497690337, + "wxid": "filehelper" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + +#### 29.下载附件** +###### 接口功能 +> 下载附件,保存在微信文件目录下 wxid_xxx/wxhelper 目录下 + +###### 接口地址 +> [/api/downloadAttach](/api/downloadAttach) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId|number|msgId| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "msgId": 887159588949767 +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + + +#### 30.解码图片** +###### 接口功能 +> 解码图片 + +###### 接口地址 +> [/api/decodeImage](/api/decodeImage) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|filePath|string|待解码图片地址| +|storeDir|string|解码后图片的存储目录| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "filePath": "C:\\886206666148161980131.dat", + "storeDir":"C:\\test" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + +#### 31.获取语音** +###### 接口功能 +> 获取语音,SILK v3格式,可自行转换mp3格式 + +###### 接口地址 +> [/api/getVoiceByMsgId](/api/getVoiceByMsgId) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId|number|消息id| +|storeDir|string|语音的存储目录| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ +"msgId":78804324411226, +"storeDir":"c:\\test" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + +#### 32.发送图片** +###### 接口功能 +> 发送图片 + +###### 接口地址 +> [/api/sendImagesMsg](/api/sendImagesMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|wxid| +|imagePath|string|图片路径| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "wxid":"filehelper", + "imagePath":"C:\\test.png" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + +#### 33.发送自定义表情** +###### 接口功能 +> 发送自定义表情 + +###### 接口地址 +> [/api/sendCustomEmotion](/api/sendCustomEmotion) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|wxid| +|filePath|string|表情路径,可以直接查询CustomEmotion表的MD5字段,路径规则见下面示例| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ + "wxid":"filehelper", + "filePath":"C:\\wechatDir\\WeChat Files\\wxid_123\\FileStorage\\CustomEmotion\\8F\\8F6423BC2E69188DCAC797E279C81DE9" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} ``` \ No newline at end of file diff --git a/python/3.9.5.81/http_client.py b/python/3.9.5.81/http_client.py index d7e63f7..c661baa 100644 --- a/python/3.9.5.81/http_client.py +++ b/python/3.9.5.81/http_client.py @@ -378,7 +378,7 @@ def getContactProfile(): def sendAtText(): - print("modify wxids chatRoomId") + print("modify wxids chatRoomId") raise RuntimeError("modify wxids chatRoomId then deleted me") url = "127.0.0.1:19088/api/sendAtText" @@ -395,6 +395,98 @@ def sendAtText(): print(response.text) +def forwardPublicMsg(): + print("modify param ") + raise RuntimeError("modify param then deleted me") + url = "127.0.0.1:19088/api/forwardPublicMsg" + + payload = json.dumps({ + "appName": "", + "userName": "", + "title": "", + "url": "", + "thumbUrl": "", + "digest": "", + "wxid": "filehelper" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + +def forwardPublicMsgByMsgId(): + print("modify param ") + raise RuntimeError("modify param then deleted me") + url = "127.0.0.1:19088/api/forwardPublicMsgByMsgId" + + payload = json.dumps({ + "msgId": 123, + "wxid": "filehelper" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + +def downloadAttach(): + print("modify param ") + raise RuntimeError("modify param then deleted me") + url = "127.0.0.1:19088/api/downloadAttach" + + payload = json.dumps({ + "msgId": 123 + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +def decodeImage(): + print("modify param ") + raise RuntimeError("modify param then deleted me") + url = "127.0.0.1:19088/api/decodeImage" + + payload = json.dumps({ + "filePath": "C:\\66664816980131.dat", + "storeDir": "C:\\test" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +def getVoiceByMsgId(): + print("modify param ") + raise RuntimeError("modify param then deleted me") + url = "127.0.0.1:19088/api/getVoiceByMsgId" + + payload = json.dumps({ + "msgId": 7880439644200, + "storeDir": "c:\\test" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + if __name__ == '__main__': checkLogin() diff --git a/src/hooks.cc b/src/hooks.cc index 9b3e0c9..4defcfd 100644 --- a/src/hooks.cc +++ b/src/hooks.cc @@ -78,7 +78,7 @@ VOID CALLBACK SendMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, goto clean; } char recv_buf[1024] = {0}; - ret = send(client_socket, jstr.c_str(), jstr.size(), 0); + ret = send(client_socket, jstr.c_str(), static_cast(jstr.size()) , 0); if (ret < 0) { SPDLOG_ERROR("socket send fail ,ret:{}", ret); goto clean; @@ -125,9 +125,10 @@ void HandleSyncMsg(INT64 param1, INT64 param2, INT64 param3) { msg["toUser"] = Utils::ReadSKBuiltinString(*(INT64 *)(param2 + 0x28)); msg["content"] = Utils::ReadSKBuiltinString(*(INT64 *)(param2 + 0x30)); msg["signature"] = Utils::ReadWeChatStr(*(INT64 *)(param2 + 0x48)); - msg["msgId"] = *(INT64 *)(param2 + 0x58); - msg["msgSequence"] = *(DWORD *)(param2 + 0x20); - msg["fromUserName"] = Utils::ReadWeChatStr(*(INT64 *)(param2 + 0x50)); + msg["msgId"] = *(INT64 *)(param2 + 0x60); + msg["msgSequence"] = *(DWORD *)(param2 + 0x5C); + msg["createTime"] = *(DWORD *)(param2 + 0x58); + msg["displayFullContent"] = Utils::ReadWeChatStr(*(INT64 *)(param2 + 0x50)); DWORD type = *(DWORD *)(param2 + 0x24); msg["type"] = type; if (type == 3) { @@ -195,7 +196,7 @@ void HandleSNSMsg(INT64 param1, INT64 param2, INT64 param3) { common::InnerMessageStruct *inner_msg = new common::InnerMessageStruct; inner_msg->buffer = new char[jstr.size() + 1]; memcpy(inner_msg->buffer, jstr.c_str(), jstr.size() + 1); - inner_msg->length = jstr.size(); + inner_msg->length = jstr.size(); if (kEnableHttp) { bool add = ThreadPool::GetInstance().AddWork(SendHttpMsgCallback, inner_msg); SPDLOG_INFO("hook sns add http msg work:{}", add); @@ -288,7 +289,7 @@ int UnHookSyncMsg() { DetourAttach(&(PVOID &)R_Log, &HandlePrintLog); LONG ret = DetourTransactionCommit(); if (ret == NO_ERROR) { - kMsgHookFlag = true; + kLogHookFlag = true; } return ret; } diff --git a/src/http_client.cc b/src/http_client.cc index fbf1a2a..60f0d1c 100644 --- a/src/http_client.cc +++ b/src/http_client.cc @@ -34,7 +34,7 @@ namespace wxhelper { } // Send request - int content_length = data.post_data.size(); + size_t content_length = data.post_data.size(); mg_printf(c, "POST %s HTTP/1.0\r\n" "Host: %.*s\r\n" diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 603beb3..f9ddaf0 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -484,8 +484,9 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { } else if (mg_http_match_uri(hm, "/api/getContactProfile")) { std::wstring wxid = GetWStringParam(j_param, "wxid"); common::ContactProfileInner profile; - INT64 success = wxhelper::GlobalContext::GetInstance().mgr->GetContactByWxid( - wxid, profile); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->GetContactByWxid(wxid, + profile); nlohmann::json ret_data = { {"code", success}, {"msg", "success"}, {"data", {}}}; if (success == 1) { @@ -498,15 +499,75 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { } ret = ret_data.dump(); return ret; - - } else { - nlohmann::json ret_data = { - {"code", 200}, {"data", {}}, {"msg", "not support url"}}; - ret = ret_data.dump(); - return ret; - } - nlohmann::json ret_data = { - {"code", 200}, {"data", {}}, {"msg", "unreachable code."}}; - ret = ret_data.dump(); - return ret; + } else if (mg_http_match_uri(hm, "/api/downloadAttach")) { + UINT64 msg_id = GetUINT64Param(j_param, "msgId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->DoDownloadTask(msg_id); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/forwardPublicMsg")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + std::wstring appname = GetWStringParam(j_param, "appName"); + std::wstring username = GetWStringParam(j_param, "userName"); + std::wstring title = GetWStringParam(j_param, "title"); + std::wstring url = GetWStringParam(j_param, "url"); + std::wstring thumburl = GetWStringParam(j_param, "thumbUrl"); + std::wstring digest = GetWStringParam(j_param, "digest"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->ForwardPublicMsg( + wxid, title, url, thumburl, username, appname, digest); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/forwardPublicMsgByMsgId")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + UINT64 msg_id = GetUINT64Param(j_param, "msgId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->ForwardPublicMsgByMsgId( + wxid, msg_id); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/decodeImage")) { + std::wstring file_path = GetWStringParam(j_param, "filePath"); + std::wstring store_dir = GetWStringParam(j_param, "storeDir"); + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->DecodeImage( + file_path, store_dir); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/getVoiceByMsgId")) { + UINT64 msg_id = GetUINT64Param(j_param, "msgId"); + std::wstring store_dir = GetWStringParam(j_param, "storeDir"); + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->GetVoiceByDB( + msg_id, store_dir); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/sendCustomEmotion")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + std::wstring file_path = GetWStringParam(j_param, "filePath"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->SendCustomEmotion(file_path, + wxid); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else { + nlohmann::json ret_data = { + {"code", 200}, {"data", {}}, {"msg", "not support url"}}; + ret = ret_data.dump(); + return ret; + } + nlohmann::json ret_data = { + {"code", 200}, {"data", {}}, {"msg", "unreachable code."}}; + ret = ret_data.dump(); + return ret; } \ No newline at end of file diff --git a/src/manager.cc b/src/manager.cc index 713afd3..be22fa6 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -4,6 +4,9 @@ #include "export.h" #include "wechat_function.h" #include "db.h" +#include "lz4.h" +#include "base64.h" +#include "tinyxml2.h" namespace offset = wxhelper::V3_9_5_81::offset; namespace prototype = wxhelper::V3_9_5_81::prototype; @@ -783,4 +786,313 @@ INT64 Manager::GetContactByWxid(const std::wstring &wxid, free_contact(contact); return success; } + +INT64 Manager::DoDownloadTask(UINT64 msg_id) { + INT64 success = -1; + UINT64 get_by_local_id_addr = base_addr_ + offset::kGetMgrByPrefixLocalId; + func::__GetMgrByPrefixLocalId get_by_local_id = + (func::__GetMgrByPrefixLocalId)get_by_local_id_addr; + + UINT64 get_chat_mgr_addr = base_addr_ + offset::kGetChatMgr; + func::__GetChatMgr get_chat_mgr = (func::__GetChatMgr)get_chat_mgr_addr; + + UINT64 free_chat_msg_addr = base_addr_ + offset::kFreeChatMsg; + func::__FreeChatMsg free_chat_msg = (func::__FreeChatMsg)free_chat_msg_addr; + + UINT64 new_chat_msg_addr = base_addr_ + offset::kChatMsgInstanceCounter; + func::__NewChatMsg new_chat_msg = (func::__NewChatMsg)new_chat_msg_addr; + + UINT64 get_current_data_path_addr = base_addr_ + offset::kGetCurrentDataPath; + func::__GetCurrentDataPath GetCurrentDataPath = + (func::__GetCurrentDataPath)get_current_data_path_addr; + + UINT64 new_app_msg_info_addr = base_addr_ + offset::kNewAppMsgInfo; + func::__NewAppMsgInfo new_app_msg_info = + (func::__NewAppMsgInfo)new_app_msg_info_addr; + + UINT64 free_app_msg_info_addr = base_addr_ + offset::kFreeAppMsgInfo; + func::__FreeAppMsgInfo free_app_msg_info = + (func::__NewAppMsgInfo)free_app_msg_info_addr; + + UINT64 xml_to_app_info_addr = base_addr_ + offset::kParseAppMsgXml; + func::__ParseAppMsgXml xml_to_app_info = + (func::__ParseAppMsgXml)xml_to_app_info_addr; + + UINT64 get_pre_download_mgr_addr = base_addr_ + offset::kGetPreDownLoadMgr; + func::__GetPreDownLoadMgr get_pre_download_mgr = + (func::__GetPreDownLoadMgr)get_pre_download_mgr_addr; + + UINT64 push_attach_task_addr = base_addr_ + offset::kPushAttachTask; + func::__PushAttachTask push_attach_task = + (func::__PushAttachTask)push_attach_task_addr; + + INT64 index = 0; + INT64 local_id = DB::GetInstance().GetLocalIdByMsgId(msg_id, index); + if (local_id <= 0 || index >> 32 == 0) { + success = -2; + return success; + } + char *chat_msg = Utils::WxHeapAlloc(0x460); + LARGE_INTEGER l; + l.HighPart = index >> 32; + l.LowPart = (DWORD)local_id; + UINT64 p_chat_msg = new_chat_msg(reinterpret_cast(chat_msg)); + + get_chat_mgr(); + get_by_local_id(l.QuadPart, p_chat_msg); + + std::wstring save_path = L""; + std::wstring thumb_path = L""; + + prototype::WeChatString current_data_path; + GetCurrentDataPath(reinterpret_cast(¤t_data_path)); + + if (current_data_path.length > 0) { + save_path += current_data_path.ptr; + save_path += L"wxhelper"; + } else { + return -1; + } + + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + INT64 type = *(INT64 *)(chat_msg + 0x38); + wchar_t *content = *(wchar_t **)(chat_msg + 0x88); + DWORD len = *(DWORD *)(chat_msg + 0x94); + std::wstring tmp_content(content, len); + prototype::WeChatString *we_content = BuildWechatString(tmp_content); + + switch (type) { + case 0x3: { + save_path += L"\\image"; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + thumb_path = save_path + L"\\" + std::to_wstring(msg_id) + L"_t.dat"; + save_path = save_path + L"\\" + std::to_wstring(msg_id) + L".dat"; + break; + } + case 0x3E: + case 0x2B: { + save_path += L"\\video"; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + thumb_path = save_path + L"\\" + std::to_wstring(msg_id) + L".jpg"; + save_path = save_path + L"\\" + std::to_wstring(msg_id) + L".mp4"; + + break; + } + case 0x31: { + save_path += L"\\file"; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return -3; + } + char *p_xml_app_msg = Utils::WxHeapAlloc(0x3000); + UINT64 xml_msg = + new_app_msg_info(reinterpret_cast(p_xml_app_msg)); + UINT64 result = + xml_to_app_info(xml_msg, reinterpret_cast(we_content), 1); + if (result != 1) { + return -4; + } + std::wstring file_name = Utils::ReadWstring(xml_msg + 0x70); + save_path = + save_path + L"\\" + std::to_wstring(msg_id) + L"_" + file_name; + free_app_msg_info(xml_msg); + break; + } + default: + break; + } + prototype::WeChatString *we_save_path = BuildWechatString(save_path); + prototype::WeChatString *we_thumb_path = BuildWechatString(thumb_path); + int temp = 1; + memcpy(chat_msg + 0x280, we_thumb_path, sizeof(prototype::WeChatString)); + memcpy(chat_msg + 0x2A0, we_save_path, sizeof(prototype::WeChatString)); + memcpy(chat_msg + 0x40C, &temp, sizeof(temp)); + UINT64 mgr = get_pre_download_mgr(); + success = push_attach_task(mgr, p_chat_msg, 0, 1); + free_chat_msg(p_chat_msg); + + return success; +} + +INT64 Manager::ForwardPublicMsg(const std::wstring &wxid, + const std::wstring &title, + const std::wstring &url, + const std::wstring &thumb_url, + const std::wstring &sender_id, + const std::wstring &sender_name, + const std::wstring &digest) { + INT64 success = -1; + UINT64 new_item_addr = base_addr_ + offset::kNewMMReaderItem; + func::__NewMMReaderItem new_item = (func::__NewMMReaderItem)new_item_addr; + + UINT64 free_item_addr = base_addr_ + offset::kFreeMMReaderItem; + func::__FreeMMReaderItem free_item = (func::__FreeMMReaderItem)free_item_addr; + + UINT64 get_app_msg_mgr_addr = base_addr_ + offset::kGetAppMsgMgr; + func::__GetAppMsgMgr get_app_mgr = (func::__GetAppMsgMgr)get_app_msg_mgr_addr; + + UINT64 forward_public_msg_addr = base_addr_ + offset::kForwordPublicMsg; + func::__ForwordPublicMsg forward_public_msg = + (func::__ForwordPublicMsg)forward_public_msg_addr; + + char *p_item = Utils::WxHeapAlloc(0x3E4); + new_item(reinterpret_cast(p_item)); + prototype::WeChatString *to_user = BuildWechatString(wxid); + prototype::WeChatString *p_title = BuildWechatString(title); + prototype::WeChatString *p_url = BuildWechatString(url); + prototype::WeChatString *p_thumburl = BuildWechatString(thumb_url); + prototype::WeChatString *p_sender_id = BuildWechatString(sender_id); + prototype::WeChatString *p_name = BuildWechatString(sender_name); + prototype::WeChatString *p_digest = BuildWechatString(digest); + + memcpy(p_item + 0x8, p_title, sizeof(prototype::WeChatString)); + memcpy(p_item + 0x48, p_url, sizeof(prototype::WeChatString)); + memcpy(p_item + 0xB0, p_thumburl, sizeof(prototype::WeChatString)); + memcpy(p_item + 0xF0, p_digest, sizeof(prototype::WeChatString)); + memcpy(p_item + 0x2A0, p_sender_id, sizeof(prototype::WeChatString)); + memcpy(p_item + 0x2C0, p_name, sizeof(prototype::WeChatString)); + memcpy(p_item + 0x2C0, p_name, sizeof(prototype::WeChatString)); + + UINT64 mgr = get_app_mgr(); + success = forward_public_msg(mgr, reinterpret_cast(to_user), + reinterpret_cast(p_item)); + free_item(reinterpret_cast(p_item)); + return success; +} + +INT64 Manager::ForwardPublicMsgByMsgId(const std::wstring &wxid, + UINT64 msg_id) { + INT64 success = -1; + std::string compress_content = + DB::GetInstance().GetPublicMsgCompressContentByMsgId(msg_id); + if (compress_content.empty()) { + SPDLOG_INFO("Get compressContent is null from PublicMsg.db"); + return -3; + } + + std::string decode = base64_decode(compress_content); + size_t len = decode.size(); + const char *src = decode.c_str(); + size_t dst_len = (len << 8); + char *dst = new char[dst_len]; + + int decompress_len = LZ4_decompress_safe_partial( + src, dst, static_cast(len), static_cast(dst_len), static_cast(dst_len)); + if (decompress_len < 0) { + SPDLOG_INFO("decompress content size :{}", decompress_len); + return -1; + } + tinyxml2::XMLDocument doc; + if (doc.Parse(dst, decompress_len - 1) != 0) { + SPDLOG_INFO("tinyxml2 parse error"); + return -2; + } + const char *title = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("title") + ->GetText(); + const char *digest = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("des") + ->GetText(); + + const char *url = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("mmreader") + ->FirstChildElement("category") + ->FirstChildElement("item") + ->FirstChildElement("url") + ->GetText(); + const char *thumb_url = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("thumburl") + ->GetText(); + const char *user_name = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("mmreader") + ->FirstChildElement("publisher") + ->FirstChildElement("username") + ->GetText(); + + const char *nickname = doc.FirstChildElement("msg") + ->FirstChildElement("appmsg") + ->FirstChildElement("mmreader") + ->FirstChildElement("publisher") + ->FirstChildElement("nickname") + ->GetText(); + + std::string s_title(title); + std::string s_digest(digest); + std::string s_url(url); + std::string s_thumburl(thumb_url); + std::string s_user_name(user_name); + std::string s_nickname(nickname); + + std::wstring ws_title = Utils::UTF8ToWstring(s_title); + std::wstring ws_digest = Utils::UTF8ToWstring(s_digest); + std::wstring ws_url = Utils::UTF8ToWstring(s_url); + std::wstring ws_thumb_url = Utils::UTF8ToWstring(s_thumburl); + std::wstring ws_user_name = Utils::UTF8ToWstring(s_user_name); + std::wstring ws_nickname = Utils::UTF8ToWstring(s_nickname); + success = ForwardPublicMsg(wxid, ws_title, ws_url, ws_thumb_url, ws_user_name, + ws_nickname, ws_digest); + return success; +} + +INT64 Manager::DecodeImage(const std::wstring &file_path, const std::wstring &save_dir) { + return Utils::DecodeImage(file_path.c_str(), save_dir.c_str()); +} + +INT64 Manager::GetVoiceByDB(ULONG64 msg_id, const std::wstring& dir) { + INT64 success = -1; + std::string buff = DB::GetInstance().GetVoiceBuffByMsgId(msg_id); + if (buff.size() == 0) { + success = 0; + return success; + } + std::wstring save_path = dir; + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + success = -2; + return success; + } + save_path = save_path + L"\\" + std::to_wstring(msg_id) + L".amr"; + HANDLE file_handle = CreateFileW(save_path.c_str(), GENERIC_ALL, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (file_handle == INVALID_HANDLE_VALUE) { + SPDLOG_ERROR("GetVoiceByDB save path invalid"); + return success; + } + DWORD bytes_write = 0; + std::string decode = base64_decode(buff); + WriteFile(file_handle, (LPCVOID)decode.c_str(), static_cast(decode.size()) , &bytes_write, 0); + CloseHandle(file_handle); + success = 1; + return success; +} + +INT64 Manager::SendCustomEmotion(const std::wstring &file_path, + const std::wstring &wxid) { + INT64 success = -1; + UINT64 get_custom_smiley_mgr_addr = base_addr_ + offset::kGetCustomSmileyMgr; + func::__GetCustomSmileyMgr get_custom_smiley_mgr = + (func::__GetCustomSmileyMgr)get_custom_smiley_mgr_addr; + UINT64 send_custom_emotion_addr = base_addr_ + offset::kSendCustomEmotion; + func::__SendCustomEmotion send_custom_emotion = + (func::__SendCustomEmotion)send_custom_emotion_addr; + prototype::WeChatString *path = BuildWechatString(file_path); + prototype::WeChatString *recv = BuildWechatString(wxid); + INT64 *temp = Utils::WxHeapAlloc(0x20); + memset(temp, 0, 0x20); + UINT64 mgr = get_custom_smiley_mgr(); + success = send_custom_emotion( + mgr, reinterpret_cast(path), reinterpret_cast(temp), + reinterpret_cast(recv), 2, reinterpret_cast(temp), 0, + reinterpret_cast(temp)); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index ed7c46f..2dbf77c 100644 --- a/src/manager.h +++ b/src/manager.h @@ -43,6 +43,19 @@ class Manager { std::wstring GetContactOrChatRoomNickname(const std::wstring& wxid); INT64 GetContactByWxid(const std::wstring& wxid, common::ContactProfileInner& profile); + INT64 DoDownloadTask(UINT64 msg_id); + INT64 ForwardPublicMsg(const std::wstring& wxid, const std::wstring& title, + const std::wstring& url, const std::wstring& thumb_url, + const std::wstring& sender_id, + const std::wstring& sender_name, + const std::wstring& digest); + INT64 ForwardPublicMsgByMsgId(const std::wstring& wxid, UINT64 msg_id); + + INT64 DecodeImage(const std::wstring& file_path, + const std::wstring& save_dir); + INT64 GetVoiceByDB(ULONG64 msg_id, const std::wstring& dir); + INT64 SendCustomEmotion(const std::wstring& file_path, + const std::wstring& wxid); private: UINT64 base_addr_; diff --git a/src/utils.cc b/src/utils.cc index 43cfc40..106adc0 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -319,4 +319,83 @@ std::string Utils::ReadWstringThenConvert(INT64 addr){ std::wstring wstr = ReadWstring(addr); return WstringToUTF8(wstr); } + +INT64 Utils::DecodeImage(const wchar_t* file_path,const wchar_t* save_dir){ + std::wstring save_path(save_dir); + std::wstring orign_file_path(file_path); + if (!Utils::FindOrCreateDirectoryW(save_path.c_str())) { + return 0; + } + + INT64 pos_begin = orign_file_path.find_last_of(L"\\") + 1; + INT64 pos_end = orign_file_path.find_last_of(L"."); + std::wstring file_name = + orign_file_path.substr(pos_begin, pos_end - pos_begin); + HANDLE h_origin_file = + CreateFileW(file_path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + char buffer[BUFSIZE] = {0}; + DWORD bytes_read = 0; + DWORD bytes_write = 0; + unsigned char magic_head[4] = {0}; + std::wstring suffix; + short key = 0; + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + memcpy(magic_head, buffer, 3); + } else { + CloseHandle(h_origin_file); + return 0; + } + if ((magic_head[0] ^ JPEG0) == (magic_head[1] ^ JPEG1)) { + key = magic_head[0] ^ JPEG0; + suffix = L".jpg"; + } else if ((magic_head[0] ^ PNG1) == (magic_head[1] ^ PNG2)) { + key = magic_head[0] ^ PNG1; + suffix = L".png"; + } else if ((magic_head[0] ^ GIF0) == (magic_head[1] ^ GIF1)) { + key = magic_head[0] ^ GIF0; + suffix = L".gif"; + } else if ((magic_head[0] ^ BMP0) == (magic_head[1] ^ BMP1)) { + key = magic_head[0] ^ BMP0; + suffix = L".bmp"; + } else { + key = -1; + suffix = L".dat"; + } + std::wstring save_img_path = save_path + L"\\" + file_name + suffix; + HANDLE save_img = CreateFileW(save_img_path.c_str(), GENERIC_ALL, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (save_img == INVALID_HANDLE_VALUE) { + return 0; + } + if (key > 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i] ^= key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + + do { + if (ReadFile(h_origin_file, buffer, BUFSIZE, &bytes_read, NULL)) { + if (key > 0) { + for (unsigned int i = 0; i < bytes_read; i++) { + buffer[i] ^= key; + } + } + if (!WriteFile(save_img, (LPCVOID)buffer, bytes_read, &bytes_write, 0)) { + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 0; + } + } + } while (bytes_read == BUFSIZE); + CloseHandle(h_origin_file); + CloseHandle(save_img); + return 1; +} + } // namespace wxhelper \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index 7390410..56e6023 100644 --- a/src/utils.h +++ b/src/utils.h @@ -58,6 +58,8 @@ class Utils { static std::wstring ReadWstring(INT64 addr); static std::string ReadWstringThenConvert(INT64 addr); + static INT64 DecodeImage(const wchar_t* file_path,const wchar_t* save_dir); + template static std::vector split(T1 str, T2 letter) { std::vector arr; diff --git a/src/wechat_function.h b/src/wechat_function.h index 4acd43f..ac53f83 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -112,7 +112,7 @@ struct SqlResult { struct InnerMessageStruct { char *buffer; - int length; + INT64 length; ~InnerMessageStruct() { if (this->buffer != NULL) { delete[] this->buffer; @@ -264,6 +264,16 @@ typedef UINT64 (*__AddFavFromImage)(UINT64,UINT64,UINT64); typedef UINT64 (*__GetContact)(UINT64,UINT64,UINT64); typedef UINT64 (*__NewContact)(UINT64); typedef UINT64 (*__FreeContact)(UINT64); +typedef UINT64 (*__NewMMReaderItem)(UINT64); +typedef UINT64 (*__FreeMMReaderItem)(UINT64); +typedef UINT64 (*__ForwordPublicMsg)(UINT64,UINT64,UINT64); +typedef UINT64 (*__NewAppMsgInfo)(UINT64); +typedef UINT64 (*__FreeAppMsgInfo)(UINT64); +typedef UINT64 (*__ParseAppMsgXml)(UINT64,UINT64,UINT64); +typedef UINT64 (*__GetPreDownLoadMgr)(); +typedef UINT64 (*__PushAttachTask)(UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__GetCustomSmileyMgr)(); +typedef UINT64 (*__SendCustomEmotion)(UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64); } // namespace function @@ -384,6 +394,16 @@ const UINT64 kAddFavFromImage = 0x160b920; const UINT64 kGetContact = 0xEA5F90; const UINT64 kNewContact = 0x1212e40; const UINT64 kFreeContact = 0x12134e0; +const UINT64 kNewMMReaderItem = 0x8c79a0; +const UINT64 kFreeMMReaderItem = 0x8c6da0; +const UINT64 kForwordPublicMsg = 0xddc6c0; +const UINT64 kParseAppMsgXml = 0x11b0a70; +const UINT64 kNewAppMsgInfo = 0x91a550; +const UINT64 kFreeAppMsgInfo = 0x8fd1a0; +const UINT64 kGetPreDownLoadMgr = 0x9996f0; +const UINT64 kPushAttachTask = 0x9c0080; +const UINT64 kGetCustomSmileyMgr = 0x915c00; +const UINT64 kSendCustomEmotion = 0xec0a40; } // namespace offset } // namespace V3_9_5_81