From 2ed409e7b0bca6ff3a5af4920396a9dec3841e67 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Fri, 14 Jul 2023 22:25:11 +0800 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=E7=BE=A4=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 95 +++++++++++++++++++++++++++++++++++++ src/http_server_callback.cc | 34 +++++++++++++ src/manager.cc | 58 +++++++++++++++++++++- src/manager.h | 4 ++ src/wechat_function.h | 22 +++++++++ 5 files changed, 212 insertions(+), 1 deletion(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index 72785f1..fa053a0 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -641,4 +641,99 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 响应: ``` javascript {"code":1,"msg":"success","data":null} +``` + +#### 12.删除群成员** +###### 接口功能 +> 删除群成员 + +###### 接口地址 +> [/api/delMemberFromChatRoom](/api/delMemberFromChatRoom) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |true |string| 群id | +|memberIds |true |string| 成员id,用,分隔 | + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + +{ + "chatRoomId":"1234@chatroom", + "memberIds":"wxid_123" +} +``` +响应: +``` javascript +{"code":1,"msg":"success","data":null} +``` + + +#### 13.获取群成员** +###### 接口功能 +> 获取群成员 + +###### 接口地址 +> [/api/getMemberFromChatRoom](/api/getMemberFromChatRoom) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId |true |string| 群id | + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| +|  chatRoomId|string|群id| +|  members|string|成员id| +|  memberNickname|string|成员昵称| +|  admin|string|群管理| +|  adminNickname|string|管理昵称| + +###### 接口示例 + +入参: +``` javascript + +{ + "chatRoomId":"1234@chatroom" +} +``` +响应: +``` javascript +{ + "code": 1, + "data": { + "admin": "wxid_2222", + "adminNickname": "123", + "chatRoomId": "22224@chatroom", + "memberNickname": "^G123^G^G", + "members": "wxid_2222^Gwxid_333" + }, + "msg": "success" +} ``` \ No newline at end of file diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 367355b..0caf487 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -307,6 +307,40 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/delMemberFromChatRoom")) { + std::wstring room_id = GetWStringParam(j_param, "chatRoomId"); + std::vector wxids = GetArrayParam(j_param, "memberIds"); + std::vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++) { + wxid_list.push_back(wxids[i]); + } + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->DelMemberFromChatRoom( + room_id, wxid_list); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/getMemberFromChatRoom")) { + std::wstring room_id = GetWStringParam(j_param, "chatRoomId"); + common::ChatRoomMemberInner member; + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->GetMemberFromChatRoom( + room_id, member); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + if (success >= 0) { + nlohmann::json member_info = { + {"admin", member.admin}, + {"chatRoomId", member.chat_room_id}, + {"members", member.member}, + {"adminNickname", member.admin_nickname}, + {"memberNickname", member.member_nickname}, + }; + ret_data["data"] = member_info; + } + ret = ret_data.dump(); + return ret; } else { nlohmann::json ret_data = { {"code", 200}, {"data", {}}, {"msg", "not support url"}}; diff --git a/src/manager.cc b/src/manager.cc index a7383a3..9e516f9 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -444,4 +444,60 @@ INT64 Manager::ModChatRoomMemberNickName(const std::wstring &room_id, reinterpret_cast(self_id), reinterpret_cast(name)); return success; } -} // namespace wxhelper` \ No newline at end of file + +INT64 Manager::DelMemberFromChatRoom(const std::wstring &room_id, + const std::vector &members) { + INT64 success = -1; + UINT64 get_chat_room_mgr_addr = base_addr_ + offset::kChatRoomMgr; + UINT64 del_members_addr = base_addr_ + offset::kDelMemberFromChatRoom; + func::__GetChatRoomMgr get_chat_room_mgr = + (func::__GetChatRoomMgr)get_chat_room_mgr_addr; + func::__DoDelMemberFromChatRoom del_members = + (func::__DoDelMemberFromChatRoom)del_members_addr; + + prototype::WeChatString *chat_room_id = BuildWechatString(room_id); + std::vector member_list; + UINT64 temp[2] = {0}; + common::VectorInner *list = (common::VectorInner *)&member_list; + INT64 members_ptr = (INT64)&list->start; + for (int i = 0; i < members.size(); i++) { + prototype::WeChatString member(members[i]); + member_list.push_back(member); + } + UINT64 mgr = get_chat_room_mgr(); + success = + del_members(mgr, members_ptr, reinterpret_cast(chat_room_id)); + return success; +} + +INT64 Manager::GetMemberFromChatRoom(const std::wstring &room_id, + common::ChatRoomMemberInner &member) { + INT64 success = -1; + UINT64 get_chat_room_mgr_addr = base_addr_ + offset::kChatRoomMgr; + UINT64 get_members_addr = base_addr_ + offset::kGetMemberFromChatRoom; + UINT64 new_chat_room_addr = base_addr_ + offset::kNewChatRoom; + UINT64 free_chat_room_addr = base_addr_ + offset::kFreeChatRoom; + func::__GetChatRoomMgr get_chat_room_mgr = + (func::__GetChatRoomMgr)get_chat_room_mgr_addr; + func::__GetMemberFromChatRoom get_members = + (func::__GetMemberFromChatRoom)get_members_addr; + func::__NewChatRoom new_chat_room = (func::__NewChatRoom)new_chat_room_addr; + func::__FreeChatRoom free_chat_room = + (func::__FreeChatRoom)free_chat_room_addr; + + prototype::WeChatString chat_room_id(room_id); + char chat_room_info[0x2E0] = {0}; + UINT64 addr = reinterpret_cast(&chat_room_info); + new_chat_room(addr); + + UINT64 mgr = get_chat_room_mgr(); + success = get_members(mgr, reinterpret_cast(&chat_room_id), addr); + member.chat_room_id = Utils::ReadWstringThenConvert(addr + 0x10); + member.admin = Utils::ReadWstringThenConvert(addr + 0x78); + member.member_nickname = Utils::ReadWstringThenConvert(addr + 0x50); + member.admin_nickname = Utils::ReadWstringThenConvert(addr + 0xA0); + member.member = Utils::ReadWeChatStr(addr + 0x30); + free_chat_room(addr); + return success; +} +} // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index 24ff368..e7aeadd 100644 --- a/src/manager.h +++ b/src/manager.h @@ -21,6 +21,10 @@ class Manager { INT64 ModChatRoomMemberNickName(const std::wstring& room_id, const std::wstring& wxid, const std::wstring& nickname); + INT64 DelMemberFromChatRoom(const std::wstring& room_id, + const std::vector& members); + INT64 GetMemberFromChatRoom(const std::wstring& room_id, + common::ChatRoomMemberInner& member); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 4a70d68..9d96751 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -184,6 +184,20 @@ struct VectorInner { INT64 end; }; +struct ChatRoomMemberInner { + std::string chat_room_id; + std::string admin; + std::string admin_nickname; + std::string member_nickname; + std::string member; + ChatRoomMemberInner() + : chat_room_id(""), + admin(""), + admin_nickname(""), + member_nickname(""), + member("") {} +}; + } // namespace common namespace V3_9_5_81 { namespace function { @@ -214,6 +228,10 @@ typedef UINT64 (*__GetChatRoomDetailInfo)(UINT64,UINT64,UINT64,UINT64); typedef UINT64 (*__DoAddMemberToChatRoom)(UINT64,UINT64,UINT64,UINT64); typedef UINT64 (*__DoModChatRoomMemberNickName)(UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__DoDelMemberFromChatRoom)(UINT64,UINT64,UINT64); +typedef UINT64 (*__GetMemberFromChatRoom)(UINT64,UINT64,UINT64); +typedef UINT64 (*__NewChatRoom)(UINT64); +typedef UINT64 (*__FreeChatRoom)(UINT64); } // namespace function @@ -307,6 +325,10 @@ const UINT64 kNewChatRoomInfo = 0x12006b0; const UINT64 kFreeChatRoomInfo = 0x1200890; const UINT64 kDoAddMemberToChatRoom = 0xe63c70; const UINT64 kDoModChatRoomMemberNickName = 0xe6db00; +const UINT64 kDelMemberFromChatRoom = 0xe64290; +const UINT64 kGetMemberFromChatRoom = 0xe74de0; +const UINT64 kNewChatRoom = 0x11fde50; +const UINT64 kFreeChatRoom = 0x11fe030; } // namespace offset } // namespace V3_9_5_81 From bf52de1560142b8fa500a4e9c878e5cccfa8a4d0 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Fri, 14 Jul 2023 22:25:55 +0800 Subject: [PATCH 02/16] =?UTF-8?q?fix:=20hook=20http=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hooks.cc b/src/hooks.cc index 84d56cc..06b2305 100644 --- a/src/hooks.cc +++ b/src/hooks.cc @@ -101,7 +101,7 @@ VOID CALLBACK SendHttpMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, return; } std::string jstr = j_msg.dump() + "\n"; - // HttpClient::GetInstance().SendRequest(jstr); + HttpClient::GetInstance().SendRequest(jstr); } void HandleSyncMsg(INT64 param1, INT64 param2, INT64 param3) { From b996a9d91ce6a15b51cec21eadda2a0793f16a5c Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Sat, 22 Jul 2023 09:13:50 +0800 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=E7=BE=A4=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=BD=AE=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 90 +++++++++++++++++++++++++++++++++++++ src/db.cc | 1 + src/http_server_callback.cc | 17 +++++++ src/manager.cc | 30 +++++++++++++ src/manager.h | 2 + src/wechat_function.h | 6 +++ 6 files changed, 146 insertions(+) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index fa053a0..5c6899a 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -736,4 +736,94 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 }, "msg": "success" } +``` + +#### 14.置顶群消息** +###### 接口功能 +> 置顶群消息,需要群主权限 + +###### 接口地址 +> [/api/topMsg](/api/topMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId |true |string| 消息id | + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +{ + "msgId":8005736725060623215 +} +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + + +#### 15.移除置顶群消息** +###### 接口功能 +> 移除置顶群消息,需要群主权限 + +###### 接口地址 +> [/api/removeTopMsg](/api/removeTopMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId |true |string| 消息id | +|chatRoomId |true |string| 群id | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +{ + "msgId":8005736725060623215, + "chatRoomId":"12345678@chatroom" +} +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} ``` \ No newline at end of file diff --git a/src/db.cc b/src/db.cc index bee18c0..b667d6c 100644 --- a/src/db.cc +++ b/src/db.cc @@ -344,6 +344,7 @@ std::vector DB::GetDbHandles() { msg0_db.db_name = (wchar_t *)(*(UINT64 *)(db_addr)); msg0_db.db_name_len = *(DWORD *)(db_addr + 0x8); msg0_db.handle = msg0_db_addr; + msg0_db.extrainfo = *(UINT64 *)(*(UINT64 *)(db_addr + 0x28) + 0x1E8); ExecuteSQL(msg0_db_addr, "select * from sqlite_master where type=\"table\";", (UINT64)GetDbInfo, &msg0_db); diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 0caf487..9b4dd4b 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -341,6 +341,23 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { } ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/topMsg")) { + INT64 msg_id = GetINT64Param(j_param, "msgId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->SetTopMsg(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/removeTopMsg")) { + std::wstring room_id = GetWStringParam(j_param, "chatRoomId"); + INT64 msg_id = GetINT64Param(j_param, "msgId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->RemoveTopMsg(room_id,msg_id); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index 9e516f9..19a285d 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -3,6 +3,7 @@ #include "export.h" #include "wechat_function.h" +#include "db.h" namespace offset = wxhelper::V3_9_5_81::offset; namespace prototype = wxhelper::V3_9_5_81::prototype; @@ -500,4 +501,33 @@ INT64 Manager::GetMemberFromChatRoom(const std::wstring &room_id, free_chat_room(addr); return success; } +INT64 Manager::SetTopMsg(ULONG64 msg_id) { + INT64 success = -1; + UINT64 top_addr = base_addr_ + offset::kTopMsg; + func::__DoTopMsg top_msg = (func::__DoTopMsg)top_addr; + INT64 index = 0; + INT64 local_id = DB::GetInstance().GetLocalIdByMsgId(msg_id, index); + if (local_id <= 0 || index >> 32 == 0) { + success = -2; + return success; + } + LARGE_INTEGER l; + l.HighPart = index >> 32; + l.LowPart = (DWORD)local_id; + UINT64 ptr = reinterpret_cast(&l); + success = top_msg(ptr, 1); + + return success; +} + +INT64 Manager::RemoveTopMsg(const std::wstring &room_id, ULONG64 msg_id) { + INT64 success = -1; + UINT64 remove_addr = base_addr_ + offset::kRemoveTopMsg; + func::__RemoveTopMsg remove_top_msg = (func::__RemoveTopMsg)remove_addr; + prototype::WeChatString *chat_room_id = BuildWechatString(room_id); + const wchar_t *w_room = room_id.c_str(); + success = remove_top_msg(reinterpret_cast(w_room), msg_id, + reinterpret_cast(chat_room_id)); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index e7aeadd..2dba73d 100644 --- a/src/manager.h +++ b/src/manager.h @@ -25,6 +25,8 @@ class Manager { const std::vector& members); INT64 GetMemberFromChatRoom(const std::wstring& room_id, common::ChatRoomMemberInner& member); + INT64 SetTopMsg(ULONG64 msg_id); + INT64 RemoveTopMsg(const std::wstring& room_id,ULONG64 msg_id); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 9d96751..26ee6b3 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -233,6 +233,8 @@ typedef UINT64 (*__GetMemberFromChatRoom)(UINT64,UINT64,UINT64); typedef UINT64 (*__NewChatRoom)(UINT64); typedef UINT64 (*__FreeChatRoom)(UINT64); +typedef UINT64 (*__DoTopMsg)(UINT64,UINT64); +typedef UINT64 (*__RemoveTopMsg)(UINT64,UINT64,UINT64); } // namespace function namespace prototype { @@ -329,6 +331,10 @@ const UINT64 kDelMemberFromChatRoom = 0xe64290; const UINT64 kGetMemberFromChatRoom = 0xe74de0; const UINT64 kNewChatRoom = 0x11fde50; const UINT64 kFreeChatRoom = 0x11fe030; + +const UINT64 kTopMsg = 0xa5e4f0; +const UINT64 kRemoveTopMsg = 0xe787b0; + } // namespace offset } // namespace V3_9_5_81 From 7e06eaf4e4c9e2c384d0793ed10179ea5a0c92bc Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Sat, 22 Jul 2023 09:17:20 +0800 Subject: [PATCH 04/16] =?UTF-8?q?fix:=20=E6=B6=88=E9=99=A4=E8=AD=A6?= =?UTF-8?q?=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/db.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/db.cc b/src/db.cc index b667d6c..846e21f 100644 --- a/src/db.cc +++ b/src/db.cc @@ -459,7 +459,7 @@ std::vector DB::GetChatMsgByMsgId(ULONG64 msgid) { wchar_t dbname[20] = {0}; for (int i = 0;; i++) { swprintf_s(dbname, L"MSG%d.db", i); - DWORD handle = GetDbHandleByDbName(dbname); + UINT64 handle = GetDbHandleByDbName(dbname); if (handle == 0) { // LOG(INFO) << "MSG db handle is null"; return {}; From 17aff3b6aa16fb3642fd949dd91b506455eb6f88 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Thu, 27 Jul 2023 21:18:42 +0800 Subject: [PATCH 05/16] =?UTF-8?q?feat:=20=E9=82=80=E8=AF=B7=E5=85=A5?= =?UTF-8?q?=E7=BE=A4=EF=BC=8Chook=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 129 +++++++++++++++++++++++++++++++++++- source/injector.cc | 14 +++- src/hooks.cc | 69 +++++++++++++++++++ src/hooks.h | 4 ++ src/http_server_callback.cc | 22 ++++++ src/manager.cc | 22 ++++++ src/manager.h | 2 + src/wechat_function.h | 3 + 8 files changed, 263 insertions(+), 2 deletions(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index 5c6899a..8958bfc 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -815,7 +815,7 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 { - "msgId":8005736725060623215, + "msgId":8005736725060623215, "chatRoomId":"12345678@chatroom" } ``` @@ -826,4 +826,131 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "data": null, "msg": "success" } +``` + +#### 16.邀请入群** +###### 接口功能 +> 邀请入群,(40人以上的群需要使用邀请入群) + +###### 接口地址 +> [/api/InviteMemberToChatRoom](/api/InviteMemberToChatRoom) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|memberIds |true |string| wxid,用,分隔 | +|chatRoomId |true |string| 群id | + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +{ + "memberIds":"wxid_123,wxid_12341", + "chatRoomId":"12345678@chatroom" +} +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + + +#### 16.hook日志** +###### 接口功能 +> hook微信日志,输出在wechat安装目录的logs目录下 + +###### 接口地址 +> [/api/hookLog](/api/hookLog) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + + +``` +响应: +``` javascript +{ + "code": 0, + "data": null, + "msg": "success" +} +``` + +#### 18.取消hook日志** +###### 接口功能 +> 取消hook日志 + +###### 接口地址 +> [/api/unhookLog](/api/unhookLog) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +``` +响应: +``` javascript +{ + "code": 0, + "data": null, + "msg": "success" +} ``` \ No newline at end of file diff --git a/source/injector.cc b/source/injector.cc index c009272..8dcd67c 100644 --- a/source/injector.cc +++ b/source/injector.cc @@ -933,7 +933,10 @@ int InjectDll(wchar_t* szPName, wchar_t* szDllPath) result = 1; } else - { + { + DWORD dErrorCode = GetLastError(); + printf("dll inject fail"); + printf("error code : %d ", dErrorCode); VirtualFreeEx(hProcess, lpRemoteDllBase, ulDllLength, MEM_DECOMMIT | MEM_RELEASE); CloseHandle(hProcess); result = 0; @@ -941,6 +944,9 @@ int InjectDll(wchar_t* szPName, wchar_t* szDllPath) } else { + DWORD dErrorCode = GetLastError(); + printf("dll inject fail.VirtualAllocEx method fail."); + printf("error code : %d ", dErrorCode); CloseHandle(hProcess); result = 0; } @@ -986,6 +992,9 @@ int InjectDllByPid(unsigned int pid, wchar_t* szDllPath) } else { + DWORD dErrorCode = GetLastError(); + printf("dll inject fail"); + printf("error code : %d ", dErrorCode); VirtualFreeEx(hProcess, lpRemoteDllBase, ulDllLength, MEM_DECOMMIT | MEM_RELEASE); CloseHandle(hProcess); result = 0; @@ -993,6 +1002,9 @@ int InjectDllByPid(unsigned int pid, wchar_t* szDllPath) } else { + DWORD dErrorCode = GetLastError(); + printf("dll inject fail.VirtualAllocEx method fail."); + printf("error code : %d ", dErrorCode); CloseHandle(hProcess); result = 0; } diff --git a/src/hooks.cc b/src/hooks.cc index 06b2305..4387a42 100644 --- a/src/hooks.cc +++ b/src/hooks.cc @@ -16,12 +16,19 @@ static int kServerPort = 19099; static bool kMsgHookFlag = false; static char kServerIp[16] = "127.0.0.1"; static bool kEnableHttp = false; +static bool kLogHookFlag = false; static UINT64 (*R_DoAddMsg)(UINT64, UINT64, UINT64) = (UINT64(*)( UINT64, UINT64, UINT64))(Utils::GetWeChatWinBase() + offset::kDoAddMsg); +static UINT64 (*R_Log)(UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, + UINT64, UINT64, UINT64, UINT64, UINT64) = + (UINT64(*)(UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, + UINT64, UINT64, UINT64, + UINT64))(Utils::GetWeChatWinBase() + offset::kHookLog); + VOID CALLBACK SendMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK Work) { common::InnerMessageStruct *msg = (common::InnerMessageStruct *)context; @@ -140,6 +147,26 @@ void HandleSyncMsg(INT64 param1, INT64 param2, INT64 param3) { R_DoAddMsg(param1,param2,param3); } +UINT64 HandlePrintLog(UINT64 param1, UINT64 param2, UINT64 param3, UINT64 param4, + UINT64 param5, UINT64 param6, UINT64 param7, UINT64 param8, + UINT64 param9, UINT64 param10, UINT64 param11, + UINT64 param12) { + UINT64 p = R_Log(param1, param2, param3, param4, param5, param6, param7, param8, param9, + param10, param11, param12); +if(p== 0 || p == 1){ + return p; +} + char *msg = (char *)p; + if (msg != NULL) { + // INT64 size = *(INT64 *)(p - 0x8); + std::string str(msg); + std::wstring ws = Utils::UTF8ToWstring(str); + std::string out = Utils::WstringToAnsi(ws, CP_ACP); + spdlog::info("wechat log:{}", out); + } + return p; +} + int HookSyncMsg(std::string client_ip, int port, std::string url, uint64_t timeout, bool enable) { if (kMsgHookFlag) { @@ -196,5 +223,47 @@ int UnHookSyncMsg() { return ret; } + int HookLog() { + if (kLogHookFlag) { + SPDLOG_INFO("log hook already called"); + return 2; + } + + UINT64 base = Utils::GetWeChatWinBase(); + if (!base) { + SPDLOG_INFO("base addr is null"); + return -1; + } + + DetourRestoreAfterWith(); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + UINT64 do_add_msg_addr = base + offset::kHookLog; + DetourAttach(&(PVOID &)R_Log, &HandlePrintLog); + LONG ret = DetourTransactionCommit(); + if (ret == NO_ERROR) { + kMsgHookFlag = true; + } + return ret; + } + + int UnHookLog() { + if (!kLogHookFlag) { + kLogHookFlag = false; + SPDLOG_INFO("hook log reset"); + return NO_ERROR; + } + UINT64 base = Utils::GetWeChatWinBase(); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + UINT64 do_add_msg_addr = base + offset::kHookLog; + DetourDetach(&(PVOID &)R_Log, &HandlePrintLog); + LONG ret = DetourTransactionCommit(); + if (ret == NO_ERROR) { + kLogHookFlag = false; + } + return ret; + } + } // namespace hooks } // namespace wxhelper \ No newline at end of file diff --git a/src/hooks.h b/src/hooks.h index d952f12..f99444f 100644 --- a/src/hooks.h +++ b/src/hooks.h @@ -10,6 +10,10 @@ int HookSyncMsg(std::string client_ip, int port, std::string url, uint64_t timeo int UnHookSyncMsg(); +int HookLog(); + +int UnHookLog(); + } // namespace hooks } // namespace wxhelper #endif \ No newline at end of file diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 9b4dd4b..f4f1b7e 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -358,6 +358,28 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/InviteMemberToChatRoom")) { + std::wstring room_id = GetWStringParam(j_param, "chatRoomId"); + std::vector wxids = GetArrayParam(j_param, "memberIds"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->InviteMemberToChatRoom( + room_id, wxids); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/hookLog")) { + int success = wxhelper::hooks::HookLog(); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/unhookLog")) { + int success = wxhelper::hooks::UnHookLog(); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index 19a285d..5fa99ec 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -530,4 +530,26 @@ INT64 Manager::RemoveTopMsg(const std::wstring &room_id, ULONG64 msg_id) { reinterpret_cast(chat_room_id)); return success; } + +INT64 Manager::InviteMemberToChatRoom(const std::wstring &room_id, + const std::vector &wxids) { + INT64 success = -1; + UINT64 invite_addr = base_addr_ + offset::kInviteMember; + func::__InviteMemberToChatRoom invite = + (func::__InviteMemberToChatRoom)invite_addr; + const wchar_t *w_room = room_id.c_str(); + prototype::WeChatString *chat_room_id = BuildWechatString(room_id); + std::vector wxid_list; + common::VectorInner *list = (common::VectorInner *)&wxid_list; + INT64 head = (INT64)&list->start; + for (int i = 0; i < wxids.size(); i++) { + prototype::WeChatString id(wxids[i]); + wxid_list.push_back(id); + } + UINT64 temp[2] = {0}; + success = invite(reinterpret_cast(w_room), head, + reinterpret_cast(chat_room_id), + reinterpret_cast(&temp)); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index 2dba73d..1a5edb9 100644 --- a/src/manager.h +++ b/src/manager.h @@ -27,6 +27,8 @@ class Manager { common::ChatRoomMemberInner& member); INT64 SetTopMsg(ULONG64 msg_id); INT64 RemoveTopMsg(const std::wstring& room_id,ULONG64 msg_id); + INT64 InviteMemberToChatRoom(const std::wstring& room_id, + const std::vector& wxids); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 26ee6b3..028a80b 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -235,6 +235,7 @@ typedef UINT64 (*__FreeChatRoom)(UINT64); typedef UINT64 (*__DoTopMsg)(UINT64,UINT64); typedef UINT64 (*__RemoveTopMsg)(UINT64,UINT64,UINT64); +typedef UINT64 (*__InviteMemberToChatRoom)(UINT64,UINT64,UINT64,UINT64); } // namespace function namespace prototype { @@ -334,6 +335,8 @@ const UINT64 kFreeChatRoom = 0x11fe030; const UINT64 kTopMsg = 0xa5e4f0; const UINT64 kRemoveTopMsg = 0xe787b0; +const UINT64 kInviteMember = 0xe63650; +const UINT64 kHookLog = 0x1304e60; } // namespace offset } // namespace V3_9_5_81 From 1268d37a5773f5e615b9b7197b8f1466da90a9d5 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Sat, 29 Jul 2023 10:03:59 +0800 Subject: [PATCH 06/16] =?UTF-8?q?feat:=20=E5=BB=BA=E7=BE=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 42 ++++++++++++++++++++++++++++++++++++- src/http_server_callback.cc | 8 +++++++ src/manager.cc | 21 +++++++++++++++++++ src/manager.h | 1 + src/wechat_function.h | 5 +++++ 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index 8958bfc..8f3b8c6 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -874,7 +874,7 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 ``` -#### 16.hook日志** +#### 17.hook日志** ###### 接口功能 > hook微信日志,输出在wechat安装目录的logs目录下 @@ -953,4 +953,44 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "data": null, "msg": "success" } +``` + +#### 19.建群** +###### 接口功能 +> 建群(不建议使用,容易被封,测试期间被封了,无法保证效果) + +###### 接口地址 +> [/api/createChatRoom](/api/createChatRoom) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|memberIds|string|群成员id,以,分割| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} ``` \ No newline at end of file diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index f4f1b7e..7694606 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -380,6 +380,14 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/createChatRoom")) { + std::vector wxids = GetArrayParam(j_param, "memberIds"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->CreateChatRoom(wxids); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index 5fa99ec..9bf4e53 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -552,4 +552,25 @@ INT64 Manager::InviteMemberToChatRoom(const std::wstring &room_id, reinterpret_cast(&temp)); return success; } + +INT64 Manager::CreateChatRoom(const std::vector& wxids){ + INT64 success = -1; + UINT64 get_chat_room_mgr_addr = base_addr_ + offset::kChatRoomMgr; + UINT64 create_chat_room_addr = base_addr_ + offset::kCreateChatRoom; + func::__GetChatRoomMgr get_chat_room_mgr = + (func::__GetChatRoomMgr)get_chat_room_mgr_addr; + func::__CreateChatRoom create_chat_room = + (func::__CreateChatRoom)create_chat_room_addr; + std::vector wxid_list; + common::VectorInner *list = (common::VectorInner *)&wxid_list; + INT64 head = (INT64)&list->start; + for (int i = 0; i < wxids.size(); i++) { + prototype::WeChatString id(wxids[i]); + wxid_list.push_back(id); + } + INT64 end = list->end; + UINT64 mgr = get_chat_room_mgr(); + success = create_chat_room(mgr, head, end); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index 1a5edb9..f1edf3c 100644 --- a/src/manager.h +++ b/src/manager.h @@ -29,6 +29,7 @@ class Manager { INT64 RemoveTopMsg(const std::wstring& room_id,ULONG64 msg_id); INT64 InviteMemberToChatRoom(const std::wstring& room_id, const std::vector& wxids); + INT64 CreateChatRoom(const std::vector& wxids); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 028a80b..9656d40 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -237,6 +237,9 @@ typedef UINT64 (*__DoTopMsg)(UINT64,UINT64); typedef UINT64 (*__RemoveTopMsg)(UINT64,UINT64,UINT64); typedef UINT64 (*__InviteMemberToChatRoom)(UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__CreateChatRoom)(UINT64,UINT64,UINT64); + + } // namespace function namespace prototype { @@ -338,6 +341,8 @@ const UINT64 kRemoveTopMsg = 0xe787b0; const UINT64 kInviteMember = 0xe63650; const UINT64 kHookLog = 0x1304e60; +const UINT64 kCreateChatRoom = 0xe63340; + } // namespace offset } // namespace V3_9_5_81 From ae4443466289cf9ebabee4084c9370eb30374a6a Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Sat, 29 Jul 2023 17:11:36 +0800 Subject: [PATCH 07/16] =?UTF-8?q?fix=EF=BC=9A=E5=88=A0=E9=99=A4=E6=B5=8B?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 2 +- python/3.9.5.81/http_client.py | 280 +++++++++++++++++++++++++++++++++ src/http_server_callback.cc | 22 ++- 3 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 python/3.9.5.81/http_client.py diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index 8f3b8c6..b0fcce6 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -215,7 +215,7 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 入参: ``` javascript { - "port": "19099" + "port": "19099", "ip":"127.0.0.1", "url":"http://localhost:8080", "timeout":"3000", diff --git a/python/3.9.5.81/http_client.py b/python/3.9.5.81/http_client.py new file mode 100644 index 0000000..df84819 --- /dev/null +++ b/python/3.9.5.81/http_client.py @@ -0,0 +1,280 @@ +import requests +import json + + +def checkLogin(): + url = "127.0.0.1:19088/api/checkLogin" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def userInfo(): + url = "127.0.0.1:19088/api/userInfo" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def sendTextMsg(): + url = "127.0.0.1:19088/api/sendTextMsg" + payload = json.dumps({ + "wxid": "filehelper", + "msg": "12www" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def sendImagesMsg(): + url = "127.0.0.1:19088/api/sendImagesMsg" + print("modify imagePath") + raise RuntimeError("modify imagePath then deleted me") + payload = json.dumps({ + "wxid": "filehelper", + "imagePath": "C:\\pic.png" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +def sendFileMsg(): + url = "127.0.0.1:19088/api/sendFileMsg" + print("modify filePath") + raise RuntimeError("modify filePath then deleted me") + payload = json.dumps({ + "wxid": "filehelper", + "filePath": "C:\\test.zip" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def hookSyncMsg(): + url = "127.0.0.1:19088/api/hookSyncMsg" + print("modify ip port url ") + raise RuntimeError("modify ip port url then deleted me") + payload = json.dumps({ + "port": "19099", + "ip": "127.0.0.1", + "url": "http://localhost:8080", + "timeout": "3000", + "enableHttp": "0" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def unhookSyncMsg(): + url = "127.0.0.1:19088/api/unhookSyncMsg" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def getContactList(): + url = "127.0.0.1:19088/api/getContactList" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def getDBInfo(): + url = "127.0.0.1:19088/api/getDBInfo" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def execSql(): + url = "127.0.0.1:19088/api/execSql" + print("modify dbHandle ") + raise RuntimeError("modify dbHandle then deleted me") + payload = json.dumps({ + "dbHandle": 1713425147584, + "sql": "select * from MSG where localId =100;" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def getChatRoomDetailInfo(): + url = "127.0.0.1:19088/api/getChatRoomDetailInfo" + print("modify chatRoomId ") + raise RuntimeError("modify chatRoomId then deleted me") + payload = json.dumps({ + "chatRoomId": "123333@chatroom" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def addMemberToChatRoom(): + url = "127.0.0.1:19088/api/addMemberToChatRoom" + print("modify chatRoomId memberIds ") + raise RuntimeError("modify chatRoomId memberIds then deleted me") + payload = json.dumps({ + "chatRoomId": "123@chatroom", + "memberIds": "wxid_123" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + + +def delMemberFromChatRoom(): + url = "127.0.0.1:19088/api/delMemberFromChatRoom" + print("modify chatRoomId memberIds ") + raise RuntimeError("modify chatRoomId memberIds then deleted me") + payload = json.dumps({ + "chatRoomId": "21363231004@chatroom", + "memberIds": "wxid_123" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def modifyNickname(): + url = "127.0.0.1:19088/api/modifyNickname" + print("modify chatRoomId wxid nickName") + raise RuntimeError("modify chatRoomId wxid nickName then deleted me") + payload = json.dumps({ + "chatRoomId": "123@chatroom", + "wxid": "wxid_123", + "nickName": "test" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def getMemberFromChatRoom(): + print("modify chatRoomId ") + raise RuntimeError("modify chatRoomId then deleted me") + url = "127.0.0.1:19088/api/getMemberFromChatRoom" + payload = json.dumps({ + "chatRoomId": "123@chatroom" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def topMsg(): + print("modify msgId ") + raise RuntimeError("modify msgId then deleted me") + url = "127.0.0.1:19088/api/topMsg" + payload = json.dumps({ + "msgId": 1222222 + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def removeTopMsg(): + print("modify msgId chatRoomId ") + raise RuntimeError("modify msgId chatRoomId then deleted me") + + url = "127.0.0.1:19088/api/removeTopMsg" + + payload = json.dumps({ + "chatRoomId": "123@chatroom", + "msgId": 123 + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def InviteMemberToChatRoom(): + print("modify memberIds chatRoomId ") + raise RuntimeError("modify memberIds chatRoomId then deleted me") + + url = "127.0.0.1:19088/api/InviteMemberToChatRoom" + + payload = json.dumps({ + "chatRoomId": "123@chatroom", + "memberIds": "wxid_123" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def hookLog(): + url = "127.0.0.1:19088/api/hookLog" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def unhookLog(): + url = "127.0.0.1:19088/api/unhookLog" + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def createChatRoom(): + print("modify memberIds ") + raise RuntimeError("modify memberIds then deleted me") + url = "127.0.0.1:19088/api/createChatRoom" + + payload = json.dumps({ + "memberIds": "wxid_8yn4k908tdqp22,wxid_oyb662qhop4422" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +if __name__ == '__main__': + checkLogin() + # userInfo() diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 7694606..4bafe5a 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -7,9 +7,18 @@ #include "db.h" #define STR2LL(str) (wxhelper::Utils::IsDigit(str) ? stoll(str) : 0) +#define STR2I(str) (wxhelper::Utils::IsDigit(str) ? stoi(str) : 0) namespace common = wxhelper::common; - +int GetIntParam(nlohmann::json data, std::string key) { + int result; + try { + result = data[key].get(); + } catch (nlohmann::json::exception) { + result = STR2I(data[key].get()); + } + return result; +} INT64 GetINT64Param(nlohmann::json data, std::string key) { INT64 result; @@ -164,8 +173,17 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { ret = ret_data.dump(); return ret; } else if (mg_http_match_uri(hm, "/api/hookSyncMsg")) { + int port = GetIntParam(j_param, "port"); + std::string ip = GetStringParam(j_param, "ip"); + int enable = GetIntParam(j_param, "enableHttp"); + std::string url = ""; + int timeout = 0; + if (enable) { + url = GetStringParam(j_param, "url"); + timeout = GetIntParam(j_param, "timeout"); + } INT64 success = - wxhelper::hooks::HookSyncMsg("127.0.0.1", 19099, "", 3000, false); + wxhelper::hooks::HookSyncMsg(ip, port, url, timeout, false); nlohmann::json ret_data = { {"code", success}, {"data", {}}, {"msg", "success"}}; ret = ret_data.dump(); From 64ba6007a8a93ce6c67d4bbe4831c4aa828cdf05 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Mon, 31 Jul 2023 16:11:47 +0800 Subject: [PATCH 08/16] =?UTF-8?q?feat:=E9=80=80=E7=BE=A4=E5=92=8C=E8=BD=AC?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global_context.cc | 4 +++- src/global_context.h | 7 ------- src/http_server_callback.cc | 19 ++++++++++++++++++- src/manager.cc | 30 ++++++++++++++++++++++++++++++ src/manager.h | 2 ++ src/wechat_function.h | 4 ++++ 6 files changed, 57 insertions(+), 9 deletions(-) diff --git a/src/global_context.cc b/src/global_context.cc index e4524c8..e2fc474 100644 --- a/src/global_context.cc +++ b/src/global_context.cc @@ -17,7 +17,9 @@ GlobalContext::~GlobalContext() { void GlobalContext::initialize(HMODULE module) { state =GlobalContextState::INITIALIZING; module_ = module; - // Utils::Hide(module); + #ifndef _DEBUG + Utils::Hide(module); + #endif UINT64 base = Utils::GetWeChatWinBase(); config.emplace(); config->Initialize(); diff --git a/src/global_context.h b/src/global_context.h index 8e7e332..0e49dfc 100644 --- a/src/global_context.h +++ b/src/global_context.h @@ -24,13 +24,6 @@ class GlobalContext : public Singleton { std::unique_ptr http_server; std::unique_ptr mgr; - // std::optional contact_mgr; - // std::optional misc_mgr; - // std::optional send_mgr; - // std::optional account_mgr; - // std::optional chat_room_mgr; - // std::optional sns_mgr; - GlobalContextState state = GlobalContextState::NOT_INITIALIZED; private: diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index 4bafe5a..a495747 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -183,7 +183,7 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { timeout = GetIntParam(j_param, "timeout"); } INT64 success = - wxhelper::hooks::HookSyncMsg(ip, port, url, timeout, false); + wxhelper::hooks::HookSyncMsg(ip, port, url, timeout, enable); nlohmann::json ret_data = { {"code", success}, {"data", {}}, {"msg", "success"}}; ret = ret_data.dump(); @@ -406,6 +406,23 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/quitChatRoom")) { + std::wstring room_id = GetWStringParam(j_param, "chatRoomId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->QuitChatRoom(room_id); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/forwardMsg")) { + INT64 msg_id = GetINT64Param(j_param, "msgId"); + std::wstring wxid = GetWStringParam(j_param, "wxid"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->ForwardMsg(msg_id,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"}}; diff --git a/src/manager.cc b/src/manager.cc index 9bf4e53..0bde19b 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -573,4 +573,34 @@ INT64 Manager::CreateChatRoom(const std::vector& wxids){ success = create_chat_room(mgr, head, end); return success; } +INT64 Manager::QuitChatRoom(const std::wstring &room_id) { + INT64 success = -1; + UINT64 get_chat_room_mgr_addr = base_addr_ + offset::kChatRoomMgr; + UINT64 quit_chat_room_addr = base_addr_ + offset::kQuitChatRoom; + func::__GetChatRoomMgr get_chat_room_mgr = + (func::__GetChatRoomMgr)get_chat_room_mgr_addr; + func::__QuitChatRoom quit_chat_room = + (func::__QuitChatRoom)quit_chat_room_addr; + UINT64 mgr = get_chat_room_mgr(); + prototype::WeChatString chat_room_id(room_id); + success = quit_chat_room(mgr, reinterpret_cast(&chat_room_id), 0); + return success; +} +INT64 Manager::ForwardMsg(UINT64 msg_id, const std::wstring &wxid) { + INT64 success = -1; + UINT64 forward_addr = base_addr_ + offset::kForwardMsg; + func::__ForwardMsg forward_msg = (func::__ForwardMsg)forward_addr; + INT64 index = 0; + INT64 local_id = DB::GetInstance().GetLocalIdByMsgId(msg_id, index); + if (local_id <= 0 || index >> 32 == 0) { + success = -2; + return success; + } + LARGE_INTEGER l; + l.HighPart = index >> 32; + l.LowPart = (DWORD)local_id; + prototype::WeChatString *recv = BuildWechatString(wxid); + success = forward_msg(reinterpret_cast(recv), l.QuadPart, 0x4, 0x0); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index f1edf3c..3d0fff8 100644 --- a/src/manager.h +++ b/src/manager.h @@ -30,6 +30,8 @@ class Manager { INT64 InviteMemberToChatRoom(const std::wstring& room_id, const std::vector& wxids); INT64 CreateChatRoom(const std::vector& wxids); + INT64 QuitChatRoom(const std::wstring& room_id); + INT64 ForwardMsg(UINT64 msg_id, const std::wstring& wxid); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 9656d40..47bf639 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -238,6 +238,8 @@ typedef UINT64 (*__RemoveTopMsg)(UINT64,UINT64,UINT64); typedef UINT64 (*__InviteMemberToChatRoom)(UINT64,UINT64,UINT64,UINT64); typedef UINT64 (*__CreateChatRoom)(UINT64,UINT64,UINT64); +typedef UINT64 (*__QuitChatRoom)(UINT64,UINT64,UINT64); +typedef UINT64 (*__ForwardMsg)(UINT64,UINT64,UINT64,UINT64); } // namespace function @@ -342,6 +344,8 @@ const UINT64 kInviteMember = 0xe63650; const UINT64 kHookLog = 0x1304e60; const UINT64 kCreateChatRoom = 0xe63340; +const UINT64 kQuitChatRoom = 0xe6e3b0; +const UINT64 kForwardMsg = 0xfcd0f0; } // namespace offset } // namespace V3_9_5_81 From 9b4d326d631952918cc5ef17689e7516815620f1 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Mon, 31 Jul 2023 16:18:05 +0800 Subject: [PATCH 09/16] =?UTF-8?q?doc:=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 86 ++++++++++++++++++++++++++++++++++ python/3.9.5.81/http_client.py | 30 ++++++++++++ 2 files changed, 116 insertions(+) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index b0fcce6..f335d5e 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -985,6 +985,92 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 ``` javascript +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + +#### 20.退群** +###### 接口功能 +> 退群 + +###### 接口地址 +> [/api/quitChatRoom](/api/quitChatRoom) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|chatRoomId|string|群id| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + "chatRoomId":"123456@chatroom" +} + +``` +响应: +``` javascript +{ + "code": 119536579329, + "data": null, + "msg": "success" +} +``` + +#### 21.转发消息** +###### 接口功能 +> 转发消息 + +###### 接口地址 +> [/api/forwardMsg](/api/forwardMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|接收人id| +|msgId|string|消息id| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + "wxid":"filehelper", + "msgId":"1233312233123" +} + ``` 响应: ``` javascript diff --git a/python/3.9.5.81/http_client.py b/python/3.9.5.81/http_client.py index df84819..dc2317b 100644 --- a/python/3.9.5.81/http_client.py +++ b/python/3.9.5.81/http_client.py @@ -274,6 +274,36 @@ def createChatRoom(): response = requests.request("POST", url, headers=headers, data=payload) print(response.text) +def quitChatRoom(): + print("modify chatRoomId ") + raise RuntimeError("modify chatRoomId then deleted me") + url = "127.0.0.1:19088/api/quitChatRoom" + + payload = json.dumps({ + "chatRoomId": "123@chatroom" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + +def forwardMsg(): + print("modify msgId ") + raise RuntimeError("modify msgId then deleted me") + url = "127.0.0.1:19088/api/forwardMsg" + + payload = json.dumps({ + "wxid": "filehelper", + "msgId": "12331" + }) + headers = { + 'Content-Type': 'application/json' + } + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + if __name__ == '__main__': checkLogin() From e9a6e0da576aeeee5a0714e46bf6317e6bf7ead1 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Fri, 4 Aug 2023 16:40:10 +0800 Subject: [PATCH 10/16] feat: sns --- doc/3.9.5.81.md | 96 +++++++++++++++++++++++++++++++++++++ src/hooks.cc | 54 +++++++++++++++++++-- src/http_server_callback.cc | 27 +++++++++++ src/manager.cc | 26 ++++++++++ src/manager.h | 2 + src/wechat_function.h | 12 +++++ 6 files changed, 213 insertions(+), 4 deletions(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index f335d5e..386fa62 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -1071,6 +1071,102 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "msgId":"1233312233123" } +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + +#### 22.朋友圈首页** +###### 接口功能 +> 朋友圈首页,前置条件需先调用hook消息接口成功,具体内容会在hook消息里返回,格式如下: +``` javascript +{ + "data":[ + { + "content": "", + "createTime': 1691125287, + "senderId': "", + "snsId': 123, + "xml':"" + } + ] +} +``` + +###### 接口地址 +> [/api/getSNSFirstPage](/api/getSNSFirstPage) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript + + +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + +#### 23.朋友圈下一页** +###### 接口功能 +> 朋友圈下一页 + +###### 接口地址 +> [/api/getSNSNextPage](/api/getSNSNextPage) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|snsId|number|snsId| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + + "snsid":123 +} + ``` 响应: ``` javascript diff --git a/src/hooks.cc b/src/hooks.cc index 4387a42..9b3e0c9 100644 --- a/src/hooks.cc +++ b/src/hooks.cc @@ -18,6 +18,8 @@ static char kServerIp[16] = "127.0.0.1"; static bool kEnableHttp = false; static bool kLogHookFlag = false; +static bool kSnsFinishHookFlag = false; + static UINT64 (*R_DoAddMsg)(UINT64, UINT64, UINT64) = (UINT64(*)( @@ -29,6 +31,10 @@ static UINT64 (*R_Log)(UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64, UINT64))(Utils::GetWeChatWinBase() + offset::kHookLog); +static UINT64 (*R_OnSnsTimeLineSceneFinish)(UINT64, UINT64, UINT64) = + (UINT64(*)(UINT64, UINT64, UINT64))(Utils::GetWeChatWinBase() + + offset::kOnSnsTimeLineSceneFinish); + VOID CALLBACK SendMsgCallback(PTP_CALLBACK_INSTANCE instance, PVOID context, PTP_WORK Work) { common::InnerMessageStruct *msg = (common::InnerMessageStruct *)context; @@ -167,6 +173,39 @@ if(p== 0 || p == 1){ return p; } +void HandleSNSMsg(INT64 param1, INT64 param2, INT64 param3) { + nlohmann::json j_sns; + INT64 begin_addr = *(INT64 *)(param2 + 0x30); + INT64 end_addr = *(INT64 *)(param2 + 0x38); + if (begin_addr == 0) { + j_sns = {{"data", nlohmann::json::array()}}; + } else { + while (begin_addr < end_addr) { + nlohmann::json j_item; + j_item["snsId"] = *(UINT64 *)(begin_addr); + j_item["createTime"] = *(DWORD *)(begin_addr + 0x38); + j_item["senderId"] = Utils::ReadWstringThenConvert(begin_addr + 0x18); + j_item["content"] = Utils::ReadWstringThenConvert(begin_addr + 0x48); + j_item["xml"] = Utils::ReadWstringThenConvert(begin_addr + 0x580); + j_sns["data"].push_back(j_item); + begin_addr += 0x11E0; + } + } + std::string jstr = j_sns.dump() + '\n'; + 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(); + if (kEnableHttp) { + bool add = ThreadPool::GetInstance().AddWork(SendHttpMsgCallback, inner_msg); + SPDLOG_INFO("hook sns add http msg work:{}", add); + } else { + bool add = ThreadPool::GetInstance().AddWork(SendMsgCallback, inner_msg); + SPDLOG_INFO("hook sns add msg work:{}", add); + } + R_OnSnsTimeLineSceneFinish(param1, param2, param3); +} + int HookSyncMsg(std::string client_ip, int port, std::string url, uint64_t timeout, bool enable) { if (kMsgHookFlag) { @@ -189,15 +228,23 @@ int HookSyncMsg(std::string client_ip, int port, std::string url, return -1; } - DetourRestoreAfterWith(); + // DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); - UINT64 do_add_msg_addr = base + offset::kDoAddMsg; DetourAttach(&(PVOID&)R_DoAddMsg, &HandleSyncMsg); LONG ret = DetourTransactionCommit(); if(ret == NO_ERROR){ kMsgHookFlag = true; } + SPDLOG_INFO("hook sync {}",ret); + DetourTransactionBegin(); + DetourUpdateThread(GetCurrentThread()); + DetourAttach(&(PVOID&)R_OnSnsTimeLineSceneFinish, &HandleSNSMsg); + ret = DetourTransactionCommit(); + if(ret == NO_ERROR){ + kSnsFinishHookFlag = true; + } + SPDLOG_INFO("hook sns {}",ret); return ret; } @@ -212,7 +259,6 @@ int UnHookSyncMsg() { UINT64 base = Utils::GetWeChatWinBase(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); - UINT64 do_add_msg_addr = base + offset::kDoAddMsg; DetourDetach(&(PVOID&)R_DoAddMsg, &HandleSyncMsg); LONG ret = DetourTransactionCommit(); if (ret == NO_ERROR) { @@ -235,7 +281,7 @@ int UnHookSyncMsg() { return -1; } - DetourRestoreAfterWith(); + // DetourRestoreAfterWith(); DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); UINT64 do_add_msg_addr = base + offset::kHookLog; diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index a495747..01d364a 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -6,6 +6,8 @@ #include "hooks.h" #include "db.h" + +#define STR2ULL(str) (wxhelper::Utils::IsDigit(str) ? stoull(str) : 0) #define STR2LL(str) (wxhelper::Utils::IsDigit(str) ? stoll(str) : 0) #define STR2I(str) (wxhelper::Utils::IsDigit(str) ? stoi(str) : 0) namespace common = wxhelper::common; @@ -30,6 +32,16 @@ INT64 GetINT64Param(nlohmann::json data, std::string key) { return result; } +INT64 GetUINT64Param(nlohmann::json data, std::string key) { + UINT64 result; + try { + result = data[key].get(); + } catch (nlohmann::json::exception) { + result = STR2ULL(data[key].get()); + } + return result; +} + std::string GetStringParam(nlohmann::json data, std::string key) { return data[key].get(); } @@ -423,6 +435,21 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/getSNSFirstPage")) { + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->GetSNSFirstPage(); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/getSNSNextPage")) { + UINT64 snsid = GetUINT64Param(j_param, "snsId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->GetSNSNextPage(snsid); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index 0bde19b..47c5329 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -603,4 +603,30 @@ INT64 Manager::ForwardMsg(UINT64 msg_id, const std::wstring &wxid) { success = forward_msg(reinterpret_cast(recv), l.QuadPart, 0x4, 0x0); return success; } + +INT64 Manager::GetSNSFirstPage() { + INT64 success = -1; + UINT64 sns_data_mgr_addr = base_addr_ + offset::kSNSDataMgr; + UINT64 sns_first_page_addr = base_addr_ + offset::kSNSGetFirstPage; + func::__GetSNSDataMgr sns_data_mgr = (func::__GetSNSDataMgr)sns_data_mgr_addr; + func::__GetSNSFirstPage sns_first_page = + (func::__GetSNSFirstPage)sns_first_page_addr; + UINT64 mgr = sns_data_mgr(); + INT64 buff[16] = {0}; + success = sns_first_page(mgr, reinterpret_cast(&buff), 1); + return success; +} + +INT64 Manager::GetSNSNextPage(UINT64 sns_id) { + INT64 success = -1; + UINT64 time_line_mgr_addr = base_addr_ + offset::kSNSTimeLineMgr; + UINT64 sns_next_page_addr = base_addr_ + offset::kSNSGetNextPageScene; + func::__GetSnsTimeLineMgr time_line_mgr = + (func::__GetSnsTimeLineMgr)time_line_mgr_addr; + func::__GetSNSNextPageScene sns_next_page = + (func::__GetSNSNextPageScene)sns_next_page_addr; + UINT64 mgr = time_line_mgr(); + success = sns_next_page(mgr, sns_id); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index 3d0fff8..4c1fa30 100644 --- a/src/manager.h +++ b/src/manager.h @@ -32,6 +32,8 @@ class Manager { INT64 CreateChatRoom(const std::vector& wxids); INT64 QuitChatRoom(const std::wstring& room_id); INT64 ForwardMsg(UINT64 msg_id, const std::wstring& wxid); + INT64 GetSNSFirstPage(); + INT64 GetSNSNextPage(UINT64 sns_id); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 47bf639..d2fa115 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -241,6 +241,12 @@ typedef UINT64 (*__CreateChatRoom)(UINT64,UINT64,UINT64); typedef UINT64 (*__QuitChatRoom)(UINT64,UINT64,UINT64); typedef UINT64 (*__ForwardMsg)(UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__GetSNSFirstPage)(UINT64,UINT64,UINT64); +typedef UINT64 (*__GetSNSNextPageScene)(UINT64,UINT64); + +typedef UINT64 (*__GetSNSDataMgr)(); +typedef UINT64 (*__GetSnsTimeLineMgr)(); + } // namespace function namespace prototype { @@ -347,6 +353,12 @@ const UINT64 kCreateChatRoom = 0xe63340; const UINT64 kQuitChatRoom = 0xe6e3b0; const UINT64 kForwardMsg = 0xfcd0f0; +const UINT64 kOnSnsTimeLineSceneFinish = 0x1a73150; +const UINT64 kSNSGetFirstPage = 0x1a51dd0; +const UINT64 kSNSGetNextPageScene = 0x1a77240; +const UINT64 kSNSDataMgr = 0xeebda0; +const UINT64 kSNSTimeLineMgr = 0x19e83a0; + } // namespace offset } // namespace V3_9_5_81 From 129bb55c3627db373a4d9827b20e89684a19cc0f Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Sat, 5 Aug 2023 10:52:28 +0800 Subject: [PATCH 11/16] =?UTF-8?q?feat:=E6=94=B6=E8=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 90 ++++++++++++++++++++++++++++++++++ python/3.9.5.81/http_client.py | 57 +++++++++++++++++++++ src/http_server_callback.cc | 17 +++++++ src/manager.cc | 54 ++++++++++++++++++++ src/manager.h | 3 ++ src/wechat_function.h | 10 ++++ 6 files changed, 231 insertions(+) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index 386fa62..a98368b 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -1167,6 +1167,96 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "snsid":123 } +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + + +#### 24.收藏消息** +###### 接口功能 +> 收藏消息 + +###### 接口地址 +> [/api/addFavFromMsg](/api/addFavFromMsg) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|msgId|number|消息id| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + + "msgId":123 +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": null, + "msg": "success" +} +``` + +#### 24.收藏图片** +###### 接口功能 +> 收藏图片 + +###### 接口地址 +> [/api/addFavFromImage](/api/addFavFromImage) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|wxid| +|imagePath|string|图片地址| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,1成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + + "wxid":"wxid_12333", + "imagePath":"C:\\test\\test.png" +} + ``` 响应: ``` javascript diff --git a/python/3.9.5.81/http_client.py b/python/3.9.5.81/http_client.py index dc2317b..6415d23 100644 --- a/python/3.9.5.81/http_client.py +++ b/python/3.9.5.81/http_client.py @@ -304,6 +304,63 @@ def forwardMsg(): response = requests.request("POST", url, headers=headers, data=payload) print(response.text) +def getSNSFirstPage(): + url = "127.0.0.1:19088/api/getSNSFirstPage" + + payload = {} + headers = {} + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + +def getSNSNextPage(): + print("modify snsId ") + raise RuntimeError("modify snsId then deleted me") + url = "127.0.0.1:19088/api/getSNSNextPage" + + payload = json.dumps({ + "snsId": "" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + +def addFavFromMsg(): + print("modify msgId ") + raise RuntimeError("modify msgId then deleted me") + url = "127.0.0.1:19088/api/addFavFromMsg" + + payload = json.dumps({ + "msgId": "1222222" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + +def addFavFromImage(): + print("modify wxid imagePath ") + raise RuntimeError("modify wxid imagePath then deleted me") + url = "127.0.0.1:19088/api/addFavFromImage" + + payload = json.dumps({ + "wxid": "", + "imagePath": "" + }) + 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/http_server_callback.cc b/src/http_server_callback.cc index 01d364a..d76be63 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -450,6 +450,23 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/addFavFromMsg")) { + UINT64 msg_id = GetUINT64Param(j_param, "msgId"); + INT64 success = + wxhelper::GlobalContext::GetInstance().mgr->AddFavFromMsg(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/addFavFromImage")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + std::wstring image_path = GetWStringParam(j_param, "imagePath"); + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->AddFavFromImage( + wxid, image_path); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index 47c5329..1639638 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -629,4 +629,58 @@ INT64 Manager::GetSNSNextPage(UINT64 sns_id) { success = sns_next_page(mgr, sns_id); return success; } + +INT64 Manager::AddFavFromMsg(UINT64 msg_id) { + INT64 success = -1; + UINT64 get_chat_mgr_addr = base_addr_ + offset::kGetChatMgr; + UINT64 get_by_local_id_addr = base_addr_ + offset::kGetMgrByPrefixLocalId; + UINT64 add_fav_addr = base_addr_ + offset::kAddFavFromMsg; + UINT64 get_favorite_mgr_addr = base_addr_ + offset::kGetFavoriteMgr; + UINT64 free_chat_msg_addr = base_addr_ + offset::kFreeChatMsg; + func::__GetMgrByPrefixLocalId get_by_local_id = (func::__GetMgrByPrefixLocalId)get_by_local_id_addr; + UINT64 new_chat_msg_addr = base_addr_ + offset::kChatMsgInstanceCounter; + + func::__AddFavFromMsg add_fav = (func::__AddFavFromMsg)add_fav_addr; + func::__GetChatMgr get_chat_mgr = (func::__GetChatMgr)get_chat_mgr_addr; + func::__GetFavoriteMgr get_favorite_mgr = (func::__GetFavoriteMgr)get_favorite_mgr_addr; + func::__FreeChatMsg free_chat_msg = (func::__FreeChatMsg)free_chat_msg_addr; + func::__NewChatMsg new_chat_msg = (func::__NewChatMsg)new_chat_msg_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[0x460]= {0}; + 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); + UINT64 mgr = get_favorite_mgr(); + success = add_fav(mgr,p_chat_msg); + free_chat_msg(p_chat_msg); + return success; +} + +INT64 Manager::AddFavFromImage(const std::wstring &wxid, + const std::wstring &image_path) { + INT64 success = -1; + UINT64 get_favorite_mgr_addr = base_addr_ + offset::kGetFavoriteMgr; + UINT64 add_fav_from_image_addr = base_addr_ + offset::kAddFavFromImage; + prototype::WeChatString *send_id = BuildWechatString(wxid); + prototype::WeChatString *path = BuildWechatString(image_path); + func::__GetFavoriteMgr get_favorite_mgr = + (func::__GetFavoriteMgr)get_favorite_mgr_addr; + func::__AddFavFromImage add_fav_from_image = + (func::__AddFavFromImage)add_fav_from_image_addr; + UINT64 mgr = get_favorite_mgr(); + success = add_fav_from_image(mgr, reinterpret_cast(path), + reinterpret_cast(send_id)); + return success; +} + } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index 4c1fa30..eb3cc24 100644 --- a/src/manager.h +++ b/src/manager.h @@ -34,6 +34,9 @@ class Manager { INT64 ForwardMsg(UINT64 msg_id, const std::wstring& wxid); INT64 GetSNSFirstPage(); INT64 GetSNSNextPage(UINT64 sns_id); + INT64 AddFavFromMsg(UINT64 msg_id); + INT64 AddFavFromImage(const std::wstring& wxid, + const std::wstring& image_path); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index d2fa115..9939277 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -246,6 +246,11 @@ typedef UINT64 (*__GetSNSNextPageScene)(UINT64,UINT64); typedef UINT64 (*__GetSNSDataMgr)(); typedef UINT64 (*__GetSnsTimeLineMgr)(); +typedef UINT64 (*__GetMgrByPrefixLocalId)(UINT64,UINT64); +typedef UINT64 (*__AddFavFromMsg)(UINT64,UINT64); +typedef UINT64 (*__GetChatMgr)(); +typedef UINT64 (*__GetFavoriteMgr)(); +typedef UINT64 (*__AddFavFromImage)(UINT64,UINT64,UINT64); } // namespace function @@ -358,6 +363,11 @@ const UINT64 kSNSGetFirstPage = 0x1a51dd0; const UINT64 kSNSGetNextPageScene = 0x1a77240; const UINT64 kSNSDataMgr = 0xeebda0; const UINT64 kSNSTimeLineMgr = 0x19e83a0; +const UINT64 kGetMgrByPrefixLocalId = 0xe4add0; +const UINT64 kAddFavFromMsg = 0x1601520; +const UINT64 kGetChatMgr = 0x8f0400; +const UINT64 kGetFavoriteMgr = 0x8c69b0; +const UINT64 kAddFavFromImage = 0x160b920; } // namespace offset } // namespace V3_9_5_81 From bdf91943f366a83eda180a48d9959c09dab2438d Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Tue, 8 Aug 2023 17:41:30 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat:=20=E5=8F=91=E9=80=81@=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 100 ++++++++++++++++++++++++++++++++++++ src/http_server_callback.cc | 38 ++++++++++++-- src/manager.cc | 100 ++++++++++++++++++++++++++++++++++++ src/manager.h | 6 +++ src/wechat_function.h | 16 ++++++ 5 files changed, 257 insertions(+), 3 deletions(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index a98368b..c3f8143 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -1265,4 +1265,104 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "data": null, "msg": "success" } +``` + +#### 25.发送@消息** +###### 接口功能 +> 发送@消息 + +###### 接口地址 +> [/api/sendAtText](/api/sendAtText) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxids|string|wxid字符串,多个用,分隔,发送所有人传值"notify@all"| +|chatRoomId|string|群id| +|msg|string|消息内容| + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + + +###### 接口示例 + +入参: +``` javascript +{ + + "wxids":"notify@all", + "chatRoomId":"123@chatroom", + "msg":"你们好啊" + +} + +``` +响应: +``` javascript +{ + "code": 67316444768, + "data": null, + "msg": "success" +} +``` + +#### 26.获取群成员信息** +###### 接口功能 +> 获取群成员基础信息 + +###### 接口地址 +> [/api/getContactProfile](/api/getContactProfile) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|wxid| + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| +|  account|string|账号| +|  headImage|string|头像| +|  nickname|string|昵称| +|  v3|string|v3| +|  wxid|string|wxid| + +###### 接口示例 + +入参: +``` javascript + +{ + "wxid":"wxid_123" +} + +``` +响应: +``` javascript +{ + "code": 1, + "data": { + "account": "account", + "headImage": "https://wx.qlogo.cn/mmhead/ver_1/0", + "nickname": "test", + "v3": "wxid_123", + "wxid": "wxid_123" + }, + "msg": "success" +} ``` \ No newline at end of file diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index d76be63..b914430 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -467,12 +467,44 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; - } else { + } else if (mg_http_match_uri(hm, "/api/sendAtText")) { + std::wstring chat_room_id = GetWStringParam(j_param, "chatRoomId"); + std::vector wxids = GetArrayParam(j_param, "wxids"); + std::wstring msg = GetWStringParam(j_param, "msg"); + std::vector wxid_list; + for (unsigned int i = 0; i < wxids.size(); i++) { + wxid_list.push_back(wxids[i]); + } + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->SendAtText( + chat_room_id, wxid_list, msg); nlohmann::json ret_data = { - {"code", 200}, {"data", {}}, {"msg", "not support url"}}; + {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; - } + } else if (mg_http_match_uri(hm, "/api/getContactProfile")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + common::ContactProfileInner profile; + int success = wxhelper::GlobalContext::GetInstance().mgr->GetContactByWxid( + wxid, profile); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + if (success == 1) { + nlohmann::json contact_profile = { + {"account", profile.account}, {"headImage", profile.head_image}, + {"nickname", profile.nickname}, {"v3", profile.v3}, + {"wxid", profile.wxid}, + }; + ret_data["data"] = contact_profile; + } + 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(); diff --git a/src/manager.cc b/src/manager.cc index 1639638..713afd3 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -683,4 +683,104 @@ INT64 Manager::AddFavFromImage(const std::wstring &wxid, return success; } +INT64 Manager::SendAtText(const std::wstring &room_id, + const std::vector &wxids, + const std::wstring &msg) { + INT64 success = -1; + std::vector wxid_list; + common::VectorInner *list = (common::VectorInner *)&wxid_list; + std::wstring at_msg = L""; + int number = 0; + for (unsigned int i = 0; i < wxids.size(); i++) { + std::wstring nickname; + std::wstring at_all = L"notify@all"; + if (at_all.compare(wxids[i]) == 0 ) { + nickname = L"所有人"; + } else { + nickname = GetContactOrChatRoomNickname(wxids[i]); + } + if (nickname.length() == 0) { + continue; + } + prototype::WeChatString id(wxids[i]); + wxid_list.push_back(id); + at_msg = at_msg + L"@" + nickname + L" "; + number++; + } + if (number < 1) { + return success; + } + at_msg += msg; + + INT64 head = (INT64)&list->start; + prototype::WeChatString to_user(room_id); + prototype::WeChatString text_msg(at_msg); + UINT64 send_message_mgr_addr = base_addr_ + offset::kGetSendMessageMgr; + UINT64 send_text_msg_addr = base_addr_ + offset::kSendTextMsg; + UINT64 free_chat_msg_addr = base_addr_ + offset::kFreeChatMsg; + char chat_msg[0x460] = {0}; + func::__GetSendMessageMgr mgr; + mgr = (func::__GetSendMessageMgr)send_message_mgr_addr; + func::__SendTextMsg send; + send = (func::__SendTextMsg)send_text_msg_addr; + func::__FreeChatMsg free; + free = (func::__FreeChatMsg)free_chat_msg_addr; + mgr(); + success = send(reinterpret_cast(&chat_msg), + reinterpret_cast(&to_user), + reinterpret_cast(&text_msg), head, 1, 1, 0, 0); + free(reinterpret_cast(&chat_msg)); + return success; +} + +std::wstring Manager::GetContactOrChatRoomNickname(const std::wstring &wxid) { + prototype::WeChatString to_user(wxid); + UINT64 get_contact_mgr_addr = base_addr_ + offset::kGetContactMgr; + UINT64 new_contact_addr = base_addr_ + offset::kNewContact; + UINT64 get_contact_addr = base_addr_ + offset::kGetContact; + UINT64 free_contact_addr = base_addr_ + offset::kFreeContact; + func::__GetContactMgr get_contact_mgr = + (func::__GetContactMgr)get_contact_mgr_addr; + func::__GetContact get_contact = (func::__GetContact)get_contact_addr; + func::__NewContact new_contact = (func::__NewContact)new_contact_addr; + func::__FreeContact free_contact = (func::__FreeContact)free_contact_addr; + char buff[0x6A9] = {0}; + UINT64 contact = new_contact(reinterpret_cast(&buff)); + UINT64 mgr = get_contact_mgr(); + INT64 success = get_contact(mgr, reinterpret_cast(&to_user), contact); + if (success == 1) { + std::wstring nickname = Utils::ReadWstring(contact + 0xA0); + free_contact(contact); + return nickname; + } else { + free_contact(contact); + return L""; + } +} + +INT64 Manager::GetContactByWxid(const std::wstring &wxid, + common::ContactProfileInner &profile) { + INT64 success = -1; + prototype::WeChatString to_user(wxid); + UINT64 get_contact_mgr_addr = base_addr_ + offset::kGetContactMgr; + UINT64 new_contact_addr = base_addr_ + offset::kNewContact; + UINT64 get_contact_addr = base_addr_ + offset::kGetContact; + UINT64 free_contact_addr = base_addr_ + offset::kFreeContact; + func::__GetContactMgr get_contact_mgr = + (func::__GetContactMgr)get_contact_mgr_addr; + func::__GetContact get_contact = (func::__GetContact)get_contact_addr; + func::__NewContact new_contact = (func::__NewContact)new_contact_addr; + func::__FreeContact free_contact = (func::__FreeContact)free_contact_addr; + char buff[0x6A9] = {0}; + UINT64 contact = new_contact(reinterpret_cast(&buff)); + UINT64 mgr = get_contact_mgr(); + success = get_contact(mgr, reinterpret_cast(&to_user), contact); + profile.wxid = Utils::ReadWstringThenConvert(contact + 0x10); + profile.account = Utils::ReadWstringThenConvert(contact + 0x30); + profile.v3 = Utils::ReadWstringThenConvert(contact + 0x50); + profile.nickname = Utils::ReadWstringThenConvert(contact + 0xA0); + profile.head_image = Utils::ReadWstringThenConvert(contact + 0x188); + free_contact(contact); + return success; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/manager.h b/src/manager.h index eb3cc24..ed7c46f 100644 --- a/src/manager.h +++ b/src/manager.h @@ -37,6 +37,12 @@ class Manager { INT64 AddFavFromMsg(UINT64 msg_id); INT64 AddFavFromImage(const std::wstring& wxid, const std::wstring& image_path); + INT64 SendAtText(const std::wstring& room_id, + const std::vector& wxids, + const std::wstring& msg); + std::wstring GetContactOrChatRoomNickname(const std::wstring& wxid); + INT64 GetContactByWxid(const std::wstring& wxid, + common::ContactProfileInner& profile); private: UINT64 base_addr_; diff --git a/src/wechat_function.h b/src/wechat_function.h index 9939277..4acd43f 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -198,6 +198,16 @@ struct ChatRoomMemberInner { member("") {} }; +struct ContactProfileInner { + std::string wxid; + std::string account; + std::string v3; + std::string nickname; + std::string head_image; + ContactProfileInner() + : wxid(""), account(""), v3(""), nickname(""), head_image("") {} +}; + } // namespace common namespace V3_9_5_81 { namespace function { @@ -251,6 +261,9 @@ typedef UINT64 (*__AddFavFromMsg)(UINT64,UINT64); typedef UINT64 (*__GetChatMgr)(); typedef UINT64 (*__GetFavoriteMgr)(); typedef UINT64 (*__AddFavFromImage)(UINT64,UINT64,UINT64); +typedef UINT64 (*__GetContact)(UINT64,UINT64,UINT64); +typedef UINT64 (*__NewContact)(UINT64); +typedef UINT64 (*__FreeContact)(UINT64); } // namespace function @@ -368,6 +381,9 @@ const UINT64 kAddFavFromMsg = 0x1601520; const UINT64 kGetChatMgr = 0x8f0400; const UINT64 kGetFavoriteMgr = 0x8c69b0; const UINT64 kAddFavFromImage = 0x160b920; +const UINT64 kGetContact = 0xEA5F90; +const UINT64 kNewContact = 0x1212e40; +const UINT64 kFreeContact = 0x12134e0; } // namespace offset } // namespace V3_9_5_81 From 58caad4f34b4a079181872fb4434c2bfad7bf767 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Tue, 8 Aug 2023 17:44:49 +0800 Subject: [PATCH 13/16] doc: client --- python/3.9.5.81/http_client.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/python/3.9.5.81/http_client.py b/python/3.9.5.81/http_client.py index 6415d23..d7e63f7 100644 --- a/python/3.9.5.81/http_client.py +++ b/python/3.9.5.81/http_client.py @@ -361,6 +361,40 @@ def addFavFromImage(): print(response.text) +def getContactProfile(): + print("modify wxid ") + raise RuntimeError("modify wxid then deleted me") + url = "127.0.0.1:19088/api/getContactProfile" + + payload = json.dumps({ + "wxid": "" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + print(response.text) + + +def sendAtText(): + print("modify wxids chatRoomId") + raise RuntimeError("modify wxids chatRoomId then deleted me") + url = "127.0.0.1:19088/api/sendAtText" + + payload = json.dumps({ + "wxids": "notify@all", + "chatRoomId": "123@chatroom", + "msg": "你好啊" + }) + headers = { + 'Content-Type': 'application/json' + } + + response = requests.request("POST", url, headers=headers, data=payload) + + print(response.text) + if __name__ == '__main__': checkLogin() From 6436ce072a79bff426fcc6551fd36e8b7aeff447 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Tue, 8 Aug 2023 20:46:08 +0800 Subject: [PATCH 14/16] =?UTF-8?q?fix:=20=E8=BF=94=E5=9B=9E=E5=80=BC?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/http_server_callback.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index b914430..603beb3 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -484,7 +484,7 @@ 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; - int success = wxhelper::GlobalContext::GetInstance().mgr->GetContactByWxid( + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->GetContactByWxid( wxid, profile); nlohmann::json ret_data = { {"code", success}, {"msg", "success"}, {"data", {}}}; From 608c6d26de5c5e391619bc7de3ff09c415a64033 Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Thu, 10 Aug 2023 17:13:48 +0800 Subject: [PATCH 15/16] =?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 From 8bdc8c7e4625a93414af76973f5af277764647ae Mon Sep 17 00:00:00 2001 From: hugy <504650082@qq.com> Date: Thu, 24 Aug 2023 17:05:41 +0800 Subject: [PATCH 16/16] =?UTF-8?q?feat:=20=E5=8F=91=E9=80=81=E5=B0=8F?= =?UTF-8?q?=E7=A8=8B=E5=BA=8F(=E5=BE=85=E5=AE=8C=E5=96=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/3.9.5.81.md | 57 +++++++++++++++ src/http_server_callback.cc | 25 +++++++ src/manager.cc | 136 +++++++++++++++++++++++++++++++++++- src/manager.h | 9 ++- src/utils.cc | 59 ++++++++++++++++ src/utils.h | 4 ++ src/wechat_function.h | 36 ++++++++++ 7 files changed, 323 insertions(+), 3 deletions(-) diff --git a/doc/3.9.5.81.md b/doc/3.9.5.81.md index b3718a9..4cbe307 100644 --- a/doc/3.9.5.81.md +++ b/doc/3.9.5.81.md @@ -1672,6 +1672,63 @@ enableHttp=0时,使用ip,port的tcp服务回传消息。 "filePath":"C:\\wechatDir\\WeChat Files\\wxid_123\\FileStorage\\CustomEmotion\\8F\\8F6423BC2E69188DCAC797E279C81DE9" } +``` +响应: +``` javascript +{ + "code": 1, + "data": {}, + "msg": "success" +} +``` + + +#### 34.发送小程序** +###### 接口功能 +> 发送小程序(待完善,不稳定),相关参数可以参考示例的滴滴小程序的内容自行组装。 + +###### 接口地址 +> [/api/sendCustomEmotion](/api/sendApplet) + +###### HTTP请求方式 +> POST JSON + +###### 请求参数 +|参数|必选|类型|说明| +|---|---|---|---| +|wxid|string|接收人wxid| +|waidConcat|string|app的wxid与回调信息之类绑定的拼接字符串,伪造的数据可以随意| +|appletWxid|string|app的wxid| +|jsonParam|string|相关参数| +|headImgUrl|string|头像url| +|mainImg|string|主图的本地路径,需要在小程序的临时目录下| +|indexPage|string|小程序的跳转页面| + + + +###### 返回字段 +|返回字段|字段类型|说明 | +|---|---|---| +|code|int|返回状态,大于0成功, -1失败| +|msg|string|成功提示| +|data|object|null| + +###### 接口示例 + +入参: +``` javascript + +{ +"wxid":"filehelper", +"waidConcat":"wxaf35009675aa0b2a_118", +"waid":"wxaf35009675aa0b2a", +"appletWxid":"gh_7a5c4141778f@app", +"jsonParam":"{\"current_path\":\"home/pages/index.html\",\"current_title\":\"\",\"image_url\":\"https://ut-static.udache.com/webx/mini-pics/U7mDFxU2yh-2-r1BJ-J0X.png\",\"scene\":1001,\"scene_note\":\"\",\"sessionId\":\"SessionId@1672284921_1#1692848476899\"}", +"headImgUrl":"http://mmbiz.qpic.cn/sz_mmbiz_png/9n47wQlh4dH8afD9dQ9uQicibRm5mYz3lawXCLMjmnzFicribH51qsFYxjzPEcTGHGmgX4lkAkQ3jznia8UDEtqsX1w/640?wx_fmt=png&wxfrom=200", +"mainImg":"C:\\wxid_123123\\Applet\\wxaf35009675aa0b2a\\temp\\2.png", +"indexPage":"pages/index/index.html" +} + ``` 响应: ``` javascript diff --git a/src/http_server_callback.cc b/src/http_server_callback.cc index f9ddaf0..1d81a6f 100644 --- a/src/http_server_callback.cc +++ b/src/http_server_callback.cc @@ -560,6 +560,31 @@ std::string HttpDispatch(struct mg_connection *c, struct mg_http_message *hm) { {"code", success}, {"msg", "success"}, {"data", {}}}; ret = ret_data.dump(); return ret; + } else if (mg_http_match_uri(hm, "/api/sendApplet")) { + std::wstring wxid = GetWStringParam(j_param, "wxid"); + std::wstring waid_concat = GetWStringParam(j_param, "waidConcat"); + std::string waid = GetStringParam(j_param, "waid"); + std::string app_wxid = GetStringParam(j_param, "appletWxid"); + std::string json_param = GetStringParam(j_param, "jsonParam"); + std::string head_url = GetStringParam(j_param, "headImgUrl"); + std::string main_img = GetStringParam(j_param, "mainImg"); + std::string index_page = GetStringParam(j_param, "indexPage"); + + std::wstring waid_w = wxhelper::Utils::UTF8ToWstring(waid); + + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->SendApplet( + wxid, waid_concat, waid_w, waid, app_wxid, json_param, head_url, + main_img, index_page); + nlohmann::json ret_data = { + {"code", success}, {"msg", "success"}, {"data", {}}}; + ret = ret_data.dump(); + return ret; + } else if (mg_http_match_uri(hm, "/api/test")) { + INT64 success = wxhelper::GlobalContext::GetInstance().mgr->Test(); + 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"}}; diff --git a/src/manager.cc b/src/manager.cc index be22fa6..46605cf 100644 --- a/src/manager.cc +++ b/src/manager.cc @@ -28,7 +28,19 @@ prototype::WeChatString * BuildWechatString(const std::wstring &ws){ return p; } -Manager::Manager(UINT64 base) : base_addr_(base) {} +prototype::WeChatStr * BuildWechatStr(const std::string &str){ + prototype::WeChatStr *p = Utils::WxHeapAlloc( + sizeof(prototype::WeChatStr)); + char *p_chat_room_id = Utils::WxHeapAlloc(str.size() + 1); + memcpy(p_chat_room_id, str.c_str(), str.size() + 1); + p->ptr = p_chat_room_id; + p->len = static_cast(str.size()); + p->maxlen = static_cast(str.size()); + p->buf = NULL; + return p; +} + +Manager::Manager(UINT64 base) : base_addr_(base),js_api_addr_(0) {} Manager::~Manager() {} INT64 Manager::CheckLogin() { INT64 success = -1; @@ -1095,4 +1107,124 @@ INT64 Manager::SendCustomEmotion(const std::wstring &file_path, reinterpret_cast(temp)); return success; } -} // namespace wxhelper \ No newline at end of file + +INT64 Manager::SendApplet(const std::wstring &recv_wxid, + const std::wstring &waid_suff, + const std::wstring &waid_w, const std::string &waid_s, + const std::string &wa_wxid, + const std::string &json_param, + const std::string &head_image, + const std::string &big_image, + const std::string &index_page) { + INT64 success = -1; + if (js_api_addr_ == 0) { + auto vec2 = Utils::QWordScan(base_addr_ + 0x32D1318, 0x1000, 0x8); + for (int i = 0; i < vec2.size(); i++) { + INT64 ptr = vec2.at(i); + if (*(INT64 *)ptr == base_addr_ + 0x32D1318) { + js_api_addr_ = ptr; + break; + } + } + } + if (js_api_addr_ == 0) { + success = -2; + return success; + } + + UINT64 share_app_msg_addr = base_addr_ + offset::kNewJsApiShareAppMessage; + func::__JsApiShareAppMessage share_app_msg = + (func::__JsApiShareAppMessage)share_app_msg_addr; + + UINT64 init_addr = base_addr_ + offset::kInitJsConfig; + func::__InitJsConfig init = (func::__InitJsConfig)init_addr; + + UINT64 send_applet_addr = base_addr_ + offset::kSendApplet; + func::__SendApplet send_applet = (func::__SendApplet)send_applet_addr; + + UINT64 get_by_waid_addr = base_addr_ + offset::kGetAppInfoByWaid; + func::__GetAppInfoByWaid get_app_info = + (func::__GetAppInfoByWaid)get_by_waid_addr; + + UINT64 copy_app_req_addr = base_addr_ + offset::kCopyShareAppMessageRequest; + func::__CopyShareAppMessageRequest copy_app_req = + (func::__CopyShareAppMessageRequest)copy_app_req_addr; + + UINT64 new_wa_msg_addr = base_addr_ + offset::kNewWAUpdatableMsgInfo; + func::__NewWAUpdatableMsgInfo new_wa_msg = + (func::__NewWAUpdatableMsgInfo)new_wa_msg_addr; + + UINT64 free_wa_msg_addr = base_addr_ + offset::kFreeWAUpdatableMsgInfo; + func::__FreeWAUpdatableMsgInfo free_wa_msg = + (func::__FreeWAUpdatableMsgInfo)free_wa_msg_addr; + + std::vector *temp = + Utils::WxHeapAlloc>(0x20); + // std::vector* temp = new + // std::vector(); + common::VectorInner *list = (common::VectorInner *)temp; + + prototype::WeChatString *member = BuildWechatString(recv_wxid); + + list->head = reinterpret_cast(member); + list->start = reinterpret_cast(member); + list->finsh = reinterpret_cast(member) + 0x20; + list->end = reinterpret_cast(member) + 0x20; + + INT64 head = reinterpret_cast(&(list->start)); + + prototype::WeChatString *waid_cat = BuildWechatString(waid_suff); + prototype::WeChatString *waid = BuildWechatString(waid_w); + + prototype::WeChatString *waid_2 = BuildWechatString(waid_suff); + + prototype::WeChatStr *waid_str = BuildWechatStr(waid_s); + prototype::WeChatStr *app_wxid = BuildWechatStr(wa_wxid); + prototype::WeChatStr *json_str = BuildWechatStr(json_param); + prototype::WeChatStr *head_image_url = BuildWechatStr(head_image); + prototype::WeChatStr *image = BuildWechatStr(big_image); + prototype::WeChatStr *index = BuildWechatStr(index_page); + + UINT64 app_msg = js_api_addr_; + + UINT64 data = *(UINT64 *)(app_msg + 0x8); + char *share_req = Utils::WxHeapAlloc(0x2000); + + char *mid_ptr = Utils::WxHeapAlloc(0x18); + memcpy(mid_ptr, &share_req, sizeof(INT64)); + memcpy(mid_ptr + 0x8, &share_req, sizeof(INT64)); + memcpy(mid_ptr + 0x10, &share_req, sizeof(INT64)); + + memcpy((void *)data, mid_ptr, 0x18); + + memcpy(share_req, (void *)(app_msg + 0x8), sizeof(UINT64)); + memcpy(share_req + 0x8, (void *)(app_msg + 0x8), sizeof(UINT64)); + memcpy(share_req + 0x10, (void *)(app_msg + 0x8), sizeof(UINT64)); + memcpy(share_req + 0x20, waid_2, sizeof(prototype::WeChatString)); + memcpy(share_req + 0x48, waid_str, sizeof(prototype::WeChatStr)); + memcpy(share_req + 0x98, app_wxid, sizeof(prototype::WeChatStr)); + memcpy(share_req + 0xF8, json_str, sizeof(prototype::WeChatStr)); + memcpy(share_req + 0x178, head_image_url, sizeof(prototype::WeChatStr)); + memcpy(share_req + 0x198, image, sizeof(prototype::WeChatStr)); + memcpy(share_req + 0x1c0, index, sizeof(prototype::WeChatStr)); + + success = send_applet(app_msg, reinterpret_cast(waid_cat), head, 0); + + return success; +} + +INT64 Manager::Test() { + auto vec = Utils::QWordScan(base_addr_ + 0x32D1318, 0x1, L"WeChatWin.dll"); + for (int i = 0; i < vec.size(); i++) { + INT64 re = vec.at(i); + SPDLOG_INFO("scan result :{},{}", i, re); + } + + auto vec2 = Utils::QWordScan(base_addr_ + 0x32D1318, 0x1000, 0x8); + for (int i = 0; i < vec2.size(); i++) { + INT64 re = vec2.at(i); + SPDLOG_INFO("scan2 result :{},{}", i, re); + } + return 1; +} +} // namespace wxhelper diff --git a/src/manager.h b/src/manager.h index 2dbf77c..eea98f4 100644 --- a/src/manager.h +++ b/src/manager.h @@ -56,9 +56,16 @@ class Manager { INT64 GetVoiceByDB(ULONG64 msg_id, const std::wstring& dir); INT64 SendCustomEmotion(const std::wstring& file_path, const std::wstring& wxid); - + INT64 Manager::SendApplet( + const std::wstring& recv_wxid, const std::wstring& waid_suff, + const std::wstring& waid_w, const std::string& waid_s, + const std::string& wa_wxid, const std::string& json_param, + const std::string& head_image, const std::string& big_image, + const std::string& index_page); + INT64 Test(); private: UINT64 base_addr_; + UINT64 js_api_addr_; }; } // namespace wxhelper diff --git a/src/utils.cc b/src/utils.cc index 106adc0..2623312 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,6 +1,7 @@ #include "pch.h" #include "utils.h" #include +#include #define BUFSIZE 1024 #define JPEG0 0xFF #define JPEG1 0xD8 @@ -398,4 +399,62 @@ INT64 Utils::DecodeImage(const wchar_t* file_path,const wchar_t* save_dir){ return 1; } +std::vector Utils::QWordScan(INT64 value, int align, + const wchar_t *module) { + MODULEINFO module_info; + std::vector result; + if (GetModuleInformation(GetCurrentProcess(), GetModuleHandleW(module), + &module_info, sizeof(module_info))) { + auto start = static_cast(module_info.lpBaseOfDll); + const auto end = start + module_info.SizeOfImage - 0x8; + + auto current_addr = start; + while (current_addr < end) { + if (*(INT64*)current_addr == value) { + result.push_back(reinterpret_cast(current_addr)); + } + start += align; + current_addr = start; + } + } + return result; +} + +std::vector Utils::QWordScan(INT64 value, INT64 start,int align) { + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + std::vector result; + INT64 min_addr = + reinterpret_cast(sys_info.lpMinimumApplicationAddress); + INT64 max_addr = + reinterpret_cast(sys_info.lpMaximumApplicationAddress); + const INT64 page_size = sys_info.dwPageSize; + min_addr = min_addr > start ? min_addr : start; + + auto current_addr = min_addr; + MEMORY_BASIC_INFORMATION mem_info = {}; + HANDLE handle = GetCurrentProcess(); + while (current_addr < max_addr) { + VirtualQueryEx(handle, reinterpret_cast(current_addr), &mem_info, + sizeof(MEMORY_BASIC_INFORMATION)); + + if ((INT64)mem_info.RegionSize <= 0) { + break; + } + INT64 region_size = mem_info.RegionSize; + if ((mem_info.State & MEM_COMMIT) == MEM_COMMIT && + (mem_info.Protect & PAGE_GUARD) != PAGE_GUARD && + (mem_info.Protect & PAGE_NOACCESS) != PAGE_NOACCESS) { + for (INT64 i = 0; i < region_size; i += align) { + if (value == *(INT64 *)current_addr) { + result.push_back(current_addr); + } + current_addr += align; + } + } else { + current_addr += region_size; + } + } + return result; +} } // namespace wxhelper \ No newline at end of file diff --git a/src/utils.h b/src/utils.h index 56e6023..26a7d80 100644 --- a/src/utils.h +++ b/src/utils.h @@ -60,6 +60,10 @@ class Utils { static INT64 DecodeImage(const wchar_t* file_path,const wchar_t* save_dir); + static std::vector QWordScan(INT64 value, int align, + const wchar_t *module); + + static std::vector QWordScan(INT64 value, INT64 start,int align); 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 ac53f83..aa78d6d 100644 --- a/src/wechat_function.h +++ b/src/wechat_function.h @@ -274,6 +274,14 @@ typedef UINT64 (*__GetPreDownLoadMgr)(); typedef UINT64 (*__PushAttachTask)(UINT64,UINT64,UINT64,UINT64); typedef UINT64 (*__GetCustomSmileyMgr)(); typedef UINT64 (*__SendCustomEmotion)(UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__JsApiShareAppMessage)(UINT64); +typedef UINT64 (*__InitJsConfig)(UINT64,UINT64); +typedef UINT64 (*__SendApplet)(UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__SendAppletSecond)(UINT64,UINT64,UINT64,UINT64,UINT64,UINT64); +typedef UINT64 (*__GetAppInfoByWaid)(UINT64,UINT64); +typedef UINT64 (*__CopyShareAppMessageRequest)(UINT64,UINT64); +typedef UINT64 (*__NewWAUpdatableMsgInfo)(UINT64); +typedef UINT64 (*__FreeWAUpdatableMsgInfo)(UINT64); } // namespace function @@ -313,6 +321,26 @@ struct WeChatString { } }; +struct WeChatStr{ + char * ptr; + INT64 buf; + INT64 len; + INT64 maxlen; + + WeChatStr(const char* p) { + ptr = (char *)p; + buf = 0; + len = strlen(p); + maxlen = len | 0xF; + } + WeChatStr() { + ptr = NULL; + buf = 0; + len = 0; + maxlen = 0xF; + } +}; + } // namespace prototype namespace offset { const UINT64 kGetAccountServiceMgr = 0x8c1230; @@ -404,6 +432,14 @@ const UINT64 kGetPreDownLoadMgr = 0x9996f0; const UINT64 kPushAttachTask = 0x9c0080; const UINT64 kGetCustomSmileyMgr = 0x915c00; const UINT64 kSendCustomEmotion = 0xec0a40; +const UINT64 kNewJsApiShareAppMessage = 0x13be1a0; +const UINT64 kInitJsConfig = 0x137bc00; +const UINT64 kSendApplet = 0x13c0920; +const UINT64 kSendAppletSecond = 0x13c1150; +const UINT64 kGetAppInfoByWaid = 0x13c5790; +const UINT64 kCopyShareAppMessageRequest = 0x13c0670; +const UINT64 kNewWAUpdatableMsgInfo = 0x919ca0; +const UINT64 kFreeWAUpdatableMsgInfo = 0x8fc230; } // namespace offset } // namespace V3_9_5_81