From 554d62efcf617290e0142eff3b6930f2b1e0de7d Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 28 Jan 2025 20:31:51 +0800 Subject: [PATCH 001/132] Refactor: replace C-style code with modern C++ idioms --- WeChatFerry/spy/receive_msg.cpp | 443 ++++++++++++-------------------- WeChatFerry/spy/receive_msg.h | 69 ++++- 2 files changed, 227 insertions(+), 285 deletions(-) diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index e283ce0..4aa92e3 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -1,23 +1,15 @@ -#pragma execution_character_set("utf-8") - -#include "MinHook.h" -#include "framework.h" + #include #include #include +#include "framework.h" + #include "log.hpp" #include "receive_msg.h" #include "user_info.h" #include "util.h" -// Defined in rpc_server.cpp -extern bool gIsLogging, gIsListening, gIsListeningPyq; -extern mutex gMutex; -extern condition_variable gCV; -extern queue gMsgQueue; - -// Defined in spy.cpp extern QWORD g_WeChatWinDllAddr; #define OS_RECV_MSG_ID 0x30 @@ -41,62 +33,10 @@ extern QWORD g_WeChatWinDllAddr; #define OS_PYQ_MSG_CALL 0x2E42C90 #define OS_WXLOG 0x2613D20 -typedef QWORD (*RecvMsg_t)(QWORD, QWORD); -typedef QWORD (*WxLog_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*RecvPyq_t)(QWORD, QWORD, QWORD); - -static RecvMsg_t funcRecvMsg = nullptr; -static RecvMsg_t realRecvMsg = nullptr; -static WxLog_t funcWxLog = nullptr; -static WxLog_t realWxLog = nullptr; -static RecvPyq_t funcRecvPyq = nullptr; -static RecvPyq_t realRecvPyq = nullptr; -static bool isMH_Initialized = false; - -MsgTypes_t GetMsgTypes() +QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) { - const MsgTypes_t m = { - { 0x00, "朋友圈消息" }, - { 0x01, "文字" }, - { 0x03, "图片" }, - { 0x22, "语音" }, - { 0x25, "好友确认" }, - { 0x28, "POSSIBLEFRIEND_MSG" }, - { 0x2A, "名片" }, - { 0x2B, "视频" }, - { 0x2F, "石头剪刀布 | 表情图片" }, - { 0x30, "位置" }, - { 0x31, "共享实时位置、文件、转账、链接" }, - { 0x32, "VOIPMSG" }, - { 0x33, "微信初始化" }, - { 0x34, "VOIPNOTIFY" }, - { 0x35, "VOIPINVITE" }, - { 0x3E, "小视频" }, - { 0x42, "微信红包" }, - { 0x270F, "SYSNOTICE" }, - { 0x2710, "红包、系统消息" }, - { 0x2712, "撤回消息" }, - { 0x100031, "搜狗表情" }, - { 0x1000031, "链接" }, - { 0x1A000031, "微信红包" }, - { 0x20010031, "红包封面" }, - { 0x2D000031, "视频号视频" }, - { 0x2E000031, "视频号名片" }, - { 0x31000031, "引用消息" }, - { 0x37000031, "拍一拍" }, - { 0x3A000031, "视频号直播" }, - { 0x3A100031, "商品链接" }, - { 0x3A200031, "视频号直播" }, - { 0x3E000031, "音乐链接" }, - { 0x41000031, "文件" }, - }; - - return m; -} - -static QWORD DispatchMsg(QWORD arg1, QWORD arg2) -{ - WxMsg_t wxMsg = { 0 }; + auto &handler = getInstance(); + WxMsg_t wxMsg = {}; try { wxMsg.id = GET_QWORD(arg2 + OS_RECV_MSG_ID); wxMsg.type = GET_DWORD(arg2 + OS_RECV_MSG_TYPE); @@ -105,66 +45,45 @@ static QWORD DispatchMsg(QWORD arg1, QWORD arg2) wxMsg.content = GetStringByWstrAddr(arg2 + OS_RECV_MSG_CONTENT); wxMsg.sign = GetStringByWstrAddr(arg2 + OS_RECV_MSG_SIGN); wxMsg.xml = GetStringByWstrAddr(arg2 + OS_RECV_MSG_XML); + wxMsg.roomid = GetStringByWstrAddr(arg2 + OS_RECV_MSG_ROOMID); - string roomid = GetStringByWstrAddr(arg2 + OS_RECV_MSG_ROOMID); - wxMsg.roomid = roomid; - if (roomid.find("@chatroom") != string::npos) { // 群 ID 的格式为 xxxxxxxxxxx@chatroom + if (wxMsg.roomid.find("@chatroom") != std::string::npos) { wxMsg.is_group = true; - if (wxMsg.is_self) { - wxMsg.sender = GetSelfWxid(); - } else { - wxMsg.sender = GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); - } + wxMsg.sender = wxMsg.is_self ? GetSelfWxid() : GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); } else { wxMsg.is_group = false; - if (wxMsg.is_self) { - wxMsg.sender = GetSelfWxid(); - } else { - wxMsg.sender = roomid; - } - } - - wxMsg.thumb = GetStringByWstrAddr(arg2 + OS_RECV_MSG_THUMB); - if (!wxMsg.thumb.empty()) { - wxMsg.thumb = GetHomePath() + wxMsg.thumb; - replace(wxMsg.thumb.begin(), wxMsg.thumb.end(), '\\', '/'); - } - - wxMsg.extra = GetStringByWstrAddr(arg2 + OS_RECV_MSG_EXTRA); - if (!wxMsg.extra.empty()) { - wxMsg.extra = GetHomePath() + wxMsg.extra; - replace(wxMsg.extra.begin(), wxMsg.extra.end(), '\\', '/'); + wxMsg.sender = wxMsg.is_self ? GetSelfWxid() : wxMsg.roomid; } } catch (const std::exception &e) { LOG_ERROR(GB2312ToUtf8(e.what())); - } catch (...) { - LOG_ERROR("Unknow exception."); } { - unique_lock lock(gMutex); - gMsgQueue.push(wxMsg); // 推送到队列 + std::unique_lock lock(handler.mutex_); + handler.msgQueue_.push(wxMsg); } - gCV.notify_all(); // 通知各方消息就绪 - return realRecvMsg(arg1, arg2); + handler.cv_.notify_all(); + return handler.realRecvMsg(arg1, arg2); } -static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9, - QWORD a10, QWORD a11, QWORD a12) +QWORD MessageHandler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, + QWORD a9, QWORD a10, QWORD a11, QWORD a12) { - QWORD p = realWxLog(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); + auto &handler = getInstance(); + + QWORD p = handler.realWxLog(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); if (p == 0 || p == 1) { return p; } LOG_INFO("【WX】\n{}", GB2312ToUtf8((char *)p)); - return p; } -static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) +void MessageHandler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) { + auto &handler = getInstance(); QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START); QWORD endAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_END); @@ -175,7 +94,7 @@ static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) while (startAddr < endAddr) { WxMsg_t wxMsg; - wxMsg.type = 0x00; // 朋友圈消息 + wxMsg.type = 0x00; wxMsg.is_self = false; wxMsg.is_group = false; wxMsg.id = GET_QWORD(startAddr); @@ -185,194 +104,162 @@ static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) wxMsg.content = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_CONTENT); { - unique_lock lock(gMutex); - gMsgQueue.push(wxMsg); // 推送到队列 + std::unique_lock lock(handler.mutex_); + handler.msgQueue_.push(wxMsg); } - gCV.notify_all(); // 通知各方消息就绪 - + handler.cv_.notify_all(); startAddr += 0x1618; } } -static MH_STATUS InitializeHook() +MessageHandler &MessageHandler::getInstance() { - if (isMH_Initialized) { - return MH_OK; + static MessageHandler instance; + return instance; +} + +MessageHandler::MessageHandler() +{ + isLogging = false; + isListeningMsg = false; + isListeningPyq = false; +} + +MessageHandler::~MessageHandler() +{ + DisableLog(); + UnListenMsg(); + UnListenPyq(); +} + +MsgTypes_t MessageHandler::GetMsgTypes() +{ + return { { 0x00, "朋友圈消息" }, + { 0x01, "文字" }, + { 0x03, "图片" }, + { 0x22, "语音" }, + { 0x25, "好友确认" }, + { 0x28, "POSSIBLEFRIEND_MSG" }, + { 0x2A, "名片" }, + { 0x2B, "视频" }, + { 0x2F, "石头剪刀布 | 表情图片" }, + { 0x30, "位置" }, + { 0x31, "共享实时位置、文件、转账、链接" }, + { 0x32, "VOIPMSG" }, + { 0x33, "微信初始化" }, + { 0x34, "VOIPNOTIFY" }, + { 0x35, "VOIPINVITE" }, + { 0x3E, "小视频" }, + { 0x42, "微信红包" }, + { 0x270F, "SYSNOTICE" }, + { 0x2710, "红包、系统消息" }, + { 0x2712, "撤回消息" }, + { 0x100031, "搜狗表情" }, + { 0x1000031, "链接" }, + { 0x1A000031, "微信红包" }, + { 0x20010031, "红包封面" }, + { 0x2D000031, "视频号视频" }, + { 0x2E000031, "视频号名片" }, + { 0x31000031, "引用消息" }, + { 0x37000031, "拍一拍" }, + { 0x3A000031, "视频号直播" }, + { 0x3A100031, "商品链接" }, + { 0x3A200031, "视频号直播" }, + { 0x3E000031, "音乐链接" }, + { 0x41000031, "文件" } }; +} + +std::optional MessageHandler::popMessage() +{ + std::lock_guard lock(mutex_); + if (msgQueue_.empty()) { + return std::nullopt; } + WxMsg_t msg = std::move(msgQueue_.front()); + msgQueue_.pop(); + return msg; +} + +int MessageHandler::EnableLog() +{ + if (isLogging) return 1; + + funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OS_WXLOG); + + if (InitializeHook() != MH_OK) return -1; + if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -1; + if (MH_EnableHook(funcWxLog) != MH_OK) return -1; + + isLogging = true; + return 0; +} + +int MessageHandler::DisableLog() +{ + if (!isLogging) return 1; + if (MH_DisableHook(funcWxLog) != MH_OK) return -1; + if (UninitializeHook() != MH_OK) return -1; + isLogging = false; + return 0; +} + +int MessageHandler::ListenMsg() +{ + if (isListeningMsg) return 1; + + funcRecvMsg = reinterpret_cast(g_WeChatWinDllAddr + OS_RECV_MSG_CALL); + if (InitializeHook() != MH_OK) return -1; + if (MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)) != MH_OK) return -1; + if (MH_EnableHook(funcRecvMsg) != MH_OK) return -1; + + isListeningMsg = true; + return 0; +} + +int MessageHandler::UnListenMsg() +{ + if (!isListeningMsg) return 1; + if (MH_DisableHook(funcRecvMsg) != MH_OK) return -1; + if (UninitializeHook() != MH_OK) return -1; + isListeningMsg = false; + return 0; +} + +int MessageHandler::ListenPyq() +{ + if (isListeningPyq) return 1; + + funcRecvPyq = reinterpret_cast(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL); + if (InitializeHook() != MH_OK) return -1; + if (MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)) != MH_OK) return -1; + if (MH_EnableHook(funcRecvPyq) != MH_OK) return -1; + + isListeningPyq = true; + return 0; +} + +int MessageHandler::UnListenPyq() +{ + if (!isListeningPyq) return 1; + if (MH_DisableHook(funcRecvPyq) != MH_OK) return -1; + if (UninitializeHook() != MH_OK) return -1; + isListeningPyq = false; + return 0; +} + +MH_STATUS MessageHandler::InitializeHook() +{ + if (isMH_Initialized) return MH_OK; MH_STATUS status = MH_Initialize(); - if (status == MH_OK) { - isMH_Initialized = true; - } + if (status == MH_OK) isMH_Initialized = true; return status; } -static MH_STATUS UninitializeHook() +MH_STATUS MessageHandler::UninitializeHook() { - if (!isMH_Initialized) { - return MH_OK; - } - if (gIsLogging || gIsListening || gIsListeningPyq) { - return MH_OK; - } + if (!isMH_Initialized || isLogging || isListeningMsg || isListeningPyq) return MH_OK; MH_STATUS status = MH_Uninitialize(); - if (status == MH_OK) { - isMH_Initialized = false; - } + if (status == MH_OK) isMH_Initialized = false; return status; } - -void EnableLog() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsLogging) { - LOG_WARN("gIsLogging"); - return; - } - WxLog_t funcWxLog = (WxLog_t)(g_WeChatWinDllAddr + OS_WXLOG); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcWxLog); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - gIsLogging = true; -} - -void DisableLog() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsLogging) { - return; - } - - status = MH_DisableHook(funcWxLog); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsLogging = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} - -void ListenMessage() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsListening) { - LOG_WARN("gIsListening"); - return; - } - funcRecvMsg = (RecvMsg_t)(g_WeChatWinDllAddr + OS_RECV_MSG_CALL); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcRecvMsg); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - - gIsListening = true; -} - -void UnListenMessage() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsListening) { - return; - } - - status = MH_DisableHook(funcRecvMsg); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsListening = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} - -void ListenPyq() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsListeningPyq) { - LOG_WARN("gIsListeningPyq"); - return; - } - funcRecvPyq = (RecvPyq_t)(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcRecvPyq); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - - gIsListeningPyq = true; -} - -void UnListenPyq() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsListeningPyq) { - return; - } - - status = MH_DisableHook(funcRecvPyq); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsListeningPyq = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} diff --git a/WeChatFerry/spy/receive_msg.h b/WeChatFerry/spy/receive_msg.h index 73d8759..0e2f314 100644 --- a/WeChatFerry/spy/receive_msg.h +++ b/WeChatFerry/spy/receive_msg.h @@ -1,11 +1,66 @@ #pragma once +#include +#include +#include +#include +#include +#include + +#include "MinHook.h" + #include "pb_types.h" -void EnableLog(); -void DisableLog(); -void ListenPyq(); -void UnListenPyq(); -void ListenMessage(); -void UnListenMessage(); -MsgTypes_t GetMsgTypes(); +class MessageHandler +{ +public: + static MessageHandler &getInstance(); + + // 0: 成功, -1: 失败, 1: 已经开启 + int EnableLog(); + int DisableLog(); + int ListenPyq(); + int UnListenPyq(); + int ListenMsg(); + int UnListenMsg(); + + MsgTypes_t GetMsgTypes(); + + bool isLoggingEnabled() const { return isLogging.load(); } + bool isMessageListening() const { return isListeningMsg.load(); } + bool isPyqListening() const { return isListeningPyq.load(); } + + std::optional popMessage(); + std::condition_variable &getConditionVariable() { return cv_; }; + std::mutex &getMutex() { return mutex_; }; + +private: + MessageHandler(); + ~MessageHandler(); + + mutable std::mutex mutex_; + std::condition_variable cv_; + std::queue msgQueue_; + + std::atomic isLogging { false }; + std::atomic isListeningMsg { false }; + std::atomic isListeningPyq { false }; + + using funcRecvMsg_t = QWORD (*)(QWORD, QWORD); + using funcWxLog_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); + using funcRecvPyq_t = QWORD (*)(QWORD, QWORD, QWORD); + + funcWxLog_t funcWxLog, realWxLog; + funcRecvMsg_t funcRecvMsg, realRecvMsg; + funcRecvPyq_t funcRecvPyq, realRecvPyq; + + bool isMH_Initialized { false }; + + MH_STATUS InitializeHook(); + MH_STATUS UninitializeHook(); + + static QWORD DispatchMsg(QWORD arg1, QWORD arg2); + static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9, + QWORD a10, QWORD a11, QWORD a12); + static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3); +}; From 15267632a10e1e0d9c8a239cbdd900bf9dcce03b Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 29 Jan 2025 18:32:20 +0800 Subject: [PATCH 002/132] Refactor: replace C-style code with modern C++ idioms --- WeChatFerry/spy/funcs.cpp | 43 ++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp index c12f9b5..56b1b8c 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/funcs.cpp @@ -8,13 +8,13 @@ #include "exec_sql.h" #include "funcs.h" #include "log.hpp" +#include "receive_msg.h" #include "spy_types.h" #include "util.h" using namespace std; namespace fs = std::filesystem; -extern bool gIsListeningPyq; extern QWORD g_WeChatWinDllAddr; #define HEADER_PNG1 0x89 @@ -111,32 +111,25 @@ string DecryptImage(string src, string dir) string dst = ""; - try { - if (dir.empty()) { - dst = fs::path(src).replace_extension(ext).string(); - } else { - dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - - // 判断dir文件夹是否存在,若不存在则创建(否则将无法创建出文件) - if (!fs::exists(dst)) {//判断该文件夹是否存在 - bool success = fs::create_directories(dst); //Windows创建文件夹 - if (!success) { //创建失败 - LOG_ERROR("Failed to mkdir:{}", dst); - return ""; - } + if (dir.empty()) { + dst = fs::path(src).replace_extension(ext).string(); + } else { + dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); + + // 判断dir文件夹是否存在,若不存在则创建(否则将无法创建出文件) + if (!fs::exists(dst)) { // 判断该文件夹是否存在 + bool success = fs::create_directories(dst); // Windows创建文件夹 + if (!success) { // 创建失败 + LOG_ERROR("Failed to mkdir:{}", dst); + return ""; } - - dst += fs::path(src).stem().string() + ext; } - replace(dst.begin(), dst.end(), '\\', '/'); - } catch (const std::exception &e) { - LOG_ERROR(GB2312ToUtf8(e.what())); - } catch (...) { - LOG_ERROR("Unknow exception."); - return ""; + dst += fs::path(src).stem().string() + ext; } + replace(dst.begin(), dst.end(), '\\', '/'); + ofstream out(dst.c_str(), ios::binary); if (!out.is_open()) { LOG_ERROR("Failed to write file {}", dst); @@ -178,7 +171,8 @@ static int GetNextPage(QWORD id) int RefreshPyq(QWORD id) { - if (!gIsListeningPyq) { + auto &msgHandler = MessageHandler::getInstance(); + if (!msgHandler.isPyqListening()) { LOG_ERROR("没有启动朋友圈消息接收,参考:enable_receiving_msg"); return -1; } @@ -319,7 +313,7 @@ string GetPCMAudio(uint64_t id, string dir, int32_t sr) SilkDecode(silk, pcm, sr); errno_t err; - FILE* fPCM; + FILE *fPCM; err = fopen_s(&fPCM, pcmpath.c_str(), "wb"); if (err != 0) { printf("Error: could not open input file %s\n", pcmpath.c_str()); @@ -332,7 +326,6 @@ string GetPCMAudio(uint64_t id, string dir, int32_t sr) return pcmpath; } - OcrResult_t GetOcrResult(string path) { OcrResult_t ret = { -1, "" }; From 2a27dec354b4ce28dfe85cdf8ae37c9a193ce4fc Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 30 Jan 2025 20:34:45 +0800 Subject: [PATCH 003/132] Refactor: replace C-style code with modern C++ idioms --- WeChatFerry/spy/rpc_server.cpp | 196 ++++++++++++++++++--------------- 1 file changed, 106 insertions(+), 90 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 1d09e30..71c8f60 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -39,18 +39,19 @@ namespace fs = std::filesystem; -bool gIsLogging = false; -bool gIsListening = false; -bool gIsListeningPyq = false; -mutex gMutex; -condition_variable gCV; -queue gMsgQueue; +constexpr size_t DEFAULT_BUF_SIZE = 16 * 1024 * 1024; -static int lport = 0; -static DWORD lThreadId = 0; -static bool lIsRunning = false; -static nng_socket cmdSock, msgSock; // TODO: 断开检测 -static uint8_t gBuffer[G_BUF_SIZE] = { 0 }; +static int cmdPort = 0; +static bool isRpcRunning = false; +static bool isReveivingMsg = false; +static HANDLE cmdThread = NULL; +static HANDLE msgThread = NULL; +static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 +static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 + +auto &msgHandler = MessageHandler::getInstance(); + +static std::string BuildUrl(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } bool func_is_login(uint8_t *out, size_t *len) { @@ -116,7 +117,7 @@ bool func_get_msg_types(uint8_t *out, size_t *len) rsp.func = Functions_FUNC_GET_MSG_TYPES; rsp.which_msg = Response_types_tag; - MsgTypes_t types = GetMsgTypes(); + MsgTypes_t types = msgHandler.GetMsgTypes(); rsp.msg.types.types.funcs.encode = encode_types; rsp.msg.types.types.arg = &types; @@ -429,67 +430,68 @@ bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t *len) static void PushMessage() { - static uint8_t buffer[G_BUF_SIZE] = { 0 }; - int rv; Response rsp = Response_init_default; rsp.func = Functions_FUNC_ENABLE_RECV_TXT; rsp.which_msg = Response_wxmsg_tag; + std::vector msgBuffer(DEFAULT_BUF_SIZE); - pb_ostream_t stream = pb_ostream_from_buffer(buffer, G_BUF_SIZE); + pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); - char url[URL_SIZE + 1] = { 0 }; - sprintf_s(url, URL_SIZE, "%s:%d", BASE_URL, lport + 1); + std::string url = BuildUrl(cmdPort + 1); if ((rv = nng_pair1_open(&msgSock)) != 0) { LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); return; } - if ((rv = nng_listen(msgSock, url, NULL, 0)) != 0) { + if ((rv = nng_listen(msgSock, url.c_str(), NULL, 0)) != 0) { LOG_ERROR("nng_listen error {}", nng_strerror(rv)); return; } - LOG_INFO("MSG Server listening on {}", url); - if ((rv = nng_setopt_ms(msgSock, NNG_OPT_SENDTIMEO, 2000)) != 0) { + LOG_INFO("MSG Server listening on {}", url.c_str()); + if ((rv = nng_setopt_ms(msgSock, NNG_OPT_SENDTIMEO, 5000)) != 0) { LOG_ERROR("nng_setopt_ms: {}", nng_strerror(rv)); return; } - while (gIsListening) { - unique_lock lock(gMutex); - if (gCV.wait_for(lock, chrono::milliseconds(1000), []() { return !gMsgQueue.empty(); })) { - while (!gMsgQueue.empty()) { - auto wxmsg = gMsgQueue.front(); - rsp.msg.wxmsg.id = wxmsg.id; - rsp.msg.wxmsg.is_self = wxmsg.is_self; - rsp.msg.wxmsg.is_group = wxmsg.is_group; - rsp.msg.wxmsg.type = wxmsg.type; - rsp.msg.wxmsg.ts = wxmsg.ts; - rsp.msg.wxmsg.roomid = (char *)wxmsg.roomid.c_str(); - rsp.msg.wxmsg.content = (char *)wxmsg.content.c_str(); - rsp.msg.wxmsg.sender = (char *)wxmsg.sender.c_str(); - rsp.msg.wxmsg.sign = (char *)wxmsg.sign.c_str(); - rsp.msg.wxmsg.thumb = (char *)wxmsg.thumb.c_str(); - rsp.msg.wxmsg.extra = (char *)wxmsg.extra.c_str(); - rsp.msg.wxmsg.xml = (char *)wxmsg.xml.c_str(); - gMsgQueue.pop(); - LOG_DEBUG("Push msg: {}", wxmsg.content); - pb_ostream_t stream = pb_ostream_from_buffer(buffer, G_BUF_SIZE); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - continue; - } + while (msgHandler.isMessageListening()) { + std::unique_lock lock(msgHandler.getMutex()); + std::optional msgOpt; + auto hasMessage = [&]() { + msgOpt = msgHandler.popMessage(); + return msgOpt.has_value(); + }; - rv = nng_send(msgSock, buffer, stream.bytes_written, 0); - if (rv != 0) { - LOG_ERROR("msgSock-nng_send: {}", nng_strerror(rv)); - } - LOG_DEBUG("Send data length {}", stream.bytes_written); + if (msgHandler.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { + WxMsg_t wxmsg = std::move(msgOpt.value()); + rsp.msg.wxmsg.id = wxmsg.id; + rsp.msg.wxmsg.is_self = wxmsg.is_self; + rsp.msg.wxmsg.is_group = wxmsg.is_group; + rsp.msg.wxmsg.type = wxmsg.type; + rsp.msg.wxmsg.ts = wxmsg.ts; + rsp.msg.wxmsg.roomid = (char *)wxmsg.roomid.c_str(); + rsp.msg.wxmsg.content = (char *)wxmsg.content.c_str(); + rsp.msg.wxmsg.sender = (char *)wxmsg.sender.c_str(); + rsp.msg.wxmsg.sign = (char *)wxmsg.sign.c_str(); + rsp.msg.wxmsg.thumb = (char *)wxmsg.thumb.c_str(); + rsp.msg.wxmsg.extra = (char *)wxmsg.extra.c_str(); + rsp.msg.wxmsg.xml = (char *)wxmsg.xml.c_str(); + + LOG_DEBUG("Push msg: {}", wxmsg.content); + pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); + if (!pb_encode(&stream, Response_fields, &rsp)) { + LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); + continue; } + + rv = nng_send(msgSock, msgBuffer.data(), stream.bytes_written, 0); + if (rv != 0) { + LOG_ERROR("msgSock-nng_send: {}", nng_strerror(rv)); + } + LOG_DEBUG("Send data length {}", stream.bytes_written); } } - nng_close(msgSock); } bool func_enable_recv_txt(bool pyq, uint8_t *out, size_t *len) @@ -497,22 +499,18 @@ bool func_enable_recv_txt(bool pyq, uint8_t *out, size_t *len) Response rsp = Response_init_default; rsp.func = Functions_FUNC_ENABLE_RECV_TXT; rsp.which_msg = Response_status_tag; - rsp.msg.status = 0; + rsp.msg.status = msgHandler.ListenMsg(); - if (!gIsListening) { - ListenMessage(); + if (rsp.msg.status == 0) { if (pyq) { - ListenPyq(); + msgHandler.ListenPyq(); } - HANDLE msgThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PushMessage, NULL, NULL, NULL); + msgThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PushMessage, NULL, NULL, NULL); if (msgThread == NULL) { rsp.msg.status = GetLastError(); LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); - } else { - CloseHandle(msgThread); } } - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); if (!pb_encode(&stream, Response_fields, &rsp)) { LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); @@ -528,10 +526,15 @@ bool func_disable_recv_txt(uint8_t *out, size_t *len) Response rsp = Response_init_default; rsp.func = Functions_FUNC_DISABLE_RECV_TXT; rsp.which_msg = Response_status_tag; - rsp.msg.status = 0; + rsp.msg.status = msgHandler.UnListenMsg(); - UnListenPyq(); - UnListenMessage(); // 可能需要1秒之后才能退出,见 PushMessage + if (rsp.msg.status == 0) { + msgHandler.UnListenPyq(); + if (msgThread != NULL) { + TerminateThread(msgThread, 0); + msgThread = NULL; + } + } pb_ostream_t stream = pb_ostream_from_buffer(out, *len); if (!pb_encode(&stream, Response_fields, &rsp)) { @@ -990,52 +993,51 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len return ret; } -static int RunServer() +static int RunRpcServer() { - int rv = 0; - char url[URL_SIZE + 1] = { 0 }; - sprintf_s(url, URL_SIZE, "%s:%d", BASE_URL, lport); + int rv = 0; + std::string url = BuildUrl(cmdPort); if ((rv = nng_pair1_open(&cmdSock)) != 0) { LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); return rv; } - if ((rv = nng_listen(cmdSock, (char *)url, NULL, 0)) != 0) { + if ((rv = nng_listen(cmdSock, url.c_str(), NULL, 0)) != 0) { LOG_ERROR("nng_listen error {}", nng_strerror(rv)); return rv; } - LOG_INFO("CMD Server listening on {}", (char *)url); + LOG_INFO("CMD Server listening on {}", url.c_str()); if ((rv = nng_setopt_ms(cmdSock, NNG_OPT_SENDTIMEO, 1000)) != 0) { LOG_ERROR("nng_setopt_ms error: {}", nng_strerror(rv)); return rv; } - lIsRunning = true; - while (lIsRunning) { + std::vector cmdBuffer(DEFAULT_BUF_SIZE); + isRpcRunning = true; + while (isRpcRunning) { uint8_t *in = NULL; - size_t in_len, out_len = G_BUF_SIZE; + size_t in_len, out_len = cmdBuffer.size(); if ((rv = nng_recv(cmdSock, &in, &in_len, NNG_FLAG_ALLOC)) != 0) { LOG_ERROR("cmdSock-nng_recv error: {}", nng_strerror(rv)); break; } try { // LOG_BUFFER(in, in_len); - if (dispatcher(in, in_len, gBuffer, &out_len)) { + if (dispatcher(in, in_len, cmdBuffer.data(), &out_len)) { LOG_DEBUG("Send data length {}", out_len); - // LOG_BUFFER(gBuffer, out_len); - rv = nng_send(cmdSock, gBuffer, out_len, 0); + // LOG_BUFFER(cmdBuffer.data(), out_len); + rv = nng_send(cmdSock, cmdBuffer.data(), out_len, 0); if (rv != 0) { LOG_ERROR("cmdSock-nng_send: {}", nng_strerror(rv)); } } else { // Error LOG_ERROR("Dispatcher failed..."); - rv = nng_send(cmdSock, gBuffer, 0, 0); + rv = nng_send(cmdSock, cmdBuffer.data(), 0, 0); if (rv != 0) { LOG_ERROR("cmdSock-nng_send: {}", nng_strerror(rv)); } - // break; } } catch (const std::exception &e) { LOG_ERROR(GB2312ToUtf8(e.what())); @@ -1045,21 +1047,22 @@ static int RunServer() nng_free(in, in_len); } RpcStopServer(); - LOG_DEBUG("Leave RunServer"); + LOG_DEBUG("Leave RunRpcServer"); return rv; } int RpcStartServer(int port) { - if (lIsRunning) { - return 0; + if (isRpcRunning) { + LOG_WARN("RPC 服务已经启动"); + return 1; } - lport = port; - - HANDLE rpcThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunServer, NULL, NULL, &lThreadId); - if (rpcThread != 0) { - CloseHandle(rpcThread); + cmdPort = port; + cmdThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunRpcServer, NULL, NULL, NULL); + if (cmdThread == NULL) { + LOG_ERROR("CreateThread failed: {}", GetLastError()); + return -1; } #if ENABLE_WX_LOG EnableLog(); @@ -1069,16 +1072,29 @@ int RpcStartServer(int port) int RpcStopServer() { - if (lIsRunning) { - nng_close(cmdSock); - nng_close(msgSock); - // UnListenMessage(); - lIsRunning = false; - Sleep(1000); - LOG_INFO("Server stoped."); + if (!isRpcRunning) { + LOG_WARN("RPC 服务未启动"); + return 1; } + + nng_close(cmdSock); + nng_close(msgSock); + msgHandler.UnListenPyq(); + msgHandler.UnListenMsg(); #if ENABLE_WX_LOG DisableLog(); #endif + if (cmdThread != NULL) { + WaitForSingleObject(cmdThread, INFINITE); + CloseHandle(cmdThread); + cmdThread = NULL; + } + + if (msgThread != NULL) { + WaitForSingleObject(msgThread, INFINITE); + CloseHandle(msgThread); + msgThread = NULL; + } + isRpcRunning = false; return 0; } From 7f4f1c54d6cb3246cea13ce836aaf3d01643a25c Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 30 Jan 2025 20:47:06 +0800 Subject: [PATCH 004/132] Remove unused code --- WeChatFerry/spy/rpc_server.cpp | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 71c8f60..84740d6 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -33,21 +33,16 @@ #include "user_info.h" #include "util.h" -#define URL_SIZE 20 -#define BASE_URL "tcp://0.0.0.0" -#define G_BUF_SIZE (16 * 1024 * 1024) - namespace fs = std::filesystem; constexpr size_t DEFAULT_BUF_SIZE = 16 * 1024 * 1024; -static int cmdPort = 0; -static bool isRpcRunning = false; -static bool isReveivingMsg = false; -static HANDLE cmdThread = NULL; -static HANDLE msgThread = NULL; -static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 -static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 +static int cmdPort = 0; +static bool isRpcRunning = false; +static HANDLE cmdThread = NULL; +static HANDLE msgThread = NULL; +static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 +static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 auto &msgHandler = MessageHandler::getInstance(); From d59aa751cb6056ae832356148cdfb6e72e13fbf6 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 30 Jan 2025 22:30:56 +0800 Subject: [PATCH 005/132] Refactoring --- WeChatFerry/spy/rpc_server.cpp | 895 ++++++++++----------------------- 1 file changed, 279 insertions(+), 616 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 84740d6..2192558 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -48,12 +48,14 @@ auto &msgHandler = MessageHandler::getInstance(); static std::string BuildUrl(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } -bool func_is_login(uint8_t *out, size_t *len) +template +static bool FillResponse(int which_msg, uint8_t *out, size_t *len, AssignFunc assign) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_IS_LOGIN; - rsp.which_msg = Response_status_tag; - rsp.msg.status = IsLogin(); + Response rsp = Response_init_default; + rsp.func = funcType; + rsp.which_msg = which_msg; + + assign(rsp); pb_ostream_t stream = pb_ostream_from_buffer(out, *len); if (!pb_encode(&stream, Response_fields, &rsp)) { @@ -61,366 +63,199 @@ bool func_is_login(uint8_t *out, size_t *len) return false; } *len = stream.bytes_written; - return true; } -bool func_get_self_wxid(uint8_t *out, size_t *len) +static bool func_is_login(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_SELF_WXID; - rsp.which_msg = Response_str_tag; - - string wxid = GetSelfWxid(); - rsp.msg.str = (char *)wxid.c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, + [](Response &rsp) { rsp.msg.status = IsLogin(); }); } -bool func_get_user_info(uint8_t *out, size_t *len) +static bool func_get_self_wxid(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_USER_INFO; - rsp.which_msg = Response_ui_tag; - - UserInfo_t ui = GetUserInfo(); - rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); - rsp.msg.ui.name = (char *)ui.name.c_str(); - rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); - rsp.msg.ui.home = (char *)ui.home.c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_str_tag, out, len, [](Response &rsp) { + std::string wxid = GetSelfWxid(); + rsp.msg.str = wxid.empty() ? nullptr : (char *)wxid.c_str(); + }); } -bool func_get_msg_types(uint8_t *out, size_t *len) +static bool func_get_user_info(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_MSG_TYPES; - rsp.which_msg = Response_types_tag; - - MsgTypes_t types = msgHandler.GetMsgTypes(); - rsp.msg.types.types.funcs.encode = encode_types; - rsp.msg.types.types.arg = &types; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_ui_tag, out, len, [](Response &rsp) { + UserInfo_t ui = GetUserInfo(); + rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); + rsp.msg.ui.name = (char *)ui.name.c_str(); + rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); + rsp.msg.ui.home = (char *)ui.home.c_str(); + }); } -bool func_get_contacts(uint8_t *out, size_t *len) +static bool func_get_msg_types(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_CONTACTS; - rsp.which_msg = Response_contacts_tag; - - vector contacts = GetContacts(); - rsp.msg.contacts.contacts.funcs.encode = encode_contacts; - rsp.msg.contacts.contacts.arg = &contacts; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_types_tag, out, len, [](Response &rsp) { + static MsgTypes_t types = msgHandler.GetMsgTypes(); + rsp.msg.types.types.funcs.encode = encode_types; + rsp.msg.types.types.arg = &types; + }); } -bool func_get_db_names(uint8_t *out, size_t *len) +static bool func_get_contacts(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_DB_NAMES; - rsp.which_msg = Response_dbs_tag; - - DbNames_t dbnames = GetDbNames(); - rsp.msg.dbs.names.funcs.encode = encode_dbnames; - rsp.msg.dbs.names.arg = &dbnames; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_contacts_tag, out, len, [](Response &rsp) { + static std::vector contacts = GetContacts(); + rsp.msg.contacts.contacts.funcs.encode = encode_contacts; + rsp.msg.contacts.contacts.arg = &contacts; + }); } -bool func_get_db_tables(char *db, uint8_t *out, size_t *len) +static bool func_get_db_names(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_DB_TABLES; - rsp.which_msg = Response_tables_tag; - - DbTables_t tables = GetDbTables(db); - rsp.msg.tables.tables.funcs.encode = encode_tables; - rsp.msg.tables.tables.arg = &tables; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_dbs_tag, out, len, [](Response &rsp) { + static DbNames_t dbnames = GetDbNames(); + rsp.msg.dbs.names.funcs.encode = encode_dbnames; + rsp.msg.dbs.names.arg = &dbnames; + }); } -bool func_get_audio_msg(uint64_t id, char *dir, uint8_t *out, size_t *len) +static bool func_get_db_tables(char *db, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_AUDIO_MSG; - rsp.which_msg = Response_str_tag; - string path = ""; - - if (dir == NULL) { - LOG_ERROR("Empty dir."); - } else { - path = GetAudio(id, dir); - } - - rsp.msg.str = (char *)path.c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_tables_tag, out, len, [db](Response &rsp) { + static DbTables_t tables = GetDbTables(db); + rsp.msg.tables.tables.funcs.encode = encode_tables; + rsp.msg.tables.tables.arg = &tables; + }); } -bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len) +static bool func_get_audio_msg(uint64_t id, char *dir, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_TXT; - rsp.which_msg = Response_status_tag; - - if ((txt.msg == NULL) || (txt.receiver == NULL)) { - LOG_ERROR("Empty message or receiver."); - rsp.msg.status = -1; // Empty message or empty receiver - } else { - string msg(txt.msg); - string receiver(txt.receiver); - string aters(txt.aters ? txt.aters : ""); - - SendTextMessage(receiver, msg, aters); - rsp.msg.status = 0; - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_str_tag, out, len, [id, dir](Response &rsp) { + std::string path = (dir == nullptr) ? "" : GetAudio(id, dir); + rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); + }); } -bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) +static bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_IMG; - rsp.which_msg = Response_status_tag; - - if ((path == NULL) || (receiver == NULL)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else if (!fs::exists(String2Wstring(path))) { - LOG_ERROR("Path does not exists: {}", path); - rsp.msg.status = -2; - } else { - SendImageMessage(receiver, path); - rsp.msg.status = 0; - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [txt](Response &rsp) { + if ((txt.msg == NULL) || (txt.receiver == NULL)) { + LOG_ERROR("Empty message or receiver."); + rsp.msg.status = -1; + } else { + std::string msg(txt.msg); + std::string receiver(txt.receiver); + std::string aters(txt.aters ? txt.aters : ""); + SendTextMessage(receiver, msg, aters); + rsp.msg.status = 0; + } + }); } -bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len) +static bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_FILE; - rsp.which_msg = Response_status_tag; - - if ((path == NULL) || (receiver == NULL)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else if (!fs::exists(String2Wstring(path))) { - LOG_ERROR("Path does not exists: {}", path); - rsp.msg.status = -2; - } else { - SendFileMessage(receiver, path); - rsp.msg.status = 0; - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { + if ((path == NULL) || (receiver == NULL)) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else if (!fs::exists(String2Wstring(path))) { + LOG_ERROR("Path does not exist: {}", path); + rsp.msg.status = -2; + } else { + SendImageMessage(receiver, path); + rsp.msg.status = 0; + } + }); } -bool func_send_emotion(char *path, char *receiver, uint8_t *out, size_t *len) +static bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_EMOTION; - rsp.which_msg = Response_status_tag; - - if ((path == NULL) || (receiver == NULL)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else { - SendEmotionMessage(receiver, path); - rsp.msg.status = 0; - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { + if ((path == nullptr) || (receiver == nullptr)) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else if (!fs::exists(String2Wstring(path))) { + LOG_ERROR("Path does not exist: {}", path); + rsp.msg.status = -2; + } else { + SendFileMessage(receiver, path); + rsp.msg.status = 0; + } + }); } -bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len) +static bool func_send_emotion(char *path, char *receiver, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_XML; - rsp.which_msg = Response_status_tag; - - if ((xml.content == NULL) || (xml.receiver == NULL)) { - LOG_ERROR("Empty content or receiver."); - rsp.msg.status = -1; - } else { - string receiver(xml.receiver); - string content(xml.content); - string path(xml.path ? xml.path : ""); - uint64_t type = (uint64_t)xml.type; - SendXmlMessage(receiver, content, path, type); - rsp.msg.status = 0; - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { + if ((path == nullptr) || (receiver == nullptr)) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else { + SendEmotionMessage(receiver, path); + rsp.msg.status = 0; + } + }); } -bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) +static bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_RICH_TXT; - rsp.which_msg = Response_status_tag; - - if (rt.receiver == NULL) { - LOG_ERROR("Empty receiver."); - rsp.msg.status = -1; - } else { - RichText_t rtt; - rtt.account = string(rt.account ? rt.account : ""); - rtt.digest = string(rt.digest ? rt.digest : ""); - rtt.name = string(rt.name ? rt.name : ""); - rtt.receiver = string(rt.receiver ? rt.receiver : ""); - rtt.thumburl = string(rt.thumburl ? rt.thumburl : ""); - rtt.title = string(rt.title ? rt.title : ""); - rtt.url = string(rt.url ? rt.url : ""); - - rsp.msg.status = SendRichTextMessage(rtt); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [xml](Response &rsp) { + if ((xml.content == nullptr) || (xml.receiver == nullptr)) { + LOG_ERROR("Empty content or receiver."); + rsp.msg.status = -1; + } else { + std::string receiver(xml.receiver); + std::string content(xml.content); + std::string path(xml.path ? xml.path : ""); + uint64_t type = static_cast(xml.type); + SendXmlMessage(receiver, content, path, type); + rsp.msg.status = 0; + } + }); } -bool func_send_pat_msg(char *roomid, char *wxid, uint8_t *out, size_t *len) +static bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_SEND_PAT_MSG; - rsp.which_msg = Response_status_tag; + return FillResponse(Response_status_tag, out, len, [rt](Response &rsp) { + if (rt.receiver == nullptr) { + LOG_ERROR("Empty receiver."); + rsp.msg.status = -1; + } else { + RichText_t rtt; + rtt.account = std::string(rt.account ? rt.account : ""); + rtt.digest = std::string(rt.digest ? rt.digest : ""); + rtt.name = std::string(rt.name ? rt.name : ""); + rtt.receiver = std::string(rt.receiver ? rt.receiver : ""); + rtt.thumburl = std::string(rt.thumburl ? rt.thumburl : ""); + rtt.title = std::string(rt.title ? rt.title : ""); + rtt.url = std::string(rt.url ? rt.url : ""); - if ((roomid == NULL) || (wxid == NULL)) { - LOG_ERROR("Empty roomid or wxid."); - rsp.msg.status = -1; - } else { - rsp.msg.status = SendPatMessage(roomid, wxid); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + rsp.msg.status = SendRichTextMessage(rtt); + } + }); } -bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t *len) +static bool func_send_pat_msg(char *roomid, char *wxid, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_FORWARD_MSG; - rsp.which_msg = Response_status_tag; + return FillResponse(Response_status_tag, out, len, [roomid, wxid](Response &rsp) { + if ((roomid == nullptr) || (wxid == nullptr)) { + LOG_ERROR("Empty roomid or wxid."); + rsp.msg.status = -1; + } else { + rsp.msg.status = SendPatMessage(roomid, wxid); + } + }); +} - if (receiver == NULL) { - LOG_ERROR("Empty roomid or wxid."); - rsp.msg.status = -1; - } else { - rsp.msg.status = ForwardMessage(id, receiver); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; +static bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t *len) +{ + return FillResponse(Response_status_tag, out, len, [id, receiver](Response &rsp) { + if (receiver == nullptr) { + LOG_ERROR("Empty receiver."); + rsp.msg.status = -1; + } else { + rsp.msg.status = ForwardMessage(id, receiver); + } + }); } static void PushMessage() @@ -489,351 +324,179 @@ static void PushMessage() } } -bool func_enable_recv_txt(bool pyq, uint8_t *out, size_t *len) +static bool func_enable_recv_txt(bool pyq, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_ENABLE_RECV_TXT; - rsp.which_msg = Response_status_tag; - rsp.msg.status = msgHandler.ListenMsg(); - - if (rsp.msg.status == 0) { - if (pyq) { - msgHandler.ListenPyq(); + return FillResponse(Response_status_tag, out, len, [pyq](Response &rsp) { + rsp.msg.status = msgHandler.ListenMsg(); + if (rsp.msg.status == 0) { + if (pyq) { + msgHandler.ListenPyq(); + } + msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)PushMessage, nullptr, 0, nullptr); + if (msgThread == nullptr) { + rsp.msg.status = GetLastError(); + LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); + } } - msgThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PushMessage, NULL, NULL, NULL); - if (msgThread == NULL) { - rsp.msg.status = GetLastError(); - LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); + }); +} + +static bool func_disable_recv_txt(uint8_t *out, size_t *len) +{ + return FillResponse(Response_status_tag, out, len, [](Response &rsp) { + rsp.msg.status = msgHandler.UnListenMsg(); + if (rsp.msg.status == 0) { + msgHandler.UnListenPyq(); + if (msgThread != nullptr) { + TerminateThread(msgThread, 0); + msgThread = nullptr; + } } - } - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + }); } -bool func_disable_recv_txt(uint8_t *out, size_t *len) +static bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_DISABLE_RECV_TXT; - rsp.which_msg = Response_status_tag; - rsp.msg.status = msgHandler.UnListenMsg(); - - if (rsp.msg.status == 0) { - msgHandler.UnListenPyq(); - if (msgThread != NULL) { - TerminateThread(msgThread, 0); - msgThread = NULL; + return FillResponse(Response_rows_tag, out, len, [db, sql](Response &rsp) { + static DbRows_t rows; + if ((db == nullptr) || (sql == nullptr)) { + LOG_ERROR("Empty db or sql."); + } else { + rows = ExecDbQuery(db, sql); } - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + rsp.msg.rows.rows.arg = &rows; + rsp.msg.rows.rows.funcs.encode = encode_rows; + }); } -bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) +static bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_EXEC_DB_QUERY; - rsp.which_msg = Response_rows_tag; - DbRows_t rows; - - if ((db == NULL) || (sql == NULL)) { - LOG_ERROR("Empty db or sql."); - } else { - rows = ExecDbQuery(db, sql); - } - - rsp.msg.rows.rows.arg = &rows; - rsp.msg.rows.rows.funcs.encode = encode_rows; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, + [id](Response &rsp) { rsp.msg.status = RefreshPyq(id); }); } -bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) +static bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_REFRESH_PYQ; - rsp.which_msg = Response_status_tag; - - rsp.msg.status = RefreshPyq(id); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [att](Response &rsp) { + std::string thumb = att.thumb ? att.thumb : ""; + std::string extra = att.extra ? att.extra : ""; + rsp.msg.status = DownloadAttach(att.id, thumb, extra); + }); } -bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len) +static bool func_revoke_msg(uint64_t id, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_DOWNLOAD_ATTACH; - rsp.which_msg = Response_status_tag; - - uint64_t id = att.id; - string thumb = string(att.thumb ? att.thumb : ""); - string extra = string(att.extra ? att.extra : ""); - - rsp.msg.status = DownloadAttach(id, thumb, extra); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, + [id](Response &rsp) { rsp.msg.status = RevokeMsg(id); }); } -bool func_revoke_msg(uint64_t id, uint8_t *out, size_t *len) +static bool func_refresh_qrcode(uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_REVOKE_MSG; - rsp.which_msg = Response_status_tag; - - rsp.msg.status = RevokeMsg(id); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_str_tag, out, len, [](Response &rsp) { + std::string url = GetLoginUrl(); + rsp.msg.str = url.empty() ? nullptr : (char *)url.c_str(); + }); } -bool func_refresh_qrcode(uint8_t *out, size_t *len) +static bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_REFRESH_QRCODE; - rsp.which_msg = Response_str_tag; - - rsp.msg.str = (char *)GetLoginUrl().c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; -} - -bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, size_t *len) -{ - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_RECV_TRANSFER; - rsp.which_msg = Response_status_tag; - - if ((wxid == NULL) || (tfid == NULL) || (taid == NULL)) { - rsp.msg.status = -1; - LOG_ERROR("Empty wxid, tfid or taid."); - } else { - rsp.msg.status = ReceiveTransfer(wxid, tfid, taid); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [wxid, tfid, taid](Response &rsp) { + if ((wxid == nullptr) || (tfid == nullptr) || (taid == nullptr)) { + LOG_ERROR("Empty wxid, tfid, or taid."); + rsp.msg.status = -1; + } else { + rsp.msg.status = ReceiveTransfer(wxid, tfid, taid); + } + }); } #if 0 -bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, size_t *len) +static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_ACCEPT_FRIEND; - rsp.which_msg = Response_status_tag; - - if ((v3 == NULL) || (v4 == NULL)) { - rsp.msg.status = -1; - LOG_ERROR("Empty V3 or V4."); - } else { - rsp.msg.status = AcceptNewFriend(v3, v4, scene); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [v3, v4, scene](Response &rsp) { + if ((v3 == nullptr) || (v4 == nullptr)) { + LOG_ERROR("Empty V3 or V4."); + rsp.msg.status = -1; + } else { + rsp.msg.status = AcceptNewFriend(v3, v4, scene); + } + }); } -bool func_get_contact_info(string wxid, uint8_t *out, size_t *len) +static bool func_get_contact_info(std::string wxid, uint8_t *out, size_t *len) { - /*借用 Functions_FUNC_GET_CONTACTS */ - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_GET_CONTACT_INFO; - rsp.which_msg = Response_contacts_tag; + return FillResponse(Response_contacts_tag, out, len, [wxid](Response &rsp) { + static std::vector contacts; + contacts.clear(); + contacts.push_back(GetContactByWxid(wxid)); - vector contacts; - contacts.push_back(GetContactByWxid(wxid)); - - rsp.msg.contacts.contacts.funcs.encode = encode_contacts; - rsp.msg.contacts.contacts.arg = &contacts; - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + rsp.msg.contacts.contacts.funcs.encode = encode_contacts; + rsp.msg.contacts.contacts.arg = &contacts; + }); } #endif -bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len) +static bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_DECRYPT_IMAGE; - rsp.which_msg = Response_str_tag; - string path = ""; - - if ((dec.src == NULL) || (dec.dst == NULL)) { - LOG_ERROR("Empty src or dst."); - } else { - path = DecryptImage(dec.src, dec.dst); - } - - rsp.msg.str = (char *)path.c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_str_tag, out, len, [dec](Response &rsp) { + std::string path; + if ((dec.src == nullptr) || (dec.dst == nullptr)) { + LOG_ERROR("Empty src or dst."); + } else { + path = DecryptImage(dec.src, dec.dst); + } + rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); + }); } -bool func_exec_ocr(char *path, uint8_t *out, size_t *len) +static bool func_exec_ocr(char *path, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_EXEC_OCR; - rsp.which_msg = Response_ocr_tag; - OcrResult_t ret = { -1, "" }; - - if (path == NULL) { - LOG_ERROR("Empty path."); - } else { - ret = GetOcrResult(path); - } - - rsp.msg.ocr.status = ret.status; - rsp.msg.ocr.result = (char *)ret.result.c_str(); - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - return true; + return FillResponse(Response_ocr_tag, out, len, [path](Response &rsp) { + OcrResult_t ret = { -1, "" }; + if (path == nullptr) { + LOG_ERROR("Empty path."); + } else { + ret = GetOcrResult(path); + } + rsp.msg.ocr.status = ret.status; + rsp.msg.ocr.result = ret.result.empty() ? nullptr : (char *)ret.result.c_str(); + }); } -bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) +static bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_ADD_ROOM_MEMBERS; - rsp.which_msg = Response_status_tag; - rsp.msg.status = 0; - - if ((roomid == NULL) || (wxids == NULL)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = AddChatroomMember(roomid, wxids); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { + if ((roomid == nullptr) || (wxids == nullptr)) { + LOG_ERROR("Empty roomid or wxids."); + rsp.msg.status = -1; + } else { + rsp.msg.status = AddChatroomMember(roomid, wxids); + } + }); } -bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) +static bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_DEL_ROOM_MEMBERS; - rsp.which_msg = Response_status_tag; - rsp.msg.status = 0; - - if ((roomid == NULL) || (wxids == NULL)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = DelChatroomMember(roomid, wxids); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { + if ((roomid == nullptr) || (wxids == nullptr)) { + LOG_ERROR("Empty roomid or wxids."); + rsp.msg.status = -1; + } else { + rsp.msg.status = DelChatroomMember(roomid, wxids); + } + }); } -bool func_invite_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) +static bool func_invite_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) { - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_INV_ROOM_MEMBERS; - rsp.which_msg = Response_status_tag; - rsp.msg.status = 0; - - if ((roomid == NULL) || (wxids == NULL)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = InviteChatroomMember(roomid, wxids); - } - - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - - return true; + return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { + if ((roomid == nullptr) || (wxids == nullptr)) { + LOG_ERROR("Empty roomid or wxids."); + rsp.msg.status = -1; + } else { + rsp.msg.status = InviteChatroomMember(roomid, wxids); + } + }); } static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) From bfe333675371ca0cc8bd1b84e09c0efac0a2074e Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 00:50:19 +0800 Subject: [PATCH 006/132] refactor: use template to simplify response logic --- WeChatFerry/spy/fill_response.h | 60 +++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 WeChatFerry/spy/fill_response.h diff --git a/WeChatFerry/spy/fill_response.h b/WeChatFerry/spy/fill_response.h new file mode 100644 index 0000000..2cf1ddb --- /dev/null +++ b/WeChatFerry/spy/fill_response.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +#include "log.hpp" +#include "pb_encode.h" +#include "pb_types.h" + +static const std::unordered_map rpc_function_map + = { { Functions_FUNC_IS_LOGIN, Response_status_tag }, + { Functions_FUNC_GET_SELF_WXID, Response_str_tag }, + { Functions_FUNC_GET_USER_INFO, Response_ui_tag }, + { Functions_FUNC_GET_MSG_TYPES, Response_types_tag }, + { Functions_FUNC_GET_CONTACTS, Response_contacts_tag }, + { Functions_FUNC_GET_DB_NAMES, Response_dbs_tag }, + { Functions_FUNC_GET_DB_TABLES, Response_tables_tag }, + { Functions_FUNC_GET_AUDIO_MSG, Response_str_tag }, + { Functions_FUNC_SEND_TXT, Response_status_tag }, + { Functions_FUNC_SEND_IMG, Response_status_tag }, + { Functions_FUNC_SEND_FILE, Response_status_tag }, + { Functions_FUNC_SEND_RICH_TXT, Response_status_tag }, + { Functions_FUNC_SEND_PAT_MSG, Response_status_tag }, + { Functions_FUNC_FORWARD_MSG, Response_status_tag }, + { Functions_FUNC_SEND_EMOTION, Response_status_tag }, + { Functions_FUNC_ENABLE_RECV_TXT, Response_status_tag }, + { Functions_FUNC_DISABLE_RECV_TXT, Response_status_tag }, + { Functions_FUNC_EXEC_DB_QUERY, Response_rows_tag }, + { Functions_FUNC_REFRESH_PYQ, Response_status_tag }, + { Functions_FUNC_DOWNLOAD_ATTACH, Response_status_tag }, + { Functions_FUNC_RECV_TRANSFER, Response_status_tag }, + { Functions_FUNC_REVOKE_MSG, Response_status_tag }, + { Functions_FUNC_REFRESH_QRCODE, Response_str_tag }, + { Functions_FUNC_DECRYPT_IMAGE, Response_str_tag }, + { Functions_FUNC_EXEC_OCR, Response_ocr_tag }, + { Functions_FUNC_ADD_ROOM_MEMBERS, Response_status_tag }, + { Functions_FUNC_DEL_ROOM_MEMBERS, Response_status_tag }, + { Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag } }; + +template bool fill_response(uint8_t *out, size_t *len, AssignFunc assign) +{ + Response rsp = Response_init_default; + rsp.func = FuncType; + + auto it = rpc_function_map.find(FuncType); + if (it == rpc_function_map.end()) { + LOG_ERROR("Unknown function type: {}", FuncType); + return false; + } + rsp.which_msg = it->second; + + assign(rsp); + + pb_ostream_t stream = pb_ostream_from_buffer(out, *len); + if (!pb_encode(&stream, Response_fields, &rsp)) { + LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); + return false; + } + *len = stream.bytes_written; + return true; +} From 86a55cf00cdf2640b6c484124ff61c82ccf3483c Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 01:08:55 +0800 Subject: [PATCH 007/132] refactoring --- WeChatFerry/spy/rpc_server.cpp | 4 +- WeChatFerry/spy/user_info.cpp | 78 ++++++++++++++++++++++------------ WeChatFerry/spy/user_info.h | 20 +++++++-- 3 files changed, 70 insertions(+), 32 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 2192558..ab99536 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -75,7 +75,7 @@ static bool func_is_login(uint8_t *out, size_t *len) static bool func_get_self_wxid(uint8_t *out, size_t *len) { return FillResponse(Response_str_tag, out, len, [](Response &rsp) { - std::string wxid = GetSelfWxid(); + std::string wxid = user_info::get_self_wxid(); rsp.msg.str = wxid.empty() ? nullptr : (char *)wxid.c_str(); }); } @@ -83,7 +83,7 @@ static bool func_get_self_wxid(uint8_t *out, size_t *len) static bool func_get_user_info(uint8_t *out, size_t *len) { return FillResponse(Response_ui_tag, out, len, [](Response &rsp) { - UserInfo_t ui = GetUserInfo(); + UserInfo_t ui = user_info::get_user_info(); rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); rsp.msg.ui.name = (char *)ui.name.c_str(); rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index a273592..7566ff3 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -1,7 +1,14 @@ -#include "user_info.h" +#include +#include + +#include "fill_response.h" #include "log.hpp" +#include "user_info.h" #include "util.h" +namespace user_info +{ + extern UINT64 g_WeChatWinDllAddr; #define OS_USER_HOME 0x5932770 @@ -9,50 +16,69 @@ extern UINT64 g_WeChatWinDllAddr; #define OS_USER_NAME 0x595C3D8 #define OS_USER_MOBILE 0x595C318 -static char home[MAX_PATH] = { 0 }; - -string GetHomePath() +std::string get_home_path() { - if (home[0] == 0) { - string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; - strncpy_s(home, path.c_str(), path.size()); - } + static std::once_flag flag; + static std::string home_path; - return string(home); + std::call_once(flag, [] { + std::string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; + home_path = std::filesystem::absolute(path).string(); + }); + + return home_path; } -string GetSelfWxid() +std::optional get_self_wxid() { - UINT64 wxidType = 0; + UINT64 wxid_type = 0; try { - wxidType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); - if (wxidType == 0xF) { + wxid_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); + if (wxid_type == 0xF) { return GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); } else { return GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); } } catch (...) { - LOG_ERROR("wxid type: {:#x}", wxidType); - LOG_BUFFER((uint8_t *)(g_WeChatWinDllAddr + OS_USER_WXID), 20); - return "empty_wxid"; + LOG_ERROR("Failed to get wxid, type: {:#x}", wxid_type); + LOG_BUFFER(reinterpret_cast(g_WeChatWinDllAddr + OS_USER_WXID), 20); + return std::nullopt; } } -UserInfo_t GetUserInfo() +UserInfo_t get_user_info() { UserInfo_t ui; + auto wxid = get_self_wxid(); + ui.wxid = wxid.value_or("unknown_wxid"); - ui.wxid = GetSelfWxid(); - - UINT64 nameType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); - if (nameType == 0xF) { - ui.name = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME); - } else { // 0x1F - ui.name = GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME); - } + UINT64 name_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); + ui.name = (name_type == 0xF) ? GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME) + : GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME); ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_MOBILE); - ui.home = GetHomePath(); + ui.home = get_home_path(); return ui; } + +bool rpc_get_self_wxid(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { + auto wxid = get_self_wxid(); + rsp.msg.str = wxid ? wxid->c_str() : "error"; + }); +} + +bool rpc_get_user_info(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { + UserInfo_t ui = get_user_info(); + rsp.msg.ui.wxid = ui.wxid.c_str(); + rsp.msg.ui.name = ui.name.c_str(); + rsp.msg.ui.mobile = ui.mobile.c_str(); + rsp.msg.ui.home = ui.home.c_str(); + }); +} + +} // namespace user_info diff --git a/WeChatFerry/spy/user_info.h b/WeChatFerry/spy/user_info.h index 8d70dc7..a959b04 100644 --- a/WeChatFerry/spy/user_info.h +++ b/WeChatFerry/spy/user_info.h @@ -1,12 +1,24 @@ #pragma once +#include #include #include "pb_types.h" -using namespace std; +namespace user_info +{ -string GetHomePath(); -string GetSelfWxid(); +// 获取 WeChat 数据存储路径 +std::string get_home_path(); -UserInfo_t GetUserInfo(); +// 获取自身 wxid +std::optional get_self_wxid(); + +// 获取用户信息 +UserInfo_t get_user_info(); + +// RPC 方法 +bool rpc_get_self_wxid(uint8_t *out, size_t *len); +bool rpc_get_user_info(uint8_t *out, size_t *len); + +} // namespace user_info From bc40b6e922a5afdf90da792721e2a38434302573 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 09:54:08 +0800 Subject: [PATCH 008/132] Fix logging and compiling errors --- WeChatFerry/spy/fill_response.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WeChatFerry/spy/fill_response.h b/WeChatFerry/spy/fill_response.h index 2cf1ddb..f4e19c0 100644 --- a/WeChatFerry/spy/fill_response.h +++ b/WeChatFerry/spy/fill_response.h @@ -1,7 +1,10 @@ #pragma once +#include #include +#include "wcf.pb.h" + #include "log.hpp" #include "pb_encode.h" #include "pb_types.h" @@ -43,7 +46,7 @@ template bool fill_response(uint8_t *o auto it = rpc_function_map.find(FuncType); if (it == rpc_function_map.end()) { - LOG_ERROR("Unknown function type: {}", FuncType); + LOG_ERROR("Unknown function type: {}", magic_enum::enum_name(rsp.func)); return false; } rsp.which_msg = it->second; From f2c017de104d58f874659c89a4d71f6aca0ff7aa Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 09:55:44 +0800 Subject: [PATCH 009/132] Refatoring --- WeChatFerry/spy/receive_msg.cpp | 4 +-- WeChatFerry/spy/send_msg.cpp | 4 +-- WeChatFerry/spy/user_info.cpp | 58 ++++++++++++++++++--------------- WeChatFerry/spy/user_info.h | 2 +- 4 files changed, 36 insertions(+), 32 deletions(-) diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index 4aa92e3..5becce8 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -49,10 +49,10 @@ QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) if (wxMsg.roomid.find("@chatroom") != std::string::npos) { wxMsg.is_group = true; - wxMsg.sender = wxMsg.is_self ? GetSelfWxid() : GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); + wxMsg.sender = wxMsg.is_self ? user_info::get_self_wxid() : GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); } else { wxMsg.is_group = false; - wxMsg.sender = wxMsg.is_self ? GetSelfWxid() : wxMsg.roomid; + wxMsg.sender = wxMsg.is_self ? user_info::get_self_wxid() : wxMsg.roomid; } } catch (const std::exception &e) { LOG_ERROR(GB2312ToUtf8(e.what())); diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index dca37f7..4e5bbdb 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -7,11 +7,11 @@ #include "log.hpp" #include "send_msg.h" #include "spy_types.h" +#include "user_info.h" #include "util.h" extern HANDLE g_hEvent; extern QWORD g_WeChatWinDllAddr; -extern string GetSelfWxid(); // Defined in spy.cpp #define SRTM_SIZE 0x3F0 @@ -263,7 +263,7 @@ void SendXmlMessage(string receiver, string xml, string path, QWORD type) WxString *pReceiver = NewWxStringFromStr(receiver); WxString *pXml = NewWxStringFromStr(xml); WxString *pPath = NewWxStringFromStr(path); - WxString *pSender = NewWxStringFromStr(GetSelfWxid()); + WxString *pSender = NewWxStringFromStr(user_info::get_self_wxid()); sendXmlMsg(pBuf, (QWORD)pSender, (QWORD)pReceiver, (QWORD)pXml, (QWORD)pPath, (QWORD)(&nullBuf), type, 0x4, sign, pBuf2); diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index 7566ff3..6cf95b7 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -6,17 +6,17 @@ #include "user_info.h" #include "util.h" -namespace user_info -{ - extern UINT64 g_WeChatWinDllAddr; +namespace user_info +{ #define OS_USER_HOME 0x5932770 #define OS_USER_WXID 0x595C270 #define OS_USER_NAME 0x595C3D8 #define OS_USER_MOBILE 0x595C318 -std::string get_home_path() + std::string + get_home_path() { static std::once_flag flag; static std::string home_path; @@ -29,28 +29,34 @@ std::string get_home_path() return home_path; } -std::optional get_self_wxid() +std::string get_self_wxid() { - UINT64 wxid_type = 0; - try { - wxid_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); - if (wxid_type == 0xF) { - return GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); - } else { - return GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); + static std::once_flag flag; + static std::string wxid; + + std::call_once(flag, [] { + UINT64 wxid_type = 0; + try { + wxid_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); + if (wxid_type == 0xF) { + wxid = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); + } else { + wxid = GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); + } + + } catch (...) { + LOG_ERROR("Failed to get wxid, type: {:#x}", wxid_type); + LOG_BUFFER(reinterpret_cast(g_WeChatWinDllAddr + OS_USER_WXID), 20); + wxid = "获取wxid失败"; } - } catch (...) { - LOG_ERROR("Failed to get wxid, type: {:#x}", wxid_type); - LOG_BUFFER(reinterpret_cast(g_WeChatWinDllAddr + OS_USER_WXID), 20); - return std::nullopt; - } + }); + return wxid; } UserInfo_t get_user_info() { UserInfo_t ui; - auto wxid = get_self_wxid(); - ui.wxid = wxid.value_or("unknown_wxid"); + ui.wxid = get_self_wxid(); UINT64 name_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); ui.name = (name_type == 0xF) ? GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME) @@ -64,20 +70,18 @@ UserInfo_t get_user_info() bool rpc_get_self_wxid(uint8_t *out, size_t *len) { - return fill_response(out, len, [](Response &rsp) { - auto wxid = get_self_wxid(); - rsp.msg.str = wxid ? wxid->c_str() : "error"; - }); + return fill_response( + out, len, [](Response &rsp) { rsp.msg.str = (char *)get_self_wxid().c_str(); }); } bool rpc_get_user_info(uint8_t *out, size_t *len) { return fill_response(out, len, [](Response &rsp) { UserInfo_t ui = get_user_info(); - rsp.msg.ui.wxid = ui.wxid.c_str(); - rsp.msg.ui.name = ui.name.c_str(); - rsp.msg.ui.mobile = ui.mobile.c_str(); - rsp.msg.ui.home = ui.home.c_str(); + rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); + rsp.msg.ui.name = (char *)ui.name.c_str(); + rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); + rsp.msg.ui.home = (char *)ui.home.c_str(); }); } diff --git a/WeChatFerry/spy/user_info.h b/WeChatFerry/spy/user_info.h index a959b04..f248ba7 100644 --- a/WeChatFerry/spy/user_info.h +++ b/WeChatFerry/spy/user_info.h @@ -12,7 +12,7 @@ namespace user_info std::string get_home_path(); // 获取自身 wxid -std::optional get_self_wxid(); +std::string get_self_wxid(); // 获取用户信息 UserInfo_t get_user_info(); From b109bd72d263f83cea3b6910f1c9b6aedd3731d7 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 10:18:02 +0800 Subject: [PATCH 010/132] Refactoring --- WeChatFerry/spy/contact_mgmt.cpp | 138 +++++++++++++++++++------------ WeChatFerry/spy/contact_mgmt.h | 27 ++++-- WeChatFerry/spy/rpc_server.cpp | 11 +-- 3 files changed, 108 insertions(+), 68 deletions(-) diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 84f916a..1adae24 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -1,10 +1,15 @@ #pragma execution_character_set("utf-8") #include "contact_mgmt.h" +#include "fill_response.h" #include "log.hpp" #include "util.h" using namespace std; + +namespace contact_mgmt +{ + extern QWORD g_WeChatWinDllAddr; #define OS_GET_CONTACT_MGR 0x1B417A0 @@ -18,30 +23,29 @@ extern QWORD g_WeChatWinDllAddr; #define OS_CONTACT_GENDER 0x0E #define OS_CONTACT_STEP 0x6A8 -typedef QWORD (*GetContactMgr_t)(); -typedef QWORD (*GetContactList_t)(QWORD, QWORD); +using get_contact_mgr_t = QWORD (*)(); +using get_contact_list_t = QWORD (*)(QWORD, QWORD); #define FEAT_LEN 5 static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 }; static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 }; static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 }; -static QWORD FindMem(QWORD start, QWORD end, const void *target, size_t len) +static QWORD find_mem(QWORD start, QWORD end, const void *target, size_t len) { - uint8_t *p = (uint8_t *)start; - while ((QWORD)p < end) { - if (memcmp((void *)p, target, len) == 0) { - return (QWORD)p; + uint8_t *p = reinterpret_cast(start); + while (reinterpret_cast(p) < end) { + if (memcmp(p, target, len) == 0) { + return reinterpret_cast(p); } p++; } - return 0; } -static string GetCntString(QWORD start, QWORD end, const uint8_t *feat, size_t len) +static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t len) { - QWORD pfeat = FindMem(start, end, feat, len); + QWORD pfeat = find_mem(start, end, feat, len); if (pfeat == 0) { return ""; } @@ -54,21 +58,23 @@ static string GetCntString(QWORD start, QWORD end, const uint8_t *feat, size_t l return Wstring2String(wstring(GET_WSTRING_FROM_P(pfeat + FEAT_LEN + 4), lfeat)); } -vector GetContacts() +vector get_contacts() { vector contacts; - GetContactMgr_t funcGetContactMgr = (GetContactMgr_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR); - GetContactList_t funcGetContactList = (GetContactList_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST); + get_contact_mgr_t func_get_contact_mgr + = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR); + get_contact_list_t func_get_contact_list + = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST); - QWORD mgr = funcGetContactMgr(); + QWORD mgr = func_get_contact_mgr(); QWORD addr[3] = { 0 }; - if (funcGetContactList(mgr, (QWORD)addr) != 1) { - LOG_ERROR("GetContacts failed"); + if (func_get_contact_list(mgr, reinterpret_cast(addr)) != 1) { + LOG_ERROR("get_contacts failed"); return contacts; } - QWORD pstart = (QWORD)addr[0]; - QWORD pend = (QWORD)addr[2]; + QWORD pstart = addr[0]; + QWORD pend = addr[2]; while (pstart < pend) { RpcContact_t cnt; QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN); @@ -79,15 +85,11 @@ vector GetContacts() cnt.remark = GetStringByWstrAddr(pstart + OS_CONTACT_REMARK); cnt.name = GetStringByWstrAddr(pstart + OS_CONTACT_NAME); - cnt.country = GetCntString(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN); - cnt.province = GetCntString(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN); - cnt.city = GetCntString(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN); + cnt.country = get_cnt_string(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN); + cnt.province = get_cnt_string(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN); + cnt.city = get_cnt_string(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN); - if (pbin == 0) { - cnt.gender = 0; - } else { - cnt.gender = (DWORD) * (uint8_t *)(pbin + OS_CONTACT_GENDER); - } + cnt.gender = (pbin == 0) ? 0 : static_cast(*(uint8_t *)(pbin + OS_CONTACT_GENDER)); contacts.push_back(cnt); pstart += OS_CONTACT_STEP; @@ -96,68 +98,69 @@ vector GetContacts() return contacts; } -#if 0 -int AcceptNewFriend(string v3, string v4, int scene) +int accept_new_friend(string v3, string v4, int scene) { - int success = 0; - - DWORD acceptNewFriendCall1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1; - DWORD acceptNewFriendCall2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2; - DWORD acceptNewFriendCall3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3; - DWORD acceptNewFriendCall4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4; + int success = -1; +#if 0 + DWORD accept_new_friend_call1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1; + DWORD accept_new_friend_call2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2; + DWORD accept_new_friend_call3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3; + DWORD accept_new_friend_call4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4; char buffer[0x40] = { 0 }; char nullbuffer[0x3CC] = { 0 }; LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene); - wstring wsV3 = String2Wstring(v3); - wstring wsV4 = String2Wstring(v4); - WxString wxV3(wsV3); - WxString wxV4(wsV4); + wstring ws_v3 = String2Wstring(v3); + wstring ws_v4 = String2Wstring(v4); + WxString wx_v3(ws_v3); + WxString wx_v4(ws_v4); __asm { pushad; pushfd; lea ecx, buffer; - call acceptNewFriendCall1; + call accept_new_friend_call1; mov esi, 0x0; mov edi, scene; push esi; push edi; sub esp, 0x14; mov ecx, esp; - lea eax, wxV4; + lea eax, wx_v4; push eax; - call acceptNewFriendCall2; + call accept_new_friend_call2; sub esp, 0x8; push 0x0; lea eax, nullbuffer; push eax; - lea eax, wxV3; + lea eax, wx_v3; push eax; lea ecx, buffer; - call acceptNewFriendCall3; + call accept_new_friend_call3; mov success, eax; lea ecx, buffer; - call acceptNewFriendCall4; + call accept_new_friend_call4; popfd; popad; } - +#endif return success; // 成功返回 1 } -/*没啥用,非好友获取不到*/ -RpcContact_t GetContactByWxid(string wxid) +RpcContact_t get_contact_by_wxid(string wxid) { RpcContact_t contact; +#if 0 char buff[0x440] = { 0 }; - wstring wsWxid = String2Wstring(wxid); - WxString pri(wsWxid); + wstring ws_wxid = String2Wstring(wxid); + WxString pri(ws_wxid); + DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0; DWORD get_contact_addr = g_WeChatWinDllAddr + 0xC04E00; DWORD free_contact_addr = g_WeChatWinDllAddr + 0xEA7880; + __asm { PUSHAD PUSHFD @@ -173,10 +176,10 @@ RpcContact_t GetContactByWxid(string wxid) } contact.wxid = wxid; - contact.code = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxCode); - contact.remark = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxRemark); - contact.name = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxName); - contact.gender = GET_DWORD((DWORD)buff + 0x148); + contact.code = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxCode); + contact.remark = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxRemark); + contact.name = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxName); + contact.gender = GET_DWORD(reinterpret_cast(buff) + 0x148); __asm { PUSHAD @@ -186,7 +189,32 @@ RpcContact_t GetContactByWxid(string wxid) POPFD POPAD } - +#endif return contact; } -#endif + +bool rpc_get_contacts(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { + vector contacts = get_contacts(); + rsp.msg.contacts.contacts.funcs.encode = encode_contacts; + rsp.msg.contacts.contacts.arg = &contacts; + }); +} + +bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + vector contacts = { get_contact_by_wxid(wxid) }; + rsp.msg.contacts.contacts.funcs.encode = encode_contacts; + rsp.msg.contacts.contacts.arg = &contacts; + }); +} + +bool rpc_accept_friend(const string &v3, const string &v4, int scene, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = accept_friend(v3, v4, scene); }); +} + +} // namespace contact_mgmt diff --git a/WeChatFerry/spy/contact_mgmt.h b/WeChatFerry/spy/contact_mgmt.h index 460bf89..3ff0662 100644 --- a/WeChatFerry/spy/contact_mgmt.h +++ b/WeChatFerry/spy/contact_mgmt.h @@ -1,11 +1,28 @@ #pragma once -#include "string" +#include #include #include "pb_types.h" -vector GetContacts(); -int AcceptNewFriend(std::string v3, std::string v4, int scene); -int AddFriendByWxid(std::string wxid, std::string msg); -RpcContact_t GetContactByWxid(std::string wxid); +namespace contact_mgmt +{ + +// 获取所有联系人 +std::vector get_contacts(); + +// 根据 wxid 获取联系人信息 +RpcContact_t get_contact_by_wxid(const std::string &wxid); + +// 接受好友请求 +int accept_friend(const std::string &v3, const std::string &v4, int scene); + +// 发送好友请求 +// int add_friend_by_wxid(const std::string &wxid, const std::string &msg); + +// RPC 方法 +bool rpc_get_contacts(uint8_t *out, size_t *len); +bool rpc_get_contact_info(const std::string &wxid, uint8_t *out, size_t *len); +bool rpc_accept_friend(const std::string &v3, const std::string &v4, int scene, uint8_t *out, size_t *len); + +} // namespace contact_mgmt diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index ab99536..e90fa6a 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -103,7 +103,7 @@ static bool func_get_msg_types(uint8_t *out, size_t *len) static bool func_get_contacts(uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [](Response &rsp) { - static std::vector contacts = GetContacts(); + static std::vector contacts = contact_mgmt::get_contacts(); rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); @@ -410,7 +410,6 @@ static bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *o }); } -#if 0 static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, size_t *len) { return FillResponse(Response_status_tag, out, len, [v3, v4, scene](Response &rsp) { @@ -418,7 +417,7 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, LOG_ERROR("Empty V3 or V4."); rsp.msg.status = -1; } else { - rsp.msg.status = AcceptNewFriend(v3, v4, scene); + rsp.msg.status = contact_mgmt::accept_friend(v3, v4, scene); } }); } @@ -426,15 +425,11 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, static bool func_get_contact_info(std::string wxid, uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [wxid](Response &rsp) { - static std::vector contacts; - contacts.clear(); - contacts.push_back(GetContactByWxid(wxid)); - + std::vector contacts = contact_mgmt::get_contact_by_wxid(wxid); rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); } -#endif static bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len) { From 065db1d236fcb781dbc75388e6360a35091fcf22 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 10:51:35 +0800 Subject: [PATCH 011/132] Fix function signature --- WeChatFerry/spy/contact_mgmt.cpp | 12 ++++++------ WeChatFerry/spy/contact_mgmt.h | 2 +- WeChatFerry/spy/rpc_server.cpp | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 1adae24..192b20f 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -3,15 +3,15 @@ #include "contact_mgmt.h" #include "fill_response.h" #include "log.hpp" +#include "pb_util.h" #include "util.h" using namespace std; -namespace contact_mgmt -{ - extern QWORD g_WeChatWinDllAddr; +namespace contact_mgmt +{ #define OS_GET_CONTACT_MGR 0x1B417A0 #define OS_GET_CONTACT_LIST 0x219ED10 #define OS_CONTACT_BIN 0x200 @@ -98,7 +98,7 @@ vector get_contacts() return contacts; } -int accept_new_friend(string v3, string v4, int scene) +int accept_new_friend(const std::string &v3, const std::string &v4, int scene) { int success = -1; #if 0 @@ -149,7 +149,7 @@ int accept_new_friend(string v3, string v4, int scene) return success; // 成功返回 1 } -RpcContact_t get_contact_by_wxid(string wxid) +RpcContact_t get_contact_by_wxid(const string &wxid) { RpcContact_t contact; #if 0 @@ -214,7 +214,7 @@ bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len) bool rpc_accept_friend(const string &v3, const string &v4, int scene, uint8_t *out, size_t *len) { return fill_response( - out, len, [&](Response &rsp) { rsp.msg.status = accept_friend(v3, v4, scene); }); + out, len, [&](Response &rsp) { rsp.msg.status = accept_new_friend(v3, v4, scene); }); } } // namespace contact_mgmt diff --git a/WeChatFerry/spy/contact_mgmt.h b/WeChatFerry/spy/contact_mgmt.h index 3ff0662..0fb8091 100644 --- a/WeChatFerry/spy/contact_mgmt.h +++ b/WeChatFerry/spy/contact_mgmt.h @@ -15,7 +15,7 @@ std::vector get_contacts(); RpcContact_t get_contact_by_wxid(const std::string &wxid); // 接受好友请求 -int accept_friend(const std::string &v3, const std::string &v4, int scene); +int accept_new_friend(const std::string &v3, const std::string &v4, int scene); // 发送好友请求 // int add_friend_by_wxid(const std::string &wxid, const std::string &msg); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index e90fa6a..5d4ecb8 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -103,9 +103,9 @@ static bool func_get_msg_types(uint8_t *out, size_t *len) static bool func_get_contacts(uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [](Response &rsp) { - static std::vector contacts = contact_mgmt::get_contacts(); - rsp.msg.contacts.contacts.funcs.encode = encode_contacts; - rsp.msg.contacts.contacts.arg = &contacts; + std::vector contacts = contact_mgmt::get_contacts(); + rsp.msg.contacts.contacts.funcs.encode = encode_contacts; + rsp.msg.contacts.contacts.arg = &contacts; }); } @@ -417,7 +417,7 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, LOG_ERROR("Empty V3 or V4."); rsp.msg.status = -1; } else { - rsp.msg.status = contact_mgmt::accept_friend(v3, v4, scene); + rsp.msg.status = contact_mgmt::accept_new_friend(v3, v4, scene); } }); } @@ -425,7 +425,7 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, static bool func_get_contact_info(std::string wxid, uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [wxid](Response &rsp) { - std::vector contacts = contact_mgmt::get_contact_by_wxid(wxid); + std::vector contacts = { contact_mgmt::get_contact_by_wxid(wxid) }; rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); From da4dcec8bce2ecc1d700e8d1bf905334c38d4f3f Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 11:06:26 +0800 Subject: [PATCH 012/132] Refactoring --- WeChatFerry/spy/chatroom_mgmt.cpp | 150 +++++++++++++++--------------- WeChatFerry/spy/chatroom_mgmt.h | 21 ++++- WeChatFerry/spy/rpc_server.cpp | 6 +- 3 files changed, 94 insertions(+), 83 deletions(-) diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index 0950d45..602042e 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -1,113 +1,109 @@ -#include "framework.h" -#include -#include +#pragma execution_character_set("utf-8") #include "chatroom_mgmt.h" +#include "fill_response.h" #include "log.hpp" +#include "pb_util.h" #include "util.h" using namespace std; extern QWORD g_WeChatWinDllAddr; +namespace chatroom_mgmt +{ #define OS_GET_CHATROOM_MGR 0x1B83BD0 #define OS_ADD_MEMBERS 0x2155100 #define OS_DELETE_MEMBERS 0x2155740 #define OS_INVITE_MEMBERS 0x2154AE0 -typedef QWORD (*GetChatRoomMgr_t)(); -typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*DelMemberFromChatRoom_t)(QWORD, QWORD, QWORD); -typedef QWORD (*InviteMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); +using get_chatroom_mgr_t = QWORD (*)(); +using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); +using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD); +using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); -int AddChatroomMember(string roomid, string wxids) +static vector parse_wxids(const string &wxids) { - int status = -1; - - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return status; - } - - GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); - AddMemberToChatRoom_t AddMembers = (AddMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_ADD_MEMBERS); - - vector vMembers; - vector vWxMembers; + vector wx_members; wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); + wstring wstr; + while (getline(wss, wstr, L',')) { + wx_members.emplace_back(wstr); } - - QWORD temp[2] = { 0 }; - WxString *pWxRoomid = NewWxStringFromStr(roomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; - - QWORD mgr = GetChatRoomMgr(); - status = (int)AddMembers(mgr, pMembers, (QWORD)pWxRoomid, (QWORD)temp); - return status; + return wx_members; } -int DelChatroomMember(string roomid, string wxids) +int add_chatroom_member(const string &roomid, const string &wxids) { - int status = -1; - if (roomid.empty() || wxids.empty()) { LOG_ERROR("Empty roomid or wxids."); - return status; + return -1; } - GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); - DelMemberFromChatRoom_t DelMembers = (DelMemberFromChatRoom_t)(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); + get_chatroom_mgr_t get_chatroom_mgr + = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); + add_member_to_chatroom_t add_members + = reinterpret_cast(g_WeChatWinDllAddr + OS_ADD_MEMBERS); - vector vMembers; - vector vWxMembers; - wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); - } + vector wx_members = parse_wxids(wxids); + WxString *p_wx_roomid = NewWxStringFromStr(roomid); + QWORD p_members = reinterpret_cast(&wx_members.front()); - WxString *pWxRoomid = NewWxStringFromStr(roomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; - - QWORD mgr = GetChatRoomMgr(); - status = (int)DelMembers(mgr, pMembers, (QWORD)pWxRoomid); - return status; + return static_cast(add_members(get_chatroom_mgr(), p_members, reinterpret_cast(p_wx_roomid), 0)); } -int InviteChatroomMember(string roomid, string wxids) +int del_chatroom_member(const string &roomid, const string &wxids) { - int status = -1; - if (roomid.empty() || wxids.empty()) { LOG_ERROR("Empty roomid or wxids."); - return status; + return -1; } - InviteMemberToChatRoom_t InviteMembers = (InviteMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); + get_chatroom_mgr_t get_chatroom_mgr + = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); + del_member_from_chatroom_t del_members + = reinterpret_cast(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); - vector vMembers; - vector vWxMembers; - wstringstream wss(String2Wstring(wxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vMembers.push_back(wstr); - WxString wxMember(vMembers.back()); - vWxMembers.push_back(wxMember); - } - QWORD temp[2] = { 0 }; - wstring wsRoomid = String2Wstring(roomid); - WxString *pWxRoomid = NewWxStringFromWstr(wsRoomid); - QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start; + vector wx_members = parse_wxids(wxids); + WxString *p_wx_roomid = NewWxStringFromStr(roomid); + QWORD p_members = reinterpret_cast(&wx_members.front()); - status = (int)InviteMembers((QWORD)wsRoomid.c_str(), pMembers, (QWORD)pWxRoomid, (QWORD)temp); - return status; + return static_cast(del_members(get_chatroom_mgr(), p_members, reinterpret_cast(p_wx_roomid))); } + +int invite_chatroom_member(const string &roomid, const string &wxids) +{ + if (roomid.empty() || wxids.empty()) { + LOG_ERROR("Empty roomid or wxids."); + return -1; + } + + invite_member_to_chatroom_t invite_members + = reinterpret_cast(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); + + vector wx_members = parse_wxids(wxids); + WxString *p_wx_roomid = NewWxStringFromStr(roomid); + QWORD p_members = reinterpret_cast(&wx_members.front()); + + return static_cast(invite_members(reinterpret_cast(p_wx_roomid->c_str()), p_members, + reinterpret_cast(p_wx_roomid), 0)); +} + +bool rpc_add_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = add_chatroom_member(roomid, wxids); }); +} + +bool rpc_del_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = del_chatroom_member(roomid, wxids); }); +} + +bool rpc_invite_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = invite_chatroom_member(roomid, wxids); }); +} + +} // namespace chatroom_mgmt diff --git a/WeChatFerry/spy/chatroom_mgmt.h b/WeChatFerry/spy/chatroom_mgmt.h index da1eabf..2f9b9a6 100644 --- a/WeChatFerry/spy/chatroom_mgmt.h +++ b/WeChatFerry/spy/chatroom_mgmt.h @@ -2,6 +2,21 @@ #include -int AddChatroomMember(std::string roomid, std::string wxids); -int DelChatroomMember(std::string roomid, std::string wxids); -int InviteChatroomMember(std::string roomid, std::string wxids); +namespace chatroom_mgmt +{ + +// 添加成员到群聊 +int add_chatroom_member(const std::string &roomid, const std::string &wxids); + +// 从群聊中移除成员 +int del_chatroom_member(const std::string &roomid, const std::string &wxids); + +// 邀请成员加入群聊 +int invite_chatroom_member(const std::string &roomid, const std::string &wxids); + +// RPC 方法 +bool rpc_add_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); +bool rpc_del_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); +bool rpc_invite_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); + +} // namespace chatroom_mgmt diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 5d4ecb8..10a5f85 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -465,7 +465,7 @@ static bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_ LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = AddChatroomMember(roomid, wxids); + rsp.msg.status = chatroom_mgmt::add_chatroom_member(roomid, wxids); } }); } @@ -477,7 +477,7 @@ static bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_ LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = DelChatroomMember(roomid, wxids); + rsp.msg.status = chatroom_mgmt::del_chatroom_member(roomid, wxids); } }); } @@ -489,7 +489,7 @@ static bool func_invite_room_members(char *roomid, char *wxids, uint8_t *out, si LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = InviteChatroomMember(roomid, wxids); + rsp.msg.status = chatroom_mgmt::invite_chatroom_member(roomid, wxids); } }); } From 2033ae2ab8be0c1a5121b89a0888245a3024585f Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 31 Jan 2025 11:17:41 +0800 Subject: [PATCH 013/132] Refactoring --- WeChatFerry/spy/chatroom_mgmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index 602042e..db496b3 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -84,7 +84,7 @@ int invite_chatroom_member(const string &roomid, const string &wxids) WxString *p_wx_roomid = NewWxStringFromStr(roomid); QWORD p_members = reinterpret_cast(&wx_members.front()); - return static_cast(invite_members(reinterpret_cast(p_wx_roomid->c_str()), p_members, + return static_cast(invite_members(reinterpret_cast(p_wx_roomid->wptr), p_members, reinterpret_cast(p_wx_roomid), 0)); } From ff5f2378c0f472f650d0324d34bc1a04769d2755 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 1 Feb 2025 08:45:28 +0800 Subject: [PATCH 014/132] Refactoring --- WeChatFerry/spy/exec_sql.cpp | 289 +++++++++++++++++++-------------- WeChatFerry/spy/exec_sql.h | 31 +++- WeChatFerry/spy/rpc_server.cpp | 6 +- 3 files changed, 200 insertions(+), 126 deletions(-) diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 834c856..5ba9457 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -1,10 +1,16 @@ -#include +#include +#include #include "exec_sql.h" +#include "fill_response.h" #include "log.hpp" #include "sqlite3.h" #include "util.h" +extern UINT64 g_WeChatWinDllAddr; + +namespace exec_sql +{ #define OFFSET_DB_INSTANCE 0x5902000 #define OFFSET_DB_MICROMSG 0xB8 #define OFFSET_DB_CHAT_MSG 0x2C8 @@ -16,131 +22,138 @@ #define OFFSET_DB_NAME 0x28 #define OFFSET_DB_MSG_MGR 0x595F900 -extern UINT64 g_WeChatWinDllAddr; +using db_map_t = std::map; +static db_map_t db_map; -typedef map dbMap_t; -static dbMap_t dbMap; - -static void GetDbHandle(QWORD base, QWORD offset) +static void get_db_handle(QWORD base, QWORD offset) { - wchar_t *wsp = (wchar_t *)(*(QWORD *)(base + offset + OFFSET_DB_NAME)); - string dbname = Wstring2String(wstring(wsp)); - dbMap[dbname] = GET_QWORD(base + offset); + auto *wsp = reinterpret_cast(*(QWORD *)(base + offset + OFFSET_DB_NAME)); + std::string dbname = Wstring2String(std::wstring(wsp)); + db_map[dbname] = GET_QWORD(base + offset); } -static void GetMsgDbHandle(QWORD msgMgrAddr) +static void get_msg_db_handle(QWORD msg_mgr_addr) { - QWORD dbIndex = GET_QWORD(msgMgrAddr + 0x68); - QWORD pStart = GET_QWORD(msgMgrAddr + 0x50); - for (uint32_t i = 0; i < dbIndex; i++) { - QWORD dbAddr = GET_QWORD(pStart + i * 0x08); - if (dbAddr) { + QWORD db_index = GET_QWORD(msg_mgr_addr + 0x68); + QWORD p_start = GET_QWORD(msg_mgr_addr + 0x50); + for (uint32_t i = 0; i < db_index; i++) { + QWORD db_addr = GET_QWORD(p_start + i * 0x08); + if (db_addr) { // MSGi.db - string dbname = Wstring2String(GET_WSTRING(dbAddr)); - dbMap[dbname] = GET_QWORD(dbAddr + 0x78); + std::string dbname = Wstring2String(GET_WSTRING(db_addr)); + db_map[dbname] = GET_QWORD(db_addr + 0x78); // MediaMsgi.db - QWORD mmdbAddr = GET_QWORD(dbAddr + 0x20); - string mmdbname = Wstring2String(GET_WSTRING(mmdbAddr + 0x78)); - dbMap[mmdbname] = GET_QWORD(mmdbAddr + 0x50); + QWORD mmdb_addr = GET_QWORD(db_addr + 0x20); + std::string mmdbname = Wstring2String(GET_WSTRING(mmdb_addr + 0x78)); + db_map[mmdbname] = GET_QWORD(mmdb_addr + 0x50); } } } -dbMap_t GetDbHandles() +db_map_t get_db_handles() { - dbMap.clear(); + db_map.clear(); + QWORD db_instance_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); - QWORD dbInstanceAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); + get_db_handle(db_instance_addr, OFFSET_DB_MICROMSG); // MicroMsg.db + get_db_handle(db_instance_addr, OFFSET_DB_CHAT_MSG); // ChatMsg.db + get_db_handle(db_instance_addr, OFFSET_DB_MISC); // Misc.db + get_db_handle(db_instance_addr, OFFSET_DB_EMOTION); // Emotion.db + get_db_handle(db_instance_addr, OFFSET_DB_MEDIA); // Media.db + get_db_handle(db_instance_addr, OFFSET_DB_FUNCTION_MSG); // Function.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_MICROMSG); // MicroMsg.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_CHAT_MSG); // ChatMsg.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_MISC); // Misc.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_EMOTION); // Emotion.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_MEDIA); // Media.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_FUNCTION_MSG); // Function.db + get_msg_db_handle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db - GetMsgDbHandle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db - - return dbMap; + return db_map; } -DbNames_t GetDbNames() +DbNames_t get_db_names() { - DbNames_t names; - if (dbMap.empty()) { - dbMap = GetDbHandles(); + if (db_map.empty()) { + db_map = get_db_handles(); } - for (auto &[k, v] : dbMap) { + DbNames_t names; + for (const auto &[k, _] : db_map) { names.push_back(k); } - return names; } -static int cbGetTables(void *ret, int argc, char **argv, char **azColName) +static int cb_get_tables(void *ret, int argc, char **argv, char **azColName) { - DbTables_t *tbls = (DbTables_t *)ret; + auto *tables = static_cast(ret); DbTable_t tbl; for (int i = 0; i < argc; i++) { if (strcmp(azColName[i], "name") == 0) { tbl.name = argv[i] ? argv[i] : ""; } else if (strcmp(azColName[i], "sql") == 0) { - string sql(argv[i]); + std::string sql(argv[i]); sql.erase(std::remove(sql.begin(), sql.end(), '\t'), sql.end()); - tbl.sql = sql.c_str(); + tbl.sql = sql; } } - tbls->push_back(tbl); + tables->push_back(tbl); return 0; } -DbTables_t GetDbTables(const string db) +DbTables_t get_db_tables(const std::string &db) { DbTables_t tables; - if (dbMap.empty()) { - dbMap = GetDbHandles(); + if (db_map.empty()) { + db_map = get_db_handles(); } - auto it = dbMap.find(db); - if (it == dbMap.end()) { - return tables; // DB not found + auto it = db_map.find(db); + if (it == db_map.end()) { + return tables; } - const char *sql = "select name, sql from sqlite_master where type=\"table\";"; - Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET); - - p_Sqlite3_exec(it->second, sql, (Sqlite3_callback)cbGetTables, (void *)&tables, 0); + constexpr const char *sql = "SELECT name FROM sqlite_master WHERE type='table';"; + Sqlite3_exec p_sqlite3_exec = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET); + p_sqlite3_exec(it->second, sql, (Sqlite3_callback)cb_get_tables, (void *)&tables, nullptr); return tables; } -DbRows_t ExecDbQuery(const string db, const string sql) +DbRows_t exec_db_query(const std::string &db, const std::string &sql) { DbRows_t rows; - Sqlite3_prepare func_prepare = (Sqlite3_prepare)(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET); - Sqlite3_step func_step = (Sqlite3_step)(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET); - Sqlite3_column_count func_column_count = (Sqlite3_column_count)(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET); - Sqlite3_column_name func_column_name = (Sqlite3_column_name)(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET); - Sqlite3_column_type func_column_type = (Sqlite3_column_type)(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET); - Sqlite3_column_blob func_column_blob = (Sqlite3_column_blob)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET); - Sqlite3_column_bytes func_column_bytes = (Sqlite3_column_bytes)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET); - Sqlite3_finalize func_finalize = (Sqlite3_finalize)(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET); - if (dbMap.empty()) { - dbMap = GetDbHandles(); + Sqlite3_prepare func_prepare = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET); + Sqlite3_step func_step = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET); + Sqlite3_column_count func_column_count + = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET); + Sqlite3_column_name func_column_name + = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET); + Sqlite3_column_type func_column_type + = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET); + Sqlite3_column_blob func_column_blob + = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET); + Sqlite3_column_bytes func_column_bytes + = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET); + Sqlite3_finalize func_finalize = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET); + + if (db_map.empty()) { + db_map = get_db_handles(); + } + + auto it = db_map.find(db); + if (it == db_map.end() || it->second == 0) { + LOG_WARN("Empty handle for database '{}', retrying...", db); + db_map = get_db_handles(); + it = db_map.find(db); + if (it == db_map.end() || it->second == 0) { + LOG_ERROR("Failed to get handle for database '{}'", db); + return rows; + } } QWORD *stmt; - QWORD handle = dbMap[db]; - if (handle == 0) { - LOG_WARN("Empty handle, retrying..."); - dbMap = GetDbHandles(); - } - - int rc = func_prepare(dbMap[db], sql.c_str(), -1, &stmt, 0); + int rc = func_prepare(it->second, sql.c_str(), -1, &stmt, nullptr); if (rc != SQLITE_OK) { + LOG_ERROR("SQL prepare failed for '{}': error code {}", db, rc); return rows; } @@ -154,79 +167,119 @@ DbRows_t ExecDbQuery(const string db, const string sql) int length = func_column_bytes(stmt, i); const void *blob = func_column_blob(stmt, i); - if (length && (field.type != 5)) { - field.content.reserve(length); - copy((uint8_t *)blob, (uint8_t *)blob + length, back_inserter(field.content)); + if (length > 0 && field.type != SQLITE_NULL) { + field.content.resize(length); + std::memcpy(field.content.data(), blob, length); } row.push_back(field); } rows.push_back(row); } + + func_finalize(stmt); return rows; } -int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx) +int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) { - QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); // 总不能 int 还不够吧? - QWORD pStart = GET_QWORD(msgMgrAddr + 0x50); + if (!local_id || !db_idx) { + LOG_ERROR("Invalid pointer arguments!"); + return -1; + } - *dbIdx = 0; - for (int i = dbIndex - 1; i >= 0; i--) { // 从后往前遍历 - QWORD dbAddr = GET_QWORD(pStart + i * 0x08); - if (dbAddr) { - string dbname = Wstring2String(GET_WSTRING(dbAddr)); - dbMap[dbname] = GET_QWORD(dbAddr + 0x78); - string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + to_string(id) + ";"; - DbRows_t rows = ExecDbQuery(dbname, sql); - if (rows.empty()) { - continue; - } - DbRow_t row = rows.front(); - if (row.empty()) { - continue; - } - DbField_t field = row.front(); - if ((field.column.compare("localId") != 0) && (field.type != 1)) { - continue; - } + QWORD msg_mgr_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + int db_index = static_cast(GET_QWORD(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧? + QWORD p_start = GET_QWORD(msg_mgr_addr + 0x50); - *localId = strtoull((const char *)(field.content.data()), NULL, 10); - *dbIdx = (uint32_t)(GET_QWORD(GET_QWORD(dbAddr + 0x28) + 0x1E8) >> 32); - - return 0; + *db_idx = 0; + for (int i = db_index - 1; i >= 0; i--) { // 从后往前遍历 + QWORD db_addr = GET_QWORD(p_start + i * 0x08); + if (!db_addr) { + continue; } + + std::string dbname = Wstring2String(GET_WSTRING(db_addr)); + db_map[dbname] = GET_QWORD(db_addr + 0x78); + + std::string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + std::to_string(id) + ";"; + DbRows_t rows = exec_db_query(dbname, sql); + + if (rows.empty() || rows.front().empty()) { + continue; + } + + const DbField_t &field = rows.front().front(); + if (field.column != "localId" || field.type != SQLITE_INTEGER) { + continue; + } + + std::string id_str(field.content.begin(), field.content.end()); + try { + *local_id = std::stoull(id_str); + } catch (const std::exception &e) { + LOG_ERROR("Failed to parse localId: {}", e.what()); + continue; + } + + *db_idx = static_cast(GET_QWORD(GET_QWORD(db_addr + 0x28) + 0x1E8) >> 32); + return 0; } return -1; } -vector GetAudioData(uint64_t id) +std::vector get_audio_data(uint64_t id) { - QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); + QWORD msg_mgr_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + int db_index = static_cast(GET_QWORD(msg_mgr_addr + 0x68)); - string sql = "SELECT Buf FROM Media WHERE Reserved0=" + to_string(id) + ";"; - for (int i = dbIndex - 1; i >= 0; i--) { - string dbname = "MediaMSG" + to_string(i) + ".db"; - DbRows_t rows = ExecDbQuery(dbname, sql); - if (rows.empty()) { + std::string sql = "SELECT Buf FROM Media WHERE Reserved0=" + std::to_string(id) + ";"; + for (int i = db_index - 1; i >= 0; i--) { + std::string dbname = "MediaMSG" + std::to_string(i) + ".db"; + DbRows_t rows = exec_db_query(dbname, sql); + + if (rows.empty() || rows.front().empty()) { continue; } - DbRow_t row = rows.front(); - if (row.empty()) { - continue; - } - DbField_t field = row.front(); - if (field.column.compare("Buf") != 0) { + + const DbField_t &field = rows.front().front(); + if (field.column != "Buf" || field.content.empty()) { continue; } // 首字节为 0x02,估计是混淆用的?去掉。 - vector rv(field.content.begin() + 1, field.content.end()); + if (field.content.front() == 0x02) { + return std::vector(field.content.begin() + 1, field.content.end()); + } - return rv; + return field.content; } - return vector(); + return {}; } + +bool rpc_get_db_names(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + rsp.msg.dbs.names.funcs.encode = encode_dbnames; + rsp.msg.dbs.names.arg = &get_db_names(); + }); +} + +bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + rsp.msg.tables.tables.funcs.encode = encode_tables; + rsp.msg.tables.tables.arg = &get_db_tables(db); + }); +} + +bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + rsp.msg.rows.rows.funcs.encode = encode_rows; + rsp.msg.rows.rows.arg = &exec_db_query(db, sql); + }); +} + +} // namespace exec_sql diff --git a/WeChatFerry/spy/exec_sql.h b/WeChatFerry/spy/exec_sql.h index ea7fc8f..538f06e 100644 --- a/WeChatFerry/spy/exec_sql.h +++ b/WeChatFerry/spy/exec_sql.h @@ -1,11 +1,32 @@ #pragma once +#include +#include #include #include "pb_types.h" -DbNames_t GetDbNames(); -DbTables_t GetDbTables(const string db); -DbRows_t ExecDbQuery(const string db, const string sql); -int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx); -vector GetAudioData(uint64_t msgid); +namespace exec_sql +{ + +// 获取数据库名称列表 +DbNames_t get_db_names(); + +// 获取指定数据库的表列表 +DbTables_t get_db_tables(const std::string &db); + +// 执行 SQL 查询 +DbRows_t exec_db_query(const std::string &db, const std::string &sql); + +// 获取本地消息 ID 和数据库索引 +std::optional> get_local_id_and_dbidx(uint64_t id); + +// 获取音频数据 +std::vector get_audio_data(uint64_t msg_id); + +// RPC 方法 +bool rpc_get_db_names(uint8_t *out, size_t *len); +bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len); +bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len); + +} // namespace exec_sql diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 10a5f85..b558f38 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -112,7 +112,7 @@ static bool func_get_contacts(uint8_t *out, size_t *len) static bool func_get_db_names(uint8_t *out, size_t *len) { return FillResponse(Response_dbs_tag, out, len, [](Response &rsp) { - static DbNames_t dbnames = GetDbNames(); + static DbNames_t dbnames = exec_sql::get_db_names(); rsp.msg.dbs.names.funcs.encode = encode_dbnames; rsp.msg.dbs.names.arg = &dbnames; }); @@ -121,7 +121,7 @@ static bool func_get_db_names(uint8_t *out, size_t *len) static bool func_get_db_tables(char *db, uint8_t *out, size_t *len) { return FillResponse(Response_tables_tag, out, len, [db](Response &rsp) { - static DbTables_t tables = GetDbTables(db); + static DbTables_t tables = exec_sql::get_db_tables(db); rsp.msg.tables.tables.funcs.encode = encode_tables; rsp.msg.tables.tables.arg = &tables; }); @@ -362,7 +362,7 @@ static bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) if ((db == nullptr) || (sql == nullptr)) { LOG_ERROR("Empty db or sql."); } else { - rows = ExecDbQuery(db, sql); + rows = exec_sql::exec_db_query(db, sql); } rsp.msg.rows.rows.arg = &rows; rsp.msg.rows.rows.funcs.encode = encode_rows; From ada071c7d2a3bc241c5a7b786682a34e8a942995 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 1 Feb 2025 18:48:56 +0800 Subject: [PATCH 015/132] Refactoring --- WeChatFerry/spy/exec_sql.cpp | 1 + WeChatFerry/spy/exec_sql.h | 2 +- WeChatFerry/spy/funcs.cpp | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 5ba9457..3c8e369 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -4,6 +4,7 @@ #include "exec_sql.h" #include "fill_response.h" #include "log.hpp" +#include "pb_util.h" #include "sqlite3.h" #include "util.h" diff --git a/WeChatFerry/spy/exec_sql.h b/WeChatFerry/spy/exec_sql.h index 538f06e..411c338 100644 --- a/WeChatFerry/spy/exec_sql.h +++ b/WeChatFerry/spy/exec_sql.h @@ -19,7 +19,7 @@ DbTables_t get_db_tables(const std::string &db); DbRows_t exec_db_query(const std::string &db, const std::string &sql); // 获取本地消息 ID 和数据库索引 -std::optional> get_local_id_and_dbidx(uint64_t id); +int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx); // 获取音频数据 std::vector get_audio_data(uint64_t msg_id); diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp index 56b1b8c..0ec2959 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/funcs.cpp @@ -202,7 +202,7 @@ int DownloadAttach(QWORD id, string thumb, string extra) return 0; } - if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) { + if (exec_sql::get_local_id_and_dbidx(id, &localId, &dbIdx) != 0) { LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id)); return status; } @@ -285,7 +285,7 @@ string GetAudio(QWORD id, string dir) return mp3path; } - vector silk = GetAudioData(id); + vector silk = exec_sql::get_audio_data(id); if (silk.size() == 0) { LOG_ERROR("Empty audio data."); return ""; @@ -305,7 +305,7 @@ string GetPCMAudio(uint64_t id, string dir, int32_t sr) return pcmpath; } vector pcm; - vector silk = GetAudioData(id); + vector silk = exec_sql::get_audio_data(id); if (silk.size() == 0) { LOG_ERROR("Empty audio data."); return ""; From 7dd1887dcfa10ad3d6af83d391512cfa826c522f Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 2 Feb 2025 08:54:12 +0800 Subject: [PATCH 016/132] Refactoring --- WeChatFerry/spy/exec_sql.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 3c8e369..0ed9982 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -262,24 +262,27 @@ std::vector get_audio_data(uint64_t id) bool rpc_get_db_names(uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { + DbNames_t names = get_db_names(); rsp.msg.dbs.names.funcs.encode = encode_dbnames; - rsp.msg.dbs.names.arg = &get_db_names(); + rsp.msg.dbs.names.arg = &names; }); } bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { + DbTables_t tables = get_db_tables(db); rsp.msg.tables.tables.funcs.encode = encode_tables; - rsp.msg.tables.tables.arg = &get_db_tables(db); + rsp.msg.tables.tables.arg = &tables; }); } bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { + DbRows_t rows = exec_db_query(db, sql); rsp.msg.rows.rows.funcs.encode = encode_rows; - rsp.msg.rows.rows.arg = &exec_db_query(db, sql); + rsp.msg.rows.rows.arg = &rows; }); } From e5480bf6671fb9bd154cbee157d04c3f8ca41a43 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Feb 2025 21:20:22 +0800 Subject: [PATCH 017/132] Refactoring --- WeChatFerry/spy/send_msg.cpp | 450 +++++++++++++++++++++-------------- WeChatFerry/spy/send_msg.h | 95 ++++++-- 2 files changed, 344 insertions(+), 201 deletions(-) diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index 4e5bbdb..b9451e3 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -1,11 +1,11 @@ - -#include "framework.h" +#include "send_msg.h" + #include #include #include "exec_sql.h" +#include "fill_response.h" #include "log.hpp" -#include "send_msg.h" #include "spy_types.h" #include "user_info.h" #include "util.h" @@ -32,242 +32,326 @@ extern QWORD g_WeChatWinDllAddr; #define OS_XML_BUFSIGN 0x24F0D70 #define OS_SEND_XML 0x20CF360 -typedef QWORD (*New_t)(QWORD); -typedef QWORD (*Free_t)(QWORD); -typedef QWORD (*SendMsgMgr_t)(); -typedef QWORD (*GetAppMsgMgr_t)(); -typedef QWORD (*SendTextMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*SendImageMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*SendFileMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, - QWORD); -typedef QWORD (*SendRichTextMsg_t)(QWORD, QWORD, QWORD); -typedef QWORD (*SendPatMsg_t)(QWORD, QWORD); -typedef QWORD (*ForwardMsg_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*GetEmotionMgr_t)(); -typedef QWORD (*SendEmotion_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - -typedef QWORD (*XmlBufSign_t)(QWORD, QWORD, QWORD); -typedef QWORD (*SendXmlMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - -void SendTextMessage(string wxid, string msg, string atWxids) +SendMsgManager &SendMsgManager::get_instance() { - QWORD success = 0; - wstring wsWxid = String2Wstring(wxid); - wstring wsMsg = String2Wstring(msg); - WxString wxMsg(wsMsg); - WxString wxWxid(wsWxid); - - vector vAtWxids; - vector vWxAtWxids; - if (!atWxids.empty()) { - wstringstream wss(String2Wstring(atWxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vAtWxids.push_back(wstr); - WxString wxAtWxid(vAtWxids.back()); - vWxAtWxids.push_back(wxAtWxid); - } - } else { - WxString wxEmpty = WxString(); - vWxAtWxids.push_back(wxEmpty); - } - - QWORD wxAters = (QWORD) & ((RawVector_t *)&vWxAtWxids)->start; - - char buffer[0x460] = { 0 }; - SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); - SendTextMsg_t funcSendTextMsg = (SendTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_TEXT); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - funcSendMsgMgr(); - success = funcSendTextMsg((QWORD)(&buffer), (QWORD)(&wxWxid), (QWORD)(&wxMsg), wxAters, 1, 1, 0, 0); - funcFree((QWORD)(&buffer)); + static SendMsgManager instance; + return instance; } -void SendImageMessage(string wxid, string path) +SendMsgManager::SendMsgManager() { - wstring wsWxid = String2Wstring(wxid); - wstring wsPath = String2Wstring(path); + func_new = reinterpret_cast(g_WeChatWinDllAddr + OS_NEW); + func_free = reinterpret_cast(g_WeChatWinDllAddr + OS_FREE); + func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); + func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_TEXT); + func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_IMAGE); + func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_FILE); + func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); + func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); + func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); + func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_EMOTION); + func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_XML); +} - WxString wxWxid(wsWxid); - WxString wxPath(wsPath); +std::unique_ptr SendMsgManager::new_wx_string(const std::string &str) +{ + return std::make_unique(String2Wstring(str)); +} - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); - SendImageMsg_t funcSendImage = (SendImageMsg_t)(g_WeChatWinDllAddr + OS_SEND_IMAGE); +std::vector SendMsgManager::parse_wxids(const string &wxids) +{ + vector wx_members; + wstringstream wss(String2Wstring(wxids)); + wstring wstr; + while (getline(wss, wstr, L',')) { + wx_members.emplace_back(wstr); + } + return wx_members; +} + +void SendMsgManager::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) +{ + auto wxWxid = new_wx_string(wxid); + auto wxMsg = new_wx_string(msg); + + std::vector wx_at_wxids; + if (!at_wxids.empty()) { + wx_at_wxids = parse_wxids(at_wxids); + } else { + wx_at_wxids.emplace_back(); + } + + QWORD wx_ater_list = reinterpret_cast(&(wx_at_wxids.front())); + + char buffer[0x460] = { 0 }; + func_send_msg_mgr(); + func_send_text(reinterpret_cast(&buffer), reinterpret_cast(wxWxid.get()), + reinterpret_cast(wxMsg.get()), wx_ater_list, 1, 1, 0, 0); + func_free(reinterpret_cast(&buffer)); +} + +void SendMsgManager::send_image(const std::string &wxid, const std::string &path) +{ + auto wxWxid = new_wx_string(wxid); + auto wxPath = new_wx_string(path); char msg[0x460] = { 0 }; char msgTmp[0x460] = { 0 }; - QWORD *flag[10] = { 0 }; + QWORD *flag[10] = { nullptr }; QWORD tmp1 = 0, tmp2 = 0; - QWORD pMsgTmp = funcNew((QWORD)(&msgTmp)); + QWORD pMsgTmp = func_new(reinterpret_cast(&msgTmp)); flag[8] = &tmp1; flag[9] = &tmp2; - flag[1] = (QWORD *)(pMsgTmp); + flag[1] = reinterpret_cast(pMsgTmp); - QWORD pMsg = funcNew((QWORD)(&msg)); - QWORD sendMgr = funcSendMsgMgr(); - funcSendImage(sendMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), (QWORD)(&flag)); - funcFree(pMsg); - funcFree(pMsgTmp); + QWORD pMsg = func_new(reinterpret_cast(&msg)); + QWORD sendMgr = func_send_msg_mgr(); + + funcSendImage(sendMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), + reinterpret_cast(&flag)); + + func_free(pMsg); + func_free(pMsgTmp); } -void SendFileMessage(string wxid, string path) +void SendMsgManager::send_file(const std::string &wxid, const std::string &path) { - wstring wsWxid = String2Wstring(wxid); - wstring wsPath = String2Wstring(path); - - WxString wxWxid(wsWxid); - WxString wxPath(wsPath); - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR); - SendFileMsg_t funcSendFile = (SendFileMsg_t)(g_WeChatWinDllAddr + OS_SEND_FILE); + auto wxWxid = new_wx_string(wxid); + auto wxPath = new_wx_string(path); char msg[0x460] = { 0 }; QWORD tmp1[4] = { 0 }; QWORD tmp2[4] = { 0 }; QWORD tmp3[4] = { 0 }; - QWORD pMsg = funcNew((QWORD)(&msg)); - QWORD appMgr = funcGetAppMsgMgr(); - funcSendFile(appMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), 1, tmp1, 0, tmp2, 0, tmp3, 0, 0); - funcFree(pMsg); + QWORD pMsg = func_new(reinterpret_cast(&msg)); + QWORD appMgr = func_get_app_mgr(); + + func_send_file(appMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), 1, tmp1, + 0, tmp2, 0, tmp3, 0, 0); + + func_free(pMsg); } -int SendRichTextMessage(RichText_t &rt) -{ // TODO: Fix memory leak +void SendMsgManager::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, + uint64_t type) +{ + std::unique_ptr buff(new char[0x500]()); + std::unique_ptr buff2(new char[0x500]()); + char nullBuf[0x1C] = { 0 }; + + func_new(reinterpret_cast(buff.get())); + func_new(reinterpret_cast(buff2.get())); + + QWORD sbuf[4] = { 0, 0, 0, 0 }; + QWORD sign = func_xml_buf_sign(reinterpret_cast(buff2.get()), reinterpret_cast(sbuf), 0x1); + + auto wxReceiver = new_wx_string(receiver); + auto wxXml = new_wx_string(xml); + auto wxPath = new_wx_string(path); + auto wxSender = new_wx_string(user_info::get_self_wxid()); + + func_send_xml(reinterpret_cast(buff.get()), reinterpret_cast(wxSender.get()), + reinterpret_cast(wxReceiver.get()), reinterpret_cast(wxXml.get()), + reinterpret_cast(wxPath.get()), reinterpret_cast(nullBuf), type, 0x4, sign, + reinterpret_cast(buff2.get())); + + func_free(reinterpret_cast(buff.get())); + func_free(reinterpret_cast(buff2.get())); +} + +void SendMsgManager::send_emotion(const std::string &wxid, const std::string &path) +{ + auto wxWxid = new_wx_string(wxid); + auto wxPath = new_wx_string(path); + + std::unique_ptr buff(new QWORD[8]()); // 0x20 bytes = 8 * QWORD + + if (!buff) { + LOG_ERROR("Out of Memory..."); + return; + } + + QWORD mgr = func_get_emotion_mgr(); + func_send_emotion(mgr, reinterpret_cast(wxPath.get()), reinterpret_cast(buff.get()), + reinterpret_cast(wxWxid.get()), 2, reinterpret_cast(buff.get()), 0, + reinterpret_cast(buff.get())); +} + +int SendMsgManager::send_rich_text(RichText_t &rt) +{ QWORD status = -1; - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_RTM_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_RTM_FREE); - GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR); - SendRichTextMsg_t funcForwordPublicMsg = (SendRichTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); - - char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE); - if (buff == NULL) { + char *buff = static_cast(HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE)); + if (!buff) { LOG_ERROR("Out of Memory..."); return -1; } memset(buff, 0, SRTM_SIZE); - funcNew((QWORD)buff); - WxString *pReceiver = NewWxStringFromStr(rt.receiver); - WxString *pTitle = NewWxStringFromStr(rt.title); - WxString *pUrl = NewWxStringFromStr(rt.url); - WxString *pThumburl = NewWxStringFromStr(rt.thumburl); - WxString *pDigest = NewWxStringFromStr(rt.digest); - WxString *pAccount = NewWxStringFromStr(rt.account); - WxString *pName = NewWxStringFromStr(rt.name); + func_new(reinterpret_cast(buff)); - memcpy(buff + 0x8, pTitle, sizeof(WxString)); - memcpy(buff + 0x48, pUrl, sizeof(WxString)); - memcpy(buff + 0xB0, pThumburl, sizeof(WxString)); - memcpy(buff + 0xF0, pDigest, sizeof(WxString)); - memcpy(buff + 0x2C0, pAccount, sizeof(WxString)); - memcpy(buff + 0x2E0, pName, sizeof(WxString)); + auto pReceiver = new_wx_string(rt.receiver); + auto pTitle = new_wx_string(rt.title); + auto pUrl = new_wx_string(rt.url); + auto pThumburl = new_wx_string(rt.thumburl); + auto pDigest = new_wx_string(rt.digest); + auto pAccount = new_wx_string(rt.account); + auto pName = new_wx_string(rt.name); - QWORD mgr = funcGetAppMsgMgr(); - status = funcForwordPublicMsg(mgr, (QWORD)(pReceiver), (QWORD)(buff)); - funcFree((QWORD)buff); + memcpy(buff + 0x8, pTitle.get(), sizeof(WxString)); + memcpy(buff + 0x48, pUrl.get(), sizeof(WxString)); + memcpy(buff + 0xB0, pThumburl.get(), sizeof(WxString)); + memcpy(buff + 0xF0, pDigest.get(), sizeof(WxString)); + memcpy(buff + 0x2C0, pAccount.get(), sizeof(WxString)); + memcpy(buff + 0x2E0, pName.get(), sizeof(WxString)); - return (int)status; + QWORD mgr = func_get_app_mgr(); + status = func_send_rich_text(mgr, reinterpret_cast(pReceiver.get()), reinterpret_cast(buff)); + func_free(reinterpret_cast(buff)); + + return static_cast(status); } -int SendPatMessage(string roomid, string wxid) +int SendMsgManager::send_pat(const std::string &roomid, const std::string &wxid) { QWORD status = -1; - wstring wsRoomid = String2Wstring(roomid); - wstring wsWxid = String2Wstring(wxid); - WxString wxRoomid(wsRoomid); - WxString wxWxid(wsWxid); + auto wxRoomid = new_wx_string(roomid); + auto wxWxid = new_wx_string(wxid); - SendPatMsg_t funcSendPatMsg = (SendPatMsg_t)(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); + status = func_send_pat(reinterpret_cast(wxRoomid.get()), reinterpret_cast(wxWxid.get())); - status = funcSendPatMsg((QWORD)(&wxRoomid), (QWORD)(&wxWxid)); - return (int)status; + return static_cast(status); } -int ForwardMessage(QWORD msgid, string receiver) +int SendMsgManager::forward_message(QWORD msgid, const std::string &receiver) { - int status = -1; uint32_t dbIdx = 0; QWORD localId = 0; - ForwardMsg_t funcForwardMsg = (ForwardMsg_t)(g_WeChatWinDllAddr + OS_FORWARD_MSG); - if (GetLocalIdandDbidx(msgid, &localId, &dbIdx) != 0) { - LOG_ERROR("Failed to get localId, Please check id: {}", to_string(msgid)); - return status; + if (exec_sql::get_local_id_and_dbidx(msgid, &localId, &dbIdx) != 0) { + LOG_ERROR("Failed to get localId, Please check id: {}", msgid); + return -1; } - WxString *pReceiver = NewWxStringFromStr(receiver); - LARGE_INTEGER l; - l.HighPart = dbIdx; - l.LowPart = (DWORD)localId; + l.HighPart = dbIdx; + l.LowPart = static_cast(localId); + auto wxReceiver = new_wx_string(receiver); - status = (int)funcForwardMsg((QWORD)pReceiver, l.QuadPart, 0x4, 0x0); - - return status; + return static_cast(func_forward(reinterpret_cast(wxReceiver.get()), l.QuadPart, 0x4, 0x0)); } -void SendEmotionMessage(string wxid, string path) +// RPC 方法 + +bool SendMsgManager::rpc_send_text(TextMsg &text, uint8_t *out, size_t *len) { - GetEmotionMgr_t GetEmotionMgr = (GetEmotionMgr_t)(g_WeChatWinDllAddr + OS_GET_EMOTION_MGR); - SendEmotion_t SendEmotion = (SendEmotion_t)(g_WeChatWinDllAddr + OS_SEND_EMOTION); - - WxString *pWxPath = NewWxStringFromStr(path); - WxString *pWxWxid = NewWxStringFromStr(wxid); - - QWORD *buff = (QWORD *)HeapAlloc(GetProcessHeap(), 0, 0x20); - if (buff == NULL) { - LOG_ERROR("Out of Memory..."); - return; - } - - memset(buff, 0, 0x20); - QWORD mgr = GetEmotionMgr(); - SendEmotion(mgr, (QWORD)pWxPath, (QWORD)buff, (QWORD)pWxWxid, 2, (QWORD)buff, 0, (QWORD)buff); + return fill_response(out, len, [&](Response &rsp) { + if (text.msg.empty() || text.receiver.empty()) { + LOG_ERROR("Empty message or receiver."); + rsp.msg.status = -1; + } else { + send_text(text.receiver, text.msg, text.aters); + rsp.msg.status = 0; + } + }); } -void SendXmlMessage(string receiver, string xml, string path, QWORD type) +bool SendMsgManager::rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) { - if (g_WeChatWinDllAddr == 0) { - return; - } - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - - XmlBufSign_t xmlBufSign = (XmlBufSign_t)(g_WeChatWinDllAddr + OS_XML_BUFSIGN); - SendXmlMsg_t sendXmlMsg = (SendXmlMsg_t)(g_WeChatWinDllAddr + OS_SEND_XML); - - char buff[0x500] = { 0 }; - char buff2[0x500] = { 0 }; - char nullBuf[0x1C] = { 0 }; - - QWORD pBuf = (QWORD)(&buff); - QWORD pBuf2 = (QWORD)(&buff2); - - funcNew(pBuf); - funcNew(pBuf2); - - QWORD sbuf[4] = { 0, 0, 0, 0 }; - - QWORD sign = xmlBufSign(pBuf2, (QWORD)(&sbuf), 0x1); - - WxString *pReceiver = NewWxStringFromStr(receiver); - WxString *pXml = NewWxStringFromStr(xml); - WxString *pPath = NewWxStringFromStr(path); - WxString *pSender = NewWxStringFromStr(user_info::get_self_wxid()); - - sendXmlMsg(pBuf, (QWORD)pSender, (QWORD)pReceiver, (QWORD)pXml, (QWORD)pPath, (QWORD)(&nullBuf), type, 0x4, sign, - pBuf2); - - funcFree((QWORD)&buff); - funcFree((QWORD)&buff2); + return fill_response(out, len, [&](Response &rsp) { + if (path.empty() || receiver.empty()) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else { + send_image(receiver, path); + rsp.msg.status = 0; + } + }); +} + +bool SendMsgManager::rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (path.empty() || receiver.empty()) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else { + send_file(receiver, path); + rsp.msg.status = 0; + } + }); +} + +bool SendMsgManager::rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (path.empty() || receiver.empty()) { + LOG_ERROR("Empty path or receiver."); + rsp.msg.status = -1; + } else { + send_emotion(receiver, path); + rsp.msg.status = 0; + } + }); +} + +bool SendMsgManager::rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (rt.content.empty() || rt.receiver.empty()) { + LOG_ERROR("Empty content or receiver."); + rsp.msg.status = -1; + } else { + send_xml(rt.receiver, rt.content, rt.path, rt.type); + rsp.msg.status = 0; + } + }); +} + +bool SendMsgManager::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (rt.receiver.empty()) { + LOG_ERROR("Empty receiver."); + rsp.msg.status = -1; + } else { + RichText_t rtt { + .name = rt.name, + .account = rt.account, + .title = rt.title, + .digest = rt.digest, + .url = rt.url, + .thumburl = rt.thumburl, + .receiver = rt.receiver, + }; + rsp.msg.status = send_rich_text(rtt); + } + }); +} + +bool SendMsgManager::rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (roomid.empty() || wxid.empty()) { + LOG_ERROR("Empty roomid or wxid."); + rsp.msg.status = -1; + } else { + rsp.msg.status = send_pat(roomid, wxid); + } + }); +} + +bool SendMsgManager::rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + if (receiver.empty()) { + LOG_ERROR("Empty receiver."); + rsp.msg.status = -1; + } else { + rsp.msg.status = forward(msgid, receiver); + } + }); } diff --git a/WeChatFerry/spy/send_msg.h b/WeChatFerry/spy/send_msg.h index 0218a5d..c0c4e9d 100644 --- a/WeChatFerry/spy/send_msg.h +++ b/WeChatFerry/spy/send_msg.h @@ -1,24 +1,83 @@ #pragma once +#include #include -using namespace std; +#include "spy_types.h" +#include "wcf.pb.h" -typedef struct { - string name; - string account; - string title; - string digest; - string url; - string thumburl; - string receiver; -} RichText_t; +struct RichText_t { + std::string name; + std::string account; + std::string title; + std::string digest; + std::string url; + std::string thumburl; + std::string receiver; +}; -void SendTextMessage(string wxid, string msg, string atWxids); -void SendImageMessage(string wxid, string path); -void SendFileMessage(string wxid, string path); -void SendXmlMessage(string receiver, string xml, string path, uint64_t type); -void SendEmotionMessage(string wxid, string path); -int SendRichTextMessage(RichText_t &rt); -int SendPatMessage(string roomid, string wxid); -int ForwardMessage(uint64_t msgid, string receiver); +class SendMsgManager +{ +public: + static SendMsgManager &get_instance(); + + void send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids = ""); + void send_image(const std::string &wxid, const std::string &path); + void send_file(const std::string &wxid, const std::string &path); + void send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type); + void send_emotion(const std::string &wxid, const std::string &path); + int send_rich_text(RichText_t &rt); + int send_pat(const std::string &roomid, const std::string &wxid); + int forward(uint64_t msgid, const std::string &receiver); + + // RPC 方法 + bool rpc_send_text(TextMsg &text, uint8_t *out, size_t *len); + bool rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); + bool rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); + bool rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); + bool rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len); + bool rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len); + bool rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len); + bool rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len); + +private: + SendMsgManager(); + ~SendMsgManager() = default; + + SendMsgManager(const SendMsgManager &) = delete; + SendMsgManager &operator=(const SendMsgManager &) = delete; + + using New_t = QWORD (*)(QWORD); + using Free_t = QWORD (*)(QWORD); + using SendMsgMgr_t = QWORD (*)(); + using GetAppMgr_t = QWORD (*)(); + using SendText_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); + using SendImage_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD); + using SendFile_t + = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD); + using SendRichText_t = QWORD (*)(QWORD, QWORD, QWORD); + using SendPat_t = QWORD (*)(QWORD, QWORD); + using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); + using GetEmotionMgr_t = QWORD (*)(); + using SendEmotion_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); + using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD); + using SendXml_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); + + New_t func_new; + Free_t func_free; + SendMsgMgr_t func_send_msg_mgr; + GetAppMgr_t func_get_app_mgr; + SendText_t func_send_text; + SendImage_t func_send_image; + SendFile_t func_send_file; + SendRichText_t func_send_rich_text; + SendPat_t func_send_pat; + Forward_t func_forward; + GetEmotionMgr_t func_get_emotion_mgr; + SendEmotion_t func_send_emotion; + XmlBufSign_t func_xml_buf_sign; + SendXml_t func_send_xml; + + std::unique_ptr new_wx_string(const std::string &str); + std::vector parse_wxids(const std::string &wxids); +}; From b79e163cc992dd97b34231e07bb9122a51e70591 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Feb 2025 21:36:39 +0800 Subject: [PATCH 018/132] Refactoring --- WeChatFerry/spy/rpc_server.cpp | 16 ++++++++-------- WeChatFerry/spy/send_msg.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index b558f38..e474d5f 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -145,7 +145,7 @@ static bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len) std::string msg(txt.msg); std::string receiver(txt.receiver); std::string aters(txt.aters ? txt.aters : ""); - SendTextMessage(receiver, msg, aters); + SendMsgManager::send_text(receiver, msg, aters); rsp.msg.status = 0; } }); @@ -161,7 +161,7 @@ static bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { - SendImageMessage(receiver, path); + SendMsgManager::send_image(receiver, path); rsp.msg.status = 0; } }); @@ -177,7 +177,7 @@ static bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { - SendFileMessage(receiver, path); + SendMsgManager::send_file(receiver, path); rsp.msg.status = 0; } }); @@ -190,7 +190,7 @@ static bool func_send_emotion(char *path, char *receiver, uint8_t *out, size_t * LOG_ERROR("Empty path or receiver."); rsp.msg.status = -1; } else { - SendEmotionMessage(receiver, path); + SendMsgManager::send_emotion(receiver, path); rsp.msg.status = 0; } }); @@ -207,7 +207,7 @@ static bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len) std::string content(xml.content); std::string path(xml.path ? xml.path : ""); uint64_t type = static_cast(xml.type); - SendXmlMessage(receiver, content, path, type); + SendMsgManager::send_xml(receiver, content, path, type); rsp.msg.status = 0; } }); @@ -229,7 +229,7 @@ static bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) rtt.title = std::string(rt.title ? rt.title : ""); rtt.url = std::string(rt.url ? rt.url : ""); - rsp.msg.status = SendRichTextMessage(rtt); + rsp.msg.status = SendMsgManager::send_rich_text(rtt); } }); } @@ -241,7 +241,7 @@ static bool func_send_pat_msg(char *roomid, char *wxid, uint8_t *out, size_t *le LOG_ERROR("Empty roomid or wxid."); rsp.msg.status = -1; } else { - rsp.msg.status = SendPatMessage(roomid, wxid); + rsp.msg.status = SendMsgManager::send_pat(roomid, wxid); } }); } @@ -253,7 +253,7 @@ static bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t * LOG_ERROR("Empty receiver."); rsp.msg.status = -1; } else { - rsp.msg.status = ForwardMessage(id, receiver); + rsp.msg.status = SendMsgManager::forward(id, receiver); } }); } diff --git a/WeChatFerry/spy/send_msg.h b/WeChatFerry/spy/send_msg.h index c0c4e9d..a49c68e 100644 --- a/WeChatFerry/spy/send_msg.h +++ b/WeChatFerry/spy/send_msg.h @@ -2,6 +2,7 @@ #include #include +#include #include "spy_types.h" #include "wcf.pb.h" From 0fd3449894649a7607910e40b53e8e4219fc2f0b Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Feb 2025 21:40:22 +0800 Subject: [PATCH 019/132] Refactoring --- WeChatFerry/spy/rpc_server.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index e474d5f..3891939 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -45,6 +45,7 @@ static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 auto &msgHandler = MessageHandler::getInstance(); +auto &sendMgr = SendMsgManager::get_instance(); static std::string BuildUrl(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } @@ -145,7 +146,7 @@ static bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len) std::string msg(txt.msg); std::string receiver(txt.receiver); std::string aters(txt.aters ? txt.aters : ""); - SendMsgManager::send_text(receiver, msg, aters); + sendMgr.send_text(receiver, msg, aters); rsp.msg.status = 0; } }); @@ -161,7 +162,7 @@ static bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { - SendMsgManager::send_image(receiver, path); + sendMgr.send_image(receiver, path); rsp.msg.status = 0; } }); @@ -177,7 +178,7 @@ static bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { - SendMsgManager::send_file(receiver, path); + sendMgr.send_file(receiver, path); rsp.msg.status = 0; } }); @@ -190,7 +191,7 @@ static bool func_send_emotion(char *path, char *receiver, uint8_t *out, size_t * LOG_ERROR("Empty path or receiver."); rsp.msg.status = -1; } else { - SendMsgManager::send_emotion(receiver, path); + sendMgr.send_emotion(receiver, path); rsp.msg.status = 0; } }); @@ -207,7 +208,7 @@ static bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len) std::string content(xml.content); std::string path(xml.path ? xml.path : ""); uint64_t type = static_cast(xml.type); - SendMsgManager::send_xml(receiver, content, path, type); + sendMgr.send_xml(receiver, content, path, type); rsp.msg.status = 0; } }); @@ -229,7 +230,7 @@ static bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) rtt.title = std::string(rt.title ? rt.title : ""); rtt.url = std::string(rt.url ? rt.url : ""); - rsp.msg.status = SendMsgManager::send_rich_text(rtt); + rsp.msg.status = sendMgr.send_rich_text(rtt); } }); } @@ -241,7 +242,7 @@ static bool func_send_pat_msg(char *roomid, char *wxid, uint8_t *out, size_t *le LOG_ERROR("Empty roomid or wxid."); rsp.msg.status = -1; } else { - rsp.msg.status = SendMsgManager::send_pat(roomid, wxid); + rsp.msg.status = sendMgr.send_pat(roomid, wxid); } }); } @@ -253,7 +254,7 @@ static bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t * LOG_ERROR("Empty receiver."); rsp.msg.status = -1; } else { - rsp.msg.status = SendMsgManager::forward(id, receiver); + rsp.msg.status = sendMgr.forward(id, receiver); } }); } From ff1a8c980784536fe8d3238aa8e96c43f45a4e23 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Feb 2025 23:18:20 +0800 Subject: [PATCH 020/132] Refactoring --- WeChatFerry/spy/spy_types.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/WeChatFerry/spy/spy_types.h b/WeChatFerry/spy/spy_types.h index 7fd1008..56f7c51 100644 --- a/WeChatFerry/spy/spy_types.h +++ b/WeChatFerry/spy/spy_types.h @@ -5,29 +5,42 @@ typedef uint64_t QWORD; -struct WxString { +class WxString +{ + +public: const wchar_t *wptr; DWORD size; DWORD capacity; const char *ptr; DWORD clen; + WxString() { - wptr = NULL; + wptr = nullptr; size = 0; capacity = 0; - ptr = NULL; + ptr = nullptr; clen = 0; } - WxString(std::wstring &ws) + WxString(std::wstring ws) : internal_ws(std::move(ws)) { - wptr = ws.c_str(); - size = (DWORD)ws.size(); - capacity = (DWORD)ws.capacity(); - ptr = NULL; + wptr = internal_ws.c_str(); + size = static_cast(internal_ws.size()); + capacity = static_cast(internal_ws.capacity()); + ptr = nullptr; clen = 0; } + + WxString(const WxString &) = delete; + WxString &operator=(const WxString &) = delete; + + WxString(WxString &&) noexcept = default; + WxString &operator=(WxString &&) noexcept = default; + +private: + std::wstring internal_ws; }; typedef struct RawVector { From b426e84241ae11647eba1bce5500ddcdbce9e9e5 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Feb 2025 23:18:41 +0800 Subject: [PATCH 021/132] Refatoring --- WeChatFerry/spy/rpc_server.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 3891939..f4419ac 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -221,16 +221,7 @@ static bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) LOG_ERROR("Empty receiver."); rsp.msg.status = -1; } else { - RichText_t rtt; - rtt.account = std::string(rt.account ? rt.account : ""); - rtt.digest = std::string(rt.digest ? rt.digest : ""); - rtt.name = std::string(rt.name ? rt.name : ""); - rtt.receiver = std::string(rt.receiver ? rt.receiver : ""); - rtt.thumburl = std::string(rt.thumburl ? rt.thumburl : ""); - rtt.title = std::string(rt.title ? rt.title : ""); - rtt.url = std::string(rt.url ? rt.url : ""); - - rsp.msg.status = sendMgr.send_rich_text(rtt); + rsp.msg.status = sendMgr.send_rich_text(rt); } }); } From 66e14be1361f91d00503ac83f7d6ad3d3fadf65f Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 05:27:07 +0800 Subject: [PATCH 022/132] Refactoring --- WeChatFerry/spy/send_msg.cpp | 42 +++++++++++++++--------------------- WeChatFerry/spy/send_msg.h | 23 ++++++++++---------- 2 files changed, 29 insertions(+), 36 deletions(-) diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index b9451e3..153f12a 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -10,11 +10,8 @@ #include "user_info.h" #include "util.h" -extern HANDLE g_hEvent; extern QWORD g_WeChatWinDllAddr; -#define SRTM_SIZE 0x3F0 - #define OS_NEW 0x1B5E140 #define OS_FREE 0x1B55850 #define OS_SEND_MSG_MGR 0x1B53FD0 @@ -53,6 +50,10 @@ SendMsgManager::SendMsgManager() func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_XML); } +std::unique_ptr SendMsgManager::new_wx_string(const char *str) +{ + return new_wx_string(str ? std::string(str) : std::string()); +} std::unique_ptr SendMsgManager::new_wx_string(const std::string &str) { return std::make_unique(String2Wstring(str)); @@ -108,8 +109,8 @@ void SendMsgManager::send_image(const std::string &wxid, const std::string &path QWORD pMsg = func_new(reinterpret_cast(&msg)); QWORD sendMgr = func_send_msg_mgr(); - funcSendImage(sendMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), - reinterpret_cast(&flag)); + func_send_image(sendMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), + reinterpret_cast(&flag)); func_free(pMsg); func_free(pMsgTmp); @@ -179,8 +180,9 @@ void SendMsgManager::send_emotion(const std::string &wxid, const std::string &pa reinterpret_cast(buff.get())); } -int SendMsgManager::send_rich_text(RichText_t &rt) +int SendMsgManager::send_rich_text(const RichText &rt) { +#define SRTM_SIZE 0x3F0 QWORD status = -1; char *buff = static_cast(HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE)); @@ -226,7 +228,7 @@ int SendMsgManager::send_pat(const std::string &roomid, const std::string &wxid) return static_cast(status); } -int SendMsgManager::forward_message(QWORD msgid, const std::string &receiver) +int SendMsgManager::forward(QWORD msgid, const std::string &receiver) { uint32_t dbIdx = 0; QWORD localId = 0; @@ -245,15 +247,14 @@ int SendMsgManager::forward_message(QWORD msgid, const std::string &receiver) } // RPC 方法 - -bool SendMsgManager::rpc_send_text(TextMsg &text, uint8_t *out, size_t *len) +bool SendMsgManager::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { - if (text.msg.empty() || text.receiver.empty()) { + if (text.msg == nullptr || text.receiver == nullptr || strlen(text.msg) == 0 || strlen(text.receiver) == 0) { LOG_ERROR("Empty message or receiver."); rsp.msg.status = -1; } else { - send_text(text.receiver, text.msg, text.aters); + send_text(text.receiver, text.msg, text.aters ? text.aters : ""); rsp.msg.status = 0; } }); @@ -298,14 +299,14 @@ bool SendMsgManager::rpc_send_emotion(const std::string &path, const std::string }); } -bool SendMsgManager::rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len) +bool SendMsgManager::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { - if (rt.content.empty() || rt.receiver.empty()) { + if (xml.content == nullptr || xml.receiver == nullptr) { LOG_ERROR("Empty content or receiver."); rsp.msg.status = -1; } else { - send_xml(rt.receiver, rt.content, rt.path, rt.type); + send_xml(xml.receiver, xml.content, xml.path, xml.type); rsp.msg.status = 0; } }); @@ -314,20 +315,11 @@ bool SendMsgManager::rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len) bool SendMsgManager::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { - if (rt.receiver.empty()) { + if (rt.receiver == nullptr) { LOG_ERROR("Empty receiver."); rsp.msg.status = -1; } else { - RichText_t rtt { - .name = rt.name, - .account = rt.account, - .title = rt.title, - .digest = rt.digest, - .url = rt.url, - .thumburl = rt.thumburl, - .receiver = rt.receiver, - }; - rsp.msg.status = send_rich_text(rtt); + rsp.msg.status = send_rich_text(rt); } }); } diff --git a/WeChatFerry/spy/send_msg.h b/WeChatFerry/spy/send_msg.h index a49c68e..e7a3fa7 100644 --- a/WeChatFerry/spy/send_msg.h +++ b/WeChatFerry/spy/send_msg.h @@ -7,15 +7,15 @@ #include "spy_types.h" #include "wcf.pb.h" -struct RichText_t { - std::string name; - std::string account; - std::string title; - std::string digest; - std::string url; - std::string thumburl; - std::string receiver; -}; +// struct RichText_t { +// std::string name; +// std::string account; +// std::string title; +// std::string digest; +// std::string url; +// std::string thumburl; +// std::string receiver; +// }; class SendMsgManager { @@ -27,12 +27,12 @@ public: void send_file(const std::string &wxid, const std::string &path); void send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type); void send_emotion(const std::string &wxid, const std::string &path); - int send_rich_text(RichText_t &rt); + int send_rich_text(const RichText &rt); int send_pat(const std::string &roomid, const std::string &wxid); int forward(uint64_t msgid, const std::string &receiver); // RPC 方法 - bool rpc_send_text(TextMsg &text, uint8_t *out, size_t *len); + bool rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len); bool rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); bool rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); bool rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); @@ -79,6 +79,7 @@ private: XmlBufSign_t func_xml_buf_sign; SendXml_t func_send_xml; + std::unique_ptr new_wx_string(const char *str); std::unique_ptr new_wx_string(const std::string &str); std::vector parse_wxids(const std::string &wxids); }; From 796e078c0ca9abce14939817709bae77e1f416b0 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 10:09:33 +0800 Subject: [PATCH 023/132] Remove unused code --- WeChatFerry/spy/send_msg.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/WeChatFerry/spy/send_msg.h b/WeChatFerry/spy/send_msg.h index e7a3fa7..66908d6 100644 --- a/WeChatFerry/spy/send_msg.h +++ b/WeChatFerry/spy/send_msg.h @@ -7,16 +7,6 @@ #include "spy_types.h" #include "wcf.pb.h" -// struct RichText_t { -// std::string name; -// std::string account; -// std::string title; -// std::string digest; -// std::string url; -// std::string thumburl; -// std::string receiver; -// }; - class SendMsgManager { public: From ade2c1c22a589ac69cd1dd5b28df89ae74e404a7 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 13:29:02 +0800 Subject: [PATCH 024/132] Refactoring --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index f2f79fa..46ee088 100644 --- a/README.MD +++ b/README.MD @@ -168,7 +168,7 @@ sdk.WxDestroySDK() ### 调试日志 ```c - DbgMsg("ListenMessage"); // 封装的 OutputDebugString + util::dbg_msg("ListenMessage"); // 封装的 OutputDebugString OutputDebugString(L"ListenMessage\n"); MessageBox(NULL, L"ListenMessage", L"ListenMessage", 0); ``` From c96d85db30595d5b2db42f8b3b0c74844664090d Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 15:35:09 +0800 Subject: [PATCH 025/132] Refactoring --- WeChatFerry/com/log.hpp | 2 +- WeChatFerry/com/util.cpp | 400 +++++++++++------------------- WeChatFerry/com/util.h | 100 +++++--- WeChatFerry/sdk/injector.cpp | 2 +- WeChatFerry/sdk/sdk.cpp | 4 +- WeChatFerry/spy/chatroom_mgmt.cpp | 2 +- WeChatFerry/spy/contact_mgmt.cpp | 23 +- WeChatFerry/spy/exec_sql.cpp | 13 +- WeChatFerry/spy/funcs.cpp | 9 +- WeChatFerry/spy/receive_msg.cpp | 25 +- WeChatFerry/spy/rpc_server.cpp | 9 +- WeChatFerry/spy/send_msg.cpp | 4 +- WeChatFerry/spy/spy.cpp | 41 ++- WeChatFerry/spy/spy.h | 2 - WeChatFerry/spy/user_info.cpp | 20 +- 15 files changed, 294 insertions(+), 362 deletions(-) diff --git a/WeChatFerry/com/log.hpp b/WeChatFerry/com/log.hpp index 4f30b27..4be5e76 100644 --- a/WeChatFerry/com/log.hpp +++ b/WeChatFerry/com/log.hpp @@ -48,7 +48,7 @@ inline void InitLogger(const std::string &path) logger = spdlog::rotating_logger_mt(DEFAULT_LOGGER_NAME, filename.string(), DEFAULT_LOGGER_MAX_SIZE, DEFAULT_LOGGER_MAX_FILES); } catch (const spdlog::spdlog_ex &ex) { - MessageBox(NULL, String2Wstring(ex.what()).c_str(), L"Init LOGGER ERROR", MB_ICONERROR); + MessageBox(NULL, util::s2w(ex.what()).c_str(), L"Init LOGGER ERROR", MB_ICONERROR); return; } diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 0134592..21b0039 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -1,177 +1,67 @@ -#include "Shlwapi.h" -#include "framework.h" +#include "util.h" + #include #include +#include #include #include #include #include #include +#include "framework.h" + #include "log.hpp" -#include "util.h" #pragma comment(lib, "shlwapi") #pragma comment(lib, "Version.lib") -using namespace std; - -wstring String2Wstring(string s) +namespace util { - if (s.empty()) - return wstring(); - int size_needed = MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), NULL, 0); - wstring ws(size_needed, 0); - MultiByteToWideChar(CP_UTF8, 0, &s[0], (int)s.size(), &ws[0], size_needed); + +std::wstring s2w(const std::string &s) +{ + if (s.empty()) return std::wstring(); + int size_needed = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast(s.size()), nullptr, 0); + std::wstring ws(size_needed, 0); + MultiByteToWideChar(CP_UTF8, 0, s.c_str(), static_cast(s.size()), &ws[0], size_needed); return ws; } -string Wstring2String(wstring ws) +std::string w2s(const std::wstring &ws) { - if (ws.empty()) - return string(); - int size_needed = WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), NULL, 0, NULL, NULL); - string s(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, &ws[0], (int)ws.size(), &s[0], size_needed, NULL, NULL); + if (ws.empty()) return std::string(); + int size_needed + = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), static_cast(ws.size()), nullptr, 0, nullptr, nullptr); + std::string s(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), static_cast(ws.size()), &s[0], size_needed, nullptr, nullptr); return s; } -string GB2312ToUtf8(const char *gb2312) +std::string gb2312_to_utf8(const char *gb2312) { - int size_needed = 0; + if (!gb2312) return ""; - size_needed = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0); - wstring ws(size_needed, 0); + int size_needed = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, nullptr, 0); + std::wstring ws(size_needed, 0); MultiByteToWideChar(CP_ACP, 0, gb2312, -1, &ws[0], size_needed); - size_needed = WideCharToMultiByte(CP_UTF8, 0, &ws[0], -1, NULL, 0, NULL, NULL); - string s(size_needed, 0); - WideCharToMultiByte(CP_UTF8, 0, &ws[0], -1, &s[0], size_needed, NULL, NULL); + size_needed = WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, nullptr, 0, nullptr, nullptr); + std::string s(size_needed, 0); + WideCharToMultiByte(CP_UTF8, 0, ws.c_str(), -1, &s[0], size_needed, nullptr, nullptr); return s; } -static int GetWeChatPath(wchar_t *path) +DWORD get_wechat_pid() { - int ret = -1; - HKEY hKey = NULL; - // HKEY_CURRENT_USER\Software\Tencent\WeChat InstallPath = xx - if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, L"Software\\Tencent\\WeChat", &hKey)) { - ret = GetLastError(); - return ret; - } + DWORD pid = 0; + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (hSnapshot == INVALID_HANDLE_VALUE) return 0; - DWORD Type = REG_SZ; - DWORD cbData = MAX_PATH * sizeof(WCHAR); - if (ERROR_SUCCESS != RegQueryValueEx(hKey, L"InstallPath", 0, &Type, (LPBYTE)path, &cbData)) { - ret = GetLastError(); - goto __exit; - } - - if (path != NULL) { - PathAppend(path, WECHAREXE); - } - -__exit: - if (hKey) { - RegCloseKey(hKey); - } - - return ERROR_SUCCESS; -} - -static int GetWeChatWinDLLPath(wchar_t *path) -{ - int ret = GetWeChatPath(path); - if (ret != ERROR_SUCCESS) { - return ret; - } - - PathRemoveFileSpecW(path); - PathAppendW(path, WECHATWINDLL); - if (!PathFileExists(path)) { - // 微信从(大约)3.7开始,增加了一层版本目录: [3.7.0.29] - PathRemoveFileSpec(path); - _wfinddata_t findData; - wstring dir = wstring(path) + L"\\[*.*"; - intptr_t handle = _wfindfirst(dir.c_str(), &findData); - if (handle == -1) { // 检查是否成功 - return -1; - } - wstring dllPath = wstring(path) + L"\\" + findData.name; - wcscpy_s(path, MAX_PATH, dllPath.c_str()); - PathAppend(path, WECHATWINDLL); - } - - return ret; -} - -static bool GetFileVersion(const wchar_t *filePath, wchar_t *version) -{ - if (wcslen(filePath) > 0 && PathFileExists(filePath)) { - VS_FIXEDFILEINFO *pVerInfo = NULL; - DWORD dwTemp, dwSize; - BYTE *pData = NULL; - UINT uLen; - - dwSize = GetFileVersionInfoSize(filePath, &dwTemp); - if (dwSize == 0) { - return false; - } - - pData = new BYTE[dwSize + 1]; - if (pData == NULL) { - return false; - } - - if (!GetFileVersionInfo(filePath, 0, dwSize, pData)) { - delete[] pData; - return false; - } - - if (!VerQueryValue(pData, TEXT("\\"), (void **)&pVerInfo, &uLen)) { - delete[] pData; - return false; - } - - UINT64 verMS = pVerInfo->dwFileVersionMS; - UINT64 verLS = pVerInfo->dwFileVersionLS; - UINT64 major = HIWORD(verMS); - UINT64 minor = LOWORD(verMS); - UINT64 build = HIWORD(verLS); - UINT64 revision = LOWORD(verLS); - delete[] pData; - - StringCbPrintf(version, 0x20, TEXT("%d.%d.%d.%d"), major, minor, build, revision); - - return true; - } - - return false; -} - -int GetWeChatVersion(wchar_t *version) -{ - WCHAR Path[MAX_PATH] = { 0 }; - - int ret = GetWeChatWinDLLPath(Path); - if (ret != ERROR_SUCCESS) { - return ret; - } - - ret = GetFileVersion(Path, version); - - return ret; -} - -DWORD GetWeChatPid() -{ - DWORD pid = 0; - HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; while (Process32Next(hSnapshot, &pe32)) { - wstring strProcess = pe32.szExeFile; - if (strProcess == WECHAREXE) { + if (std::wstring(pe32.szExeFile) == WECHATEXE) { pid = pe32.th32ProcessID; break; } @@ -180,52 +70,20 @@ DWORD GetWeChatPid() return pid; } -enum class WindowsArchiture { x32, x64 }; -static WindowsArchiture GetWindowsArchitecture() +int open_wechat(DWORD *pid) { -#ifdef _WIN64 - return WindowsArchiture::x64; -#else - return WindowsArchiture::x32; -#endif -} + *pid = get_wechat_pid(); + if (*pid) return ERROR_SUCCESS; -BOOL IsProcessX64(DWORD pid) -{ - BOOL isWow64 = false; - HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, pid); - if (!hProcess) - return false; - BOOL result = IsWow64Process(hProcess, &isWow64); - CloseHandle(hProcess); - if (!result) - return false; - if (isWow64) - return false; - else if (GetWindowsArchitecture() == WindowsArchiture::x32) - return false; - else - return true; -} - -int OpenWeChat(DWORD *pid) -{ - *pid = GetWeChatPid(); - if (*pid) { - return ERROR_SUCCESS; + WCHAR path[MAX_PATH] = { 0 }; + if (GetModuleFileNameW(nullptr, path, MAX_PATH) == 0) { + return GetLastError(); } - int ret = -1; STARTUPINFO si = { sizeof(si) }; - WCHAR Path[MAX_PATH] = { 0 }; PROCESS_INFORMATION pi = { 0 }; - ret = GetWeChatPath(Path); - if (ERROR_SUCCESS != ret) { - return ret; - } - - if (!CreateProcess(NULL, Path, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + if (!CreateProcessW(nullptr, path, nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi)) { return GetLastError(); } @@ -233,115 +91,153 @@ int OpenWeChat(DWORD *pid) CloseHandle(pi.hProcess); *pid = pi.dwProcessId; - return ERROR_SUCCESS; } -size_t GetWstringByAddress(UINT64 addr, wchar_t *buffer, UINT64 buffer_size) +static std::optional get_wechat_win_dll_path() { - size_t strLength = GET_DWORD(addr + 8); - if (strLength == 0) { - return 0; - } else if (strLength > buffer_size) { - strLength = buffer_size - 1; + char path[MAX_PATH] = { 0 }; + if (GetWeChatPath(path) != ERROR_SUCCESS) { + return std::nullopt; } - wmemcpy_s(buffer, strLength + 1, GET_WSTRING(addr), strLength + 1); + PathRemoveFileSpecA(path); + PathAppendA(path, WECHATWINDLL); - return strLength; + if (!PathFileExistsA(path)) { + // 微信 3.7+ 版本增加了一层目录 + PathRemoveFileSpecA(path); + _finddata_t findData; + std::string dir = std::string(path) + "\\[*.*"; + intptr_t handle = _findfirst(dir.c_str(), &findData); + if (handle == -1) { + return std::nullopt; + } + _findclose(handle); + + std::string dllPath = std::string(path) + "\\" + findData.name + "\\" + WECHATWINDLL; + return dllPath; + } + + return std::string(path); } -string GetStringByAddress(UINT64 addr) +static std::optional get_file_version(const std::string &filePath) { - size_t strLength = GET_DWORD(addr + 8); - return Wstring2String(wstring(GET_WSTRING(addr), strLength)); + if (filePath.empty() || !PathFileExistsA(filePath.c_str())) { + return std::nullopt; + } + + DWORD handle = 0; + DWORD size = GetFileVersionInfoSizeA(filePath.c_str(), &handle); + if (size == 0) { + return std::nullopt; + } + + std::vector data(size); + if (!GetFileVersionInfoA(filePath.c_str(), 0, size, data.data())) { + return std::nullopt; + } + + VS_FIXEDFILEINFO *verInfo = nullptr; + UINT len = 0; + if (!VerQueryValueA(data.data(), "\\", reinterpret_cast(&verInfo), &len) || len == 0) { + return std::nullopt; + } + + char version[32]; + StringCbPrintfA(version, sizeof(version), "%d.%d.%d.%d", HIWORD(verInfo->dwFileVersionMS), + LOWORD(verInfo->dwFileVersionMS), HIWORD(verInfo->dwFileVersionLS), + LOWORD(verInfo->dwFileVersionLS)); + + return std::string(version); } -string GetStringByStrAddr(UINT64 addr) +std::string get_wechat_version() { - size_t strLength = GET_DWORD(addr + 8); - return strLength ? string(GET_STRING(addr), strLength) : string(); + std::string version = ""; + + auto dllPath = get_wechat_win_dll_path(); + if (!dllPath) { + return version; + } + + version = get_file_version(*dllPath); + return version ? version : ""; } -string GetStringByWstrAddr(UINT64 addr) +uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr) { - size_t strLength = GET_DWORD(addr + 8); - return strLength ? Wstring2String(wstring(GET_WSTRING(addr), strLength)) : string(); -} - -UINT32 GetMemoryIntByAddress(HANDLE hProcess, UINT64 addr) -{ - UINT32 value = 0; + uint32_t value = 0; + if (!addr || !hProcess) return value; unsigned char data[4] = { 0 }; - if (ReadProcessMemory(hProcess, (LPVOID)addr, data, 4, 0)) { - value = data[0] & 0xFF; - value |= ((data[1] << 8) & 0xFF00); - value |= ((data[2] << 16) & 0xFF0000); - value |= ((data[3] << 24) & 0xFF000000); + if (ReadProcessMemory(hProcess, reinterpret_cast(addr), data, sizeof(data), nullptr)) { + value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); } return value; } -wstring GetUnicodeInfoByAddress(HANDLE hProcess, UINT64 address) +std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t address) { - wstring value = L""; + if (!hProcess || !address) return L""; - UINT64 strAddress = GetMemoryIntByAddress(hProcess, address); - UINT64 strLen = GetMemoryIntByAddress(hProcess, address + 0x4); - if (strLen > 500) - return value; + uint64_t str_address = get_memory_int_by_address(hProcess, address); + uint64_t str_len = get_memory_int_by_address(hProcess, address + 0x4); + if (str_len > 500) return L""; wchar_t cValue[500] = { 0 }; - memset(cValue, 0, sizeof(cValue) / sizeof(wchar_t)); - if (ReadProcessMemory(hProcess, (LPVOID)strAddress, cValue, (strLen + 1) * 2, 0)) { - value = wstring(cValue); + if (ReadProcessMemory(hProcess, reinterpret_cast(str_address), cValue, (str_len + 1) * sizeof(wchar_t), + nullptr)) { + return std::wstring(cValue); } - return value; + return L""; } -void DbgMsg(const char *zcFormat, ...) +void dbg_msg(const char *format, ...) { - // initialize use of the variable argument array - va_list vaArgs; - va_start(vaArgs, zcFormat); + if (!format) return; - // reliably acquire the size - // from a copy of the variable argument array - // and a functionally reliable call to mock the formatting - va_list vaArgsCopy; - va_copy(vaArgsCopy, vaArgs); - const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy); - va_end(vaArgsCopy); + va_list args; + va_start(args, format); - // return a formatted string without risking memory mismanagement - // and without assuming any compiler or platform specific behavior - std::vector zc(iLen + 1); - std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs); - va_end(vaArgs); - std::string strText(zc.data(), iLen); + va_list args_copy; + va_copy(args_copy, args); + int len = vsnprintf(nullptr, 0, format, args_copy); + va_end(args_copy); - OutputDebugStringA(strText.c_str()); + std::vector buffer(len + 1); + vsnprintf(buffer.data(), buffer.size(), format, args); + va_end(args); + + OutputDebugStringA(buffer.data()); } -WxString *NewWxStringFromStr(const string &str) { return NewWxStringFromWstr(String2Wstring(str)); } - -WxString *NewWxStringFromWstr(const wstring &ws) +std::unique_ptr new_wx_string(const char *str) { - WxString *p = (WxString *)HeapAlloc(GetProcessHeap(), 0, sizeof(WxString)); - wchar_t *pWstring = (wchar_t *)HeapAlloc(GetProcessHeap(), 0, (ws.size() + 1) * 2); - if (p == NULL || pWstring == NULL) { - LOG_ERROR("Out of Memory..."); - return NULL; + return new_wx_string(str ? std::string(str) : std::string()); +} + +std::unique_ptr new_wx_string(const std::string &str) { return std::make_unique(s2w(str)); } + +std::unique_ptr new_wx_string(const wchar_t *wstr) +{ + return new_wx_string(wstr ? std::wstring(wstr) : std::wstring()); +} + +std::unique_ptr new_wx_string(const std::wstring &wstr) { return std::make_unique(wstr); } + +std::vector parse_wxids(const std::string &wxids) +{ + std::vector wx_members; + std::wstringstream wss(s2w(wxids)); + std::wstring wstr; + while (getline(wss, wstr, L',')) { + wx_members.emplace_back(wstr); } - - wmemcpy(pWstring, ws.c_str(), ws.size() + 1); - p->wptr = pWstring; - p->size = (DWORD)ws.size(); - p->capacity = (DWORD)ws.size(); - p->ptr = 0; - p->clen = 0; - return p; + return wx_members; } + +} // namespace util diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index 8f252df..9169ddd 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -1,41 +1,81 @@ #pragma once +#include #include #include "spy_types.h" -#define WECHAREXE L"WeChat.exe" -#define WECHATWINDLL L"WeChatWin.dll" -#define WCFSDKDLL L"sdk.dll" -#define WCFSPYDLL L"spy.dll" -#define WCFSPYDLL_DEBUG L"spy_debug.dll" +namespace util +{ -#define GET_UINT64(addr) ((UINT64) * (UINT64 *)(addr)) -#define GET_DWORD(addr) ((DWORD) * (UINT64 *)(addr)) -#define GET_QWORD(addr) ((UINT64) * (UINT64 *)(addr)) -#define GET_STRING(addr) ((CHAR *)(*(UINT64 *)(addr))) -#define GET_WSTRING(addr) ((WCHAR *)(*(UINT64 *)(addr))) -#define GET_STRING_FROM_P(addr) ((CHAR *)(addr)) -#define GET_WSTRING_FROM_P(addr) ((WCHAR *)(addr)) +inline constexpr wchar_t WECHATEXE[] = L"WeChat.exe"; +inline constexpr wchar_t WECHATWINDLL[] = L"WeChatWin.dll"; +inline constexpr wchar_t WCFSDKDLL[] = L"sdk.dll"; +inline constexpr wchar_t WCFSPYDLL[] = L"spy.dll"; +inline constexpr wchar_t WCFSPYDLL_DEBUG[] = L"spy_debug.dll"; -typedef struct PortPath { +struct PortPath { int port; char path[MAX_PATH]; -} PortPath_t; +}; -DWORD GetWeChatPid(); -BOOL IsProcessX64(DWORD pid); -int OpenWeChat(DWORD *pid); -int GetWeChatVersion(wchar_t *version); -size_t GetWstringByAddress(UINT64 address, wchar_t *buffer, UINT64 buffer_size); -UINT32 GetMemoryIntByAddress(HANDLE hProcess, UINT64 address); -std::wstring GetUnicodeInfoByAddress(HANDLE hProcess, UINT64 address); -std::wstring String2Wstring(std::string s); -std::string Wstring2String(std::wstring ws); -std::string GB2312ToUtf8(const char *gb2312); -std::string GetStringByAddress(UINT64 address); -std::string GetStringByStrAddr(UINT64 addr); -std::string GetStringByWstrAddr(UINT64 addr); -void DbgMsg(const char *zcFormat, ...); -WxString *NewWxStringFromStr(const std::string &str); -WxString *NewWxStringFromWstr(const std::wstring &ws); +inline DWORD get_dword(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } +inline QWORD get_qword(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } +inline uint64_t get_uint64(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } +inline std::string get_p_string(uint64_t addr) { return addr ? std::string(reinterpret_cast(addr)) : ""; } +inline std::string get_p_string(uint64_t addr, size_t len) +{ + return addr ? std::string(reinterpret_cast(addr), len) : ""; +} +inline std::wstring get_p_wstring(uint64_t addr) +{ + return addr ? std::wstring(reinterpret_cast(addr)) : L""; +} +inline std::wstring get_p_wstring(uint64_t addr, size_t len) +{ + return addr ? std::wstring(reinterpret_cast(addr), len) : L""; +} +inline std::string get_pp_string(uint64_t addr) +{ + if (!addr) return ""; + + const char *ptr = *reinterpret_cast(addr); + return (ptr && *ptr) ? std::string(ptr) : ""; +} +inline std::wstring get_pp_wstring(uint64_t addr) +{ + if (!addr) return L""; + + const wchar_t *ptr = *reinterpret_cast(addr); + return (ptr && *ptr) ? std::wstring(ptr) : ""; +} +inline std::string get_pp_len_string(uint64_t addr) +{ + size_t len = get_dword(addr + 8); + return (addr && len) ? std::string(*reinterpret_cast(addr), len) : L""; +} +inline std::wstring get_pp_len_wstring(uint64_t addr) +{ + size_t len = get_dword(addr + 8); + return (addr && len) ? std::wstring(*reinterpret_cast(addr), len) : L""; +} +inline std::string get_str_by_wstr_addr(uint64_t addr) { return w2s(get_pp_len_wstring(addr)); } + +DWORD get_wechat_pid(); +int open_wechat(DWORD *pid); +std::string get_wechat_version(); +uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr); +std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t addr); +std::wstring s2w(const std::string &s); +std::string w2s(const std::wstring &ws); +std::string gb2312_to_utf8(const char *gb2312); +void dbg_msg(const char *format, ...); + +std::unique_ptr new_wx_string(const char *str); +std::unique_ptr new_wx_string(const wchar_t *wstr); +std::unique_ptr new_wx_string(const std::string &str); +std::unique_ptr new_wx_string(const std::wstring &wstr); + +std::vector parse_wxids(const std::string &wxids); + +} // namespace util diff --git a/WeChatFerry/sdk/injector.cpp b/WeChatFerry/sdk/injector.cpp index cc06298..975faa9 100644 --- a/WeChatFerry/sdk/injector.cpp +++ b/WeChatFerry/sdk/injector.cpp @@ -80,7 +80,7 @@ HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase) WaitForSingleObject(hThread, -1); CloseHandle(hThread); - *injectedBase = GetTargetModuleBase(hProcess, filesystem::path(Wstring2String(dllPath)).filename().string()); + *injectedBase = GetTargetModuleBase(hProcess, filesystem::path(util::w2s(dllPath)).filename().string()); VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE); // CloseHandle(hProcess); // Close when exit diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index 8b1c790..fca3b0f 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -28,7 +28,7 @@ static std::optional ReadDisclaimerText(const char *filePath) } std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - return String2Wstring(content); + return util::s2w(content); } static bool ShowDisclaimer() @@ -94,7 +94,7 @@ int WxInitSDK(bool debug, int port) return ERROR_FILE_NOT_FOUND; // DLL 文件路径不存在 } - status = OpenWeChat(&wcPid); + status = util::open_wechat(&wcPid); if (status != 0) { MessageBox(NULL, L"打开微信失败", L"WxInitSDK", 0); return status; diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index db496b3..589fd52 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -24,7 +24,7 @@ using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); static vector parse_wxids(const string &wxids) { vector wx_members; - wstringstream wss(String2Wstring(wxids)); + wstringstream wss(util::s2w(wxids)); wstring wstr; while (getline(wss, wstr, L',')) { wx_members.emplace_back(wstr); diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 192b20f..85c5740 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -1,6 +1,7 @@ #pragma execution_character_set("utf-8") #include "contact_mgmt.h" + #include "fill_response.h" #include "log.hpp" #include "pb_util.h" @@ -55,7 +56,7 @@ static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t return ""; } - return Wstring2String(wstring(GET_WSTRING_FROM_P(pfeat + FEAT_LEN + 4), lfeat)); + return util::w2s(util::get_p_wstring(pfeat + FEAT_LEN + 4, lfeat)); } vector get_contacts() @@ -80,10 +81,10 @@ vector get_contacts() QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN); QWORD lenbin = GET_DWORD(pstart + OS_CONTACT_BIN_LEN); - cnt.wxid = GetStringByWstrAddr(pstart + OS_CONTACT_WXID); - cnt.code = GetStringByWstrAddr(pstart + OS_CONTACT_CODE); - cnt.remark = GetStringByWstrAddr(pstart + OS_CONTACT_REMARK); - cnt.name = GetStringByWstrAddr(pstart + OS_CONTACT_NAME); + cnt.wxid = util::get_str_by_wstr_addr(pstart + OS_CONTACT_WXID); + cnt.code = util::get_str_by_wstr_addr(pstart + OS_CONTACT_CODE); + cnt.remark = util::get_str_by_wstr_addr(pstart + OS_CONTACT_REMARK); + cnt.name = util::get_str_by_wstr_addr(pstart + OS_CONTACT_NAME); cnt.country = get_cnt_string(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN); cnt.province = get_cnt_string(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN); @@ -112,8 +113,8 @@ int accept_new_friend(const std::string &v3, const std::string &v4, int scene) LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene); - wstring ws_v3 = String2Wstring(v3); - wstring ws_v4 = String2Wstring(v4); + wstring ws_v3 = util::s2w(v3); + wstring ws_v4 = util::s2w(v4); WxString wx_v3(ws_v3); WxString wx_v4(ws_v4); @@ -154,7 +155,7 @@ RpcContact_t get_contact_by_wxid(const string &wxid) RpcContact_t contact; #if 0 char buff[0x440] = { 0 }; - wstring ws_wxid = String2Wstring(wxid); + wstring ws_wxid = util::s2w(wxid); WxString pri(ws_wxid); DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0; @@ -176,9 +177,9 @@ RpcContact_t get_contact_by_wxid(const string &wxid) } contact.wxid = wxid; - contact.code = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxCode); - contact.remark = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxRemark); - contact.name = GetStringByWstrAddr(reinterpret_cast(buff) + g_WxCalls.contact.wxName); + contact.code = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxCode); + contact.remark = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxRemark); + contact.name = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxName); contact.gender = GET_DWORD(reinterpret_cast(buff) + 0x148); __asm { diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 0ed9982..89678c1 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -1,7 +1,8 @@ -#include +#include "exec_sql.h" + +#include #include -#include "exec_sql.h" #include "fill_response.h" #include "log.hpp" #include "pb_util.h" @@ -29,7 +30,7 @@ static db_map_t db_map; static void get_db_handle(QWORD base, QWORD offset) { auto *wsp = reinterpret_cast(*(QWORD *)(base + offset + OFFSET_DB_NAME)); - std::string dbname = Wstring2String(std::wstring(wsp)); + std::string dbname = util::w2s(std::wstring(wsp)); db_map[dbname] = GET_QWORD(base + offset); } @@ -41,12 +42,12 @@ static void get_msg_db_handle(QWORD msg_mgr_addr) QWORD db_addr = GET_QWORD(p_start + i * 0x08); if (db_addr) { // MSGi.db - std::string dbname = Wstring2String(GET_WSTRING(db_addr)); + std::string dbname = util::w2s(get_pp_wstring(db_addr)); db_map[dbname] = GET_QWORD(db_addr + 0x78); // MediaMsgi.db QWORD mmdb_addr = GET_QWORD(db_addr + 0x20); - std::string mmdbname = Wstring2String(GET_WSTRING(mmdb_addr + 0x78)); + std::string mmdbname = util::w2s(get_pp_wstring(mmdb_addr + 0x78)); db_map[mmdbname] = GET_QWORD(mmdb_addr + 0x50); } } @@ -199,7 +200,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) continue; } - std::string dbname = Wstring2String(GET_WSTRING(db_addr)); + std::string dbname = util::w2s(get_pp_wstring(db_addr)); db_map[dbname] = GET_QWORD(db_addr + 0x78); std::string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + std::to_string(id) + ";"; diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp index 0ec2959..94b8b64 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/funcs.cpp @@ -1,12 +1,13 @@ #pragma warning(disable : 4244) +#include "funcs.h" -#include "framework.h" #include #include +#include "framework.h" + #include "codec.h" #include "exec_sql.h" -#include "funcs.h" #include "log.hpp" #include "receive_msg.h" #include "spy_types.h" @@ -342,7 +343,7 @@ OcrResult_t GetOcrResult(string path) QWORD *pUnk1 = &unk1; QWORD *pUnk2 = &unk2; // 路径分隔符有要求,必须为 `\` - wstring wsPath = String2Wstring(fs::path(path).make_preferred().string()); + wstring wsPath = util::s2w(fs::path(path).make_preferred().string()); WxString wxPath(wsPath); vector *pv = (vector *)HeapAlloc(GetProcessHeap(), 0, 0x20); RawVector_t *pRv = (RawVector_t *)pv; @@ -358,7 +359,7 @@ OcrResult_t GetOcrResult(string path) QWORD header = GET_QWORD(buff); for (QWORD i = 0; i < count; i++) { QWORD content = GET_QWORD(header); - ret.result += Wstring2String(GET_WSTRING(content + 0x28)); + ret.result += util::w2s(get_pp_wstring(content + 0x28)); ret.result += "\n"; header = content; } diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index 5becce8..6ce84a2 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -1,4 +1,5 @@ - +#include "receive_msg.h" + #include #include #include @@ -6,7 +7,6 @@ #include "framework.h" #include "log.hpp" -#include "receive_msg.h" #include "user_info.h" #include "util.h" @@ -42,20 +42,21 @@ QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) wxMsg.type = GET_DWORD(arg2 + OS_RECV_MSG_TYPE); wxMsg.is_self = GET_DWORD(arg2 + OS_RECV_MSG_SELF); wxMsg.ts = GET_DWORD(arg2 + OS_RECV_MSG_TS); - wxMsg.content = GetStringByWstrAddr(arg2 + OS_RECV_MSG_CONTENT); - wxMsg.sign = GetStringByWstrAddr(arg2 + OS_RECV_MSG_SIGN); - wxMsg.xml = GetStringByWstrAddr(arg2 + OS_RECV_MSG_XML); - wxMsg.roomid = GetStringByWstrAddr(arg2 + OS_RECV_MSG_ROOMID); + wxMsg.content = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_CONTENT); + wxMsg.sign = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_SIGN); + wxMsg.xml = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_XML); + wxMsg.roomid = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_ROOMID); if (wxMsg.roomid.find("@chatroom") != std::string::npos) { wxMsg.is_group = true; - wxMsg.sender = wxMsg.is_self ? user_info::get_self_wxid() : GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); + wxMsg.sender + = wxMsg.is_self ? user_info::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); } else { wxMsg.is_group = false; wxMsg.sender = wxMsg.is_self ? user_info::get_self_wxid() : wxMsg.roomid; } } catch (const std::exception &e) { - LOG_ERROR(GB2312ToUtf8(e.what())); + LOG_ERROR(util::gb2312_to_utf8(e.what())); } { @@ -77,7 +78,7 @@ QWORD MessageHandler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a return p; } - LOG_INFO("【WX】\n{}", GB2312ToUtf8((char *)p)); + LOG_INFO("【WX】\n{}", util::gb2312_to_utf8((char *)p)); return p; } @@ -99,9 +100,9 @@ void MessageHandler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) wxMsg.is_group = false; wxMsg.id = GET_QWORD(startAddr); wxMsg.ts = GET_DWORD(startAddr + OS_PYQ_MSG_TS); - wxMsg.xml = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_XML); - wxMsg.sender = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_SENDER); - wxMsg.content = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_CONTENT); + wxMsg.xml = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_XML); + wxMsg.sender = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_SENDER); + wxMsg.content = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_CONTENT); { std::unique_lock lock(handler.mutex_); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index f4419ac..111a0a1 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -1,5 +1,7 @@ #pragma warning(disable : 4251) +#include "rpc_server.h" + #include #include #include @@ -26,7 +28,6 @@ #include "pb_types.h" #include "pb_util.h" #include "receive_msg.h" -#include "rpc_server.h" #include "send_msg.h" #include "spy.h" #include "spy_types.h" @@ -158,7 +159,7 @@ static bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) if ((path == NULL) || (receiver == NULL)) { LOG_ERROR("Empty path or receiver."); rsp.msg.status = -1; - } else if (!fs::exists(String2Wstring(path))) { + } else if (!fs::exists(util::s2w(path))) { LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { @@ -174,7 +175,7 @@ static bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len if ((path == nullptr) || (receiver == nullptr)) { LOG_ERROR("Empty path or receiver."); rsp.msg.status = -1; - } else if (!fs::exists(String2Wstring(path))) { + } else if (!fs::exists(util::s2w(path))) { LOG_ERROR("Path does not exist: {}", path); rsp.msg.status = -2; } else { @@ -685,7 +686,7 @@ static int RunRpcServer() } } } catch (const std::exception &e) { - LOG_ERROR(GB2312ToUtf8(e.what())); + LOG_ERROR(util::gb2312_to_utf8(e.what())); } catch (...) { LOG_ERROR("Unknow exception."); } diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index 153f12a..1134cbd 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -56,13 +56,13 @@ std::unique_ptr SendMsgManager::new_wx_string(const char *str) } std::unique_ptr SendMsgManager::new_wx_string(const std::string &str) { - return std::make_unique(String2Wstring(str)); + return std::make_unique(util::s2w(str)); } std::vector SendMsgManager::parse_wxids(const string &wxids) { vector wx_members; - wstringstream wss(String2Wstring(wxids)); + wstringstream wss(util::s2w(wxids)); wstring wstr; while (getline(wss, wstr, L',')) { wx_members.emplace_back(wstr); diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index e179aa0..f78b65d 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -1,44 +1,37 @@ -#include +#include "spy.h" + +#include +#include #include "log.hpp" #include "rpc_server.h" -#include "spy.h" #include "util.h" +constexpr std::string_view SUPPORT_VERSION = "3.9.11.25"; + UINT64 g_WeChatWinDllAddr = 0; -static bool IsWxVersionMatched(const wchar_t *version) -{ - if (wcscmp(version, SUPPORT_VERSION) != 0) { - return false; - } - return true; -} - void InitSpy(LPVOID args) { - - wchar_t version[16] = { 0 }; - PortPath_t *pp = (PortPath_t *)args; + auto *pp = static_cast(args); Log::InitLogger(pp->path); - g_WeChatWinDllAddr = (UINT64)GetModuleHandle(L"WeChatWin.dll"); // 获取wechatWin模块地址 - if (g_WeChatWinDllAddr == 0) { - LOG_ERROR("获取 wechatWin.dll 模块地址失败"); + if (auto dll_addr = GetModuleHandle(L"WeChatWin.dll")) { + g_WeChatWinDllAddr = reinterpret_cast(dll_addr); + } else { + LOG_ERROR("获取 WeChatWin.dll 模块地址失败"); return; // TODO: 退出进程,避免后面操作失败 } - if (!GetWeChatVersion(version)) { // 获取微信版本 - LOG_ERROR("获取微信版本失败"); - return; - } - LOG_INFO("WeChat version: {}", Wstring2String(version).c_str()); - if (!IsWxVersionMatched(version)) { - LOG_ERROR("不支持当前版本"); - MessageBox(NULL, L"不支持当前版本", L"错误", 0); + std::string version = util::get_wechat_version(); + std::string msg = "WCF 支持版本: " + SUPPORT_VERSION + ",当前版本: " + version; + if (version != SUPPORT_VERSION) { + LOG_ERROR(msg); + MessageBoxA(NULL, msg.c_str(), "错误", MB_ICONERROR); return; } + LOG_INFO(msg); RpcStartServer(pp->port); } diff --git a/WeChatFerry/spy/spy.h b/WeChatFerry/spy/spy.h index 525109a..3aa41dd 100644 --- a/WeChatFerry/spy/spy.h +++ b/WeChatFerry/spy/spy.h @@ -2,7 +2,5 @@ #include "framework.h" -#define SUPPORT_VERSION L"3.9.11.25" - void InitSpy(int port); void CleanupSpy(); diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index 6cf95b7..2515d44 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -1,9 +1,10 @@ -#include +#include "user_info.h" + +#include #include #include "fill_response.h" #include "log.hpp" -#include "user_info.h" #include "util.h" extern UINT64 g_WeChatWinDllAddr; @@ -15,14 +16,13 @@ namespace user_info #define OS_USER_NAME 0x595C3D8 #define OS_USER_MOBILE 0x595C318 - std::string - get_home_path() +std::string get_home_path() { static std::once_flag flag; static std::string home_path; std::call_once(flag, [] { - std::string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; + std::string path = util::w2s(get_pp_wstring(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; home_path = std::filesystem::absolute(path).string(); }); @@ -39,9 +39,9 @@ std::string get_self_wxid() try { wxid_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); if (wxid_type == 0xF) { - wxid = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); + wxid = util::get_p_string(g_WeChatWinDllAddr + OS_USER_WXID); } else { - wxid = GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); + wxid = util::get_pp_string(g_WeChatWinDllAddr + OS_USER_WXID); } } catch (...) { @@ -59,10 +59,10 @@ UserInfo_t get_user_info() ui.wxid = get_self_wxid(); UINT64 name_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); - ui.name = (name_type == 0xF) ? GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME) - : GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME); + ui.name = (name_type == 0xF) ? util::get_p_string(g_WeChatWinDllAddr + OS_USER_NAME) + : util::get_pp_string(g_WeChatWinDllAddr + OS_USER_NAME); - ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_MOBILE); + ui.mobile = util::get_p_string(g_WeChatWinDllAddr + OS_USER_MOBILE); ui.home = get_home_path(); return ui; From dc51b7b5a79db4f8e64b0249bc9fd5521d2866e5 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 17:27:35 +0800 Subject: [PATCH 026/132] Refactoring --- WeChatFerry/com/util.cpp | 141 +++++++++++++++++------------- WeChatFerry/com/util.h | 30 ++++--- WeChatFerry/spy/chatroom_mgmt.cpp | 14 +-- WeChatFerry/spy/contact_mgmt.cpp | 8 +- WeChatFerry/spy/exec_sql.cpp | 40 ++++----- WeChatFerry/spy/funcs.cpp | 20 ++--- WeChatFerry/spy/funcs.h | 4 +- WeChatFerry/spy/receive_msg.cpp | 12 +-- WeChatFerry/spy/receive_msg.h | 2 +- WeChatFerry/spy/spy.cpp | 2 +- WeChatFerry/spy/user_info.cpp | 6 +- 11 files changed, 152 insertions(+), 127 deletions(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 21b0039..7d13348 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -2,14 +2,13 @@ #include #include -#include -#include +#include #include -#include -#include #include #include "framework.h" +#include +#include #include "log.hpp" @@ -61,7 +60,7 @@ DWORD get_wechat_pid() PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; while (Process32Next(hSnapshot, &pe32)) { - if (std::wstring(pe32.szExeFile) == WECHATEXE) { + if (w2s(pe32.szExeFile) == WECHATEXE) { pid = pe32.th32ProcessID; break; } @@ -70,100 +69,125 @@ DWORD get_wechat_pid() return pid; } -int open_wechat(DWORD *pid) +int open_wechat(DWORD &pid) { - *pid = get_wechat_pid(); - if (*pid) return ERROR_SUCCESS; + pid = get_wechat_pid(); + if (pid != 0) { + return ERROR_SUCCESS; + } - WCHAR path[MAX_PATH] = { 0 }; - if (GetModuleFileNameW(nullptr, path, MAX_PATH) == 0) { - return GetLastError(); + auto wechat_path = util::get_wechat_path(); + if (!wechat_path) { + LOG_ERROR("获取 WeChat 安装路径失败"); + return ERROR_FILE_NOT_FOUND; } STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi = { 0 }; - - if (!CreateProcessW(nullptr, path, nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, &si, &pi)) { + PROCESS_INFORMATION pi = {}; + if (!CreateProcessA(wechat_path->c_str(), nullptr, nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, + &si, &pi)) { return GetLastError(); } CloseHandle(pi.hThread); CloseHandle(pi.hProcess); - *pid = pi.dwProcessId; + pid = pi.dwProcessId; return ERROR_SUCCESS; } -static std::optional get_wechat_win_dll_path() +std::optional get_wechat_path() { - char path[MAX_PATH] = { 0 }; - if (GetWeChatPath(path) != ERROR_SUCCESS) { + HKEY hKey; + if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Tencent\\WeChat", 0, KEY_READ, &hKey) != ERROR_SUCCESS) { + LOG_ERROR("无法打开注册表项"); return std::nullopt; } - PathRemoveFileSpecA(path); - PathAppendA(path, WECHATWINDLL); - - if (!PathFileExistsA(path)) { - // 微信 3.7+ 版本增加了一层目录 - PathRemoveFileSpecA(path); - _finddata_t findData; - std::string dir = std::string(path) + "\\[*.*"; - intptr_t handle = _findfirst(dir.c_str(), &findData); - if (handle == -1) { - return std::nullopt; - } - _findclose(handle); - - std::string dllPath = std::string(path) + "\\" + findData.name + "\\" + WECHATWINDLL; - return dllPath; + char path[MAX_PATH] = { 0 }; + DWORD type = REG_SZ; + DWORD size = sizeof(path); + if (RegQueryValueExA(hKey, "InstallPath", nullptr, &type, reinterpret_cast(path), &size) != ERROR_SUCCESS) { + RegCloseKey(hKey); + LOG_ERROR("无法读取注册表中的 InstallPath"); + return std::nullopt; } + RegCloseKey(hKey); + PathAppendA(path, WECHATEXE); return std::string(path); } -static std::optional get_file_version(const std::string &filePath) +std::optional get_wechat_win_dll_path() { - if (filePath.empty() || !PathFileExistsA(filePath.c_str())) { + auto wechat_path = get_wechat_path(); + if (!wechat_path) { return std::nullopt; } - DWORD handle = 0; - DWORD size = GetFileVersionInfoSizeA(filePath.c_str(), &handle); + std::string dll_path = *wechat_path; + PathRemoveFileSpecA(dll_path.data()); + PathAppendA(dll_path.data(), WECHATWINDLL); + + if (PathFileExistsA(dll_path.c_str())) { + return dll_path; + } + + // 微信从(大约)3.7开始,增加了一层版本目录: [3.7.0.29] + PathRemoveFileSpecA(dll_path.data()); + WIN32_FIND_DATAA find_data; + HANDLE hFind = FindFirstFileA((dll_path + "\\*.*").c_str(), &find_data); + if (hFind == INVALID_HANDLE_VALUE) { + return std::nullopt; + } + FindClose(hFind); + + std::string versioned_path = dll_path + "\\" + find_data.cFileName + WECHATWINDLL; + return PathFileExistsA(versioned_path.c_str()) ? std::optional(versioned_path) : std::nullopt; +} + +std::optional get_file_version(const std::string &file_path) +{ + if (!PathFileExistsA(file_path.c_str())) { + return std::nullopt; + } + + DWORD dummy = 0; + DWORD size = GetFileVersionInfoSizeA(file_path.c_str(), &dummy); if (size == 0) { return std::nullopt; } - std::vector data(size); - if (!GetFileVersionInfoA(filePath.c_str(), 0, size, data.data())) { + std::vector buffer(size); + if (!GetFileVersionInfoA(file_path.c_str(), 0, size, buffer.data())) { return std::nullopt; } - VS_FIXEDFILEINFO *verInfo = nullptr; - UINT len = 0; - if (!VerQueryValueA(data.data(), "\\", reinterpret_cast(&verInfo), &len) || len == 0) { + VS_FIXEDFILEINFO *ver_info = nullptr; + UINT ver_size = 0; + if (!VerQueryValueA(buffer.data(), "\\", reinterpret_cast(&ver_info), &ver_size)) { return std::nullopt; } - char version[32]; - StringCbPrintfA(version, sizeof(version), "%d.%d.%d.%d", HIWORD(verInfo->dwFileVersionMS), - LOWORD(verInfo->dwFileVersionMS), HIWORD(verInfo->dwFileVersionLS), - LOWORD(verInfo->dwFileVersionLS)); - - return std::string(version); + return fmt::format("{}.{}.{}.{}", HIWORD(ver_info->dwFileVersionMS), LOWORD(ver_info->dwFileVersionMS), + HIWORD(ver_info->dwFileVersionLS), LOWORD(ver_info->dwFileVersionLS)); } std::string get_wechat_version() { - std::string version = ""; - - auto dllPath = get_wechat_win_dll_path(); - if (!dllPath) { - return version; + auto dll_path = get_wechat_win_dll_path(); + if (!dll_path) { + LOG_ERROR("无法获取 WeChatWin.dll 路径"); + return ""; } - version = get_file_version(*dllPath); - return version ? version : ""; + auto version = get_file_version(*dll_path); + if (!version) { + LOG_ERROR("无法获取 WeChat 版本信息"); + return ""; + } + + return *version; } uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr) @@ -171,10 +195,7 @@ uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr) uint32_t value = 0; if (!addr || !hProcess) return value; - unsigned char data[4] = { 0 }; - if (ReadProcessMemory(hProcess, reinterpret_cast(addr), data, sizeof(data), nullptr)) { - value = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); - } + ReadProcessMemory(hProcess, reinterpret_cast(addr), &value, sizeof(value), nullptr); return value; } diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index 9169ddd..71d0bd7 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -1,15 +1,17 @@ #pragma once #include +#include #include +#include #include "spy_types.h" namespace util { -inline constexpr wchar_t WECHATEXE[] = L"WeChat.exe"; -inline constexpr wchar_t WECHATWINDLL[] = L"WeChatWin.dll"; +inline constexpr char WECHATEXE[] = "WeChat.exe"; +inline constexpr char WECHATWINDLL[] = "WeChatWin.dll"; inline constexpr wchar_t WCFSDKDLL[] = L"sdk.dll"; inline constexpr wchar_t WCFSPYDLL[] = L"spy.dll"; inline constexpr wchar_t WCFSPYDLL_DEBUG[] = L"spy_debug.dll"; @@ -19,6 +21,16 @@ struct PortPath { char path[MAX_PATH]; }; +DWORD get_wechat_pid(); +int open_wechat(DWORD *pid); +std::string get_wechat_version(); +uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr); +std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t addr); +std::wstring s2w(const std::string &s); +std::string w2s(const std::wstring &ws); +std::string gb2312_to_utf8(const char *gb2312); +void dbg_msg(const char *format, ...); + inline DWORD get_dword(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } inline QWORD get_qword(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } inline uint64_t get_uint64(uint64_t addr) { return addr ? *reinterpret_cast(addr) : 0; } @@ -47,12 +59,12 @@ inline std::wstring get_pp_wstring(uint64_t addr) if (!addr) return L""; const wchar_t *ptr = *reinterpret_cast(addr); - return (ptr && *ptr) ? std::wstring(ptr) : ""; + return (ptr && *ptr) ? std::wstring(ptr) : L""; } inline std::string get_pp_len_string(uint64_t addr) { size_t len = get_dword(addr + 8); - return (addr && len) ? std::string(*reinterpret_cast(addr), len) : L""; + return (addr && len) ? std::string(*reinterpret_cast(addr), len) : ""; } inline std::wstring get_pp_len_wstring(uint64_t addr) { @@ -61,16 +73,6 @@ inline std::wstring get_pp_len_wstring(uint64_t addr) } inline std::string get_str_by_wstr_addr(uint64_t addr) { return w2s(get_pp_len_wstring(addr)); } -DWORD get_wechat_pid(); -int open_wechat(DWORD *pid); -std::string get_wechat_version(); -uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr); -std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t addr); -std::wstring s2w(const std::string &s); -std::string w2s(const std::wstring &ws); -std::string gb2312_to_utf8(const char *gb2312); -void dbg_msg(const char *format, ...); - std::unique_ptr new_wx_string(const char *str); std::unique_ptr new_wx_string(const wchar_t *wstr); std::unique_ptr new_wx_string(const std::string &str); diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index 589fd52..5cef8df 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -45,10 +45,10 @@ int add_chatroom_member(const string &roomid, const string &wxids) = reinterpret_cast(g_WeChatWinDllAddr + OS_ADD_MEMBERS); vector wx_members = parse_wxids(wxids); - WxString *p_wx_roomid = NewWxStringFromStr(roomid); + auto wx_roomid = util::new_wx_string(roomid); QWORD p_members = reinterpret_cast(&wx_members.front()); - return static_cast(add_members(get_chatroom_mgr(), p_members, reinterpret_cast(p_wx_roomid), 0)); + return static_cast(add_members(get_chatroom_mgr(), p_members, reinterpret_cast(wx_roomid.get()), 0)); } int del_chatroom_member(const string &roomid, const string &wxids) @@ -64,10 +64,10 @@ int del_chatroom_member(const string &roomid, const string &wxids) = reinterpret_cast(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); vector wx_members = parse_wxids(wxids); - WxString *p_wx_roomid = NewWxStringFromStr(roomid); + auto wx_roomid = util::new_wx_string(roomid); QWORD p_members = reinterpret_cast(&wx_members.front()); - return static_cast(del_members(get_chatroom_mgr(), p_members, reinterpret_cast(p_wx_roomid))); + return static_cast(del_members(get_chatroom_mgr(), p_members, reinterpret_cast(wx_roomid.get()))); } int invite_chatroom_member(const string &roomid, const string &wxids) @@ -81,11 +81,11 @@ int invite_chatroom_member(const string &roomid, const string &wxids) = reinterpret_cast(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); vector wx_members = parse_wxids(wxids); - WxString *p_wx_roomid = NewWxStringFromStr(roomid); + auto wx_roomid = util::new_wx_string(roomid); QWORD p_members = reinterpret_cast(&wx_members.front()); - return static_cast(invite_members(reinterpret_cast(p_wx_roomid->wptr), p_members, - reinterpret_cast(p_wx_roomid), 0)); + return static_cast(invite_members(reinterpret_cast(wx_roomid.get()->wptr), p_members, + reinterpret_cast(wx_roomid.get()), 0)); } bool rpc_add_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 85c5740..6c6c631 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -51,7 +51,7 @@ static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t return ""; } - DWORD lfeat = GET_DWORD(pfeat + len); + DWORD lfeat = util::get_dword(pfeat + len); if (lfeat <= 2) { return ""; } @@ -78,8 +78,8 @@ vector get_contacts() QWORD pend = addr[2]; while (pstart < pend) { RpcContact_t cnt; - QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN); - QWORD lenbin = GET_DWORD(pstart + OS_CONTACT_BIN_LEN); + QWORD pbin = util::get_qword(pstart + OS_CONTACT_BIN); + QWORD lenbin = util::get_dword(pstart + OS_CONTACT_BIN_LEN); cnt.wxid = util::get_str_by_wstr_addr(pstart + OS_CONTACT_WXID); cnt.code = util::get_str_by_wstr_addr(pstart + OS_CONTACT_CODE); @@ -180,7 +180,7 @@ RpcContact_t get_contact_by_wxid(const string &wxid) contact.code = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxCode); contact.remark = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxRemark); contact.name = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxName); - contact.gender = GET_DWORD(reinterpret_cast(buff) + 0x148); + contact.gender = util::get_dword(reinterpret_cast(buff) + 0x148); __asm { PUSHAD diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 89678c1..1993ecd 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -31,24 +31,24 @@ static void get_db_handle(QWORD base, QWORD offset) { auto *wsp = reinterpret_cast(*(QWORD *)(base + offset + OFFSET_DB_NAME)); std::string dbname = util::w2s(std::wstring(wsp)); - db_map[dbname] = GET_QWORD(base + offset); + db_map[dbname] = util::get_qword(base + offset); } static void get_msg_db_handle(QWORD msg_mgr_addr) { - QWORD db_index = GET_QWORD(msg_mgr_addr + 0x68); - QWORD p_start = GET_QWORD(msg_mgr_addr + 0x50); + QWORD db_index = util::get_qword(msg_mgr_addr + 0x68); + QWORD p_start = util::get_qword(msg_mgr_addr + 0x50); for (uint32_t i = 0; i < db_index; i++) { - QWORD db_addr = GET_QWORD(p_start + i * 0x08); + QWORD db_addr = util::get_qword(p_start + i * 0x08); if (db_addr) { // MSGi.db - std::string dbname = util::w2s(get_pp_wstring(db_addr)); - db_map[dbname] = GET_QWORD(db_addr + 0x78); + std::string dbname = util::w2s(util::get_pp_wstring(db_addr)); + db_map[dbname] = util::get_qword(db_addr + 0x78); // MediaMsgi.db - QWORD mmdb_addr = GET_QWORD(db_addr + 0x20); - std::string mmdbname = util::w2s(get_pp_wstring(mmdb_addr + 0x78)); - db_map[mmdbname] = GET_QWORD(mmdb_addr + 0x50); + QWORD mmdb_addr = util::get_qword(db_addr + 0x20); + std::string mmdbname = util::w2s(util::get_pp_wstring(mmdb_addr + 0x78)); + db_map[mmdbname] = util::get_qword(mmdb_addr + 0x50); } } } @@ -56,7 +56,7 @@ static void get_msg_db_handle(QWORD msg_mgr_addr) db_map_t get_db_handles() { db_map.clear(); - QWORD db_instance_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); + QWORD db_instance_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); get_db_handle(db_instance_addr, OFFSET_DB_MICROMSG); // MicroMsg.db get_db_handle(db_instance_addr, OFFSET_DB_CHAT_MSG); // ChatMsg.db @@ -65,7 +65,7 @@ db_map_t get_db_handles() get_db_handle(db_instance_addr, OFFSET_DB_MEDIA); // Media.db get_db_handle(db_instance_addr, OFFSET_DB_FUNCTION_MSG); // Function.db - get_msg_db_handle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db + get_msg_db_handle(util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db return db_map; } @@ -189,19 +189,19 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) return -1; } - QWORD msg_mgr_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int db_index = static_cast(GET_QWORD(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧? - QWORD p_start = GET_QWORD(msg_mgr_addr + 0x50); + QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧? + QWORD p_start = util::get_qword(msg_mgr_addr + 0x50); *db_idx = 0; for (int i = db_index - 1; i >= 0; i--) { // 从后往前遍历 - QWORD db_addr = GET_QWORD(p_start + i * 0x08); + QWORD db_addr = util::get_qword(p_start + i * 0x08); if (!db_addr) { continue; } - std::string dbname = util::w2s(get_pp_wstring(db_addr)); - db_map[dbname] = GET_QWORD(db_addr + 0x78); + std::string dbname = util::w2s(util::get_pp_wstring(db_addr)); + db_map[dbname] = util::get_qword(db_addr + 0x78); std::string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + std::to_string(id) + ";"; DbRows_t rows = exec_db_query(dbname, sql); @@ -223,7 +223,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) continue; } - *db_idx = static_cast(GET_QWORD(GET_QWORD(db_addr + 0x28) + 0x1E8) >> 32); + *db_idx = static_cast(util::get_qword(util::get_qword(db_addr + 0x28) + 0x1E8) >> 32); return 0; } @@ -232,8 +232,8 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) std::vector get_audio_data(uint64_t id) { - QWORD msg_mgr_addr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int db_index = static_cast(GET_QWORD(msg_mgr_addr + 0x68)); + QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); std::string sql = "SELECT Buf FROM Media WHERE Reserved0=" + std::to_string(id) + ";"; for (int i = db_index - 1; i >= 0; i--) { diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp index 94b8b64..b27642d 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/funcs.cpp @@ -51,7 +51,7 @@ typedef QWORD (*PushAttachTask_t)(QWORD, QWORD, QWORD, QWORD); typedef QWORD (*GetOCRManager_t)(); typedef QWORD (*DoOCRTask_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -int IsLogin(void) { return (int)GET_QWORD(g_WeChatWinDllAddr + OS_LOGIN_STATUS); } +int IsLogin(void) { return (int)util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS); } static string get_key(uint8_t header1, uint8_t header2, uint8_t *key) { @@ -230,7 +230,7 @@ int DownloadAttach(QWORD id, string thumb, string extra) GetChatMgr(); GetMgrByPrefixLocalId(l.QuadPart, pChatMsg); - QWORD type = GET_QWORD(buff + 0x38); + QWORD type = util::get_qword(reinterpret_cast(buff) + 0x38); string save_path = ""; string thumb_path = ""; @@ -262,12 +262,12 @@ int DownloadAttach(QWORD id, string thumb, string extra) // 创建父目录,由于路径来源于微信,不做检查 fs::create_directory(fs::path(save_path).parent_path().string()); - int temp = 1; - WxString *pSavePath = NewWxStringFromStr(save_path); - WxString *pThumbPath = NewWxStringFromStr(thumb_path); + int temp = 1; + auto wx_save_path = util::new_wx_string(save_path); + auto wx_thumb_path = util::new_wx_string(thumb_path); - memcpy(&buff[0x280], pThumbPath, sizeof(WxString)); - memcpy(&buff[0x2A0], pSavePath, sizeof(WxString)); + memcpy(&buff[0x280], wx_thumb_path.get(), sizeof(WxString)); + memcpy(&buff[0x2A0], wx_save_path.get(), sizeof(WxString)); memcpy(&buff[0x40C], &temp, sizeof(temp)); QWORD mgr = GetPreDownLoadMgr(); @@ -354,11 +354,11 @@ OcrResult_t GetOcrResult(string path) QWORD mgr = GetOCRManager(); ret.status = (int)DoOCRTask(mgr, (QWORD)&wxPath, unused, (QWORD)buff, (QWORD)&pUnk1, (QWORD)&pUnk2); - QWORD count = GET_QWORD(buff + 0x8); + QWORD count = util::get_qword(buff + 0x8); if (count > 0) { - QWORD header = GET_QWORD(buff); + QWORD header = util::get_qword(buff); for (QWORD i = 0; i < count; i++) { - QWORD content = GET_QWORD(header); + QWORD content = util::get_qword(header); ret.result += util::w2s(get_pp_wstring(content + 0x28)); ret.result += "\n"; header = content; diff --git a/WeChatFerry/spy/funcs.h b/WeChatFerry/spy/funcs.h index cc0f89d..c25f076 100644 --- a/WeChatFerry/spy/funcs.h +++ b/WeChatFerry/spy/funcs.h @@ -3,6 +3,8 @@ #include "stdint.h" #include +#include "pb_types.h" + int IsLogin(void); std::string GetAudio(uint64_t id, std::string dir); std::string GetPCMAudio(uint64_t id, std::string dir, int32_t sr); @@ -11,5 +13,5 @@ int RefreshPyq(uint64_t id); int DownloadAttach(uint64_t id, std::string thumb, std::string extra); int RevokeMsg(uint64_t id); OcrResult_t GetOcrResult(std::string path); -string GetLoginUrl(); +std::string GetLoginUrl(); int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid); diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index 6ce84a2..ceab7dc 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -38,10 +38,10 @@ QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) auto &handler = getInstance(); WxMsg_t wxMsg = {}; try { - wxMsg.id = GET_QWORD(arg2 + OS_RECV_MSG_ID); - wxMsg.type = GET_DWORD(arg2 + OS_RECV_MSG_TYPE); - wxMsg.is_self = GET_DWORD(arg2 + OS_RECV_MSG_SELF); - wxMsg.ts = GET_DWORD(arg2 + OS_RECV_MSG_TS); + wxMsg.id = util::get_qword(arg2 + OS_RECV_MSG_ID); + wxMsg.type = util::get_dword(arg2 + OS_RECV_MSG_TYPE); + wxMsg.is_self = util::get_dword(arg2 + OS_RECV_MSG_SELF); + wxMsg.ts = util::get_dword(arg2 + OS_RECV_MSG_TS); wxMsg.content = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_CONTENT); wxMsg.sign = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_SIGN); wxMsg.xml = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_XML); @@ -98,8 +98,8 @@ void MessageHandler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) wxMsg.type = 0x00; wxMsg.is_self = false; wxMsg.is_group = false; - wxMsg.id = GET_QWORD(startAddr); - wxMsg.ts = GET_DWORD(startAddr + OS_PYQ_MSG_TS); + wxMsg.id = util::get_qword(startAddr); + wxMsg.ts = util::get_dword(startAddr + OS_PYQ_MSG_TS); wxMsg.xml = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_XML); wxMsg.sender = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_SENDER); wxMsg.content = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_CONTENT); diff --git a/WeChatFerry/spy/receive_msg.h b/WeChatFerry/spy/receive_msg.h index 0e2f314..a2559ef 100644 --- a/WeChatFerry/spy/receive_msg.h +++ b/WeChatFerry/spy/receive_msg.h @@ -5,11 +5,11 @@ #include #include #include -#include #include "MinHook.h" #include "pb_types.h" +#include "spy_types.h" class MessageHandler { diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index f78b65d..5fbe07f 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -13,7 +13,7 @@ UINT64 g_WeChatWinDllAddr = 0; void InitSpy(LPVOID args) { - auto *pp = static_cast(args); + auto *pp = static_cast(args); Log::InitLogger(pp->path); if (auto dll_addr = GetModuleHandle(L"WeChatWin.dll")) { diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index 2515d44..48ad23e 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -22,7 +22,7 @@ std::string get_home_path() static std::string home_path; std::call_once(flag, [] { - std::string path = util::w2s(get_pp_wstring(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; + std::string path = util::w2s(util::get_pp_wstring(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; home_path = std::filesystem::absolute(path).string(); }); @@ -37,7 +37,7 @@ std::string get_self_wxid() std::call_once(flag, [] { UINT64 wxid_type = 0; try { - wxid_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); + wxid_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); if (wxid_type == 0xF) { wxid = util::get_p_string(g_WeChatWinDllAddr + OS_USER_WXID); } else { @@ -58,7 +58,7 @@ UserInfo_t get_user_info() UserInfo_t ui; ui.wxid = get_self_wxid(); - UINT64 name_type = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); + UINT64 name_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); ui.name = (name_type == 0xF) ? util::get_p_string(g_WeChatWinDllAddr + OS_USER_NAME) : util::get_pp_string(g_WeChatWinDllAddr + OS_USER_NAME); From 1bec8ce8a2a01b1baa8e9c90d9570b570d98b0bb Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 17:34:48 +0800 Subject: [PATCH 027/132] Refactoring --- WeChatFerry/com/util.cpp | 64 +++++++++++++++++++++------------------- WeChatFerry/spy/spy.cpp | 2 +- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 7d13348..ed2a302 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -52,7 +52,7 @@ std::string gb2312_to_utf8(const char *gb2312) return s; } -DWORD get_wechat_pid() +static DWORD get_wechat_pid() { DWORD pid = 0; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); @@ -69,34 +69,7 @@ DWORD get_wechat_pid() return pid; } -int open_wechat(DWORD &pid) -{ - pid = get_wechat_pid(); - if (pid != 0) { - return ERROR_SUCCESS; - } - - auto wechat_path = util::get_wechat_path(); - if (!wechat_path) { - LOG_ERROR("获取 WeChat 安装路径失败"); - return ERROR_FILE_NOT_FOUND; - } - - STARTUPINFO si = { sizeof(si) }; - PROCESS_INFORMATION pi = {}; - if (!CreateProcessA(wechat_path->c_str(), nullptr, nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, - &si, &pi)) { - return GetLastError(); - } - - CloseHandle(pi.hThread); - CloseHandle(pi.hProcess); - - pid = pi.dwProcessId; - return ERROR_SUCCESS; -} - -std::optional get_wechat_path() +static std::optional get_wechat_path() { HKEY hKey; if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Tencent\\WeChat", 0, KEY_READ, &hKey) != ERROR_SUCCESS) { @@ -118,7 +91,7 @@ std::optional get_wechat_path() return std::string(path); } -std::optional get_wechat_win_dll_path() +static std::optional get_wechat_win_dll_path() { auto wechat_path = get_wechat_path(); if (!wechat_path) { @@ -146,7 +119,7 @@ std::optional get_wechat_win_dll_path() return PathFileExistsA(versioned_path.c_str()) ? std::optional(versioned_path) : std::nullopt; } -std::optional get_file_version(const std::string &file_path) +static std::optional get_file_version(const std::string &file_path) { if (!PathFileExistsA(file_path.c_str())) { return std::nullopt; @@ -190,6 +163,35 @@ std::string get_wechat_version() return *version; } +int open_wechat(DWORD &pid) +{ + pid = get_wechat_pid(); + if (pid != 0) { + return ERROR_SUCCESS; + } + + auto wechat_path = util::get_wechat_path(); + if (!wechat_path) { + LOG_ERROR("获取 WeChat 安装路径失败"); + return ERROR_FILE_NOT_FOUND; + } + + STARTUPINFOA si = { sizeof(si) }; + PROCESS_INFORMATION pi = {}; + + std::string command_line = *wechat_path; + if (!CreateProcessA(nullptr, command_line.data(), nullptr, nullptr, FALSE, CREATE_NEW_CONSOLE, nullptr, nullptr, + &si, &pi)) { + return GetLastError(); + } + + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + pid = pi.dwProcessId; + return ERROR_SUCCESS; +} + uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr) { uint32_t value = 0; diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index 5fbe07f..26fc287 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -24,7 +24,7 @@ void InitSpy(LPVOID args) } std::string version = util::get_wechat_version(); - std::string msg = "WCF 支持版本: " + SUPPORT_VERSION + ",当前版本: " + version; + std::string msg = fmt::format("WCF 支持版本: {},当前版本: {}", SUPPORT_VERSION, version); if (version != SUPPORT_VERSION) { LOG_ERROR(msg); MessageBoxA(NULL, msg.c_str(), "错误", MB_ICONERROR); From e012efc682604ee289f0c0f62afc98e353485fad Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 20:40:46 +0800 Subject: [PATCH 028/132] Refactoring --- WeChatFerry/com/log.hpp | 9 +- WeChatFerry/com/util.cpp | 5 +- WeChatFerry/com/util.h | 7 -- WeChatFerry/sdk/injector.cpp | 181 ++++++++++++++++------------------- WeChatFerry/sdk/injector.h | 12 ++- WeChatFerry/sdk/sdk.cpp | 96 +++++++++---------- 6 files changed, 146 insertions(+), 164 deletions(-) diff --git a/WeChatFerry/com/log.hpp b/WeChatFerry/com/log.hpp index 4be5e76..380a8c6 100644 --- a/WeChatFerry/com/log.hpp +++ b/WeChatFerry/com/log.hpp @@ -3,13 +3,12 @@ #include #include #include -#include -#include -#include #include #include -#include "util.h" +#include +#include +#include #define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__) #define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) @@ -48,7 +47,7 @@ inline void InitLogger(const std::string &path) logger = spdlog::rotating_logger_mt(DEFAULT_LOGGER_NAME, filename.string(), DEFAULT_LOGGER_MAX_SIZE, DEFAULT_LOGGER_MAX_FILES); } catch (const spdlog::spdlog_ex &ex) { - MessageBox(NULL, util::s2w(ex.what()).c_str(), L"Init LOGGER ERROR", MB_ICONERROR); + MessageBoxA(NULL, ex.what(), "Init LOGGER ERROR", MB_ICONERROR); return; } diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index ed2a302..2481124 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -18,6 +18,9 @@ namespace util { +constexpr std::wstring_view WECHATEXE = L"WeChat.exe"; +constexpr std::string_view WECHATWINDLL = "WeChatWin.dll"; + std::wstring s2w(const std::string &s) { if (s.empty()) return std::wstring(); @@ -60,7 +63,7 @@ static DWORD get_wechat_pid() PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; while (Process32Next(hSnapshot, &pe32)) { - if (w2s(pe32.szExeFile) == WECHATEXE) { + if (pe32.szExeFile == WECHATEXE) { pid = pe32.th32ProcessID; break; } diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index 71d0bd7..c4715cd 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -9,13 +9,6 @@ namespace util { - -inline constexpr char WECHATEXE[] = "WeChat.exe"; -inline constexpr char WECHATWINDLL[] = "WeChatWin.dll"; -inline constexpr wchar_t WCFSDKDLL[] = L"sdk.dll"; -inline constexpr wchar_t WCFSPYDLL[] = L"spy.dll"; -inline constexpr wchar_t WCFSPYDLL_DEBUG[] = L"spy_debug.dll"; - struct PortPath { int port; char path[MAX_PATH]; diff --git a/WeChatFerry/sdk/injector.cpp b/WeChatFerry/sdk/injector.cpp index 975faa9..d990854 100644 --- a/WeChatFerry/sdk/injector.cpp +++ b/WeChatFerry/sdk/injector.cpp @@ -1,112 +1,108 @@ -#include "framework.h" -#include "psapi.h" -#include -#include +#include "injector.h" -#include "injector.h" -#include "util.h" +#include + +#include "psapi.h" using namespace std; -HMODULE GetTargetModuleBase(HANDLE process, string dll) +static void handle_injection_error(HANDLE process, LPVOID remote_address, const std::string &error_msg) { - DWORD cbNeeded; - HMODULE moduleHandleList[512]; - BOOL ret = EnumProcessModulesEx(process, moduleHandleList, sizeof(moduleHandleList), &cbNeeded, LIST_MODULES_64BIT); - if (!ret) { - MessageBox(NULL, L"获取模块失败", L"GetTargetModuleBase", 0); + MessageBoxA(NULL, error_msg.c_str(), "Error", MB_ICONERROR); + if (remote_address) { + VirtualFreeEx(process, remote_address, 0, MEM_RELEASE); + } + if (process) { + CloseHandle(process); + } +} + +HMODULE get_target_module_base(HANDLE process, const string &dll) +{ + DWORD needed; + HMODULE modules[512]; + if (!EnumProcessModulesEx(process, modules, sizeof(modules), &needed, LIST_MODULES_64BIT)) { + MessageBoxA(NULL, "获取模块失败", "get_target_module_base", 0); return NULL; } - if (cbNeeded > sizeof(moduleHandleList)) { - MessageBox(NULL, L"模块数量过多", L"GetTargetModuleBase", 0); - return NULL; - } - DWORD processCount = cbNeeded / sizeof(HMODULE); - - char moduleName[32]; - for (DWORD i = 0; i < processCount; i++) { - GetModuleBaseNameA(process, moduleHandleList[i], moduleName, 32); - if (!strncmp(dll.c_str(), moduleName, dll.size())) { - return moduleHandleList[i]; + DWORD count = needed / sizeof(HMODULE); + char module_name[MAX_PATH]; + for (DWORD i = 0; i < count; i++) { + GetModuleBaseNameA(process, modules[i], module_name, sizeof(module_name)); + if (!strncmp(dll.c_str(), module_name, dll.size())) { + return modules[i]; } } return NULL; } -HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase) +HANDLE inject_dll(DWORD pid, const string &dll_path, HMODULE *injected_base) { - HANDLE hThread; - SIZE_T cszDLL = (wcslen(dllPath) + 1) * sizeof(WCHAR); + SIZE_T path_size = dll_path.size() + 1; // 1. 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); - if (hProcess == NULL) { - MessageBox(NULL, L"打开进程失败", L"InjectDll", 0); + if (!hProcess) { + MessageBoxA(NULL, "打开进程失败", "inject_dll", 0); return NULL; } // 2. 在目标进程的内存里开辟空间 - LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, cszDLL, MEM_COMMIT, PAGE_READWRITE); - if (pRemoteAddress == NULL) { - MessageBox(NULL, L"DLL 路径写入失败", L"InjectDll", 0); + LPVOID pRemoteAddress = VirtualAllocEx(hProcess, NULL, path_size, MEM_COMMIT, PAGE_READWRITE); + if (!pRemoteAddress) { + handle_injection_error(hProcess, NULL, "DLL 路径写入失败"); return NULL; } // 3. 把 dll 的路径写入到目标进程的内存空间中 - WriteProcessMemory(hProcess, pRemoteAddress, dllPath, cszDLL, NULL); + WriteProcessMemory(hProcess, pRemoteAddress, dll_path.c_str(), path_size, NULL); - // 3. 创建一个远程线程,让目标进程调用 LoadLibrary - HMODULE k32 = GetModuleHandle(L"kernel32.dll"); - if (k32 == NULL) { - MessageBox(NULL, L"获取 kernel32 失败", L"InjectDll", 0); + // 4. 创建一个远程线程,让目标进程调用 LoadLibrary + HMODULE k32 = GetModuleHandleA("kernel32.dll"); + if (!k32) { + handle_injection_error(hProcess, pRemoteAddress, "获取 kernel32 失败"); return NULL; } - FARPROC libAddr = GetProcAddress(k32, "LoadLibraryW"); + FARPROC libAddr = GetProcAddress(k32, "LoadLibraryA"); if (!libAddr) { - MessageBox(NULL, L"获取 LoadLibrary 失败", L"InjectDll", 0); + handle_injection_error(hProcess, pRemoteAddress, "获取 LoadLibrary 失败"); return NULL; } - hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, pRemoteAddress, 0, NULL); - if (hThread == NULL) { - VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE); - CloseHandle(hProcess); - MessageBox(NULL, L"CreateRemoteThread 失败", L"InjectDll", 0); + HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, pRemoteAddress, 0, NULL); + if (!hThread) { + handle_injection_error(hProcess, pRemoteAddress, "CreateRemoteThread 失败"); return NULL; } - WaitForSingleObject(hThread, -1); + WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); - *injectedBase = GetTargetModuleBase(hProcess, filesystem::path(util::w2s(dllPath)).filename().string()); + *injected_base = get_target_module_base(hProcess, filesystem::path(dll_path).filename().string()); VirtualFreeEx(hProcess, pRemoteAddress, 0, MEM_RELEASE); - // CloseHandle(hProcess); // Close when exit - return hProcess; } -bool EjectDll(HANDLE process, HMODULE dllBase) +bool eject_dll(HANDLE process, HMODULE dll_base) { - HANDLE hThread = NULL; - - // 使目标进程调用 FreeLibrary,卸载 DLL - HMODULE k32 = GetModuleHandle(L"kernel32.dll"); - if (k32 == NULL) { - MessageBox(NULL, L"获取 kernel32 失败", L"InjectDll", 0); - return NULL; + HMODULE k32 = GetModuleHandleA("kernel32.dll"); + if (!k32) { + MessageBoxA(NULL, "获取 kernel32 失败", "eject_dll", 0); + return false; } FARPROC libAddr = GetProcAddress(k32, "FreeLibraryAndExitThread"); if (!libAddr) { - MessageBox(NULL, L"获取 FreeLibrary 失败", L"InjectDll", 0); - return NULL; + MessageBoxA(NULL, "获取 FreeLibrary 失败", "eject_dll", 0); + return false; } - hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, (LPVOID)dllBase, 0, NULL); - if (hThread == NULL) { - MessageBox(NULL, L"FreeLibrary 调用失败!", L"EjectDll", 0); + + HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, (LPVOID)dll_base, 0, NULL); + if (!hThread) { + MessageBoxA(NULL, "FreeLibrary 调用失败!", "eject_dll", 0); return false; } @@ -116,38 +112,34 @@ bool EjectDll(HANDLE process, HMODULE dllBase) return true; } -static UINT64 GetFuncOffset(LPCWSTR dllPath, LPCSTR funcName) +static uint64_t get_func_offset(const string &dll_path, const string &func_name) { - HMODULE dll = LoadLibrary(dllPath); - if (dll == NULL) { - MessageBox(NULL, L"获取 DLL 失败", L"GetFuncOffset", 0); + HMODULE dll = LoadLibraryA(dll_path.c_str()); + if (!dll) { + MessageBoxA(NULL, "获取 DLL 失败", "get_func_offset", 0); return 0; } - LPVOID absAddr = GetProcAddress(dll, funcName); - UINT64 offset = (UINT64)absAddr - (UINT64)dll; + LPVOID absAddr = GetProcAddress(dll, func_name.c_str()); + uint64_t offset = reinterpret_cast(absAddr) - reinterpret_cast(dll); FreeLibrary(dll); return offset; } -bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPDWORD ret) +bool call_dll_func(HANDLE process, const string &dll_path, HMODULE dll_base, const string &func_name, DWORD *ret) { - UINT64 offset = GetFuncOffset(dllPath, funcName); - if (offset == 0) { - return false; + uint64_t offset = get_func_offset(dll_path, func_name); + if (offset == 0 || offset > (UINT64_MAX - reinterpret_cast(dll_base))) { + return false; // 避免溢出 } - UINT64 pFunc = (UINT64)dllBase + GetFuncOffset(dllPath, funcName); - if (pFunc <= (UINT64)dllBase) { - return false; - } - + uint64_t pFunc = reinterpret_cast(dll_base) + offset; HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, NULL, 0, NULL); - if (hThread == NULL) { + if (!hThread) { return false; } WaitForSingleObject(hThread, INFINITE); - if (ret != NULL) { + if (ret) { GetExitCodeThread(hThread, ret); } @@ -155,35 +147,32 @@ bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcNa return true; } -bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz, - LPDWORD ret) +bool call_dll_func_ex(HANDLE process, const string &dll_path, HMODULE dll_base, const string &func_name, + LPVOID parameter, size_t size, DWORD *ret) { - UINT64 offset = GetFuncOffset(dllPath, funcName); - if (offset == 0) { - return false; + uint64_t offset = get_func_offset(dll_path, func_name); + if (offset == 0 || offset > (UINT64_MAX - reinterpret_cast(dll_base))) { + return false; // 避免溢出 } - UINT64 pFunc = (UINT64)dllBase + GetFuncOffset(dllPath, funcName); - if (pFunc <= (UINT64)dllBase) { + uint64_t pFunc = reinterpret_cast(dll_base) + offset; + LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, size, MEM_COMMIT, PAGE_READWRITE); + if (!pRemoteAddress) { + MessageBoxA(NULL, "申请内存失败", "call_dll_func_ex", 0); return false; } - LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, sz, MEM_COMMIT, PAGE_READWRITE); - if (pRemoteAddress == NULL) { - MessageBox(NULL, L"申请内存失败", L"CallDllFuncEx", 0); - return NULL; - } - - WriteProcessMemory(process, pRemoteAddress, parameter, sz, NULL); + WriteProcessMemory(process, pRemoteAddress, parameter, size, NULL); HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pRemoteAddress, 0, NULL); - if (hThread == NULL) { - VirtualFree(pRemoteAddress, 0, MEM_RELEASE); - MessageBox(NULL, L"远程调用失败", L"CallDllFuncEx", 0); + if (!hThread) { + VirtualFreeEx(process, pRemoteAddress, 0, MEM_RELEASE); + MessageBoxA(NULL, "远程调用失败", "call_dll_func_ex", 0); return false; } + WaitForSingleObject(hThread, INFINITE); - VirtualFree(pRemoteAddress, 0, MEM_RELEASE); - if (ret != NULL) { + VirtualFreeEx(process, pRemoteAddress, 0, MEM_RELEASE); + if (ret) { GetExitCodeThread(hThread, ret); } diff --git a/WeChatFerry/sdk/injector.h b/WeChatFerry/sdk/injector.h index 07b4a28..83988a6 100644 --- a/WeChatFerry/sdk/injector.h +++ b/WeChatFerry/sdk/injector.h @@ -1,9 +1,11 @@ #pragma once +#include + #include "framework.h" -HANDLE InjectDll(DWORD pid, LPCWSTR dllPath, HMODULE *injectedBase); -bool EjectDll(HANDLE process, HMODULE dllBase); -bool CallDllFunc(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, DWORD *ret); -bool CallDllFuncEx(HANDLE process, LPCWSTR dllPath, HMODULE dllBase, LPCSTR funcName, LPVOID parameter, size_t sz, - DWORD *ret); +HANDLE inject_dll(DWORD pid, const std::string &dll_path, HMODULE *injected_base); +bool eject_dll(HANDLE process, HMODULE dll_base); +bool call_dll_func(HANDLE process, const std::string &dll_path, HMODULE dll_base, const std::string &func, DWORD *ret); +bool call_dll_func_ex(HANDLE process, const std::string &dll_path, HMODULE dll_base, const std::string &func, + LPVOID parameter, size_t size, DWORD *ret); diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index fca3b0f..69ff632 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -1,4 +1,5 @@ -#include "framework.h" +#include "sdk.h" + #include #include #include @@ -6,55 +7,56 @@ #include #include #include + +#include "framework.h" #include #include "injector.h" -#include "sdk.h" #include "util.h" -static BOOL injected = false; +static bool injected = false; static HANDLE wcProcess = NULL; static HMODULE spyBase = NULL; -static std::wstring spyDllPath; +static std::string spyDllPath; -constexpr char DISCLAIMER_FILE[] = ".license_accepted.flag"; -constexpr char DISCLAIMER_TEXT_FILE[] = "DISCLAIMER.md"; +constexpr std::string_view WCFSDKDLL = "sdk.dll"; +constexpr std::string_view WCFSPYDLL = "spy.dll"; +constexpr std::string_view WCFSPYDLL_DEBUG = "spy_debug.dll"; +constexpr std::string_view DISCLAIMER_FLAG = ".license_accepted.flag"; +constexpr std::string_view DISCLAIMER_TEXT_FILE = "DISCLAIMER.md"; -static std::optional ReadDisclaimerText(const char *filePath) +static std::optional read_disclaimer_text(const std::string &path) { - std::ifstream file(filePath, std::ios::binary); + std::ifstream file(path, std::ios::binary); if (!file.is_open()) { return std::nullopt; // 文件打开失败 } - std::string content((std::istreambuf_iterator(file)), std::istreambuf_iterator()); - return util::s2w(content); + return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); } -static bool ShowDisclaimer() +static bool show_disclaimer() { - if (std::filesystem::exists(DISCLAIMER_FILE)) { + if (std::filesystem::exists(DISCLAIMER_FLAG)) { return true; } - std::optional disclaimerTextOpt = ReadDisclaimerText(DISCLAIMER_TEXT_FILE); - if (!disclaimerTextOpt.has_value() || disclaimerTextOpt->empty()) { - MessageBox(NULL, L"免责声明文件为空或读取失败。", L"错误", MB_ICONERROR); + auto disclaimerTextOpt = read_disclaimer_text(std::string(DISCLAIMER_TEXT_FILE)); + if (!disclaimerTextOpt || disclaimerTextOpt->empty()) { + MessageBoxA(NULL, "免责声明文件为空或读取失败。", "错误", MB_ICONERROR); return false; } - std::wstring disclaimerText = *disclaimerTextOpt; - - int result = MessageBox(NULL, disclaimerText.c_str(), L"免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2); - + int result + = MessageBoxA(NULL, disclaimerTextOpt->c_str(), "免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2); if (result == IDCANCEL) { - MessageBox(NULL, L"您拒绝了免责声明,程序将退出。", L"提示", MB_ICONINFORMATION); + MessageBoxA(NULL, "您拒绝了免责声明,程序将退出。", "提示", MB_ICONINFORMATION); return false; } - std::ofstream flagFile(DISCLAIMER_FILE, std::ios::out | std::ios::trunc); + std::ofstream flagFile(std::string(DISCLAIMER_FLAG), std::ios::out | std::ios::trunc); if (!flagFile) { - MessageBox(NULL, L"无法创建协议标志文件。", L"错误", MB_ICONERROR); + MessageBoxA(NULL, "无法创建协议标志文件。", "错误", MB_ICONERROR); return false; } flagFile << "User accepted the license agreement."; @@ -62,62 +64,56 @@ static bool ShowDisclaimer() return true; } -static std::wstring GetDllPath(bool debug) +static std::string get_dll_path(bool debug) { - WCHAR buffer[MAX_PATH] = { 0 }; - GetModuleFileName(GetModuleHandle(WCFSDKDLL), buffer, MAX_PATH); + char buffer[MAX_PATH] = { 0 }; + GetModuleFileNameA(GetModuleHandleA(WCFSDKDLL), buffer, MAX_PATH); std::filesystem::path path(buffer); - path.remove_filename(); // 移除文件名,保留目录路径 - + path.remove_filename(); // 只保留目录路径 path /= debug ? WCFSPYDLL_DEBUG : WCFSPYDLL; if (!std::filesystem::exists(path)) { - MessageBox(NULL, path.c_str(), L"文件不存在", MB_ICONERROR); - return L""; + MessageBoxA(NULL, path.string().c_str(), "文件不存在", MB_ICONERROR); + return ""; } - return path.wstring(); + return path.string(); } int WxInitSDK(bool debug, int port) { - if (!ShowDisclaimer()) { + if (!show_disclaimer()) { exit(-1); // 用户拒绝协议,退出程序 } int status = 0; DWORD wcPid = 0; - spyDllPath = GetDllPath(debug); + spyDllPath = get_dll_path(debug); if (spyDllPath.empty()) { return ERROR_FILE_NOT_FOUND; // DLL 文件路径不存在 } status = util::open_wechat(&wcPid); if (status != 0) { - MessageBox(NULL, L"打开微信失败", L"WxInitSDK", 0); + MessageBoxA(NULL, "打开微信失败", "WxInitSDK", 0); return status; } - if (!IsProcessX64(wcPid)) { - MessageBox(NULL, L"只支持 64 位微信", L"WxInitSDK", 0); - return -1; - } - std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待微信打开 - wcProcess = InjectDll(wcPid, spyDllPath.c_str(), &spyBase); + wcProcess = inject_dll(wcPid, spyDllPath, &spyBase); if (wcProcess == NULL) { - MessageBox(NULL, L"注入失败", L"WxInitSDK", 0); + MessageBoxA(NULL, "注入失败", "WxInitSDK", 0); return -1; } - PortPath_t pp = { 0 }; - pp.port = port; - sprintf_s(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); + util::PortPath pp = { 0 }; + pp.port = port; + snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); - if (!CallDllFuncEx(wcProcess, spyDllPath.c_str(), spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) { - MessageBox(NULL, L"初始化失败", L"WxInitSDK", 0); + if (!call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) { + MessageBoxA(NULL, "初始化失败", "WxInitSDK", 0); return -1; } @@ -128,16 +124,16 @@ int WxInitSDK(bool debug, int port) int WxDestroySDK() { if (!injected) { + return 1; // 未注入 + } + + if (!call_dll_func(wcProcess, spyDllPath, spyBase, "CleanupSpy", NULL)) { return -1; } - if (!CallDllFunc(wcProcess, spyDllPath.c_str(), spyBase, "CleanupSpy", NULL)) { + if (!eject_dll(wcProcess, spyBase)) { return -2; } - if (!EjectDll(wcProcess, spyBase)) { - return -3; // TODO: Unify error codes - } - return 0; } From 1cbd3a3ebe9c81d6797b6a7dd27ea8d4d915dbee Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 21:00:29 +0800 Subject: [PATCH 029/132] Refatoring --- WeChatFerry/com/util.cpp | 6 +++--- WeChatFerry/com/util.h | 2 +- WeChatFerry/sdk/sdk.cpp | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 2481124..9bc0753 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -18,8 +18,8 @@ namespace util { -constexpr std::wstring_view WECHATEXE = L"WeChat.exe"; -constexpr std::string_view WECHATWINDLL = "WeChatWin.dll"; +constexpr char WECHATEXE[] = "WeChat.exe"; +constexpr char WECHATWINDLL[] = "WeChatWin.dll"; std::wstring s2w(const std::string &s) { @@ -63,7 +63,7 @@ static DWORD get_wechat_pid() PROCESSENTRY32 pe32 = { sizeof(PROCESSENTRY32) }; while (Process32Next(hSnapshot, &pe32)) { - if (pe32.szExeFile == WECHATEXE) { + if (pe32.szExeFile == s2w(WECHATEXE)) { pid = pe32.th32ProcessID; break; } diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index c4715cd..de61c80 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -15,7 +15,7 @@ struct PortPath { }; DWORD get_wechat_pid(); -int open_wechat(DWORD *pid); +int open_wechat(DWORD &pid); std::string get_wechat_version(); uint32_t get_memory_int_by_address(HANDLE hProcess, uint64_t addr); std::wstring get_unicode_info_by_address(HANDLE hProcess, uint64_t addr); diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index 69ff632..6f9b4c3 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -19,9 +19,10 @@ static HANDLE wcProcess = NULL; static HMODULE spyBase = NULL; static std::string spyDllPath; -constexpr std::string_view WCFSDKDLL = "sdk.dll"; -constexpr std::string_view WCFSPYDLL = "spy.dll"; -constexpr std::string_view WCFSPYDLL_DEBUG = "spy_debug.dll"; +constexpr char WCFSDKDLL[] = "sdk.dll"; +constexpr char WCFSPYDLL[] = "spy.dll"; +constexpr char WCFSPYDLL_DEBUG[] = "spy_debug.dll"; + constexpr std::string_view DISCLAIMER_FLAG = ".license_accepted.flag"; constexpr std::string_view DISCLAIMER_TEXT_FILE = "DISCLAIMER.md"; @@ -95,7 +96,7 @@ int WxInitSDK(bool debug, int port) return ERROR_FILE_NOT_FOUND; // DLL 文件路径不存在 } - status = util::open_wechat(&wcPid); + status = util::open_wechat(wcPid); if (status != 0) { MessageBoxA(NULL, "打开微信失败", "WxInitSDK", 0); return status; @@ -112,7 +113,7 @@ int WxInitSDK(bool debug, int port) pp.port = port; snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); - if (!call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(PortPath_t), NULL)) { + if (!call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), NULL)) { MessageBoxA(NULL, "初始化失败", "WxInitSDK", 0); return -1; } From 9e306e2452832583a1a9498ef6045893ad1e120e Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 22:07:36 +0800 Subject: [PATCH 030/132] Refactoring --- WeChatFerry/com/log.hpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/com/log.hpp b/WeChatFerry/com/log.hpp index 380a8c6..4a08c8b 100644 --- a/WeChatFerry/com/log.hpp +++ b/WeChatFerry/com/log.hpp @@ -1,5 +1,9 @@ #pragma once +#ifdef ENABLE_DEBUG_LOG +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG +#endif + #include #include #include @@ -58,11 +62,10 @@ inline void InitLogger(const std::string &path) spdlog::set_level(spdlog::level::debug); logger->flush_on(spdlog::level::debug); #else - spdlog::set_level(spdlog::level::info); logger->flush_on(spdlog::level::info); #endif - SPDLOG_DEBUG("Logger initialized with default settings."); + LOG_DEBUG("InitLogger with debug level"); } #ifdef ENABLE_DEBUG_LOG @@ -79,7 +82,7 @@ inline void log_buffer(uint8_t *buffer, size_t len) } } - SPDLOG_DEBUG(oss.str()); + LOG_DEBUG(oss.str()); } #endif From 0d228745acfc057a79588c70b6202090f61db187 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 22:29:54 +0800 Subject: [PATCH 031/132] Refactoring --- WeChatFerry/com/util.cpp | 47 +++++++++++++++++++++++++--------------- WeChatFerry/sdk/sdk.cpp | 4 +++- WeChatFerry/spy/spy.cpp | 5 +++-- WeChatFerry/spy/spy.h | 2 +- 4 files changed, 37 insertions(+), 21 deletions(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 9bc0753..2c45632 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -1,6 +1,7 @@ #include "util.h" #include +#include #include #include #include @@ -15,6 +16,8 @@ #pragma comment(lib, "shlwapi") #pragma comment(lib, "Version.lib") +namespace fs = std::filesystem; + namespace util { @@ -101,47 +104,57 @@ static std::optional get_wechat_win_dll_path() return std::nullopt; } - std::string dll_path = *wechat_path; - PathRemoveFileSpecA(dll_path.data()); - PathAppendA(dll_path.data(), WECHATWINDLL); + fs::path dll_path = *wechat_path; + dll_path = dll_path.parent_path(); - if (PathFileExistsA(dll_path.c_str())) { - return dll_path; + fs::path wechat_dll_path = dll_path / WECHATWINDLL; + if (fs::exists(wechat_dll_path)) { // 尝试直接查找 WeChatWin.dll + return wechat_dll_path.string(); } // 微信从(大约)3.7开始,增加了一层版本目录: [3.7.0.29] - PathRemoveFileSpecA(dll_path.data()); - WIN32_FIND_DATAA find_data; - HANDLE hFind = FindFirstFileA((dll_path + "\\*.*").c_str(), &find_data); - if (hFind == INVALID_HANDLE_VALUE) { - return std::nullopt; + std::optional found_path; + for (const auto &entry : fs::directory_iterator(dll_path)) { + if (entry.is_directory()) { + fs::path possible_dll = entry.path() / WECHATWINDLL; + if (fs::exists(possible_dll)) { + found_path = possible_dll.string(); + break; // 取第一个找到的版本号文件夹 + } + } } - FindClose(hFind); - std::string versioned_path = dll_path + "\\" + find_data.cFileName + WECHATWINDLL; - return PathFileExistsA(versioned_path.c_str()) ? std::optional(versioned_path) : std::nullopt; + if (!found_path) { + LOG_ERROR("未找到 WeChatWin.dll"); + } + + return found_path; } -static std::optional get_file_version(const std::string &file_path) +static std::optional get_file_version(const std::string &path) { - if (!PathFileExistsA(file_path.c_str())) { + if (!PathFileExistsA(path.c_str())) { + LOG_ERROR("文件不存在: {}", path); return std::nullopt; } DWORD dummy = 0; - DWORD size = GetFileVersionInfoSizeA(file_path.c_str(), &dummy); + DWORD size = GetFileVersionInfoSizeA(path.c_str(), &dummy); if (size == 0) { + LOG_ERROR("无法获取文件版本信息大小: {}", path); return std::nullopt; } std::vector buffer(size); - if (!GetFileVersionInfoA(file_path.c_str(), 0, size, buffer.data())) { + if (!GetFileVersionInfoA(path.c_str(), 0, size, buffer.data())) { + LOG_ERROR("无法获取文件版本信息: {}", path); return std::nullopt; } VS_FIXEDFILEINFO *ver_info = nullptr; UINT ver_size = 0; if (!VerQueryValueA(buffer.data(), "\\", reinterpret_cast(&ver_info), &ver_size)) { + LOG_ERROR("无法获取文件版本信息: {}", path); return std::nullopt; } diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index 6f9b4c3..0635c70 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -113,8 +113,10 @@ int WxInitSDK(bool debug, int port) pp.port = port; snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); - if (!call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), NULL)) { + bool ok = call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), &status); + if (!ok || status != 0) { MessageBoxA(NULL, "初始化失败", "WxInitSDK", 0); + WxDestroySDK(); return -1; } diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index 26fc287..785f116 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -11,7 +11,7 @@ constexpr std::string_view SUPPORT_VERSION = "3.9.11.25"; UINT64 g_WeChatWinDllAddr = 0; -void InitSpy(LPVOID args) +int InitSpy(LPVOID args) { auto *pp = static_cast(args); @@ -28,11 +28,12 @@ void InitSpy(LPVOID args) if (version != SUPPORT_VERSION) { LOG_ERROR(msg); MessageBoxA(NULL, msg.c_str(), "错误", MB_ICONERROR); - return; + return -1; } LOG_INFO(msg); RpcStartServer(pp->port); + return 0; } void CleanupSpy() diff --git a/WeChatFerry/spy/spy.h b/WeChatFerry/spy/spy.h index 3aa41dd..07825e6 100644 --- a/WeChatFerry/spy/spy.h +++ b/WeChatFerry/spy/spy.h @@ -2,5 +2,5 @@ #include "framework.h" -void InitSpy(int port); +int InitSpy(int port); void CleanupSpy(); From 53370ca8b6f66094330a22811f512bc9a47b7683 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Feb 2025 22:52:36 +0800 Subject: [PATCH 032/132] Refactoring --- WeChatFerry/sdk/sdk.cpp | 13 +++++++------ WeChatFerry/spy/spy.cpp | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index 0635c70..e1dbdc8 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -108,20 +108,20 @@ int WxInitSDK(bool debug, int port) MessageBoxA(NULL, "注入失败", "WxInitSDK", 0); return -1; } + injected = true; util::PortPath pp = { 0 }; pp.port = port; snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); - bool ok = call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), &status); - if (!ok || status != 0) { - MessageBoxA(NULL, "初始化失败", "WxInitSDK", 0); + status = -3; // TODO: 统一错误码 + bool success = call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), + (DWORD *)&status); + if (!success || status != 0) { WxDestroySDK(); - return -1; } - injected = true; - return 0; + return status; } int WxDestroySDK() @@ -137,6 +137,7 @@ int WxDestroySDK() if (!eject_dll(wcProcess, spyBase)) { return -2; } + injected = false; return 0; } diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index 785f116..edb89b6 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -20,7 +20,7 @@ int InitSpy(LPVOID args) g_WeChatWinDllAddr = reinterpret_cast(dll_addr); } else { LOG_ERROR("获取 WeChatWin.dll 模块地址失败"); - return; // TODO: 退出进程,避免后面操作失败 + return -1; } std::string version = util::get_wechat_version(); @@ -28,7 +28,7 @@ int InitSpy(LPVOID args) if (version != SUPPORT_VERSION) { LOG_ERROR(msg); MessageBoxA(NULL, msg.c_str(), "错误", MB_ICONERROR); - return -1; + return -2; } LOG_INFO(msg); From b5d2f39002889ac6577babbac5b22987aa69348c Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Feb 2025 08:16:44 +0800 Subject: [PATCH 033/132] Refactoring --- WeChatFerry/spy/spy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index edb89b6..db59d50 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -27,7 +27,7 @@ int InitSpy(LPVOID args) std::string msg = fmt::format("WCF 支持版本: {},当前版本: {}", SUPPORT_VERSION, version); if (version != SUPPORT_VERSION) { LOG_ERROR(msg); - MessageBoxA(NULL, msg.c_str(), "错误", MB_ICONERROR); + MessageBoxA(NULL, msg.c_str(), "微信版本错误", MB_ICONERROR); return -2; } From b99a3aa377ccebb9f40fadc8d2e4717846d2554c Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Feb 2025 08:44:16 +0800 Subject: [PATCH 034/132] Refactoring --- WeChatFerry/spy/chatroom_mgmt.cpp | 2 +- WeChatFerry/spy/contact_mgmt.cpp | 2 +- WeChatFerry/spy/exec_sql.cpp | 2 +- WeChatFerry/spy/{fill_response.h => rpc_helper.h} | 0 WeChatFerry/spy/send_msg.cpp | 2 +- WeChatFerry/spy/user_info.cpp | 2 +- 6 files changed, 5 insertions(+), 5 deletions(-) rename WeChatFerry/spy/{fill_response.h => rpc_helper.h} (100%) diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index 5cef8df..d7d156f 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -1,9 +1,9 @@ #pragma execution_character_set("utf-8") #include "chatroom_mgmt.h" -#include "fill_response.h" #include "log.hpp" #include "pb_util.h" +#include "rpc_helper.h" #include "util.h" using namespace std; diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 6c6c631..f100c3b 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -2,9 +2,9 @@ #include "contact_mgmt.h" -#include "fill_response.h" #include "log.hpp" #include "pb_util.h" +#include "rpc_helper.h" #include "util.h" using namespace std; diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 1993ecd..5581357 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -3,9 +3,9 @@ #include #include -#include "fill_response.h" #include "log.hpp" #include "pb_util.h" +#include "rpc_helper.h" #include "sqlite3.h" #include "util.h" diff --git a/WeChatFerry/spy/fill_response.h b/WeChatFerry/spy/rpc_helper.h similarity index 100% rename from WeChatFerry/spy/fill_response.h rename to WeChatFerry/spy/rpc_helper.h diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index 1134cbd..ff1609d 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -4,8 +4,8 @@ #include #include "exec_sql.h" -#include "fill_response.h" #include "log.hpp" +#include "rpc_helper.h" #include "spy_types.h" #include "user_info.h" #include "util.h" diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index 48ad23e..c58e471 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -3,8 +3,8 @@ #include #include -#include "fill_response.h" #include "log.hpp" +#include "rpc_helper.h" #include "util.h" extern UINT64 g_WeChatWinDllAddr; From 228ebbf4d1354c8d37cef21c2cfce28e12d0434b Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Feb 2025 09:17:50 +0800 Subject: [PATCH 035/132] Refactoring --- WeChatFerry/sdk/SDK.vcxproj | 1 - WeChatFerry/sdk/SDK.vcxproj.filters | 3 -- WeChatFerry/spy/Spy.vcxproj | 29 ++++++++--------- WeChatFerry/spy/Spy.vcxproj.filters | 31 ++++++++++--------- ...chatroom_mgmt.cpp => chatroom_manager.cpp} | 2 +- .../{chatroom_mgmt.h => chatroom_manager.h} | 0 .../{contact_mgmt.cpp => contact_manager.cpp} | 2 +- .../spy/{contact_mgmt.h => contact_manager.h} | 0 .../{exec_sql.cpp => database_executor.cpp} | 2 +- .../spy/{exec_sql.h => database_executor.h} | 0 .../{receive_msg.cpp => message_handler.cpp} | 4 +-- .../spy/{receive_msg.h => message_handler.h} | 0 .../spy/{send_msg.cpp => message_sender.cpp} | 6 ++-- .../spy/{send_msg.h => message_sender.h} | 0 .../spy/{funcs.cpp => misc_manager.cpp} | 6 ++-- WeChatFerry/spy/{funcs.h => misc_manager.h} | 0 WeChatFerry/spy/rpc_server.cpp | 14 ++++----- .../{user_info.cpp => userinfo_manager.cpp} | 2 +- .../spy/{user_info.h => userinfo_manager.h} | 0 19 files changed, 51 insertions(+), 51 deletions(-) rename WeChatFerry/spy/{chatroom_mgmt.cpp => chatroom_manager.cpp} (99%) rename WeChatFerry/spy/{chatroom_mgmt.h => chatroom_manager.h} (100%) rename WeChatFerry/spy/{contact_mgmt.cpp => contact_manager.cpp} (99%) rename WeChatFerry/spy/{contact_mgmt.h => contact_manager.h} (100%) rename WeChatFerry/spy/{exec_sql.cpp => database_executor.cpp} (99%) rename WeChatFerry/spy/{exec_sql.h => database_executor.h} (100%) rename WeChatFerry/spy/{receive_msg.cpp => message_handler.cpp} (99%) rename WeChatFerry/spy/{receive_msg.h => message_handler.h} (100%) rename WeChatFerry/spy/{send_msg.cpp => message_sender.cpp} (99%) rename WeChatFerry/spy/{send_msg.h => message_sender.h} (100%) rename WeChatFerry/spy/{funcs.cpp => misc_manager.cpp} (99%) rename WeChatFerry/spy/{funcs.h => misc_manager.h} (100%) rename WeChatFerry/spy/{user_info.cpp => userinfo_manager.cpp} (98%) rename WeChatFerry/spy/{user_info.h => userinfo_manager.h} (100%) diff --git a/WeChatFerry/sdk/SDK.vcxproj b/WeChatFerry/sdk/SDK.vcxproj index 5f549ad..cb0fd9d 100644 --- a/WeChatFerry/sdk/SDK.vcxproj +++ b/WeChatFerry/sdk/SDK.vcxproj @@ -141,7 +141,6 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry - diff --git a/WeChatFerry/sdk/SDK.vcxproj.filters b/WeChatFerry/sdk/SDK.vcxproj.filters index 323d545..5adfeab 100644 --- a/WeChatFerry/sdk/SDK.vcxproj.filters +++ b/WeChatFerry/sdk/SDK.vcxproj.filters @@ -27,9 +27,6 @@ 头文件 - - 头文件 - diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj index 6a66d2d..3f601af 100644 --- a/WeChatFerry/spy/Spy.vcxproj +++ b/WeChatFerry/spy/Spy.vcxproj @@ -247,19 +247,20 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - - - + + + - - + + + - + - + @@ -268,16 +269,16 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - - + + - - - + + + - + - + diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters index 3bf11bb..dea6857 100644 --- a/WeChatFerry/spy/Spy.vcxproj.filters +++ b/WeChatFerry/spy/Spy.vcxproj.filters @@ -24,16 +24,16 @@ 头文件 - + 头文件 - + 头文件 - + 头文件 - + 头文件 @@ -63,16 +63,16 @@ nnrpc - + 头文件 - + 头文件 头文件 - + 头文件 @@ -87,6 +87,9 @@ 头文件 + + 头文件 + @@ -95,16 +98,16 @@ 源文件 - + 源文件 - + 源文件 - + 源文件 - + 源文件 @@ -125,13 +128,13 @@ nnrpc - + 源文件 - + 源文件 - + 源文件 diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_manager.cpp similarity index 99% rename from WeChatFerry/spy/chatroom_mgmt.cpp rename to WeChatFerry/spy/chatroom_manager.cpp index d7d156f..c8b2042 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -1,6 +1,6 @@ #pragma execution_character_set("utf-8") -#include "chatroom_mgmt.h" +#include "chatroom_manager.h" #include "log.hpp" #include "pb_util.h" #include "rpc_helper.h" diff --git a/WeChatFerry/spy/chatroom_mgmt.h b/WeChatFerry/spy/chatroom_manager.h similarity index 100% rename from WeChatFerry/spy/chatroom_mgmt.h rename to WeChatFerry/spy/chatroom_manager.h diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_manager.cpp similarity index 99% rename from WeChatFerry/spy/contact_mgmt.cpp rename to WeChatFerry/spy/contact_manager.cpp index f100c3b..17cc7f8 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -1,6 +1,6 @@ #pragma execution_character_set("utf-8") -#include "contact_mgmt.h" +#include "contact_manager.h" #include "log.hpp" #include "pb_util.h" diff --git a/WeChatFerry/spy/contact_mgmt.h b/WeChatFerry/spy/contact_manager.h similarity index 100% rename from WeChatFerry/spy/contact_mgmt.h rename to WeChatFerry/spy/contact_manager.h diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/database_executor.cpp similarity index 99% rename from WeChatFerry/spy/exec_sql.cpp rename to WeChatFerry/spy/database_executor.cpp index 5581357..7ff8290 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -1,4 +1,4 @@ -#include "exec_sql.h" +#include "database_executor.h" #include #include diff --git a/WeChatFerry/spy/exec_sql.h b/WeChatFerry/spy/database_executor.h similarity index 100% rename from WeChatFerry/spy/exec_sql.h rename to WeChatFerry/spy/database_executor.h diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/message_handler.cpp similarity index 99% rename from WeChatFerry/spy/receive_msg.cpp rename to WeChatFerry/spy/message_handler.cpp index ceab7dc..cf8b884 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -1,4 +1,4 @@ -#include "receive_msg.h" +#include "message_handler.h" #include #include @@ -7,7 +7,7 @@ #include "framework.h" #include "log.hpp" -#include "user_info.h" +#include "userinfo_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; diff --git a/WeChatFerry/spy/receive_msg.h b/WeChatFerry/spy/message_handler.h similarity index 100% rename from WeChatFerry/spy/receive_msg.h rename to WeChatFerry/spy/message_handler.h diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/message_sender.cpp similarity index 99% rename from WeChatFerry/spy/send_msg.cpp rename to WeChatFerry/spy/message_sender.cpp index ff1609d..36b4d5f 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -1,13 +1,13 @@ -#include "send_msg.h" +#include "message_sender.h" #include #include -#include "exec_sql.h" +#include "database_executor.h" #include "log.hpp" #include "rpc_helper.h" #include "spy_types.h" -#include "user_info.h" +#include "userinfo_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; diff --git a/WeChatFerry/spy/send_msg.h b/WeChatFerry/spy/message_sender.h similarity index 100% rename from WeChatFerry/spy/send_msg.h rename to WeChatFerry/spy/message_sender.h diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/misc_manager.cpp similarity index 99% rename from WeChatFerry/spy/funcs.cpp rename to WeChatFerry/spy/misc_manager.cpp index b27642d..f6f252f 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -1,5 +1,5 @@ #pragma warning(disable : 4244) -#include "funcs.h" +#include "misc_manager.h" #include #include @@ -7,9 +7,9 @@ #include "framework.h" #include "codec.h" -#include "exec_sql.h" +#include "database_executor.h" #include "log.hpp" -#include "receive_msg.h" +#include "message_handler.h" #include "spy_types.h" #include "util.h" diff --git a/WeChatFerry/spy/funcs.h b/WeChatFerry/spy/misc_manager.h similarity index 100% rename from WeChatFerry/spy/funcs.h rename to WeChatFerry/spy/misc_manager.h diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 111a0a1..96e71f7 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -20,18 +20,18 @@ #include "wcf.pb.h" -#include "chatroom_mgmt.h" -#include "contact_mgmt.h" -#include "exec_sql.h" -#include "funcs.h" +#include "chatroom_manager.h" +#include "contact_manager.h" +#include "database_executor.h" +#include "misc_manager.h" #include "log.hpp" #include "pb_types.h" #include "pb_util.h" -#include "receive_msg.h" -#include "send_msg.h" +#include "message_handler.h" +#include "message_sender.h" #include "spy.h" #include "spy_types.h" -#include "user_info.h" +#include "userinfo_manager.h" #include "util.h" namespace fs = std::filesystem; diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/userinfo_manager.cpp similarity index 98% rename from WeChatFerry/spy/user_info.cpp rename to WeChatFerry/spy/userinfo_manager.cpp index c58e471..b8720ef 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/userinfo_manager.cpp @@ -1,4 +1,4 @@ -#include "user_info.h" +#include "userinfo_manager.h" #include #include diff --git a/WeChatFerry/spy/user_info.h b/WeChatFerry/spy/userinfo_manager.h similarity index 100% rename from WeChatFerry/spy/user_info.h rename to WeChatFerry/spy/userinfo_manager.h From 505eefd03ce3f1fe0311dc4adff7d20937d3b735 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Feb 2025 16:05:40 +0800 Subject: [PATCH 036/132] Refactoring --- WeChatFerry/spy/chatroom_manager.cpp | 4 +- WeChatFerry/spy/chatroom_manager.h | 4 +- WeChatFerry/spy/contact_manager.cpp | 4 +- WeChatFerry/spy/contact_manager.h | 4 +- WeChatFerry/spy/database_executor.cpp | 4 +- WeChatFerry/spy/database_executor.h | 4 +- WeChatFerry/spy/message_handler.cpp | 43 +++++++++++---------- WeChatFerry/spy/message_handler.h | 12 ++++-- WeChatFerry/spy/message_sender.cpp | 54 ++++++++++++++------------- WeChatFerry/spy/message_sender.h | 17 ++++++--- WeChatFerry/spy/misc_manager.cpp | 8 ++-- WeChatFerry/spy/rpc_server.cpp | 26 ++++++------- WeChatFerry/spy/userinfo_manager.cpp | 4 +- WeChatFerry/spy/userinfo_manager.h | 4 +- 14 files changed, 104 insertions(+), 88 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index c8b2042..00a9bf7 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -9,7 +9,7 @@ using namespace std; extern QWORD g_WeChatWinDllAddr; -namespace chatroom_mgmt +namespace chatroom { #define OS_GET_CHATROOM_MGR 0x1B83BD0 #define OS_ADD_MEMBERS 0x2155100 @@ -106,4 +106,4 @@ bool rpc_invite_chatroom_member(const string &roomid, const string &wxids, uint8 out, len, [&](Response &rsp) { rsp.msg.status = invite_chatroom_member(roomid, wxids); }); } -} // namespace chatroom_mgmt +} // namespace chatroom diff --git a/WeChatFerry/spy/chatroom_manager.h b/WeChatFerry/spy/chatroom_manager.h index 2f9b9a6..9791ed7 100644 --- a/WeChatFerry/spy/chatroom_manager.h +++ b/WeChatFerry/spy/chatroom_manager.h @@ -2,7 +2,7 @@ #include -namespace chatroom_mgmt +namespace chatroom { // 添加成员到群聊 @@ -19,4 +19,4 @@ bool rpc_add_chatroom_member(const std::string &roomid, const std::string &wxids bool rpc_del_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); bool rpc_invite_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); -} // namespace chatroom_mgmt +} // namespace chatroom diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index 17cc7f8..bb4efe8 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -11,7 +11,7 @@ using namespace std; extern QWORD g_WeChatWinDllAddr; -namespace contact_mgmt +namespace contact { #define OS_GET_CONTACT_MGR 0x1B417A0 #define OS_GET_CONTACT_LIST 0x219ED10 @@ -218,4 +218,4 @@ bool rpc_accept_friend(const string &v3, const string &v4, int scene, uint8_t *o out, len, [&](Response &rsp) { rsp.msg.status = accept_new_friend(v3, v4, scene); }); } -} // namespace contact_mgmt +} // namespace contact diff --git a/WeChatFerry/spy/contact_manager.h b/WeChatFerry/spy/contact_manager.h index 0fb8091..5b330c0 100644 --- a/WeChatFerry/spy/contact_manager.h +++ b/WeChatFerry/spy/contact_manager.h @@ -5,7 +5,7 @@ #include "pb_types.h" -namespace contact_mgmt +namespace contact { // 获取所有联系人 @@ -25,4 +25,4 @@ bool rpc_get_contacts(uint8_t *out, size_t *len); bool rpc_get_contact_info(const std::string &wxid, uint8_t *out, size_t *len); bool rpc_accept_friend(const std::string &v3, const std::string &v4, int scene, uint8_t *out, size_t *len); -} // namespace contact_mgmt +} // namespace contact diff --git a/WeChatFerry/spy/database_executor.cpp b/WeChatFerry/spy/database_executor.cpp index 7ff8290..3f850a3 100644 --- a/WeChatFerry/spy/database_executor.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -11,7 +11,7 @@ extern UINT64 g_WeChatWinDllAddr; -namespace exec_sql +namespace db { #define OFFSET_DB_INSTANCE 0x5902000 #define OFFSET_DB_MICROMSG 0xB8 @@ -287,4 +287,4 @@ bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *o }); } -} // namespace exec_sql +} // namespace db diff --git a/WeChatFerry/spy/database_executor.h b/WeChatFerry/spy/database_executor.h index 411c338..4f2752d 100644 --- a/WeChatFerry/spy/database_executor.h +++ b/WeChatFerry/spy/database_executor.h @@ -6,7 +6,7 @@ #include "pb_types.h" -namespace exec_sql +namespace db { // 获取数据库名称列表 @@ -29,4 +29,4 @@ bool rpc_get_db_names(uint8_t *out, size_t *len); bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len); bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len); -} // namespace exec_sql +} // namespace db diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index cf8b884..849382e 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -33,7 +33,9 @@ extern QWORD g_WeChatWinDllAddr; #define OS_PYQ_MSG_CALL 0x2E42C90 #define OS_WXLOG 0x2613D20 -QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) +namespace message +{ +QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2) { auto &handler = getInstance(); WxMsg_t wxMsg = {}; @@ -50,10 +52,10 @@ QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) if (wxMsg.roomid.find("@chatroom") != std::string::npos) { wxMsg.is_group = true; wxMsg.sender - = wxMsg.is_self ? user_info::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); + = wxMsg.is_self ? userinfo::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); } else { wxMsg.is_group = false; - wxMsg.sender = wxMsg.is_self ? user_info::get_self_wxid() : wxMsg.roomid; + wxMsg.sender = wxMsg.is_self ? userinfo::get_self_wxid() : wxMsg.roomid; } } catch (const std::exception &e) { LOG_ERROR(util::gb2312_to_utf8(e.what())); @@ -68,8 +70,8 @@ QWORD MessageHandler::DispatchMsg(QWORD arg1, QWORD arg2) return handler.realRecvMsg(arg1, arg2); } -QWORD MessageHandler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, - QWORD a9, QWORD a10, QWORD a11, QWORD a12) +QWORD Handler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9, + QWORD a10, QWORD a11, QWORD a12) { auto &handler = getInstance(); @@ -82,7 +84,7 @@ QWORD MessageHandler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a return p; } -void MessageHandler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) +void Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) { auto &handler = getInstance(); QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START); @@ -114,27 +116,27 @@ void MessageHandler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) } } -MessageHandler &MessageHandler::getInstance() +Handler &Handler::getInstance() { - static MessageHandler instance; + static Handler instance; return instance; } -MessageHandler::MessageHandler() +Handler::Handler() { isLogging = false; isListeningMsg = false; isListeningPyq = false; } -MessageHandler::~MessageHandler() +Handler::~Handler() { DisableLog(); UnListenMsg(); UnListenPyq(); } -MsgTypes_t MessageHandler::GetMsgTypes() +MsgTypes_t Handler::GetMsgTypes() { return { { 0x00, "朋友圈消息" }, { 0x01, "文字" }, @@ -171,7 +173,7 @@ MsgTypes_t MessageHandler::GetMsgTypes() { 0x41000031, "文件" } }; } -std::optional MessageHandler::popMessage() +std::optional Handler::popMessage() { std::lock_guard lock(mutex_); if (msgQueue_.empty()) { @@ -182,7 +184,7 @@ std::optional MessageHandler::popMessage() return msg; } -int MessageHandler::EnableLog() +int Handler::EnableLog() { if (isLogging) return 1; @@ -196,7 +198,7 @@ int MessageHandler::EnableLog() return 0; } -int MessageHandler::DisableLog() +int Handler::DisableLog() { if (!isLogging) return 1; if (MH_DisableHook(funcWxLog) != MH_OK) return -1; @@ -205,7 +207,7 @@ int MessageHandler::DisableLog() return 0; } -int MessageHandler::ListenMsg() +int Handler::ListenMsg() { if (isListeningMsg) return 1; @@ -218,7 +220,7 @@ int MessageHandler::ListenMsg() return 0; } -int MessageHandler::UnListenMsg() +int Handler::UnListenMsg() { if (!isListeningMsg) return 1; if (MH_DisableHook(funcRecvMsg) != MH_OK) return -1; @@ -227,7 +229,7 @@ int MessageHandler::UnListenMsg() return 0; } -int MessageHandler::ListenPyq() +int Handler::ListenPyq() { if (isListeningPyq) return 1; @@ -240,7 +242,7 @@ int MessageHandler::ListenPyq() return 0; } -int MessageHandler::UnListenPyq() +int Handler::UnListenPyq() { if (!isListeningPyq) return 1; if (MH_DisableHook(funcRecvPyq) != MH_OK) return -1; @@ -249,7 +251,7 @@ int MessageHandler::UnListenPyq() return 0; } -MH_STATUS MessageHandler::InitializeHook() +MH_STATUS Handler::InitializeHook() { if (isMH_Initialized) return MH_OK; MH_STATUS status = MH_Initialize(); @@ -257,10 +259,11 @@ MH_STATUS MessageHandler::InitializeHook() return status; } -MH_STATUS MessageHandler::UninitializeHook() +MH_STATUS Handler::UninitializeHook() { if (!isMH_Initialized || isLogging || isListeningMsg || isListeningPyq) return MH_OK; MH_STATUS status = MH_Uninitialize(); if (status == MH_OK) isMH_Initialized = false; return status; } +} \ No newline at end of file diff --git a/WeChatFerry/spy/message_handler.h b/WeChatFerry/spy/message_handler.h index a2559ef..1fb06ee 100644 --- a/WeChatFerry/spy/message_handler.h +++ b/WeChatFerry/spy/message_handler.h @@ -11,10 +11,12 @@ #include "pb_types.h" #include "spy_types.h" -class MessageHandler +namespace message { + +class Handler { public: - static MessageHandler &getInstance(); + static Handler &getInstance(); // 0: 成功, -1: 失败, 1: 已经开启 int EnableLog(); @@ -35,8 +37,8 @@ public: std::mutex &getMutex() { return mutex_; }; private: - MessageHandler(); - ~MessageHandler(); + Handler(); + ~Handler(); mutable std::mutex mutex_; std::condition_variable cv_; @@ -64,3 +66,5 @@ private: QWORD a10, QWORD a11, QWORD a12); static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3); }; + +} // namespace message diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 36b4d5f..0c79503 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -29,13 +29,16 @@ extern QWORD g_WeChatWinDllAddr; #define OS_XML_BUFSIGN 0x24F0D70 #define OS_SEND_XML 0x20CF360 -SendMsgManager &SendMsgManager::get_instance() +namespace message { - static SendMsgManager instance; + +Sender &Sender::get_instance() +{ + static Sender instance; return instance; } -SendMsgManager::SendMsgManager() +Sender::Sender() { func_new = reinterpret_cast(g_WeChatWinDllAddr + OS_NEW); func_free = reinterpret_cast(g_WeChatWinDllAddr + OS_FREE); @@ -50,16 +53,16 @@ SendMsgManager::SendMsgManager() func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_XML); } -std::unique_ptr SendMsgManager::new_wx_string(const char *str) +std::unique_ptr Sender::new_wx_string(const char *str) { return new_wx_string(str ? std::string(str) : std::string()); } -std::unique_ptr SendMsgManager::new_wx_string(const std::string &str) +std::unique_ptr Sender::new_wx_string(const std::string &str) { return std::make_unique(util::s2w(str)); } -std::vector SendMsgManager::parse_wxids(const string &wxids) +std::vector Sender::parse_wxids(const string &wxids) { vector wx_members; wstringstream wss(util::s2w(wxids)); @@ -70,7 +73,7 @@ std::vector SendMsgManager::parse_wxids(const string &wxids) return wx_members; } -void SendMsgManager::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) +void Sender::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) { auto wxWxid = new_wx_string(wxid); auto wxMsg = new_wx_string(msg); @@ -91,7 +94,7 @@ void SendMsgManager::send_text(const std::string &wxid, const std::string &msg, func_free(reinterpret_cast(&buffer)); } -void SendMsgManager::send_image(const std::string &wxid, const std::string &path) +void Sender::send_image(const std::string &wxid, const std::string &path) { auto wxWxid = new_wx_string(wxid); auto wxPath = new_wx_string(path); @@ -116,7 +119,7 @@ void SendMsgManager::send_image(const std::string &wxid, const std::string &path func_free(pMsgTmp); } -void SendMsgManager::send_file(const std::string &wxid, const std::string &path) +void Sender::send_file(const std::string &wxid, const std::string &path) { auto wxWxid = new_wx_string(wxid); auto wxPath = new_wx_string(path); @@ -135,8 +138,7 @@ void SendMsgManager::send_file(const std::string &wxid, const std::string &path) func_free(pMsg); } -void SendMsgManager::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, - uint64_t type) +void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type) { std::unique_ptr buff(new char[0x500]()); std::unique_ptr buff2(new char[0x500]()); @@ -151,7 +153,7 @@ void SendMsgManager::send_xml(const std::string &receiver, const std::string &xm auto wxReceiver = new_wx_string(receiver); auto wxXml = new_wx_string(xml); auto wxPath = new_wx_string(path); - auto wxSender = new_wx_string(user_info::get_self_wxid()); + auto wxSender = new_wx_string(userinfo::get_self_wxid()); func_send_xml(reinterpret_cast(buff.get()), reinterpret_cast(wxSender.get()), reinterpret_cast(wxReceiver.get()), reinterpret_cast(wxXml.get()), @@ -162,7 +164,7 @@ void SendMsgManager::send_xml(const std::string &receiver, const std::string &xm func_free(reinterpret_cast(buff2.get())); } -void SendMsgManager::send_emotion(const std::string &wxid, const std::string &path) +void Sender::send_emotion(const std::string &wxid, const std::string &path) { auto wxWxid = new_wx_string(wxid); auto wxPath = new_wx_string(path); @@ -180,7 +182,7 @@ void SendMsgManager::send_emotion(const std::string &wxid, const std::string &pa reinterpret_cast(buff.get())); } -int SendMsgManager::send_rich_text(const RichText &rt) +int Sender::send_rich_text(const RichText &rt) { #define SRTM_SIZE 0x3F0 QWORD status = -1; @@ -216,7 +218,7 @@ int SendMsgManager::send_rich_text(const RichText &rt) return static_cast(status); } -int SendMsgManager::send_pat(const std::string &roomid, const std::string &wxid) +int Sender::send_pat(const std::string &roomid, const std::string &wxid) { QWORD status = -1; @@ -228,12 +230,12 @@ int SendMsgManager::send_pat(const std::string &roomid, const std::string &wxid) return static_cast(status); } -int SendMsgManager::forward(QWORD msgid, const std::string &receiver) +int Sender::forward(QWORD msgid, const std::string &receiver) { uint32_t dbIdx = 0; QWORD localId = 0; - if (exec_sql::get_local_id_and_dbidx(msgid, &localId, &dbIdx) != 0) { + if (db::get_local_id_and_dbidx(msgid, &localId, &dbIdx) != 0) { LOG_ERROR("Failed to get localId, Please check id: {}", msgid); return -1; } @@ -247,7 +249,7 @@ int SendMsgManager::forward(QWORD msgid, const std::string &receiver) } // RPC 方法 -bool SendMsgManager::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len) +bool Sender::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (text.msg == nullptr || text.receiver == nullptr || strlen(text.msg) == 0 || strlen(text.receiver) == 0) { @@ -260,7 +262,7 @@ bool SendMsgManager::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *le }); } -bool SendMsgManager::rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { @@ -273,7 +275,7 @@ bool SendMsgManager::rpc_send_image(const std::string &path, const std::string & }); } -bool SendMsgManager::rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { @@ -286,7 +288,7 @@ bool SendMsgManager::rpc_send_file(const std::string &path, const std::string &r }); } -bool SendMsgManager::rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { @@ -299,7 +301,7 @@ bool SendMsgManager::rpc_send_emotion(const std::string &path, const std::string }); } -bool SendMsgManager::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) +bool Sender::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (xml.content == nullptr || xml.receiver == nullptr) { @@ -312,7 +314,7 @@ bool SendMsgManager::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) }); } -bool SendMsgManager::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len) +bool Sender::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (rt.receiver == nullptr) { @@ -324,7 +326,7 @@ bool SendMsgManager::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t }); } -bool SendMsgManager::rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len) +bool Sender::rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (roomid.empty() || wxid.empty()) { @@ -336,7 +338,7 @@ bool SendMsgManager::rpc_send_pat(const std::string &roomid, const std::string & }); } -bool SendMsgManager::rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { if (receiver.empty()) { @@ -347,3 +349,5 @@ bool SendMsgManager::rpc_forward(uint64_t msgid, const std::string &receiver, ui } }); } + +} \ No newline at end of file diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 66908d6..e528065 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -7,10 +7,13 @@ #include "spy_types.h" #include "wcf.pb.h" -class SendMsgManager +namespace message +{ + +class Sender { public: - static SendMsgManager &get_instance(); + static Sender &get_instance(); void send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids = ""); void send_image(const std::string &wxid, const std::string &path); @@ -32,11 +35,11 @@ public: bool rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len); private: - SendMsgManager(); - ~SendMsgManager() = default; + Sender(); + ~Sender() = default; - SendMsgManager(const SendMsgManager &) = delete; - SendMsgManager &operator=(const SendMsgManager &) = delete; + Sender(const Sender &) = delete; + Sender &operator=(const Sender &) = delete; using New_t = QWORD (*)(QWORD); using Free_t = QWORD (*)(QWORD); @@ -73,3 +76,5 @@ private: std::unique_ptr new_wx_string(const std::string &str); std::vector parse_wxids(const std::string &wxids); }; + +} \ No newline at end of file diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index f6f252f..6454c0b 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -172,7 +172,7 @@ static int GetNextPage(QWORD id) int RefreshPyq(QWORD id) { - auto &msgHandler = MessageHandler::getInstance(); + auto &msgHandler = message::Handler::getInstance(); if (!msgHandler.isPyqListening()) { LOG_ERROR("没有启动朋友圈消息接收,参考:enable_receiving_msg"); return -1; @@ -203,7 +203,7 @@ int DownloadAttach(QWORD id, string thumb, string extra) return 0; } - if (exec_sql::get_local_id_and_dbidx(id, &localId, &dbIdx) != 0) { + if (db::get_local_id_and_dbidx(id, &localId, &dbIdx) != 0) { LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id)); return status; } @@ -286,7 +286,7 @@ string GetAudio(QWORD id, string dir) return mp3path; } - vector silk = exec_sql::get_audio_data(id); + vector silk = db::get_audio_data(id); if (silk.size() == 0) { LOG_ERROR("Empty audio data."); return ""; @@ -306,7 +306,7 @@ string GetPCMAudio(uint64_t id, string dir, int32_t sr) return pcmpath; } vector pcm; - vector silk = exec_sql::get_audio_data(id); + vector silk = db::get_audio_data(id); if (silk.size() == 0) { LOG_ERROR("Empty audio data."); return ""; diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 96e71f7..dbbbf36 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -45,8 +45,8 @@ static HANDLE msgThread = NULL; static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 -auto &msgHandler = MessageHandler::getInstance(); -auto &sendMgr = SendMsgManager::get_instance(); +auto &msgHandler = message::Handler::getInstance(); +auto &sendMgr = message::Sender::get_instance(); static std::string BuildUrl(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } @@ -77,7 +77,7 @@ static bool func_is_login(uint8_t *out, size_t *len) static bool func_get_self_wxid(uint8_t *out, size_t *len) { return FillResponse(Response_str_tag, out, len, [](Response &rsp) { - std::string wxid = user_info::get_self_wxid(); + std::string wxid = userinfo::get_self_wxid(); rsp.msg.str = wxid.empty() ? nullptr : (char *)wxid.c_str(); }); } @@ -85,7 +85,7 @@ static bool func_get_self_wxid(uint8_t *out, size_t *len) static bool func_get_user_info(uint8_t *out, size_t *len) { return FillResponse(Response_ui_tag, out, len, [](Response &rsp) { - UserInfo_t ui = user_info::get_user_info(); + UserInfo_t ui = userinfo::get_user_info(); rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); rsp.msg.ui.name = (char *)ui.name.c_str(); rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); @@ -105,7 +105,7 @@ static bool func_get_msg_types(uint8_t *out, size_t *len) static bool func_get_contacts(uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [](Response &rsp) { - std::vector contacts = contact_mgmt::get_contacts(); + std::vector contacts = contact::get_contacts(); rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); @@ -114,7 +114,7 @@ static bool func_get_contacts(uint8_t *out, size_t *len) static bool func_get_db_names(uint8_t *out, size_t *len) { return FillResponse(Response_dbs_tag, out, len, [](Response &rsp) { - static DbNames_t dbnames = exec_sql::get_db_names(); + static DbNames_t dbnames = db::get_db_names(); rsp.msg.dbs.names.funcs.encode = encode_dbnames; rsp.msg.dbs.names.arg = &dbnames; }); @@ -123,7 +123,7 @@ static bool func_get_db_names(uint8_t *out, size_t *len) static bool func_get_db_tables(char *db, uint8_t *out, size_t *len) { return FillResponse(Response_tables_tag, out, len, [db](Response &rsp) { - static DbTables_t tables = exec_sql::get_db_tables(db); + static DbTables_t tables = db::get_db_tables(db); rsp.msg.tables.tables.funcs.encode = encode_tables; rsp.msg.tables.tables.arg = &tables; }); @@ -355,7 +355,7 @@ static bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) if ((db == nullptr) || (sql == nullptr)) { LOG_ERROR("Empty db or sql."); } else { - rows = exec_sql::exec_db_query(db, sql); + rows = db::exec_db_query(db, sql); } rsp.msg.rows.rows.arg = &rows; rsp.msg.rows.rows.funcs.encode = encode_rows; @@ -410,7 +410,7 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, LOG_ERROR("Empty V3 or V4."); rsp.msg.status = -1; } else { - rsp.msg.status = contact_mgmt::accept_new_friend(v3, v4, scene); + rsp.msg.status = contact::accept_new_friend(v3, v4, scene); } }); } @@ -418,7 +418,7 @@ static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, static bool func_get_contact_info(std::string wxid, uint8_t *out, size_t *len) { return FillResponse(Response_contacts_tag, out, len, [wxid](Response &rsp) { - std::vector contacts = { contact_mgmt::get_contact_by_wxid(wxid) }; + std::vector contacts = { contact::get_contact_by_wxid(wxid) }; rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); @@ -458,7 +458,7 @@ static bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_ LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = chatroom_mgmt::add_chatroom_member(roomid, wxids); + rsp.msg.status = chatroom::add_chatroom_member(roomid, wxids); } }); } @@ -470,7 +470,7 @@ static bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_ LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = chatroom_mgmt::del_chatroom_member(roomid, wxids); + rsp.msg.status = chatroom::del_chatroom_member(roomid, wxids); } }); } @@ -482,7 +482,7 @@ static bool func_invite_room_members(char *roomid, char *wxids, uint8_t *out, si LOG_ERROR("Empty roomid or wxids."); rsp.msg.status = -1; } else { - rsp.msg.status = chatroom_mgmt::invite_chatroom_member(roomid, wxids); + rsp.msg.status = chatroom::invite_chatroom_member(roomid, wxids); } }); } diff --git a/WeChatFerry/spy/userinfo_manager.cpp b/WeChatFerry/spy/userinfo_manager.cpp index b8720ef..eddf0d5 100644 --- a/WeChatFerry/spy/userinfo_manager.cpp +++ b/WeChatFerry/spy/userinfo_manager.cpp @@ -9,7 +9,7 @@ extern UINT64 g_WeChatWinDllAddr; -namespace user_info +namespace userinfo { #define OS_USER_HOME 0x5932770 #define OS_USER_WXID 0x595C270 @@ -85,4 +85,4 @@ bool rpc_get_user_info(uint8_t *out, size_t *len) }); } -} // namespace user_info +} // namespace userinfo diff --git a/WeChatFerry/spy/userinfo_manager.h b/WeChatFerry/spy/userinfo_manager.h index f248ba7..6868817 100644 --- a/WeChatFerry/spy/userinfo_manager.h +++ b/WeChatFerry/spy/userinfo_manager.h @@ -5,7 +5,7 @@ #include "pb_types.h" -namespace user_info +namespace userinfo { // 获取 WeChat 数据存储路径 @@ -21,4 +21,4 @@ UserInfo_t get_user_info(); bool rpc_get_self_wxid(uint8_t *out, size_t *len); bool rpc_get_user_info(uint8_t *out, size_t *len); -} // namespace user_info +} // namespace userinfo From d4707997ba2a82c8ef67d3da996df9529af0719c Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Feb 2025 23:07:46 +0800 Subject: [PATCH 037/132] Refacoring --- WeChatFerry/spy/misc_manager.cpp | 239 ++++++++++++------------------- WeChatFerry/spy/misc_manager.h | 44 ++++-- WeChatFerry/spy/rpc_server.cpp | 24 ++-- 3 files changed, 139 insertions(+), 168 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 6454c0b..d6b9afe 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -18,6 +18,8 @@ namespace fs = std::filesystem; extern QWORD g_WeChatWinDllAddr; +namespace misc +{ #define HEADER_PNG1 0x89 #define HEADER_PNG2 0x50 #define HEADER_JPG1 0xFF @@ -38,117 +40,73 @@ extern QWORD g_WeChatWinDllAddr; #define OS_PUSH_ATTACH_TASK 0x1CDF4E0 #define OS_LOGIN_QR_CODE 0x59620D8 -typedef QWORD (*GetSNSDataMgr_t)(); -typedef QWORD (*GetSnsTimeLineMgr_t)(); -typedef QWORD (*GetSNSFirstPage_t)(QWORD, QWORD, QWORD); -typedef QWORD (*GetSNSNextPageScene_t)(QWORD, QWORD); -typedef QWORD (*GetChatMgr_t)(); -typedef QWORD (*NewChatMsg_t)(QWORD); -typedef QWORD (*FreeChatMsg_t)(QWORD); -typedef QWORD (*GetPreDownLoadMgr_t)(); -typedef QWORD (*GetMgrByPrefixLocalId_t)(QWORD, QWORD); -typedef QWORD (*PushAttachTask_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*GetOCRManager_t)(); -typedef QWORD (*DoOCRTask_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); +using get_sns_data_mgr_t = QWORD (*)(); +using get_sns_timeline_mgr_t = QWORD (*)(); +using get_sns_first_page_t = QWORD (*)(QWORD, QWORD, QWORD); +using get_sns_next_page_scene_t = QWORD (*)(QWORD, QWORD); +using get_chat_mgr_t = QWORD (*)(); +using new_chat_msg_t = QWORD (*)(QWORD); +using free_chat_msg_t = QWORD (*)(QWORD); +using get_pre_download_mgr_t = QWORD (*)(); +using get_mgr_by_prefix_localid_t = QWORD (*)(QWORD, QWORD); +using push_attach_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); +using get_ocr_manager_t = QWORD (*)(); +using do_ocr_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -int IsLogin(void) { return (int)util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS); } +bool is_logged_in() { return util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS) != 0; } -static string get_key(uint8_t header1, uint8_t header2, uint8_t *key) +static std::string detect_image_extension(uint8_t header1, uint8_t header2, uint8_t &key) { - // PNG? - *key = HEADER_PNG1 ^ header1; - if ((HEADER_PNG2 ^ *key) == header2) { - return ".png"; - } - - // JPG? - *key = HEADER_JPG1 ^ header1; - if ((HEADER_JPG2 ^ *key) == header2) { - return ".jpg"; - } - - // GIF? - *key = HEADER_GIF1 ^ header1; - if ((HEADER_GIF2 ^ *key) == header2) { - return ".gif"; - } - - return ""; // 错误 + if ((key = HEADER_PNG1 ^ header1) && (HEADER_PNG2 ^ key) == header2) return ".png"; + if ((key = HEADER_JPG1 ^ header1) && (HEADER_JPG2 ^ key) == header2) return ".jpg"; + if ((key = HEADER_GIF1 ^ header1) && (HEADER_GIF2 ^ key) == header2) return ".gif"; + return ""; } -string DecryptImage(string src, string dir) +std::string decrypt_image(const fs::path &src, const fs::path &dst_dir) { if (!fs::exists(src)) { - LOG_ERROR("File not exists: {}", src); + LOG_ERROR("文件不存在: {}", src.string()); return ""; } - ifstream in(src.c_str(), ios::binary); - if (!in.is_open()) { - LOG_ERROR("Failed to read file {}", src); + std::ifstream in(src, std::ios::binary); + if (!in) { + LOG_ERROR("无法打开文件: {}", src.string()); return ""; } - filebuf *pfb = in.rdbuf(); - size_t size = pfb->pubseekoff(0, ios::end, ios::in); - pfb->pubseekpos(0, ios::in); - - vector buff; - buff.reserve(size); - char *pBuf = buff.data(); - pfb->sgetn(pBuf, size); - in.close(); + std::vector buffer(std::istreambuf_iterator(in), {}); + if (buffer.size() < 2) return ""; uint8_t key = 0x00; - string ext = get_key(pBuf[0], pBuf[1], &key); - if (ext.empty()) { - LOG_ERROR("Failed to get key."); + auto ext = detect_image_extension(buffer[0], buffer[1], key); + if (!ext) { + LOG_ERROR("无法检测文件类型."); return ""; } - for (size_t i = 0; i < size; i++) { - pBuf[i] ^= key; - } + std::for_each(buffer.begin(), buffer.end(), [key](char &c) { c ^= key; }); - string dst = ""; + fs::path dst_path = dst_dir / (src.stem().string() + *ext); + if (!fs::exists(dst_dir)) fs::create_directories(dst_dir); - if (dir.empty()) { - dst = fs::path(src).replace_extension(ext).string(); - } else { - dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - - // 判断dir文件夹是否存在,若不存在则创建(否则将无法创建出文件) - if (!fs::exists(dst)) { // 判断该文件夹是否存在 - bool success = fs::create_directories(dst); // Windows创建文件夹 - if (!success) { // 创建失败 - LOG_ERROR("Failed to mkdir:{}", dst); - return ""; - } - } - - dst += fs::path(src).stem().string() + ext; - } - - replace(dst.begin(), dst.end(), '\\', '/'); - - ofstream out(dst.c_str(), ios::binary); - if (!out.is_open()) { - LOG_ERROR("Failed to write file {}", dst); + std::ofstream out(dst_path, std::ios::binary); + if (!out) { + LOG_ERROR("写入文件失败: {}", dst_path.string()); return ""; } - out.write(pBuf, size); - out.close(); - - return dst; + out.write(buffer.data(), buffer.size()); + return dst_path.string(); } -static int GetFirstPage() +static int get_first_page() { int status = -1; - GetSNSDataMgr_t GetSNSDataMgr = (GetSNSDataMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR); - GetSNSFirstPage_t GetSNSFirstPage = (GetSNSFirstPage_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE); + get_sns_data_mgr_t GetSNSDataMgr = (get_sns_data_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR); + get_sns_first_page_t GetSNSFirstPage = (get_sns_first_page_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE); QWORD buff[16] = { 0 }; QWORD mgr = GetSNSDataMgr(); @@ -157,12 +115,13 @@ static int GetFirstPage() return status; } -static int GetNextPage(QWORD id) +static int get_next_page(QWORD id) { int status = -1; - GetSnsTimeLineMgr_t GetSnsTimeLineMgr = (GetSnsTimeLineMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR); - GetSNSNextPageScene_t GetSNSNextPageScene = (GetSNSNextPageScene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE); + get_sns_timeline_mgr_t GetSnsTimeLineMgr = (get_sns_timeline_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR); + get_sns_next_page_scene_t GetSNSNextPageScene + = (get_sns_next_page_scene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE); QWORD mgr = GetSnsTimeLineMgr(); status = (int)GetSNSNextPageScene(mgr, id); @@ -170,7 +129,7 @@ static int GetNextPage(QWORD id) return status; } -int RefreshPyq(QWORD id) +int refresh_pyq(uint64_t id) { auto &msgHandler = message::Handler::getInstance(); if (!msgHandler.isPyqListening()) { @@ -179,10 +138,10 @@ int RefreshPyq(QWORD id) } if (id == 0) { - return GetFirstPage(); + return get_first_page(); } - return GetNextPage(id); + return get_next_page(id); } /******************************************************************************* @@ -193,7 +152,7 @@ int RefreshPyq(QWORD id) * thumb:图片或者视频的缩略图路径;如果是视频,后缀为 mp4 后就是存在路径了 * extra:图片、文件的路径 *******************************************************************************/ -int DownloadAttach(QWORD id, string thumb, string extra) +int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extra) { int status = -1; QWORD localId; @@ -208,13 +167,13 @@ int DownloadAttach(QWORD id, string thumb, string extra) return status; } - NewChatMsg_t NewChatMsg = (NewChatMsg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG); - FreeChatMsg_t FreeChatMsg = (FreeChatMsg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG); - GetChatMgr_t GetChatMgr = (GetChatMgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR); - GetPreDownLoadMgr_t GetPreDownLoadMgr = (GetPreDownLoadMgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR); - PushAttachTask_t PushAttachTask = (PushAttachTask_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK); - GetMgrByPrefixLocalId_t GetMgrByPrefixLocalId - = (GetMgrByPrefixLocalId_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID); + new_chat_msg_t NewChatMsg = (new_chat_msg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG); + free_chat_msg_t FreeChatMsg = (free_chat_msg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG); + get_chat_mgr_t GetChatMgr = (get_chat_mgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR); + get_pre_download_mgr_t GetPreDownLoadMgr = (get_pre_download_mgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR); + push_attach_task_t PushAttachTask = (push_attach_task_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK); + get_mgr_by_prefix_localid_t GetMgrByPrefixLocalId + = (get_mgr_by_prefix_localid_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID); LARGE_INTEGER l; l.HighPart = dbIdx; @@ -260,7 +219,7 @@ int DownloadAttach(QWORD id, string thumb, string extra) LOG_DEBUG("path: {}", save_path); // 创建父目录,由于路径来源于微信,不做检查 - fs::create_directory(fs::path(save_path).parent_path().string()); + fs::create_directory(fs::path(save_path).parent_path()); int temp = 1; auto wx_save_path = util::new_wx_string(save_path); @@ -277,57 +236,50 @@ int DownloadAttach(QWORD id, string thumb, string extra) return status; } -string GetAudio(QWORD id, string dir) +std::string get_audio(uint64_t id, const fs::path &dir) { - string mp3path = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - mp3path += to_string(id) + ".mp3"; - replace(mp3path.begin(), mp3path.end(), '\\', '/'); - if (fs::exists(mp3path)) { // 不重复下载 - return mp3path; - } + if (!fs::exists(dir)) fs::create_directories(dir); - vector silk = db::get_audio_data(id); - if (silk.size() == 0) { + fs::path mp3path = dir / (std::to_string(id) + ".mp3"); + if (fs::exists(mp3path)) return mp3path.string(); + + auto silk = db::get_audio_data(id); + if (silk.empty()) { LOG_ERROR("Empty audio data."); return ""; } - Silk2Mp3(silk, mp3path, 24000); - - return mp3path; + Silk2Mp3(silk, mp3path.string(), 24000); + return mp3path.string(); } -string GetPCMAudio(uint64_t id, string dir, int32_t sr) +std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) { - string pcmpath = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - pcmpath += to_string(id) + ".pcm"; - replace(pcmpath.begin(), pcmpath.end(), '\\', '/'); - if (fs::exists(pcmpath)) { // 不重复下载 - return pcmpath; - } - vector pcm; - vector silk = db::get_audio_data(id); - if (silk.size() == 0) { + if (!fs::exists(dir)) fs::create_directories(dir); + + fs::path pcmpath = dir / (std::to_string(id) + ".pcm"); + if (fs::exists(pcmpath)) return pcmpath.string(); + + auto silk = db::get_audio_data(id); + if (silk.empty()) { LOG_ERROR("Empty audio data."); - return ""; + return std::nullopt; } + std::vector pcm; SilkDecode(silk, pcm, sr); - errno_t err; - FILE *fPCM; - err = fopen_s(&fPCM, pcmpath.c_str(), "wb"); - if (err != 0) { - printf("Error: could not open input file %s\n", pcmpath.c_str()); - exit(0); + + std::ofstream out(pcmpath, std::ios::binary); + if (!out) { + LOG_ERROR("Failed to write file: {}", pcmpath.string()); + return std::nullopt; } - fwrite(pcm.data(), sizeof(uint8_t), pcm.size(), fPCM); - fclose(fPCM); - - return pcmpath; + out.write(reinterpret_cast(pcm.data()), pcm.size()); + return pcmpath.string(); } -OcrResult_t GetOcrResult(string path) +OcrResult_t get_ocr_result(const std::filesystem::path &path) { OcrResult_t ret = { -1, "" }; #if 0 // 参数没调好,会抛异常,看看有没有好心人来修复 @@ -336,8 +288,8 @@ OcrResult_t GetOcrResult(string path) return ret; } - GetOCRManager_t GetOCRManager = (GetOCRManager_t)(g_WeChatWinDllAddr + 0x1D6C3C0); - DoOCRTask_t DoOCRTask = (DoOCRTask_t)(g_WeChatWinDllAddr + 0x2D10BC0); + get_ocr_manager_t GetOCRManager = (get_ocr_manager_t)(g_WeChatWinDllAddr + 0x1D6C3C0); + do_ocr_task_t DoOCRTask = (do_ocr_task_t)(g_WeChatWinDllAddr + 0x2D10BC0); QWORD unk1 = 0, unk2 = 0, unused = 0; QWORD *pUnk1 = &unk1; @@ -368,7 +320,7 @@ OcrResult_t GetOcrResult(string path) return ret; } -int RevokeMsg(QWORD id) +int revoke_message(uint64_t id) { int status = -1; #if 0 // 这个挺鸡肋的,因为自己发的消息没法直接获得 msgid,就这样吧 @@ -382,23 +334,20 @@ int RevokeMsg(QWORD id) return status; } -string GetLoginUrl() +std::string get_login_url() { LPVOID targetAddress = reinterpret_cast(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE; - - char *dataPtr = *reinterpret_cast(targetAddress); // 读取指针内容 + char *dataPtr = *reinterpret_cast(targetAddress); if (!dataPtr) { - LOG_ERROR("Failed to get login url"); - return "error"; + LOG_ERROR("Failed to get login URL."); + return ""; } - - // 读取字符串内容 - std::string data(dataPtr, 22); - return "http://weixin.qq.com/x/" + data; + return "http://weixin.qq.com/x/" + std::string(dataPtr, 22); } -int ReceiveTransfer(string wxid, string transferid, string transactionid) +int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid) { // 别想了,这个不实现了 return -1; } +} // namespace misc \ No newline at end of file diff --git a/WeChatFerry/spy/misc_manager.h b/WeChatFerry/spy/misc_manager.h index c25f076..b3e58ba 100644 --- a/WeChatFerry/spy/misc_manager.h +++ b/WeChatFerry/spy/misc_manager.h @@ -1,17 +1,39 @@ #pragma once -#include "stdint.h" +#include +#include +#include #include +#include #include "pb_types.h" -int IsLogin(void); -std::string GetAudio(uint64_t id, std::string dir); -std::string GetPCMAudio(uint64_t id, std::string dir, int32_t sr); -std::string DecryptImage(std::string src, std::string dst); -int RefreshPyq(uint64_t id); -int DownloadAttach(uint64_t id, std::string thumb, std::string extra); -int RevokeMsg(uint64_t id); -OcrResult_t GetOcrResult(std::string path); -std::string GetLoginUrl(); -int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid); +namespace misc +{ + +bool is_logged_in(); + +std::string get_audio(uint64_t id, const std::filesystem::path &dir); +std::string get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr); +std::string decrypt_image(const std::filesystem::path &src, const std::filesystem::path &dst); +std::string get_login_url(); + +int refresh_pyq(uint64_t id); +int download_attachment(uint64_t id, const std::filesystem::path &thumb, const std::filesystem::path &extra); +int revoke_message(uint64_t id); + +OcrResult_t get_ocr_result(const std::filesystem::path &path); +int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid); + +// RPC +bool rpc_is_logged_in(uint8_t *out, size_t *len); +bool rpc_get_audio(uint64_t id, const std::filesystem::path &dir, uint8_t *out, size_t *len); +bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len); +bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len); +bool rpc_get_login_url(uint8_t *out, size_t *len); +bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len); +bool rpc_download_attachment(uint64_t id, const std::filesystem::path &thumb, const std::filesystem::path &extra, uint8_t *out, size_t *len); +bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len); +bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len); +bool rpc_receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid, uint8_t *out, size_t *len); +} // namespace misc diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index dbbbf36..b23b27c 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -23,12 +23,12 @@ #include "chatroom_manager.h" #include "contact_manager.h" #include "database_executor.h" -#include "misc_manager.h" #include "log.hpp" -#include "pb_types.h" -#include "pb_util.h" #include "message_handler.h" #include "message_sender.h" +#include "misc_manager.h" +#include "pb_types.h" +#include "pb_util.h" #include "spy.h" #include "spy_types.h" #include "userinfo_manager.h" @@ -71,7 +71,7 @@ static bool FillResponse(int which_msg, uint8_t *out, size_t *len, AssignFunc as static bool func_is_login(uint8_t *out, size_t *len) { return FillResponse(Response_status_tag, out, len, - [](Response &rsp) { rsp.msg.status = IsLogin(); }); + [](Response &rsp) { rsp.msg.status = misc::is_logged_in(); }); } static bool func_get_self_wxid(uint8_t *out, size_t *len) @@ -132,7 +132,7 @@ static bool func_get_db_tables(char *db, uint8_t *out, size_t *len) static bool func_get_audio_msg(uint64_t id, char *dir, uint8_t *out, size_t *len) { return FillResponse(Response_str_tag, out, len, [id, dir](Response &rsp) { - std::string path = (dir == nullptr) ? "" : GetAudio(id, dir); + std::string path = (dir == nullptr) ? "" : misc::get_audio(id, dir); rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); }); } @@ -365,7 +365,7 @@ static bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) static bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) { return FillResponse(Response_status_tag, out, len, - [id](Response &rsp) { rsp.msg.status = RefreshPyq(id); }); + [id](Response &rsp) { rsp.msg.status = misc::refresh_pyq(id); }); } static bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len) @@ -373,20 +373,20 @@ static bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len) return FillResponse(Response_status_tag, out, len, [att](Response &rsp) { std::string thumb = att.thumb ? att.thumb : ""; std::string extra = att.extra ? att.extra : ""; - rsp.msg.status = DownloadAttach(att.id, thumb, extra); + rsp.msg.status = misc::download_attachment(att.id, thumb, extra); }); } static bool func_revoke_msg(uint64_t id, uint8_t *out, size_t *len) { return FillResponse(Response_status_tag, out, len, - [id](Response &rsp) { rsp.msg.status = RevokeMsg(id); }); + [id](Response &rsp) { rsp.msg.status = misc::revoke_message(id); }); } static bool func_refresh_qrcode(uint8_t *out, size_t *len) { return FillResponse(Response_str_tag, out, len, [](Response &rsp) { - std::string url = GetLoginUrl(); + std::string url = misc::get_login_url(); rsp.msg.str = url.empty() ? nullptr : (char *)url.c_str(); }); } @@ -398,7 +398,7 @@ static bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *o LOG_ERROR("Empty wxid, tfid, or taid."); rsp.msg.status = -1; } else { - rsp.msg.status = ReceiveTransfer(wxid, tfid, taid); + rsp.msg.status = misc::receive_transfer(wxid, tfid, taid); } }); } @@ -431,7 +431,7 @@ static bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len) if ((dec.src == nullptr) || (dec.dst == nullptr)) { LOG_ERROR("Empty src or dst."); } else { - path = DecryptImage(dec.src, dec.dst); + path = misc::decrypt_image(dec.src, dec.dst); } rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); }); @@ -444,7 +444,7 @@ static bool func_exec_ocr(char *path, uint8_t *out, size_t *len) if (path == nullptr) { LOG_ERROR("Empty path."); } else { - ret = GetOcrResult(path); + ret = misc::get_ocr_result(path); } rsp.msg.ocr.status = ret.status; rsp.msg.ocr.result = ret.result.empty() ? nullptr : (char *)ret.result.c_str(); From 730f51344cc706075560a96d3836be263f774d68 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 6 Feb 2025 07:02:19 +0800 Subject: [PATCH 038/132] Refactoring --- WeChatFerry/spy/misc_manager.cpp | 37 ++++++++++++++++---------------- WeChatFerry/spy/misc_manager.h | 4 ++++ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index d6b9afe..4cf414f 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -81,14 +81,14 @@ std::string decrypt_image(const fs::path &src, const fs::path &dst_dir) uint8_t key = 0x00; auto ext = detect_image_extension(buffer[0], buffer[1], key); - if (!ext) { + if (!ext.empty()) { LOG_ERROR("无法检测文件类型."); return ""; } std::for_each(buffer.begin(), buffer.end(), [key](char &c) { c ^= key; }); - fs::path dst_path = dst_dir / (src.stem().string() + *ext); + fs::path dst_path = dst_dir / (src.stem().string() + ext); if (!fs::exists(dst_dir)) fs::create_directories(dst_dir); std::ofstream out(dst_path, std::ios::binary); @@ -163,7 +163,7 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr } if (db::get_local_id_and_dbidx(id, &localId, &dbIdx) != 0) { - LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id)); + LOG_ERROR("获取 localId 失败, 请检查消息 id: {} 是否正确", to_string(id)); return status; } @@ -181,7 +181,7 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460); if (buff == nullptr) { - LOG_ERROR("Failed to allocate memory."); + LOG_ERROR("申请内存失败."); return status; } @@ -191,9 +191,7 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr QWORD type = util::get_qword(reinterpret_cast(buff) + 0x38); - string save_path = ""; - string thumb_path = ""; - + fs::path save_path, thumb_path; switch (type) { case 0x03: { // Image: extra save_path = extra; @@ -210,20 +208,21 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr break; } default: - break; + LOG_ERROR("不支持的文件类型: {}", type); + return -2; } if (fs::exists(save_path)) { // 不重复下载。TODO: 通过文件大小来判断 return 0; } - LOG_DEBUG("path: {}", save_path); + LOG_DEBUG("保存路径: {}", save_path.string()); // 创建父目录,由于路径来源于微信,不做检查 - fs::create_directory(fs::path(save_path).parent_path()); + fs::create_directory(save_path.parent_path()); int temp = 1; - auto wx_save_path = util::new_wx_string(save_path); - auto wx_thumb_path = util::new_wx_string(thumb_path); + auto wx_save_path = util::new_wx_string(save_path.string()); + auto wx_thumb_path = util::new_wx_string(thumb_path.string()); memcpy(&buff[0x280], wx_thumb_path.get(), sizeof(WxString)); memcpy(&buff[0x2A0], wx_save_path.get(), sizeof(WxString)); @@ -245,7 +244,7 @@ std::string get_audio(uint64_t id, const fs::path &dir) auto silk = db::get_audio_data(id); if (silk.empty()) { - LOG_ERROR("Empty audio data."); + LOG_ERROR("没有获取到语音数据."); return ""; } @@ -262,8 +261,8 @@ std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) auto silk = db::get_audio_data(id); if (silk.empty()) { - LOG_ERROR("Empty audio data."); - return std::nullopt; + LOG_ERROR("没有获取到语音数据."); + return ""; } std::vector pcm; @@ -271,8 +270,8 @@ std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) std::ofstream out(pcmpath, std::ios::binary); if (!out) { - LOG_ERROR("Failed to write file: {}", pcmpath.string()); - return std::nullopt; + LOG_ERROR("创建文件失败: {}", pcmpath.string()); + return ""; } out.write(reinterpret_cast(pcm.data()), pcm.size()); @@ -339,7 +338,7 @@ std::string get_login_url() LPVOID targetAddress = reinterpret_cast(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE; char *dataPtr = *reinterpret_cast(targetAddress); if (!dataPtr) { - LOG_ERROR("Failed to get login URL."); + LOG_ERROR("获取二维码失败."); return ""; } return "http://weixin.qq.com/x/" + std::string(dataPtr, 22); @@ -350,4 +349,4 @@ int receive_transfer(const std::string &wxid, const std::string &transferid, con // 别想了,这个不实现了 return -1; } -} // namespace misc \ No newline at end of file +} // namespace misc diff --git a/WeChatFerry/spy/misc_manager.h b/WeChatFerry/spy/misc_manager.h index b3e58ba..378dc42 100644 --- a/WeChatFerry/spy/misc_manager.h +++ b/WeChatFerry/spy/misc_manager.h @@ -6,6 +6,8 @@ #include #include +#include "wcf.pb.h" + #include "pb_types.h" namespace misc @@ -26,6 +28,7 @@ OcrResult_t get_ocr_result(const std::filesystem::path &path); int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid); // RPC +// clang-format off bool rpc_is_logged_in(uint8_t *out, size_t *len); bool rpc_get_audio(uint64_t id, const std::filesystem::path &dir, uint8_t *out, size_t *len); bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len); @@ -36,4 +39,5 @@ bool rpc_download_attachment(uint64_t id, const std::filesystem::path &thumb, co bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len); bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len); bool rpc_receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid, uint8_t *out, size_t *len); +// clang-format on } // namespace misc From 8653f6f521733a836abb029f416a7cea4890f5c3 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 6 Feb 2025 07:44:52 +0800 Subject: [PATCH 039/132] Refactoring --- WeChatFerry/spy/rpc_helper.h | 41 +++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/rpc_helper.h b/WeChatFerry/spy/rpc_helper.h index f4e19c0..8aa76fc 100644 --- a/WeChatFerry/spy/rpc_helper.h +++ b/WeChatFerry/spy/rpc_helper.h @@ -9,7 +9,9 @@ #include "pb_encode.h" #include "pb_types.h" -static const std::unordered_map rpc_function_map +using FunctionHandler = std::function; + +static const std::unordered_map rpc_tag_map = { { Functions_FUNC_IS_LOGIN, Response_status_tag }, { Functions_FUNC_GET_SELF_WXID, Response_str_tag }, { Functions_FUNC_GET_USER_INFO, Response_ui_tag }, @@ -39,13 +41,46 @@ static const std::unordered_map rpc_function_map { Functions_FUNC_DEL_ROOM_MEMBERS, Response_status_tag }, { Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag } }; +const std::unordered_map rpc_function_map = { + { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, + // clang-format off + // clang-format on +}; + template bool fill_response(uint8_t *out, size_t *len, AssignFunc assign) { Response rsp = Response_init_default; rsp.func = FuncType; - auto it = rpc_function_map.find(FuncType); - if (it == rpc_function_map.end()) { + auto it = rpc_tag_map.find(FuncType); + if (it == rpc_tag_map.end()) { LOG_ERROR("Unknown function type: {}", magic_enum::enum_name(rsp.func)); return false; } From 2e240c3731c6f129f36d14ce77d996abfdee4d19 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 6 Feb 2025 08:05:35 +0800 Subject: [PATCH 040/132] Refactoring --- WeChatFerry/spy/message_handler.cpp | 41 ++ WeChatFerry/spy/message_handler.h | 7 +- WeChatFerry/spy/rpc_helper.h | 41 +- WeChatFerry/spy/rpc_server.cpp | 568 +++------------------------- 4 files changed, 106 insertions(+), 551 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 849382e..c1070e8 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -7,6 +7,7 @@ #include "framework.h" #include "log.hpp" +#include "rpc_helper.h" #include "userinfo_manager.h" #include "util.h" @@ -266,4 +267,44 @@ MH_STATUS Handler::UninitializeHook() if (status == MH_OK) isMH_Initialized = false; return status; } + +bool Handler::rpc_get_msg_types() +{ + return fill_response(out, len, [&](Response &rsp) { + MsgTypes_t types = GetMsgTypes(); + rsp.msg.types.types.funcs.encode = encode_types; + rsp.msg.types.types.arg = &types; + }); +} + +bool rpc_enable_recv_msg(void *cb, bool pyq, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [cb, pyq](Response &rsp) { + auto &handler = Handler::getInstance(); + rsp.msg.status = handler.ListenMsg(); + if (rsp.msg.status == 0) { + if (pyq) { + msgHandler.ListenPyq(); + } + msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)cb, nullptr, 0, nullptr); + if (msgThread == nullptr) { + rsp.msg.status = GetLastError(); + LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); + } + } + }); +} +bool Handler::rpc_disable_recv_msg(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { + rsp.msg.status = UnListenMsg(); + if (rsp.msg.status == 0) { + UnListenPyq(); + if (msgThread != nullptr) { + TerminateThread(msgThread, 0); + msgThread = nullptr; + } + } + }); +} } \ No newline at end of file diff --git a/WeChatFerry/spy/message_handler.h b/WeChatFerry/spy/message_handler.h index 1fb06ee..c9e0898 100644 --- a/WeChatFerry/spy/message_handler.h +++ b/WeChatFerry/spy/message_handler.h @@ -11,7 +11,8 @@ #include "pb_types.h" #include "spy_types.h" -namespace message { +namespace message +{ class Handler { @@ -36,6 +37,10 @@ public: std::condition_variable &getConditionVariable() { return cv_; }; std::mutex &getMutex() { return mutex_; }; + bool rpc_get_msg_types(); + bool rpc_enable_recv_msg(void *cb, bool pyq, uint8_t *out, size_t *len); + bool rpc_disable_recv_msg(uint8_t *out, size_t *len); + private: Handler(); ~Handler(); diff --git a/WeChatFerry/spy/rpc_helper.h b/WeChatFerry/spy/rpc_helper.h index 8aa76fc..963bcf7 100644 --- a/WeChatFerry/spy/rpc_helper.h +++ b/WeChatFerry/spy/rpc_helper.h @@ -1,16 +1,15 @@ #pragma once -#include #include +#include + #include "wcf.pb.h" #include "log.hpp" #include "pb_encode.h" #include "pb_types.h" -using FunctionHandler = std::function; - static const std::unordered_map rpc_tag_map = { { Functions_FUNC_IS_LOGIN, Response_status_tag }, { Functions_FUNC_GET_SELF_WXID, Response_str_tag }, @@ -23,6 +22,7 @@ static const std::unordered_map rpc_tag_map { Functions_FUNC_SEND_TXT, Response_status_tag }, { Functions_FUNC_SEND_IMG, Response_status_tag }, { Functions_FUNC_SEND_FILE, Response_status_tag }, + { Functions_FUNC_SEND_XML, Response_status_tag }, { Functions_FUNC_SEND_RICH_TXT, Response_status_tag }, { Functions_FUNC_SEND_PAT_MSG, Response_status_tag }, { Functions_FUNC_FORWARD_MSG, Response_status_tag }, @@ -32,6 +32,8 @@ static const std::unordered_map rpc_tag_map { Functions_FUNC_EXEC_DB_QUERY, Response_rows_tag }, { Functions_FUNC_REFRESH_PYQ, Response_status_tag }, { Functions_FUNC_DOWNLOAD_ATTACH, Response_status_tag }, + { Functions_FUNC_GET_CONTACT_INFO, Response_contacts_tag }, + { Functions_FUNC_ACCEPT_FRIEND, Response_status_tag }, { Functions_FUNC_RECV_TRANSFER, Response_status_tag }, { Functions_FUNC_REVOKE_MSG, Response_status_tag }, { Functions_FUNC_REFRESH_QRCODE, Response_str_tag }, @@ -41,39 +43,6 @@ static const std::unordered_map rpc_tag_map { Functions_FUNC_DEL_ROOM_MEMBERS, Response_status_tag }, { Functions_FUNC_INV_ROOM_MEMBERS, Response_status_tag } }; -const std::unordered_map rpc_function_map = { - { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *out_len) { return; } }, - // clang-format off - // clang-format on -}; - template bool fill_response(uint8_t *out, size_t *len, AssignFunc assign) { Response rsp = Response_init_default; diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index b23b27c..999166d 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -45,213 +45,51 @@ static HANDLE msgThread = NULL; static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 -auto &msgHandler = message::Handler::getInstance(); -auto &sendMgr = message::Sender::get_instance(); +using FunctionHandler = std::function; -static std::string BuildUrl(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } +inline std::string build_url(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } +void receive_message_callback(); -template -static bool FillResponse(int which_msg, uint8_t *out, size_t *len, AssignFunc assign) -{ - Response rsp = Response_init_default; - rsp.func = funcType; - rsp.which_msg = which_msg; +auto &handler = message::Handler::getInstance(); +auto &sender = message::Sender::get_instance(); - assign(rsp); +const std::unordered_map rpc_function_map = { + // clang-format off + { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_is_logged_in(out, len); } }, + { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_self_wxid(out, len)} }, + { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_user_info(out, len); } }, + { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, + { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, + { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, + { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, + { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am.id, r.msg.am.dir, out, len); } }, + { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, + { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file.path, r.msg.file.receiver, out, len); } }, + { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file.path, r.msg.file.receiver, out, len); } }, + { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, + { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, + { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm.roomid, r.msg.pm.wxid, out, len); } }, + { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm.id, r.msg.fm.receiver, out, len); } }, + { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file.path, r.msg.file.receiver, out, len); } }, + { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_enable_recv_msg(receive_message_callback, r.msg.flag, out, len); } }, + { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_disable_recv_msg(out, len); } }, + { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query.db, r.msg.query.sql, out, len); } }, + { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, + { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, + { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, + { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, + { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, + { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::revoke_message(r.msg.ui64, out, len); } }, + { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, + { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, + { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, + { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_del_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_memberr.msg.m, out, len); } }, + // clang-format on +}; - pb_ostream_t stream = pb_ostream_from_buffer(out, *len); - if (!pb_encode(&stream, Response_fields, &rsp)) { - LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); - return false; - } - *len = stream.bytes_written; - return true; -} - -static bool func_is_login(uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, - [](Response &rsp) { rsp.msg.status = misc::is_logged_in(); }); -} - -static bool func_get_self_wxid(uint8_t *out, size_t *len) -{ - return FillResponse(Response_str_tag, out, len, [](Response &rsp) { - std::string wxid = userinfo::get_self_wxid(); - rsp.msg.str = wxid.empty() ? nullptr : (char *)wxid.c_str(); - }); -} - -static bool func_get_user_info(uint8_t *out, size_t *len) -{ - return FillResponse(Response_ui_tag, out, len, [](Response &rsp) { - UserInfo_t ui = userinfo::get_user_info(); - rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); - rsp.msg.ui.name = (char *)ui.name.c_str(); - rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); - rsp.msg.ui.home = (char *)ui.home.c_str(); - }); -} - -static bool func_get_msg_types(uint8_t *out, size_t *len) -{ - return FillResponse(Response_types_tag, out, len, [](Response &rsp) { - static MsgTypes_t types = msgHandler.GetMsgTypes(); - rsp.msg.types.types.funcs.encode = encode_types; - rsp.msg.types.types.arg = &types; - }); -} - -static bool func_get_contacts(uint8_t *out, size_t *len) -{ - return FillResponse(Response_contacts_tag, out, len, [](Response &rsp) { - std::vector contacts = contact::get_contacts(); - rsp.msg.contacts.contacts.funcs.encode = encode_contacts; - rsp.msg.contacts.contacts.arg = &contacts; - }); -} - -static bool func_get_db_names(uint8_t *out, size_t *len) -{ - return FillResponse(Response_dbs_tag, out, len, [](Response &rsp) { - static DbNames_t dbnames = db::get_db_names(); - rsp.msg.dbs.names.funcs.encode = encode_dbnames; - rsp.msg.dbs.names.arg = &dbnames; - }); -} - -static bool func_get_db_tables(char *db, uint8_t *out, size_t *len) -{ - return FillResponse(Response_tables_tag, out, len, [db](Response &rsp) { - static DbTables_t tables = db::get_db_tables(db); - rsp.msg.tables.tables.funcs.encode = encode_tables; - rsp.msg.tables.tables.arg = &tables; - }); -} - -static bool func_get_audio_msg(uint64_t id, char *dir, uint8_t *out, size_t *len) -{ - return FillResponse(Response_str_tag, out, len, [id, dir](Response &rsp) { - std::string path = (dir == nullptr) ? "" : misc::get_audio(id, dir); - rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); - }); -} - -static bool func_send_txt(TextMsg txt, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [txt](Response &rsp) { - if ((txt.msg == NULL) || (txt.receiver == NULL)) { - LOG_ERROR("Empty message or receiver."); - rsp.msg.status = -1; - } else { - std::string msg(txt.msg); - std::string receiver(txt.receiver); - std::string aters(txt.aters ? txt.aters : ""); - sendMgr.send_text(receiver, msg, aters); - rsp.msg.status = 0; - } - }); -} - -static bool func_send_img(char *path, char *receiver, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { - if ((path == NULL) || (receiver == NULL)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else if (!fs::exists(util::s2w(path))) { - LOG_ERROR("Path does not exist: {}", path); - rsp.msg.status = -2; - } else { - sendMgr.send_image(receiver, path); - rsp.msg.status = 0; - } - }); -} - -static bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { - if ((path == nullptr) || (receiver == nullptr)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else if (!fs::exists(util::s2w(path))) { - LOG_ERROR("Path does not exist: {}", path); - rsp.msg.status = -2; - } else { - sendMgr.send_file(receiver, path); - rsp.msg.status = 0; - } - }); -} - -static bool func_send_emotion(char *path, char *receiver, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [path, receiver](Response &rsp) { - if ((path == nullptr) || (receiver == nullptr)) { - LOG_ERROR("Empty path or receiver."); - rsp.msg.status = -1; - } else { - sendMgr.send_emotion(receiver, path); - rsp.msg.status = 0; - } - }); -} - -static bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [xml](Response &rsp) { - if ((xml.content == nullptr) || (xml.receiver == nullptr)) { - LOG_ERROR("Empty content or receiver."); - rsp.msg.status = -1; - } else { - std::string receiver(xml.receiver); - std::string content(xml.content); - std::string path(xml.path ? xml.path : ""); - uint64_t type = static_cast(xml.type); - sendMgr.send_xml(receiver, content, path, type); - rsp.msg.status = 0; - } - }); -} - -static bool func_send_rich_txt(RichText rt, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [rt](Response &rsp) { - if (rt.receiver == nullptr) { - LOG_ERROR("Empty receiver."); - rsp.msg.status = -1; - } else { - rsp.msg.status = sendMgr.send_rich_text(rt); - } - }); -} - -static bool func_send_pat_msg(char *roomid, char *wxid, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [roomid, wxid](Response &rsp) { - if ((roomid == nullptr) || (wxid == nullptr)) { - LOG_ERROR("Empty roomid or wxid."); - rsp.msg.status = -1; - } else { - rsp.msg.status = sendMgr.send_pat(roomid, wxid); - } - }); -} - -static bool func_forward_msg(uint64_t id, char *receiver, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [id, receiver](Response &rsp) { - if (receiver == nullptr) { - LOG_ERROR("Empty receiver."); - rsp.msg.status = -1; - } else { - rsp.msg.status = sendMgr.forward(id, receiver); - } - }); -} - -static void PushMessage() +void receive_message_callback() { int rv; Response rsp = Response_init_default; @@ -261,7 +99,7 @@ static void PushMessage() pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); - std::string url = BuildUrl(cmdPort + 1); + std::string url = build_url(cmdPort + 1); if ((rv = nng_pair1_open(&msgSock)) != 0) { LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); return; @@ -278,15 +116,15 @@ static void PushMessage() return; } - while (msgHandler.isMessageListening()) { - std::unique_lock lock(msgHandler.getMutex()); + while (handler.isMessageListening()) { + std::unique_lock lock(handler.getMutex()); std::optional msgOpt; auto hasMessage = [&]() { - msgOpt = msgHandler.popMessage(); + msgOpt = handler.popMessage(); return msgOpt.has_value(); }; - if (msgHandler.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { + if (handler.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { WxMsg_t wxmsg = std::move(msgOpt.value()); rsp.msg.wxmsg.id = wxmsg.id; rsp.msg.wxmsg.is_self = wxmsg.is_self; @@ -317,176 +155,6 @@ static void PushMessage() } } -static bool func_enable_recv_txt(bool pyq, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [pyq](Response &rsp) { - rsp.msg.status = msgHandler.ListenMsg(); - if (rsp.msg.status == 0) { - if (pyq) { - msgHandler.ListenPyq(); - } - msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)PushMessage, nullptr, 0, nullptr); - if (msgThread == nullptr) { - rsp.msg.status = GetLastError(); - LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); - } - } - }); -} - -static bool func_disable_recv_txt(uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [](Response &rsp) { - rsp.msg.status = msgHandler.UnListenMsg(); - if (rsp.msg.status == 0) { - msgHandler.UnListenPyq(); - if (msgThread != nullptr) { - TerminateThread(msgThread, 0); - msgThread = nullptr; - } - } - }); -} - -static bool func_exec_db_query(char *db, char *sql, uint8_t *out, size_t *len) -{ - return FillResponse(Response_rows_tag, out, len, [db, sql](Response &rsp) { - static DbRows_t rows; - if ((db == nullptr) || (sql == nullptr)) { - LOG_ERROR("Empty db or sql."); - } else { - rows = db::exec_db_query(db, sql); - } - rsp.msg.rows.rows.arg = &rows; - rsp.msg.rows.rows.funcs.encode = encode_rows; - }); -} - -static bool func_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, - [id](Response &rsp) { rsp.msg.status = misc::refresh_pyq(id); }); -} - -static bool func_download_attach(AttachMsg att, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [att](Response &rsp) { - std::string thumb = att.thumb ? att.thumb : ""; - std::string extra = att.extra ? att.extra : ""; - rsp.msg.status = misc::download_attachment(att.id, thumb, extra); - }); -} - -static bool func_revoke_msg(uint64_t id, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, - [id](Response &rsp) { rsp.msg.status = misc::revoke_message(id); }); -} - -static bool func_refresh_qrcode(uint8_t *out, size_t *len) -{ - return FillResponse(Response_str_tag, out, len, [](Response &rsp) { - std::string url = misc::get_login_url(); - rsp.msg.str = url.empty() ? nullptr : (char *)url.c_str(); - }); -} - -static bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [wxid, tfid, taid](Response &rsp) { - if ((wxid == nullptr) || (tfid == nullptr) || (taid == nullptr)) { - LOG_ERROR("Empty wxid, tfid, or taid."); - rsp.msg.status = -1; - } else { - rsp.msg.status = misc::receive_transfer(wxid, tfid, taid); - } - }); -} - -static bool func_accept_friend(char *v3, char *v4, int32_t scene, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [v3, v4, scene](Response &rsp) { - if ((v3 == nullptr) || (v4 == nullptr)) { - LOG_ERROR("Empty V3 or V4."); - rsp.msg.status = -1; - } else { - rsp.msg.status = contact::accept_new_friend(v3, v4, scene); - } - }); -} - -static bool func_get_contact_info(std::string wxid, uint8_t *out, size_t *len) -{ - return FillResponse(Response_contacts_tag, out, len, [wxid](Response &rsp) { - std::vector contacts = { contact::get_contact_by_wxid(wxid) }; - rsp.msg.contacts.contacts.funcs.encode = encode_contacts; - rsp.msg.contacts.contacts.arg = &contacts; - }); -} - -static bool func_decrypt_image(DecPath dec, uint8_t *out, size_t *len) -{ - return FillResponse(Response_str_tag, out, len, [dec](Response &rsp) { - std::string path; - if ((dec.src == nullptr) || (dec.dst == nullptr)) { - LOG_ERROR("Empty src or dst."); - } else { - path = misc::decrypt_image(dec.src, dec.dst); - } - rsp.msg.str = path.empty() ? nullptr : (char *)path.c_str(); - }); -} - -static bool func_exec_ocr(char *path, uint8_t *out, size_t *len) -{ - return FillResponse(Response_ocr_tag, out, len, [path](Response &rsp) { - OcrResult_t ret = { -1, "" }; - if (path == nullptr) { - LOG_ERROR("Empty path."); - } else { - ret = misc::get_ocr_result(path); - } - rsp.msg.ocr.status = ret.status; - rsp.msg.ocr.result = ret.result.empty() ? nullptr : (char *)ret.result.c_str(); - }); -} - -static bool func_add_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { - if ((roomid == nullptr) || (wxids == nullptr)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = chatroom::add_chatroom_member(roomid, wxids); - } - }); -} - -static bool func_del_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { - if ((roomid == nullptr) || (wxids == nullptr)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = chatroom::del_chatroom_member(roomid, wxids); - } - }); -} - -static bool func_invite_room_members(char *roomid, char *wxids, uint8_t *out, size_t *len) -{ - return FillResponse(Response_status_tag, out, len, [roomid, wxids](Response &rsp) { - if ((roomid == nullptr) || (wxids == nullptr)) { - LOG_ERROR("Empty roomid or wxids."); - rsp.msg.status = -1; - } else { - rsp.msg.status = chatroom::invite_chatroom_member(roomid, wxids); - } - }); -} - static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) { bool ret = false; @@ -500,139 +168,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len LOG_DEBUG("{:#04x}[{}] length: {}", (uint8_t)req.func, magic_enum::enum_name(req.func), in_len); - switch (req.func) { - case Functions_FUNC_IS_LOGIN: { - ret = func_is_login(out, out_len); - break; - } - case Functions_FUNC_GET_SELF_WXID: { - ret = func_get_self_wxid(out, out_len); - break; - } - case Functions_FUNC_GET_USER_INFO: { - ret = func_get_user_info(out, out_len); - break; - } - case Functions_FUNC_GET_MSG_TYPES: { - ret = func_get_msg_types(out, out_len); - break; - } - case Functions_FUNC_GET_CONTACTS: { - ret = func_get_contacts(out, out_len); - break; - } - case Functions_FUNC_GET_DB_NAMES: { - ret = func_get_db_names(out, out_len); - break; - } - case Functions_FUNC_GET_DB_TABLES: { - ret = func_get_db_tables(req.msg.str, out, out_len); - break; - } - case Functions_FUNC_GET_AUDIO_MSG: { - ret = func_get_audio_msg(req.msg.am.id, req.msg.am.dir, out, out_len); - break; - } - case Functions_FUNC_SEND_TXT: { - ret = func_send_txt(req.msg.txt, out, out_len); - break; - } - case Functions_FUNC_SEND_IMG: { - ret = func_send_img(req.msg.file.path, req.msg.file.receiver, out, out_len); - break; - } - case Functions_FUNC_SEND_FILE: { - ret = func_send_file(req.msg.file.path, req.msg.file.receiver, out, out_len); - break; - } - case Functions_FUNC_SEND_RICH_TXT: { - ret = func_send_rich_txt(req.msg.rt, out, out_len); - break; - } - case Functions_FUNC_SEND_PAT_MSG: { - ret = func_send_pat_msg(req.msg.pm.roomid, req.msg.pm.wxid, out, out_len); - break; - } - case Functions_FUNC_FORWARD_MSG: { - ret = func_forward_msg(req.msg.fm.id, req.msg.fm.receiver, out, out_len); - break; - } - case Functions_FUNC_SEND_EMOTION: { - ret = func_send_emotion(req.msg.file.path, req.msg.file.receiver, out, out_len); - break; - } -#if 0 - case Functions_FUNC_SEND_XML: { - ret = func_send_xml(req.msg.xml, out, out_len); - break; - } -#endif - case Functions_FUNC_ENABLE_RECV_TXT: { - ret = func_enable_recv_txt(req.msg.flag, out, out_len); - break; - } - case Functions_FUNC_DISABLE_RECV_TXT: { - ret = func_disable_recv_txt(out, out_len); - break; - } - case Functions_FUNC_EXEC_DB_QUERY: { - ret = func_exec_db_query(req.msg.query.db, req.msg.query.sql, out, out_len); - break; - } - case Functions_FUNC_REFRESH_PYQ: { - ret = func_refresh_pyq(req.msg.ui64, out, out_len); - break; - } - case Functions_FUNC_DOWNLOAD_ATTACH: { - ret = func_download_attach(req.msg.att, out, out_len); - break; - } - case Functions_FUNC_RECV_TRANSFER: { - ret = func_receive_transfer(req.msg.tf.wxid, req.msg.tf.tfid, req.msg.tf.taid, out, out_len); - break; - } - case Functions_FUNC_REVOKE_MSG: { - ret = func_revoke_msg(req.msg.ui64, out, out_len); - break; - } - case Functions_FUNC_REFRESH_QRCODE: { - ret = func_refresh_qrcode(out, out_len); - break; - } -#if 0 - case Functions_FUNC_ACCEPT_FRIEND: { - ret = func_accept_friend(req.msg.v.v3, req.msg.v.v4, req.msg.v.scene, out, out_len); - break; - } - case Functions_FUNC_GET_CONTACT_INFO: { - ret = func_get_contact_info(req.msg.str, out, out_len); - break; - } -#endif - case Functions_FUNC_DECRYPT_IMAGE: { - ret = func_decrypt_image(req.msg.dec, out, out_len); - break; - } - case Functions_FUNC_EXEC_OCR: { - ret = func_exec_ocr(req.msg.str, out, out_len); - break; - } - case Functions_FUNC_ADD_ROOM_MEMBERS: { - ret = func_add_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len); - break; - } - case Functions_FUNC_DEL_ROOM_MEMBERS: { - ret = func_del_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len); - break; - } - case Functions_FUNC_INV_ROOM_MEMBERS: { - ret = func_invite_room_members(req.msg.m.roomid, req.msg.m.wxids, out, out_len); - break; - } - default: { - LOG_ERROR("[UNKNOW FUNCTION]"); - break; - } + auto it = rpc_function_map.find(req.func); + if (it != rpc_function_map.end()) { + ret = it->second(req, out, out_len); + } else { + LOG_ERROR("[未知方法]"); } pb_release(Request_fields, &req); @@ -642,7 +182,7 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len static int RunRpcServer() { int rv = 0; - std::string url = BuildUrl(cmdPort); + std::string url = build_url(cmdPort); if ((rv = nng_pair1_open(&cmdSock)) != 0) { LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); return rv; @@ -725,8 +265,8 @@ int RpcStopServer() nng_close(cmdSock); nng_close(msgSock); - msgHandler.UnListenPyq(); - msgHandler.UnListenMsg(); + handler.UnListenPyq(); + handler.UnListenMsg(); #if ENABLE_WX_LOG DisableLog(); #endif From 9232bab9e06659c1a102f8fd460a63002db84519 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 6 Feb 2025 19:33:16 +0800 Subject: [PATCH 041/132] Refactoring --- WeChatFerry/spy/chatroom_manager.cpp | 12 ++- WeChatFerry/spy/chatroom_manager.h | 8 +- WeChatFerry/spy/contact_manager.cpp | 5 +- WeChatFerry/spy/contact_manager.h | 4 +- WeChatFerry/spy/database_executor.cpp | 4 +- WeChatFerry/spy/database_executor.h | 4 +- WeChatFerry/spy/message_handler.cpp | 34 +------- WeChatFerry/spy/message_handler.h | 4 +- WeChatFerry/spy/message_sender.cpp | 22 +++-- WeChatFerry/spy/message_sender.h | 16 ++-- WeChatFerry/spy/misc_manager.cpp | 63 +++++++++++++++ WeChatFerry/spy/misc_manager.h | 6 +- WeChatFerry/spy/rpc_server.cpp | 112 ++++++++++++++++---------- 13 files changed, 191 insertions(+), 103 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 00a9bf7..4ce74ce 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -88,20 +88,26 @@ int invite_chatroom_member(const string &roomid, const string &wxids) reinterpret_cast(wx_roomid.get()), 0)); } -bool rpc_add_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = add_chatroom_member(roomid, wxids); }); } -bool rpc_del_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = del_chatroom_member(roomid, wxids); }); } -bool rpc_invite_chatroom_member(const string &roomid, const string &wxids, uint8_t *out, size_t *len) +bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = invite_chatroom_member(roomid, wxids); }); } diff --git a/WeChatFerry/spy/chatroom_manager.h b/WeChatFerry/spy/chatroom_manager.h index 9791ed7..3be999d 100644 --- a/WeChatFerry/spy/chatroom_manager.h +++ b/WeChatFerry/spy/chatroom_manager.h @@ -2,6 +2,8 @@ #include +#include "wcf.pb.h" + namespace chatroom { @@ -15,8 +17,8 @@ int del_chatroom_member(const std::string &roomid, const std::string &wxids); int invite_chatroom_member(const std::string &roomid, const std::string &wxids); // RPC 方法 -bool rpc_add_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); -bool rpc_del_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); -bool rpc_invite_chatroom_member(const std::string &roomid, const std::string &wxids, uint8_t *out, size_t *len); +bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len); +bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len); +bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len); } // namespace chatroom diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index bb4efe8..0e1ecb3 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -212,8 +212,11 @@ bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len) }); } -bool rpc_accept_friend(const string &v3, const string &v4, int scene, uint8_t *out, size_t *len) +bool rpc_accept_friend(const Verification &v, uint8_t *out, size_t *len) { + const string v3 = v.v3; + const string v4 = v.v4; + int scene = v.scene; return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = accept_new_friend(v3, v4, scene); }); } diff --git a/WeChatFerry/spy/contact_manager.h b/WeChatFerry/spy/contact_manager.h index 5b330c0..440091e 100644 --- a/WeChatFerry/spy/contact_manager.h +++ b/WeChatFerry/spy/contact_manager.h @@ -3,6 +3,8 @@ #include #include +#include "wcf.pb.h" + #include "pb_types.h" namespace contact @@ -23,6 +25,6 @@ int accept_new_friend(const std::string &v3, const std::string &v4, int scene); // RPC 方法 bool rpc_get_contacts(uint8_t *out, size_t *len); bool rpc_get_contact_info(const std::string &wxid, uint8_t *out, size_t *len); -bool rpc_accept_friend(const std::string &v3, const std::string &v4, int scene, uint8_t *out, size_t *len); +bool rpc_accept_friend(const Verification &v, uint8_t *out, size_t *len); } // namespace contact diff --git a/WeChatFerry/spy/database_executor.cpp b/WeChatFerry/spy/database_executor.cpp index 3f850a3..bd5a7f8 100644 --- a/WeChatFerry/spy/database_executor.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -278,8 +278,10 @@ bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len) }); } -bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len) +bool rpc_exec_db_query(const DbQuery query, uint8_t *out, size_t *len) { + const std::string db(query.db); + const std::string sql(query.sql); return fill_response(out, len, [&](Response &rsp) { DbRows_t rows = exec_db_query(db, sql); rsp.msg.rows.rows.funcs.encode = encode_rows; diff --git a/WeChatFerry/spy/database_executor.h b/WeChatFerry/spy/database_executor.h index 4f2752d..0d50373 100644 --- a/WeChatFerry/spy/database_executor.h +++ b/WeChatFerry/spy/database_executor.h @@ -4,6 +4,8 @@ #include #include +#include "wcf.pb.h" + #include "pb_types.h" namespace db @@ -27,6 +29,6 @@ std::vector get_audio_data(uint64_t msg_id); // RPC 方法 bool rpc_get_db_names(uint8_t *out, size_t *len); bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len); -bool rpc_exec_db_query(const std::string &db, const std::string &sql, uint8_t *out, size_t *len); +bool rpc_exec_db_query(const DbQuery query, uint8_t *out, size_t *len); } // namespace db diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index c1070e8..3889c09 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -7,6 +7,7 @@ #include "framework.h" #include "log.hpp" +#include "pb_util.h" #include "rpc_helper.h" #include "userinfo_manager.h" #include "util.h" @@ -268,7 +269,7 @@ MH_STATUS Handler::UninitializeHook() return status; } -bool Handler::rpc_get_msg_types() +bool Handler::rpc_get_msg_types(uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { MsgTypes_t types = GetMsgTypes(); @@ -276,35 +277,4 @@ bool Handler::rpc_get_msg_types() rsp.msg.types.types.arg = &types; }); } - -bool rpc_enable_recv_msg(void *cb, bool pyq, uint8_t *out, size_t *len) -{ - return fill_response(out, len, [cb, pyq](Response &rsp) { - auto &handler = Handler::getInstance(); - rsp.msg.status = handler.ListenMsg(); - if (rsp.msg.status == 0) { - if (pyq) { - msgHandler.ListenPyq(); - } - msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)cb, nullptr, 0, nullptr); - if (msgThread == nullptr) { - rsp.msg.status = GetLastError(); - LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); - } - } - }); } -bool Handler::rpc_disable_recv_msg(uint8_t *out, size_t *len) -{ - return fill_response(out, len, [](Response &rsp) { - rsp.msg.status = UnListenMsg(); - if (rsp.msg.status == 0) { - UnListenPyq(); - if (msgThread != nullptr) { - TerminateThread(msgThread, 0); - msgThread = nullptr; - } - } - }); -} -} \ No newline at end of file diff --git a/WeChatFerry/spy/message_handler.h b/WeChatFerry/spy/message_handler.h index c9e0898..c4227ec 100644 --- a/WeChatFerry/spy/message_handler.h +++ b/WeChatFerry/spy/message_handler.h @@ -37,9 +37,7 @@ public: std::condition_variable &getConditionVariable() { return cv_; }; std::mutex &getMutex() { return mutex_; }; - bool rpc_get_msg_types(); - bool rpc_enable_recv_msg(void *cb, bool pyq, uint8_t *out, size_t *len); - bool rpc_disable_recv_msg(uint8_t *out, size_t *len); + bool rpc_get_msg_types(uint8_t *out, size_t *len); private: Handler(); diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 0c79503..c1cb7c8 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -262,8 +262,10 @@ bool Sender::rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len) }); } -bool Sender::rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_image(const PathMsg &file, uint8_t *out, size_t *len) { + std::string path(file.path); + std::string receiver(file.receiver); return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { LOG_ERROR("Empty path or receiver."); @@ -275,8 +277,10 @@ bool Sender::rpc_send_image(const std::string &path, const std::string &receiver }); } -bool Sender::rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_file(const PathMsg &file, uint8_t *out, size_t *len) { + std::string path(file.path); + std::string receiver(file.receiver); return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { LOG_ERROR("Empty path or receiver."); @@ -288,8 +292,10 @@ bool Sender::rpc_send_file(const std::string &path, const std::string &receiver, }); } -bool Sender::rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_send_emotion(const PathMsg &file, uint8_t *out, size_t *len) { + std::string path(file.path); + std::string receiver(file.receiver); return fill_response(out, len, [&](Response &rsp) { if (path.empty() || receiver.empty()) { LOG_ERROR("Empty path or receiver."); @@ -326,8 +332,10 @@ bool Sender::rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len) }); } -bool Sender::rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len) +bool Sender::rpc_send_pat(const PatMsg &pat, uint8_t *out, size_t *len) { + std::string wxid(pat.wxid); + std::string roomid(pat.roomid); return fill_response(out, len, [&](Response &rsp) { if (roomid.empty() || wxid.empty()) { LOG_ERROR("Empty roomid or wxid."); @@ -338,8 +346,10 @@ bool Sender::rpc_send_pat(const std::string &roomid, const std::string &wxid, ui }); } -bool Sender::rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len) +bool Sender::rpc_forward(const ForwardMsg &fm, uint8_t *out, size_t *len) { + uint64_t msgid = fm.id; + std::string receiver(fm.receiver); return fill_response(out, len, [&](Response &rsp) { if (receiver.empty()) { LOG_ERROR("Empty receiver."); @@ -350,4 +360,4 @@ bool Sender::rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *o }); } -} \ No newline at end of file +} diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index e528065..1f0a855 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -4,9 +4,10 @@ #include #include -#include "spy_types.h" #include "wcf.pb.h" +#include "spy_types.h" + namespace message { @@ -26,13 +27,13 @@ public: // RPC 方法 bool rpc_send_text(const TextMsg &text, uint8_t *out, size_t *len); - bool rpc_send_image(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); - bool rpc_send_file(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); - bool rpc_send_emotion(const std::string &path, const std::string &receiver, uint8_t *out, size_t *len); + bool rpc_send_image(const PathMsg &file, uint8_t *out, size_t *len); + bool rpc_send_file(const PathMsg &file, uint8_t *out, size_t *len); + bool rpc_send_emotion(const PathMsg &file, uint8_t *out, size_t *len); bool rpc_send_xml(const XmlMsg &rt, uint8_t *out, size_t *len); bool rpc_send_rich_text(const RichText &rt, uint8_t *out, size_t *len); - bool rpc_send_pat(const std::string &roomid, const std::string &wxid, uint8_t *out, size_t *len); - bool rpc_forward(uint64_t msgid, const std::string &receiver, uint8_t *out, size_t *len); + bool rpc_send_pat(const PatMsg &pat, uint8_t *out, size_t *len); + bool rpc_forward(const ForwardMsg &fm, uint8_t *out, size_t *len); private: Sender(); @@ -76,5 +77,4 @@ private: std::unique_ptr new_wx_string(const std::string &str); std::vector parse_wxids(const std::string &wxids); }; - -} \ No newline at end of file +} diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 4cf414f..f6f1b3f 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -10,6 +10,7 @@ #include "database_executor.h" #include "log.hpp" #include "message_handler.h" +#include "rpc_helper.h" #include "spy_types.h" #include "util.h" @@ -349,4 +350,66 @@ int receive_transfer(const std::string &wxid, const std::string &transferid, con // 别想了,这个不实现了 return -1; } + +bool rpc_is_logged_in(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { rsp.msg.status = is_logged_in(); }); +} + +bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.str = (char *)get_audio(am.id, am.dir).c_str(); }); +} + +bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len) +{ + return false; +} + +bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.str = (char *)decrypt_image(dec.src, dec.dst).c_str(); }); +} + +bool rpc_get_login_url(uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.str = (char *)get_login_url().c_str(); }); +} + +bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) +{ + return fill_response(out, len, + [&](Response &rsp) { rsp.msg.status = refresh_pyq(id); }); +} + +bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = download_attachment(att.id, att.thumb, att.extra); }); +} + +bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len) +{ + return fill_response(out, len, + [&](Response &rsp) { rsp.msg.status = revoke_message(id); }); +} + +bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + OcrResult_t ret = { -1, "" }; + ret = get_ocr_result(path); + rsp.msg.ocr.status = ret.status; + rsp.msg.ocr.result = (char *)ret.result.c_str(); + }); +} + +bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len) +{ + return fill_response( + out, len, [&](Response &rsp) { rsp.msg.status = receive_transfer(tf.wxid, tf.tfid, tf.taid); }); +} } // namespace misc diff --git a/WeChatFerry/spy/misc_manager.h b/WeChatFerry/spy/misc_manager.h index 378dc42..2f995ae 100644 --- a/WeChatFerry/spy/misc_manager.h +++ b/WeChatFerry/spy/misc_manager.h @@ -30,14 +30,14 @@ int receive_transfer(const std::string &wxid, const std::string &transferid, con // RPC // clang-format off bool rpc_is_logged_in(uint8_t *out, size_t *len); -bool rpc_get_audio(uint64_t id, const std::filesystem::path &dir, uint8_t *out, size_t *len); +bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len); bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len); bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len); bool rpc_get_login_url(uint8_t *out, size_t *len); bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len); -bool rpc_download_attachment(uint64_t id, const std::filesystem::path &thumb, const std::filesystem::path &extra, uint8_t *out, size_t *len); +bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len); bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len); bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len); -bool rpc_receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid, uint8_t *out, size_t *len); +bool rpc_receive_transfer(const Transfer &tf, uint8_t *out, size_t *len); // clang-format on } // namespace misc diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 999166d..d4be76e 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -29,6 +29,7 @@ #include "misc_manager.h" #include "pb_types.h" #include "pb_util.h" +#include "rpc_helper.h" #include "spy.h" #include "spy_types.h" #include "userinfo_manager.h" @@ -44,52 +45,14 @@ static HANDLE cmdThread = NULL; static HANDLE msgThread = NULL; static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 +auto &handler = message::Handler::getInstance(); +auto &sender = message::Sender::get_instance(); using FunctionHandler = std::function; inline std::string build_url(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } -void receive_message_callback(); -auto &handler = message::Handler::getInstance(); -auto &sender = message::Sender::get_instance(); - -const std::unordered_map rpc_function_map = { - // clang-format off - { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_is_logged_in(out, len); } }, - { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_self_wxid(out, len)} }, - { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_user_info(out, len); } }, - { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, - { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, - { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, - { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, - { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am.id, r.msg.am.dir, out, len); } }, - { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, - { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file.path, r.msg.file.receiver, out, len); } }, - { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file.path, r.msg.file.receiver, out, len); } }, - { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, - { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, - { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm.roomid, r.msg.pm.wxid, out, len); } }, - { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm.id, r.msg.fm.receiver, out, len); } }, - { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file.path, r.msg.file.receiver, out, len); } }, - { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_enable_recv_msg(receive_message_callback, r.msg.flag, out, len); } }, - { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_disable_recv_msg(out, len); } }, - { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query.db, r.msg.query.sql, out, len); } }, - { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, - { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, - { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, - { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, - { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, - { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::revoke_message(r.msg.ui64, out, len); } }, - { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, - { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, - { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, - { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, - { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_del_chatroom_member(r.msg.m, out, len); } }, - { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_memberr.msg.m, out, len); } }, - // clang-format on -}; - -void receive_message_callback() +static void receive_message_callback() { int rv; Response rsp = Response_init_default; @@ -155,6 +118,73 @@ void receive_message_callback() } } +static bool rpc_enable_recv_msg(bool pyq, uint8_t *out, size_t *len) +{ + return fill_response(out, len, [&](Response &rsp) { + rsp.msg.status = handler.ListenMsg(); + if (rsp.msg.status == 0) { + if (pyq) { + handler.ListenPyq(); + } + msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)receive_message_callback, nullptr, 0, nullptr); + if (msgThread == nullptr) { + rsp.msg.status = GetLastError(); + LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); + } + } + }); +} + +static bool rpc_disable_recv_msg(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { + rsp.msg.status = handler.UnListenMsg(); + if (rsp.msg.status == 0) { + handler.UnListenPyq(); + if (msgThread != nullptr) { + TerminateThread(msgThread, 0); + msgThread = nullptr; + } + } + }); +} + +const std::unordered_map rpc_function_map = { + // clang-format off + { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_is_logged_in(out, len); } }, + { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_self_wxid(out, len); } }, + { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_user_info(out, len); } }, + { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, + { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, + { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, + { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, + { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, + { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, + { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, + { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, + { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, + { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, + { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_enable_recv_msg(r.msg.flag, out, len); } }, + { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_disable_recv_msg(out, len); } }, + { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, + { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, + { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, + { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, + { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, + { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, + { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, + { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, + { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, + { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, + { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, + // clang-format on +}; + static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) { bool ret = false; From 0fc9f5b94a6931f3ef2c66812718c1fd83463a48 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Feb 2025 07:40:46 +0800 Subject: [PATCH 042/132] Fix DISCLAIMER path --- README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.MD b/README.MD index 46ee088..6913e72 100644 --- a/README.MD +++ b/README.MD @@ -5,7 +5,7 @@
⚠️ 免责声明【必读】⚠️ -请阅读完整的免责声明:[点击查看](DISCLAIMER.md) +请阅读完整的免责声明:[点击查看](WeChatFerry/DISCLAIMER.md)
From 2df74fb4d21f3266d9976f94bd3e0a574eaef734 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Feb 2025 07:41:14 +0800 Subject: [PATCH 043/132] Refactoring --- WeChatFerry/spy/Spy.vcxproj | 4 ++-- WeChatFerry/spy/Spy.vcxproj.filters | 4 ++-- .../spy/{userinfo_manager.cpp => account_manager.cpp} | 6 +++--- WeChatFerry/spy/{userinfo_manager.h => account_manager.h} | 4 ++-- WeChatFerry/spy/message_handler.cpp | 6 +++--- WeChatFerry/spy/message_sender.cpp | 4 ++-- WeChatFerry/spy/rpc_server.cpp | 6 +++--- 7 files changed, 17 insertions(+), 17 deletions(-) rename WeChatFerry/spy/{userinfo_manager.cpp => account_manager.cpp} (96%) rename WeChatFerry/spy/{userinfo_manager.h => account_manager.h} (89%) diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj index 3f601af..0854bf5 100644 --- a/WeChatFerry/spy/Spy.vcxproj +++ b/WeChatFerry/spy/Spy.vcxproj @@ -260,7 +260,7 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - +
@@ -278,7 +278,7 @@ xcopy /y $(SolutionDir)DISCLAIMER.md $(SolutionDir)..\clients\python\wcferry - + diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters index dea6857..a2a537b 100644 --- a/WeChatFerry/spy/Spy.vcxproj.filters +++ b/WeChatFerry/spy/Spy.vcxproj.filters @@ -66,7 +66,7 @@ 头文件 - + 头文件 @@ -131,7 +131,7 @@ 源文件 - + 源文件 diff --git a/WeChatFerry/spy/userinfo_manager.cpp b/WeChatFerry/spy/account_manager.cpp similarity index 96% rename from WeChatFerry/spy/userinfo_manager.cpp rename to WeChatFerry/spy/account_manager.cpp index eddf0d5..52cb43d 100644 --- a/WeChatFerry/spy/userinfo_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -1,4 +1,4 @@ -#include "userinfo_manager.h" +#include "account_manager.h" #include #include @@ -9,7 +9,7 @@ extern UINT64 g_WeChatWinDllAddr; -namespace userinfo +namespace account { #define OS_USER_HOME 0x5932770 #define OS_USER_WXID 0x595C270 @@ -85,4 +85,4 @@ bool rpc_get_user_info(uint8_t *out, size_t *len) }); } -} // namespace userinfo +} // namespace account diff --git a/WeChatFerry/spy/userinfo_manager.h b/WeChatFerry/spy/account_manager.h similarity index 89% rename from WeChatFerry/spy/userinfo_manager.h rename to WeChatFerry/spy/account_manager.h index 6868817..6e90715 100644 --- a/WeChatFerry/spy/userinfo_manager.h +++ b/WeChatFerry/spy/account_manager.h @@ -5,7 +5,7 @@ #include "pb_types.h" -namespace userinfo +namespace account { // 获取 WeChat 数据存储路径 @@ -21,4 +21,4 @@ UserInfo_t get_user_info(); bool rpc_get_self_wxid(uint8_t *out, size_t *len); bool rpc_get_user_info(uint8_t *out, size_t *len); -} // namespace userinfo +} // namespace account diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 3889c09..3bf9882 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -9,7 +9,7 @@ #include "log.hpp" #include "pb_util.h" #include "rpc_helper.h" -#include "userinfo_manager.h" +#include "account_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; @@ -54,10 +54,10 @@ QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2) if (wxMsg.roomid.find("@chatroom") != std::string::npos) { wxMsg.is_group = true; wxMsg.sender - = wxMsg.is_self ? userinfo::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); + = wxMsg.is_self ? account::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); } else { wxMsg.is_group = false; - wxMsg.sender = wxMsg.is_self ? userinfo::get_self_wxid() : wxMsg.roomid; + wxMsg.sender = wxMsg.is_self ? account::get_self_wxid() : wxMsg.roomid; } } catch (const std::exception &e) { LOG_ERROR(util::gb2312_to_utf8(e.what())); diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index c1cb7c8..ac4452d 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -7,7 +7,7 @@ #include "log.hpp" #include "rpc_helper.h" #include "spy_types.h" -#include "userinfo_manager.h" +#include "account_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; @@ -153,7 +153,7 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const auto wxReceiver = new_wx_string(receiver); auto wxXml = new_wx_string(xml); auto wxPath = new_wx_string(path); - auto wxSender = new_wx_string(userinfo::get_self_wxid()); + auto wxSender = new_wx_string(account::get_self_wxid()); func_send_xml(reinterpret_cast(buff.get()), reinterpret_cast(wxSender.get()), reinterpret_cast(wxReceiver.get()), reinterpret_cast(wxXml.get()), diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index d4be76e..b237982 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -20,6 +20,7 @@ #include "wcf.pb.h" +#include "account_manager.h" #include "chatroom_manager.h" #include "contact_manager.h" #include "database_executor.h" @@ -32,7 +33,6 @@ #include "rpc_helper.h" #include "spy.h" #include "spy_types.h" -#include "userinfo_manager.h" #include "util.h" namespace fs = std::filesystem; @@ -152,8 +152,8 @@ static bool rpc_disable_recv_msg(uint8_t *out, size_t *len) const std::unordered_map rpc_function_map = { // clang-format off { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_is_logged_in(out, len); } }, - { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_self_wxid(out, len); } }, - { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return userinfo::rpc_get_user_info(out, len); } }, + { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, + { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, From a9be7b094d52cc94ff48d46066be0b9240f4107a Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Feb 2025 07:51:44 +0800 Subject: [PATCH 044/132] Refactoring --- WeChatFerry/spy/account_manager.cpp | 16 ++++++++++++---- WeChatFerry/spy/account_manager.h | 4 ++++ WeChatFerry/spy/misc_manager.cpp | 8 -------- WeChatFerry/spy/misc_manager.h | 3 --- WeChatFerry/spy/rpc_server.cpp | 2 +- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index 52cb43d..d5f9608 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -11,10 +11,13 @@ extern UINT64 g_WeChatWinDllAddr; namespace account { -#define OS_USER_HOME 0x5932770 -#define OS_USER_WXID 0x595C270 -#define OS_USER_NAME 0x595C3D8 -#define OS_USER_MOBILE 0x595C318 +#define OS_LOGIN_STATUS 0x595C9E8 +#define OS_USER_HOME 0x5932770 +#define OS_USER_WXID 0x595C270 +#define OS_USER_NAME 0x595C3D8 +#define OS_USER_MOBILE 0x595C318 + +bool is_logged_in() { return util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS) != 0; } std::string get_home_path() { @@ -68,6 +71,11 @@ UserInfo_t get_user_info() return ui; } +bool rpc_is_logged_in(uint8_t *out, size_t *len) +{ + return fill_response(out, len, [](Response &rsp) { rsp.msg.status = is_logged_in(); }); +} + bool rpc_get_self_wxid(uint8_t *out, size_t *len) { return fill_response( diff --git a/WeChatFerry/spy/account_manager.h b/WeChatFerry/spy/account_manager.h index 6e90715..e184475 100644 --- a/WeChatFerry/spy/account_manager.h +++ b/WeChatFerry/spy/account_manager.h @@ -8,6 +8,9 @@ namespace account { +// 登录状态 +bool is_logged_in(); + // 获取 WeChat 数据存储路径 std::string get_home_path(); @@ -18,6 +21,7 @@ std::string get_self_wxid(); UserInfo_t get_user_info(); // RPC 方法 +bool rpc_is_logged_in(uint8_t *out, size_t *len); bool rpc_get_self_wxid(uint8_t *out, size_t *len); bool rpc_get_user_info(uint8_t *out, size_t *len); diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index f6f1b3f..17e1e8e 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -28,7 +28,6 @@ namespace misc #define HEADER_GIF1 0x47 #define HEADER_GIF2 0x49 -#define OS_LOGIN_STATUS 0x595C9E8 #define OS_GET_SNS_DATA_MGR 0x21E2200 #define OS_GET_SNS_FIRST_PAGE 0x2E212D0 #define OS_GET_SNS_TIMELINE_MGR 0x2DB3390 @@ -54,8 +53,6 @@ using push_attach_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using get_ocr_manager_t = QWORD (*)(); using do_ocr_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -bool is_logged_in() { return util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS) != 0; } - static std::string detect_image_extension(uint8_t header1, uint8_t header2, uint8_t &key) { if ((key = HEADER_PNG1 ^ header1) && (HEADER_PNG2 ^ key) == header2) return ".png"; @@ -351,11 +348,6 @@ int receive_transfer(const std::string &wxid, const std::string &transferid, con return -1; } -bool rpc_is_logged_in(uint8_t *out, size_t *len) -{ - return fill_response(out, len, [](Response &rsp) { rsp.msg.status = is_logged_in(); }); -} - bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len) { return fill_response( diff --git a/WeChatFerry/spy/misc_manager.h b/WeChatFerry/spy/misc_manager.h index 2f995ae..0792c3f 100644 --- a/WeChatFerry/spy/misc_manager.h +++ b/WeChatFerry/spy/misc_manager.h @@ -13,8 +13,6 @@ namespace misc { -bool is_logged_in(); - std::string get_audio(uint64_t id, const std::filesystem::path &dir); std::string get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr); std::string decrypt_image(const std::filesystem::path &src, const std::filesystem::path &dst); @@ -29,7 +27,6 @@ int receive_transfer(const std::string &wxid, const std::string &transferid, con // RPC // clang-format off -bool rpc_is_logged_in(uint8_t *out, size_t *len); bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len); bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len); bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index b237982..d238642 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -151,7 +151,7 @@ static bool rpc_disable_recv_msg(uint8_t *out, size_t *len) const std::unordered_map rpc_function_map = { // clang-format off - { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_is_logged_in(out, len); } }, + { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_is_logged_in(out, len); } }, { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, From 649e627b4f1a9bbc0c4b6325524c3074996ebfbf Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 10 Feb 2025 20:32:29 +0800 Subject: [PATCH 045/132] Refactoring --- WeChatFerry/spy/rpc_server.cpp | 288 ++++++++++++++++++--------------- WeChatFerry/spy/rpc_server.h | 56 ++++++- WeChatFerry/spy/spy.cpp | 7 +- 3 files changed, 209 insertions(+), 142 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index d238642..dbbc02a 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -14,12 +14,9 @@ #include #include -#include #include #include -#include "wcf.pb.h" - #include "account_manager.h" #include "chatroom_manager.h" #include "contact_manager.h" @@ -39,30 +36,103 @@ namespace fs = std::filesystem; constexpr size_t DEFAULT_BUF_SIZE = 16 * 1024 * 1024; -static int cmdPort = 0; -static bool isRpcRunning = false; -static HANDLE cmdThread = NULL; -static HANDLE msgThread = NULL; -static nng_socket cmdSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 -static nng_socket msgSock = NNG_SOCKET_INITIALIZER; // TODO: 断开检测 -auto &handler = message::Handler::getInstance(); -auto &sender = message::Sender::get_instance(); +std::unique_ptr RpcServer::instance_ = nullptr; -using FunctionHandler = std::function; +RpcServer &RpcServer::getInstance() +{ + if (!instance_) { + instance_.reset(new RpcServer()); + } + return *instance_; +} -inline std::string build_url(int port) { return "tcp://0.0.0.0:" + std::to_string(port); } +void RpcServer::destroyInstance() +{ + if (instance_) { + instance_->stop(); + instance_.reset(); + } +} -static void receive_message_callback() +RpcServer::RpcServer(int port) : port_(port) { LOG_DEBUG("RpcServer 构造: 端口 {}", port_); } + +RpcServer::~RpcServer() +{ + stop(); + LOG_DEBUG("RpcServer 被析构,释放所有资源"); +} + +std::string RpcServer::build_url(int port) +{ + return std::string(RpcServer::RPC_SERVER_ADDRESS) + ":" + std::to_string(port); +} + +int RpcServer::start(int port) +{ + if (isRunning_.load()) { + LOG_WARN("RPC 服务已在运行"); + return 1; + } + + port_ = port; + isRunning_ = true; + + try { + cmdThread_ = std::thread(&RpcServer::runRpcServer, this); + } catch (const std::exception &e) { + LOG_ERROR("启动 RPC 服务器失败: {}", e.what()); + isRunning_ = false; + return -2; + } +#if ENABLE_WX_LOG + EnableLog(); +#endif + LOG_INFO("RPC 服务器成功启动,监听端口: {}", port_); + return 0; +} + +int RpcServer::stop() +{ + if (!isRunning_.load()) { + LOG_WARN("RPC 服务未运行"); + return 1; + } + isRunning_ = false; + + auto &handler = message::Handler::getInstance(); + handler.UnListenPyq(); + handler.UnListenMsg(); +#if ENABLE_WX_LOG + DisableLog(); +#endif + nng_fini(); + if (cmdThread_.joinable()) { + LOG_DEBUG("等待命令线程关闭"); + cmdThread_.join(); + } + LOG_DEBUG("命令线程已经关闭"); + + if (msgThread_.joinable()) { + LOG_DEBUG("等待消息线程关闭"); + msgThread_.join(); + } + LOG_DEBUG("消息线程已经关闭"); + LOG_INFO("RPC 服务已停止"); + return 0; +} + +void RpcServer::receiveMessageCallback() { int rv; - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_ENABLE_RECV_TXT; - rsp.which_msg = Response_wxmsg_tag; + nng_socket msgSock = NNG_SOCKET_INITIALIZER; + Response rsp = Response_init_default; + rsp.func = Functions_FUNC_ENABLE_RECV_TXT; + rsp.which_msg = Response_wxmsg_tag; std::vector msgBuffer(DEFAULT_BUF_SIZE); pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); - std::string url = build_url(cmdPort + 1); + std::string url = build_url(port_ + 1); if ((rv = nng_pair1_open(&msgSock)) != 0) { LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); return; @@ -79,6 +149,7 @@ static void receive_message_callback() return; } + auto &handler = message::Handler::getInstance(); while (handler.isMessageListening()) { std::unique_lock lock(handler.getMutex()); std::optional msgOpt; @@ -116,76 +187,74 @@ static void receive_message_callback() LOG_DEBUG("Send data length {}", stream.bytes_written); } } + nng_close(msgSock); } -static bool rpc_enable_recv_msg(bool pyq, uint8_t *out, size_t *len) +bool RpcServer::enableRecvMsg(bool pyq, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { + auto &handler = message::Handler::getInstance(); rsp.msg.status = handler.ListenMsg(); if (rsp.msg.status == 0) { if (pyq) { handler.ListenPyq(); } - msgThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)receive_message_callback, nullptr, 0, nullptr); - if (msgThread == nullptr) { - rsp.msg.status = GetLastError(); - LOG_ERROR("func_enable_recv_txt failed: {}", rsp.msg.status); - } + msgThread_ = std::thread(&RpcServer::receiveMessageCallback, this); } }); } -static bool rpc_disable_recv_msg(uint8_t *out, size_t *len) +bool RpcServer::disableRecvMsg(uint8_t *out, size_t *len) { - return fill_response(out, len, [](Response &rsp) { + return fill_response(out, len, [&](Response &rsp) { + auto &handler = message::Handler::getInstance(); rsp.msg.status = handler.UnListenMsg(); if (rsp.msg.status == 0) { handler.UnListenPyq(); - if (msgThread != nullptr) { - TerminateThread(msgThread, 0); - msgThread = nullptr; + if (msgThread_.joinable()) { + msgThread_.join(); } } }); } -const std::unordered_map rpc_function_map = { +const std::unordered_map RpcServer::rpcFunctionMap = { // clang-format off { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_is_logged_in(out, len); } }, - { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, - { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, - { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, - { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, - { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, - { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, - { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, - { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, - { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file, out, len); } }, - { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, - { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, - { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, - { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, - { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, - { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, - { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_enable_recv_msg(r.msg.flag, out, len); } }, - { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_disable_recv_msg(out, len); } }, - { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, - { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, - { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, - { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, - { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, - { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, - { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, - { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, - { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, - { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, - { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, - { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, - { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, + // { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, + // { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, + // { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, + // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, + // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, + // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, + // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, + // { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, + // { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file, out, len); } }, + // { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, + // { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, + // { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, + // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, + // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, + // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, + // { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_enable_recv_msg(r.msg.flag, out, len); } }, + // { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_disable_recv_msg(out, len); } }, + // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, + // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, + // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, + // { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, + // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, + // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, + // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, + // { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, + // { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, + // { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, + // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, + // { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, + // { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, // clang-format on }; -static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) +bool RpcServer::dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) { bool ret = false; Request req = Request_init_default; @@ -198,8 +267,8 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len LOG_DEBUG("{:#04x}[{}] length: {}", (uint8_t)req.func, magic_enum::enum_name(req.func), in_len); - auto it = rpc_function_map.find(req.func); - if (it != rpc_function_map.end()) { + auto it = RpcServer::rpcFunctionMap.find(req.func); + if (it != RpcServer::rpcFunctionMap.end()) { ret = it->second(req, out, out_len); } else { LOG_ERROR("[未知方法]"); @@ -209,46 +278,49 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len return ret; } -static int RunRpcServer() +void RpcServer::runRpcServer() { - int rv = 0; - std::string url = build_url(cmdPort); + int rv = 0; + nng_socket cmdSock = NNG_SOCKET_INITIALIZER; + std::string url = build_url(port_); + if ((rv = nng_pair1_open(&cmdSock)) != 0) { - LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); - return rv; + LOG_ERROR("nng_pair1_open error: {}", nng_strerror(rv)); + return; } - if ((rv = nng_listen(cmdSock, url.c_str(), NULL, 0)) != 0) { - LOG_ERROR("nng_listen error {}", nng_strerror(rv)); - return rv; + if ((rv = nng_listen(cmdSock, url.c_str(), nullptr, 0)) != 0) { + LOG_ERROR("nng_listen error: {}", nng_strerror(rv)); + return; } - LOG_INFO("CMD Server listening on {}", url.c_str()); if ((rv = nng_setopt_ms(cmdSock, NNG_OPT_SENDTIMEO, 1000)) != 0) { LOG_ERROR("nng_setopt_ms error: {}", nng_strerror(rv)); - return rv; + return; } + LOG_INFO("CMD Server listening on {}", url); std::vector cmdBuffer(DEFAULT_BUF_SIZE); - isRpcRunning = true; - while (isRpcRunning) { - uint8_t *in = NULL; + + while (isRunning_.load()) { + uint8_t *in = nullptr; size_t in_len, out_len = cmdBuffer.size(); - if ((rv = nng_recv(cmdSock, &in, &in_len, NNG_FLAG_ALLOC)) != 0) { + + rv = nng_recv(cmdSock, &in, &in_len, NNG_FLAG_ALLOC); + if (rv != 0) { LOG_ERROR("cmdSock-nng_recv error: {}", nng_strerror(rv)); break; } + try { - // LOG_BUFFER(in, in_len); if (dispatcher(in, in_len, cmdBuffer.data(), &out_len)) { LOG_DEBUG("Send data length {}", out_len); - // LOG_BUFFER(cmdBuffer.data(), out_len); + rv = nng_send(cmdSock, cmdBuffer.data(), out_len, 0); if (rv != 0) { LOG_ERROR("cmdSock-nng_send: {}", nng_strerror(rv)); } - - } else { // Error + } else { // 处理失败情况 LOG_ERROR("Dispatcher failed..."); rv = nng_send(cmdSock, cmdBuffer.data(), 0, 0); if (rv != 0) { @@ -258,59 +330,11 @@ static int RunRpcServer() } catch (const std::exception &e) { LOG_ERROR(util::gb2312_to_utf8(e.what())); } catch (...) { - LOG_ERROR("Unknow exception."); + LOG_ERROR("Unknown exception."); } + nng_free(in, in_len); } - RpcStopServer(); - LOG_DEBUG("Leave RunRpcServer"); - return rv; -} - -int RpcStartServer(int port) -{ - if (isRpcRunning) { - LOG_WARN("RPC 服务已经启动"); - return 1; - } - - cmdPort = port; - cmdThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)RunRpcServer, NULL, NULL, NULL); - if (cmdThread == NULL) { - LOG_ERROR("CreateThread failed: {}", GetLastError()); - return -1; - } -#if ENABLE_WX_LOG - EnableLog(); -#endif - return 0; -} - -int RpcStopServer() -{ - if (!isRpcRunning) { - LOG_WARN("RPC 服务未启动"); - return 1; - } - nng_close(cmdSock); - nng_close(msgSock); - handler.UnListenPyq(); - handler.UnListenMsg(); -#if ENABLE_WX_LOG - DisableLog(); -#endif - if (cmdThread != NULL) { - WaitForSingleObject(cmdThread, INFINITE); - CloseHandle(cmdThread); - cmdThread = NULL; - } - - if (msgThread != NULL) { - WaitForSingleObject(msgThread, INFINITE); - CloseHandle(msgThread); - msgThread = NULL; - } - isRpcRunning = false; - return 0; + LOG_DEBUG("Leave RunRpcServer"); } diff --git a/WeChatFerry/spy/rpc_server.h b/WeChatFerry/spy/rpc_server.h index 3bc31e1..400bbff 100644 --- a/WeChatFerry/spy/rpc_server.h +++ b/WeChatFerry/spy/rpc_server.h @@ -1,10 +1,52 @@ #pragma once -#ifdef SPY_EXPORTS -#define SPY_API __declspec(dllexport) -#else -#define SPY_API __declspec(dllimport) -#endif +#include +#include +#include +#include -int RpcStartServer(int port); -int RpcStopServer(); +#include + +#include "wcf.pb.h" + +class RpcServer +{ +public: + static RpcServer &getInstance(); + static void destroyInstance(); + + int start(int port = RPC_DEFAULT_PORT); + int stop(); + +private: + RpcServer(int port = RPC_DEFAULT_PORT); + ~RpcServer(); + RpcServer(const RpcServer &) = delete; + RpcServer &operator=(const RpcServer &) = delete; + + void runRpcServer(); + void receiveMessageCallback(); + bool enableRecvMsg(bool pyq, uint8_t *out, size_t *len); + bool disableRecvMsg(uint8_t *out, size_t *len); + bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len); + + static std::string build_url(int port); + + using FunctionHandler = std::function; + + // 服务器默认端口号和绑定地址 + static constexpr int RPC_DEFAULT_PORT = 10086; + static constexpr const char *RPC_SERVER_ADDRESS = "tcp://0.0.0.0"; + + int port_ = RPC_DEFAULT_PORT; + std::atomic isRunning_ { false }; + std::thread cmdThread_; + std::thread msgThread_; + + struct Deleter { + void operator()(RpcServer *server) const { delete server; } + }; + + static std::unique_ptr instance_; + static const std::unordered_map rpcFunctionMap; +}; diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index db59d50..b721e9f 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -7,7 +7,7 @@ #include "rpc_server.h" #include "util.h" -constexpr std::string_view SUPPORT_VERSION = "3.9.11.25"; +constexpr std::string_view SUPPORT_VERSION = "3.9.12.17"; UINT64 g_WeChatWinDllAddr = 0; @@ -32,12 +32,13 @@ int InitSpy(LPVOID args) } LOG_INFO(msg); - RpcStartServer(pp->port); + RpcServer::getInstance().start(pp->port); + return 0; } void CleanupSpy() { LOG_DEBUG("CleanupSpy"); - RpcStopServer(); + RpcServer::destroyInstance(); } From 8808c78f8ef5bf169b08e3c104cd3c3e9fb5d710 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 10 Feb 2025 20:42:53 +0800 Subject: [PATCH 046/132] refactor(util:dbg_msg): switch to OutputDebugStringW for better Unicode support --- WeChatFerry/com/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index 2c45632..ed3473b 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -251,7 +251,7 @@ void dbg_msg(const char *format, ...) vsnprintf(buffer.data(), buffer.size(), format, args); va_end(args); - OutputDebugStringA(buffer.data()); + OutputDebugStringW(s2w(buffer.data()).c_str()); } std::unique_ptr new_wx_string(const char *str) From f5d9528e1a25f33985d8f77ae35749e9f1d86854 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 11 Feb 2025 23:46:32 +0800 Subject: [PATCH 047/132] chore(python): update pb --- clients/python/wcferry/wcf_pb2.py | 463 ++++++------------------------ 1 file changed, 94 insertions(+), 369 deletions(-) diff --git a/clients/python/wcferry/wcf_pb2.py b/clients/python/wcferry/wcf_pb2.py index da7267e..acb1981 100644 --- a/clients/python/wcferry/wcf_pb2.py +++ b/clients/python/wcferry/wcf_pb2.py @@ -1,13 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: wcf.proto +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" -from google.protobuf.internal import enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import message as _message -from google.protobuf import reflection as _reflection +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'wcf.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,370 +26,86 @@ _sym_db = _symbol_database.Default() DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xff\x03\n\x07Request\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x1b\n\x05\x65mpty\x18\x02 \x01(\x0b\x32\n.wcf.EmptyH\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x03txt\x18\x04 \x01(\x0b\x32\x0c.wcf.TextMsgH\x00\x12\x1c\n\x04\x66ile\x18\x05 \x01(\x0b\x32\x0c.wcf.PathMsgH\x00\x12\x1d\n\x05query\x18\x06 \x01(\x0b\x32\x0c.wcf.DbQueryH\x00\x12\x1e\n\x01v\x18\x07 \x01(\x0b\x32\x11.wcf.VerificationH\x00\x12\x1c\n\x01m\x18\x08 \x01(\x0b\x32\x0f.wcf.MemberMgmtH\x00\x12\x1a\n\x03xml\x18\t \x01(\x0b\x32\x0b.wcf.XmlMsgH\x00\x12\x1b\n\x03\x64\x65\x63\x18\n \x01(\x0b\x32\x0c.wcf.DecPathH\x00\x12\x1b\n\x02tf\x18\x0b \x01(\x0b\x32\r.wcf.TransferH\x00\x12\x12\n\x04ui64\x18\x0c \x01(\x04\x42\x02\x30\x01H\x00\x12\x0e\n\x04\x66lag\x18\r \x01(\x08H\x00\x12\x1d\n\x03\x61tt\x18\x0e \x01(\x0b\x32\x0e.wcf.AttachMsgH\x00\x12\x1b\n\x02\x61m\x18\x0f \x01(\x0b\x32\r.wcf.AudioMsgH\x00\x12\x1b\n\x02rt\x18\x10 \x01(\x0b\x32\r.wcf.RichTextH\x00\x12\x19\n\x02pm\x18\x11 \x01(\x0b\x32\x0b.wcf.PatMsgH\x00\x12\x1d\n\x02\x66m\x18\x12 \x01(\x0b\x32\x0f.wcf.ForwardMsgH\x00\x42\x05\n\x03msg\"\xc7\x02\n\x08Response\x12\x1c\n\x04\x66unc\x18\x01 \x01(\x0e\x32\x0e.wcf.Functions\x12\x10\n\x06status\x18\x02 \x01(\x05H\x00\x12\r\n\x03str\x18\x03 \x01(\tH\x00\x12\x1b\n\x05wxmsg\x18\x04 \x01(\x0b\x32\n.wcf.WxMsgH\x00\x12\x1e\n\x05types\x18\x05 \x01(\x0b\x32\r.wcf.MsgTypesH\x00\x12$\n\x08\x63ontacts\x18\x06 \x01(\x0b\x32\x10.wcf.RpcContactsH\x00\x12\x1b\n\x03\x64\x62s\x18\x07 \x01(\x0b\x32\x0c.wcf.DbNamesH\x00\x12\x1f\n\x06tables\x18\x08 \x01(\x0b\x32\r.wcf.DbTablesH\x00\x12\x1b\n\x04rows\x18\t \x01(\x0b\x32\x0b.wcf.DbRowsH\x00\x12\x1b\n\x02ui\x18\n \x01(\x0b\x32\r.wcf.UserInfoH\x00\x12\x1a\n\x03ocr\x18\x0b \x01(\x0b\x32\x0b.wcf.OcrMsgH\x00\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xbe\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0e\n\x02id\x18\x03 \x01(\x04\x42\x02\x30\x01\x12\x0c\n\x04type\x18\x04 \x01(\r\x12\n\n\x02ts\x18\x05 \x01(\r\x12\x0e\n\x06roomid\x18\x06 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x07 \x01(\t\x12\x0e\n\x06sender\x18\x08 \x01(\t\x12\x0c\n\x04sign\x18\t \x01(\t\x12\r\n\x05thumb\x18\n \x01(\t\x12\r\n\x05\x65xtra\x18\x0b \x01(\t\x12\x0b\n\x03xml\x18\x0c \x01(\t\"7\n\x07TextMsg\x12\x0b\n\x03msg\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\x12\r\n\x05\x61ters\x18\x03 \x01(\t\")\n\x07PathMsg\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x10\n\x08receiver\x18\x02 \x01(\t\"G\n\x06XmlMsg\x12\x10\n\x08receiver\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\t\x12\x0c\n\x04path\x18\x03 \x01(\t\x12\x0c\n\x04type\x18\x04 \x01(\x04\"a\n\x08MsgTypes\x12\'\n\x05types\x18\x01 \x03(\x0b\x32\x18.wcf.MsgTypes.TypesEntry\x1a,\n\nTypesEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x87\x01\n\nRpcContact\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\t\x12\x0e\n\x06remark\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x05 \x01(\t\x12\x10\n\x08province\x18\x06 \x01(\t\x12\x0c\n\x04\x63ity\x18\x07 \x01(\t\x12\x0e\n\x06gender\x18\x08 \x01(\x05\"0\n\x0bRpcContacts\x12!\n\x08\x63ontacts\x18\x01 \x03(\x0b\x32\x0f.wcf.RpcContact\"\x18\n\x07\x44\x62Names\x12\r\n\x05names\x18\x01 \x03(\t\"$\n\x07\x44\x62Table\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"(\n\x08\x44\x62Tables\x12\x1c\n\x06tables\x18\x01 \x03(\x0b\x32\x0c.wcf.DbTable\"\"\n\x07\x44\x62Query\x12\n\n\x02\x64\x62\x18\x01 \x01(\t\x12\x0b\n\x03sql\x18\x02 \x01(\t\"8\n\x07\x44\x62\x46ield\x12\x0c\n\x04type\x18\x01 \x01(\x05\x12\x0e\n\x06\x63olumn\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\"%\n\x05\x44\x62Row\x12\x1c\n\x06\x66ields\x18\x01 \x03(\x0b\x32\x0c.wcf.DbField\"\"\n\x06\x44\x62Rows\x12\x18\n\x04rows\x18\x01 \x03(\x0b\x32\n.wcf.DbRow\"5\n\x0cVerification\x12\n\n\x02v3\x18\x01 \x01(\t\x12\n\n\x02v4\x18\x02 \x01(\t\x12\r\n\x05scene\x18\x03 \x01(\x05\"+\n\nMemberMgmt\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\r\n\x05wxids\x18\x02 \x01(\t\"D\n\x08UserInfo\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06mobile\x18\x03 \x01(\t\x12\x0c\n\x04home\x18\x04 \x01(\t\"#\n\x07\x44\x65\x63Path\x12\x0b\n\x03src\x18\x01 \x01(\t\x12\x0b\n\x03\x64st\x18\x02 \x01(\t\"4\n\x08Transfer\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x0c\n\x04tfid\x18\x02 \x01(\t\x12\x0c\n\x04taid\x18\x03 \x01(\t\"9\n\tAttachMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\r\n\x05thumb\x18\x02 \x01(\t\x12\r\n\x05\x65xtra\x18\x03 \x01(\t\"\'\n\x08\x41udioMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x0b\n\x03\x64ir\x18\x02 \x01(\t\"y\n\x08RichText\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63\x63ount\x18\x02 \x01(\t\x12\r\n\x05title\x18\x03 \x01(\t\x12\x0e\n\x06\x64igest\x18\x04 \x01(\t\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\x10\n\x08thumburl\x18\x06 \x01(\t\x12\x10\n\x08receiver\x18\x07 \x01(\t\"&\n\x06PatMsg\x12\x0e\n\x06roomid\x18\x01 \x01(\t\x12\x0c\n\x04wxid\x18\x02 \x01(\t\"(\n\x06OcrMsg\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0e\n\x06result\x18\x02 \x01(\t\".\n\nForwardMsg\x12\x0e\n\x02id\x18\x01 \x01(\x04\x42\x02\x30\x01\x12\x10\n\x08receiver\x18\x02 \x01(\t\"\xb7\x02\n\x08RoomData\x12)\n\x07members\x18\x01 \x03(\x0b\x32\x18.wcf.RoomData.RoomMember\x12\x14\n\x07\x66ield_2\x18\x02 \x01(\x05H\x00\x88\x01\x01\x12\x0f\n\x07\x66ield_3\x18\x03 \x01(\x05\x12\x14\n\x07\x66ield_4\x18\x04 \x01(\x05H\x01\x88\x01\x01\x12\x10\n\x08\x63\x61pacity\x18\x05 \x01(\x05\x12\x14\n\x07\x66ield_6\x18\x06 \x01(\tH\x02\x88\x01\x01\x12\x0f\n\x07\x66ield_7\x18\x07 \x01(\x05\x12\x0f\n\x07\x66ield_8\x18\x08 \x01(\x05\x12\x0e\n\x06\x61\x64mins\x18\t \x03(\t\x1a\x45\n\nRoomMember\x12\x0c\n\x04wxid\x18\x01 \x01(\t\x12\x11\n\x04name\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05state\x18\x03 \x01(\x05\x42\x07\n\x05_nameB\n\n\x08_field_2B\n\n\x08_field_4B\n\n\x08_field_6*\xf2\x05\n\tFunctions\x12\x11\n\rFUNC_RESERVED\x10\x00\x12\x11\n\rFUNC_IS_LOGIN\x10\x01\x12\x16\n\x12\x46UNC_GET_SELF_WXID\x10\x10\x12\x16\n\x12\x46UNC_GET_MSG_TYPES\x10\x11\x12\x15\n\x11\x46UNC_GET_CONTACTS\x10\x12\x12\x15\n\x11\x46UNC_GET_DB_NAMES\x10\x13\x12\x16\n\x12\x46UNC_GET_DB_TABLES\x10\x14\x12\x16\n\x12\x46UNC_GET_USER_INFO\x10\x15\x12\x16\n\x12\x46UNC_GET_AUDIO_MSG\x10\x16\x12\x11\n\rFUNC_SEND_TXT\x10 \x12\x11\n\rFUNC_SEND_IMG\x10!\x12\x12\n\x0e\x46UNC_SEND_FILE\x10\"\x12\x11\n\rFUNC_SEND_XML\x10#\x12\x15\n\x11\x46UNC_SEND_EMOTION\x10$\x12\x16\n\x12\x46UNC_SEND_RICH_TXT\x10%\x12\x15\n\x11\x46UNC_SEND_PAT_MSG\x10&\x12\x14\n\x10\x46UNC_FORWARD_MSG\x10\'\x12\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x16\n\x12\x46UNC_EXEC_DB_QUERY\x10P\x12\x16\n\x12\x46UNC_ACCEPT_FRIEND\x10Q\x12\x16\n\x12\x46UNC_RECV_TRANSFER\x10R\x12\x14\n\x10\x46UNC_REFRESH_PYQ\x10S\x12\x18\n\x14\x46UNC_DOWNLOAD_ATTACH\x10T\x12\x19\n\x15\x46UNC_GET_CONTACT_INFO\x10U\x12\x13\n\x0f\x46UNC_REVOKE_MSG\x10V\x12\x17\n\x13\x46UNC_REFRESH_QRCODE\x10W\x12\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x11\n\rFUNC_EXEC_OCR\x10\x61\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10q\x12\x19\n\x15\x46UNC_INV_ROOM_MEMBERS\x10rB\r\n\x0b\x63om.iamteerb\x06proto3') -_FUNCTIONS = DESCRIPTOR.enum_types_by_name['Functions'] -Functions = enum_type_wrapper.EnumTypeWrapper(_FUNCTIONS) -FUNC_RESERVED = 0 -FUNC_IS_LOGIN = 1 -FUNC_GET_SELF_WXID = 16 -FUNC_GET_MSG_TYPES = 17 -FUNC_GET_CONTACTS = 18 -FUNC_GET_DB_NAMES = 19 -FUNC_GET_DB_TABLES = 20 -FUNC_GET_USER_INFO = 21 -FUNC_GET_AUDIO_MSG = 22 -FUNC_SEND_TXT = 32 -FUNC_SEND_IMG = 33 -FUNC_SEND_FILE = 34 -FUNC_SEND_XML = 35 -FUNC_SEND_EMOTION = 36 -FUNC_SEND_RICH_TXT = 37 -FUNC_SEND_PAT_MSG = 38 -FUNC_FORWARD_MSG = 39 -FUNC_ENABLE_RECV_TXT = 48 -FUNC_DISABLE_RECV_TXT = 64 -FUNC_EXEC_DB_QUERY = 80 -FUNC_ACCEPT_FRIEND = 81 -FUNC_RECV_TRANSFER = 82 -FUNC_REFRESH_PYQ = 83 -FUNC_DOWNLOAD_ATTACH = 84 -FUNC_GET_CONTACT_INFO = 85 -FUNC_REVOKE_MSG = 86 -FUNC_REFRESH_QRCODE = 87 -FUNC_DECRYPT_IMAGE = 96 -FUNC_EXEC_OCR = 97 -FUNC_ADD_ROOM_MEMBERS = 112 -FUNC_DEL_ROOM_MEMBERS = 113 -FUNC_INV_ROOM_MEMBERS = 114 - - -_REQUEST = DESCRIPTOR.message_types_by_name['Request'] -_RESPONSE = DESCRIPTOR.message_types_by_name['Response'] -_EMPTY = DESCRIPTOR.message_types_by_name['Empty'] -_WXMSG = DESCRIPTOR.message_types_by_name['WxMsg'] -_TEXTMSG = DESCRIPTOR.message_types_by_name['TextMsg'] -_PATHMSG = DESCRIPTOR.message_types_by_name['PathMsg'] -_XMLMSG = DESCRIPTOR.message_types_by_name['XmlMsg'] -_MSGTYPES = DESCRIPTOR.message_types_by_name['MsgTypes'] -_MSGTYPES_TYPESENTRY = _MSGTYPES.nested_types_by_name['TypesEntry'] -_RPCCONTACT = DESCRIPTOR.message_types_by_name['RpcContact'] -_RPCCONTACTS = DESCRIPTOR.message_types_by_name['RpcContacts'] -_DBNAMES = DESCRIPTOR.message_types_by_name['DbNames'] -_DBTABLE = DESCRIPTOR.message_types_by_name['DbTable'] -_DBTABLES = DESCRIPTOR.message_types_by_name['DbTables'] -_DBQUERY = DESCRIPTOR.message_types_by_name['DbQuery'] -_DBFIELD = DESCRIPTOR.message_types_by_name['DbField'] -_DBROW = DESCRIPTOR.message_types_by_name['DbRow'] -_DBROWS = DESCRIPTOR.message_types_by_name['DbRows'] -_VERIFICATION = DESCRIPTOR.message_types_by_name['Verification'] -_MEMBERMGMT = DESCRIPTOR.message_types_by_name['MemberMgmt'] -_USERINFO = DESCRIPTOR.message_types_by_name['UserInfo'] -_DECPATH = DESCRIPTOR.message_types_by_name['DecPath'] -_TRANSFER = DESCRIPTOR.message_types_by_name['Transfer'] -_ATTACHMSG = DESCRIPTOR.message_types_by_name['AttachMsg'] -_AUDIOMSG = DESCRIPTOR.message_types_by_name['AudioMsg'] -_RICHTEXT = DESCRIPTOR.message_types_by_name['RichText'] -_PATMSG = DESCRIPTOR.message_types_by_name['PatMsg'] -_OCRMSG = DESCRIPTOR.message_types_by_name['OcrMsg'] -_FORWARDMSG = DESCRIPTOR.message_types_by_name['ForwardMsg'] -_ROOMDATA = DESCRIPTOR.message_types_by_name['RoomData'] -_ROOMDATA_ROOMMEMBER = _ROOMDATA.nested_types_by_name['RoomMember'] -Request = _reflection.GeneratedProtocolMessageType('Request', (_message.Message,), { - 'DESCRIPTOR' : _REQUEST, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.Request) - }) -_sym_db.RegisterMessage(Request) - -Response = _reflection.GeneratedProtocolMessageType('Response', (_message.Message,), { - 'DESCRIPTOR' : _RESPONSE, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.Response) - }) -_sym_db.RegisterMessage(Response) - -Empty = _reflection.GeneratedProtocolMessageType('Empty', (_message.Message,), { - 'DESCRIPTOR' : _EMPTY, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.Empty) - }) -_sym_db.RegisterMessage(Empty) - -WxMsg = _reflection.GeneratedProtocolMessageType('WxMsg', (_message.Message,), { - 'DESCRIPTOR' : _WXMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.WxMsg) - }) -_sym_db.RegisterMessage(WxMsg) - -TextMsg = _reflection.GeneratedProtocolMessageType('TextMsg', (_message.Message,), { - 'DESCRIPTOR' : _TEXTMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.TextMsg) - }) -_sym_db.RegisterMessage(TextMsg) - -PathMsg = _reflection.GeneratedProtocolMessageType('PathMsg', (_message.Message,), { - 'DESCRIPTOR' : _PATHMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.PathMsg) - }) -_sym_db.RegisterMessage(PathMsg) - -XmlMsg = _reflection.GeneratedProtocolMessageType('XmlMsg', (_message.Message,), { - 'DESCRIPTOR' : _XMLMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.XmlMsg) - }) -_sym_db.RegisterMessage(XmlMsg) - -MsgTypes = _reflection.GeneratedProtocolMessageType('MsgTypes', (_message.Message,), { - - 'TypesEntry' : _reflection.GeneratedProtocolMessageType('TypesEntry', (_message.Message,), { - 'DESCRIPTOR' : _MSGTYPES_TYPESENTRY, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.MsgTypes.TypesEntry) - }) - , - 'DESCRIPTOR' : _MSGTYPES, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.MsgTypes) - }) -_sym_db.RegisterMessage(MsgTypes) -_sym_db.RegisterMessage(MsgTypes.TypesEntry) - -RpcContact = _reflection.GeneratedProtocolMessageType('RpcContact', (_message.Message,), { - 'DESCRIPTOR' : _RPCCONTACT, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.RpcContact) - }) -_sym_db.RegisterMessage(RpcContact) - -RpcContacts = _reflection.GeneratedProtocolMessageType('RpcContacts', (_message.Message,), { - 'DESCRIPTOR' : _RPCCONTACTS, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.RpcContacts) - }) -_sym_db.RegisterMessage(RpcContacts) - -DbNames = _reflection.GeneratedProtocolMessageType('DbNames', (_message.Message,), { - 'DESCRIPTOR' : _DBNAMES, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbNames) - }) -_sym_db.RegisterMessage(DbNames) - -DbTable = _reflection.GeneratedProtocolMessageType('DbTable', (_message.Message,), { - 'DESCRIPTOR' : _DBTABLE, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbTable) - }) -_sym_db.RegisterMessage(DbTable) - -DbTables = _reflection.GeneratedProtocolMessageType('DbTables', (_message.Message,), { - 'DESCRIPTOR' : _DBTABLES, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbTables) - }) -_sym_db.RegisterMessage(DbTables) - -DbQuery = _reflection.GeneratedProtocolMessageType('DbQuery', (_message.Message,), { - 'DESCRIPTOR' : _DBQUERY, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbQuery) - }) -_sym_db.RegisterMessage(DbQuery) - -DbField = _reflection.GeneratedProtocolMessageType('DbField', (_message.Message,), { - 'DESCRIPTOR' : _DBFIELD, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbField) - }) -_sym_db.RegisterMessage(DbField) - -DbRow = _reflection.GeneratedProtocolMessageType('DbRow', (_message.Message,), { - 'DESCRIPTOR' : _DBROW, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbRow) - }) -_sym_db.RegisterMessage(DbRow) - -DbRows = _reflection.GeneratedProtocolMessageType('DbRows', (_message.Message,), { - 'DESCRIPTOR' : _DBROWS, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DbRows) - }) -_sym_db.RegisterMessage(DbRows) - -Verification = _reflection.GeneratedProtocolMessageType('Verification', (_message.Message,), { - 'DESCRIPTOR' : _VERIFICATION, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.Verification) - }) -_sym_db.RegisterMessage(Verification) - -MemberMgmt = _reflection.GeneratedProtocolMessageType('MemberMgmt', (_message.Message,), { - 'DESCRIPTOR' : _MEMBERMGMT, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.MemberMgmt) - }) -_sym_db.RegisterMessage(MemberMgmt) - -UserInfo = _reflection.GeneratedProtocolMessageType('UserInfo', (_message.Message,), { - 'DESCRIPTOR' : _USERINFO, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.UserInfo) - }) -_sym_db.RegisterMessage(UserInfo) - -DecPath = _reflection.GeneratedProtocolMessageType('DecPath', (_message.Message,), { - 'DESCRIPTOR' : _DECPATH, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.DecPath) - }) -_sym_db.RegisterMessage(DecPath) - -Transfer = _reflection.GeneratedProtocolMessageType('Transfer', (_message.Message,), { - 'DESCRIPTOR' : _TRANSFER, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.Transfer) - }) -_sym_db.RegisterMessage(Transfer) - -AttachMsg = _reflection.GeneratedProtocolMessageType('AttachMsg', (_message.Message,), { - 'DESCRIPTOR' : _ATTACHMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.AttachMsg) - }) -_sym_db.RegisterMessage(AttachMsg) - -AudioMsg = _reflection.GeneratedProtocolMessageType('AudioMsg', (_message.Message,), { - 'DESCRIPTOR' : _AUDIOMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.AudioMsg) - }) -_sym_db.RegisterMessage(AudioMsg) - -RichText = _reflection.GeneratedProtocolMessageType('RichText', (_message.Message,), { - 'DESCRIPTOR' : _RICHTEXT, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.RichText) - }) -_sym_db.RegisterMessage(RichText) - -PatMsg = _reflection.GeneratedProtocolMessageType('PatMsg', (_message.Message,), { - 'DESCRIPTOR' : _PATMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.PatMsg) - }) -_sym_db.RegisterMessage(PatMsg) - -OcrMsg = _reflection.GeneratedProtocolMessageType('OcrMsg', (_message.Message,), { - 'DESCRIPTOR' : _OCRMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.OcrMsg) - }) -_sym_db.RegisterMessage(OcrMsg) - -ForwardMsg = _reflection.GeneratedProtocolMessageType('ForwardMsg', (_message.Message,), { - 'DESCRIPTOR' : _FORWARDMSG, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.ForwardMsg) - }) -_sym_db.RegisterMessage(ForwardMsg) - -RoomData = _reflection.GeneratedProtocolMessageType('RoomData', (_message.Message,), { - - 'RoomMember' : _reflection.GeneratedProtocolMessageType('RoomMember', (_message.Message,), { - 'DESCRIPTOR' : _ROOMDATA_ROOMMEMBER, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.RoomData.RoomMember) - }) - , - 'DESCRIPTOR' : _ROOMDATA, - '__module__' : 'wcf_pb2' - # @@protoc_insertion_point(class_scope:wcf.RoomData) - }) -_sym_db.RegisterMessage(RoomData) -_sym_db.RegisterMessage(RoomData.RoomMember) - -if _descriptor._USE_C_DESCRIPTORS == False: - - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\013com.iamteer' - _REQUEST.fields_by_name['ui64']._options = None - _REQUEST.fields_by_name['ui64']._serialized_options = b'0\001' - _WXMSG.fields_by_name['id']._options = None - _WXMSG.fields_by_name['id']._serialized_options = b'0\001' - _MSGTYPES_TYPESENTRY._options = None - _MSGTYPES_TYPESENTRY._serialized_options = b'8\001' - _ATTACHMSG.fields_by_name['id']._options = None - _ATTACHMSG.fields_by_name['id']._serialized_options = b'0\001' - _AUDIOMSG.fields_by_name['id']._options = None - _AUDIOMSG.fields_by_name['id']._serialized_options = b'0\001' - _FORWARDMSG.fields_by_name['id']._options = None - _FORWARDMSG.fields_by_name['id']._serialized_options = b'0\001' - _FUNCTIONS._serialized_start=2728 - _FUNCTIONS._serialized_end=3482 - _REQUEST._serialized_start=19 - _REQUEST._serialized_end=530 - _RESPONSE._serialized_start=533 - _RESPONSE._serialized_end=860 - _EMPTY._serialized_start=862 - _EMPTY._serialized_end=869 - _WXMSG._serialized_start=872 - _WXMSG._serialized_end=1062 - _TEXTMSG._serialized_start=1064 - _TEXTMSG._serialized_end=1119 - _PATHMSG._serialized_start=1121 - _PATHMSG._serialized_end=1162 - _XMLMSG._serialized_start=1164 - _XMLMSG._serialized_end=1235 - _MSGTYPES._serialized_start=1237 - _MSGTYPES._serialized_end=1334 - _MSGTYPES_TYPESENTRY._serialized_start=1290 - _MSGTYPES_TYPESENTRY._serialized_end=1334 - _RPCCONTACT._serialized_start=1337 - _RPCCONTACT._serialized_end=1472 - _RPCCONTACTS._serialized_start=1474 - _RPCCONTACTS._serialized_end=1522 - _DBNAMES._serialized_start=1524 - _DBNAMES._serialized_end=1548 - _DBTABLE._serialized_start=1550 - _DBTABLE._serialized_end=1586 - _DBTABLES._serialized_start=1588 - _DBTABLES._serialized_end=1628 - _DBQUERY._serialized_start=1630 - _DBQUERY._serialized_end=1664 - _DBFIELD._serialized_start=1666 - _DBFIELD._serialized_end=1722 - _DBROW._serialized_start=1724 - _DBROW._serialized_end=1761 - _DBROWS._serialized_start=1763 - _DBROWS._serialized_end=1797 - _VERIFICATION._serialized_start=1799 - _VERIFICATION._serialized_end=1852 - _MEMBERMGMT._serialized_start=1854 - _MEMBERMGMT._serialized_end=1897 - _USERINFO._serialized_start=1899 - _USERINFO._serialized_end=1967 - _DECPATH._serialized_start=1969 - _DECPATH._serialized_end=2004 - _TRANSFER._serialized_start=2006 - _TRANSFER._serialized_end=2058 - _ATTACHMSG._serialized_start=2060 - _ATTACHMSG._serialized_end=2117 - _AUDIOMSG._serialized_start=2119 - _AUDIOMSG._serialized_end=2158 - _RICHTEXT._serialized_start=2160 - _RICHTEXT._serialized_end=2281 - _PATMSG._serialized_start=2283 - _PATMSG._serialized_end=2321 - _OCRMSG._serialized_start=2323 - _OCRMSG._serialized_end=2363 - _FORWARDMSG._serialized_start=2365 - _FORWARDMSG._serialized_end=2411 - _ROOMDATA._serialized_start=2414 - _ROOMDATA._serialized_end=2725 - _ROOMDATA_ROOMMEMBER._serialized_start=2620 - _ROOMDATA_ROOMMEMBER._serialized_end=2689 +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\013com.iamteer' + _globals['_REQUEST'].fields_by_name['ui64']._loaded_options = None + _globals['_REQUEST'].fields_by_name['ui64']._serialized_options = b'0\001' + _globals['_WXMSG'].fields_by_name['id']._loaded_options = None + _globals['_WXMSG'].fields_by_name['id']._serialized_options = b'0\001' + _globals['_MSGTYPES_TYPESENTRY']._loaded_options = None + _globals['_MSGTYPES_TYPESENTRY']._serialized_options = b'8\001' + _globals['_ATTACHMSG'].fields_by_name['id']._loaded_options = None + _globals['_ATTACHMSG'].fields_by_name['id']._serialized_options = b'0\001' + _globals['_AUDIOMSG'].fields_by_name['id']._loaded_options = None + _globals['_AUDIOMSG'].fields_by_name['id']._serialized_options = b'0\001' + _globals['_FORWARDMSG'].fields_by_name['id']._loaded_options = None + _globals['_FORWARDMSG'].fields_by_name['id']._serialized_options = b'0\001' + _globals['_FUNCTIONS']._serialized_start=2728 + _globals['_FUNCTIONS']._serialized_end=3482 + _globals['_REQUEST']._serialized_start=19 + _globals['_REQUEST']._serialized_end=530 + _globals['_RESPONSE']._serialized_start=533 + _globals['_RESPONSE']._serialized_end=860 + _globals['_EMPTY']._serialized_start=862 + _globals['_EMPTY']._serialized_end=869 + _globals['_WXMSG']._serialized_start=872 + _globals['_WXMSG']._serialized_end=1062 + _globals['_TEXTMSG']._serialized_start=1064 + _globals['_TEXTMSG']._serialized_end=1119 + _globals['_PATHMSG']._serialized_start=1121 + _globals['_PATHMSG']._serialized_end=1162 + _globals['_XMLMSG']._serialized_start=1164 + _globals['_XMLMSG']._serialized_end=1235 + _globals['_MSGTYPES']._serialized_start=1237 + _globals['_MSGTYPES']._serialized_end=1334 + _globals['_MSGTYPES_TYPESENTRY']._serialized_start=1290 + _globals['_MSGTYPES_TYPESENTRY']._serialized_end=1334 + _globals['_RPCCONTACT']._serialized_start=1337 + _globals['_RPCCONTACT']._serialized_end=1472 + _globals['_RPCCONTACTS']._serialized_start=1474 + _globals['_RPCCONTACTS']._serialized_end=1522 + _globals['_DBNAMES']._serialized_start=1524 + _globals['_DBNAMES']._serialized_end=1548 + _globals['_DBTABLE']._serialized_start=1550 + _globals['_DBTABLE']._serialized_end=1586 + _globals['_DBTABLES']._serialized_start=1588 + _globals['_DBTABLES']._serialized_end=1628 + _globals['_DBQUERY']._serialized_start=1630 + _globals['_DBQUERY']._serialized_end=1664 + _globals['_DBFIELD']._serialized_start=1666 + _globals['_DBFIELD']._serialized_end=1722 + _globals['_DBROW']._serialized_start=1724 + _globals['_DBROW']._serialized_end=1761 + _globals['_DBROWS']._serialized_start=1763 + _globals['_DBROWS']._serialized_end=1797 + _globals['_VERIFICATION']._serialized_start=1799 + _globals['_VERIFICATION']._serialized_end=1852 + _globals['_MEMBERMGMT']._serialized_start=1854 + _globals['_MEMBERMGMT']._serialized_end=1897 + _globals['_USERINFO']._serialized_start=1899 + _globals['_USERINFO']._serialized_end=1967 + _globals['_DECPATH']._serialized_start=1969 + _globals['_DECPATH']._serialized_end=2004 + _globals['_TRANSFER']._serialized_start=2006 + _globals['_TRANSFER']._serialized_end=2058 + _globals['_ATTACHMSG']._serialized_start=2060 + _globals['_ATTACHMSG']._serialized_end=2117 + _globals['_AUDIOMSG']._serialized_start=2119 + _globals['_AUDIOMSG']._serialized_end=2158 + _globals['_RICHTEXT']._serialized_start=2160 + _globals['_RICHTEXT']._serialized_end=2281 + _globals['_PATMSG']._serialized_start=2283 + _globals['_PATMSG']._serialized_end=2321 + _globals['_OCRMSG']._serialized_start=2323 + _globals['_OCRMSG']._serialized_end=2363 + _globals['_FORWARDMSG']._serialized_start=2365 + _globals['_FORWARDMSG']._serialized_end=2411 + _globals['_ROOMDATA']._serialized_start=2414 + _globals['_ROOMDATA']._serialized_end=2725 + _globals['_ROOMDATA_ROOMMEMBER']._serialized_start=2620 + _globals['_ROOMDATA_ROOMMEMBER']._serialized_end=2689 # @@protoc_insertion_point(module_scope) From 32876209a4a21e334b22db2eb13107f6aa11c9ab Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 12 Feb 2025 00:47:54 +0800 Subject: [PATCH 048/132] feat(account): add user info retrieval and RPC methods --- WeChatFerry/spy/account_manager.cpp | 87 +++++++++++++---------------- WeChatFerry/spy/offsets.h | 21 +++++++ WeChatFerry/spy/rpc_server.cpp | 4 +- 3 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 WeChatFerry/spy/offsets.h diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index d5f9608..93b0b11 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -1,9 +1,9 @@ #include "account_manager.h" #include -#include #include "log.hpp" +#include "offsets.h" #include "rpc_helper.h" #include "util.h" @@ -11,63 +11,56 @@ extern UINT64 g_WeChatWinDllAddr; namespace account { -#define OS_LOGIN_STATUS 0x595C9E8 -#define OS_USER_HOME 0x5932770 -#define OS_USER_WXID 0x595C270 -#define OS_USER_NAME 0x595C3D8 -#define OS_USER_MOBILE 0x595C318 -bool is_logged_in() { return util::get_qword(g_WeChatWinDllAddr + OS_LOGIN_STATUS) != 0; } +namespace OsAcc = Offsets::Account; -std::string get_home_path() +using get_account_service_t = QWORD (*)(); +using get_current_data_path_t = QWORD (*)(QWORD); + +static uint64_t get_account_service() { - static std::once_flag flag; - static std::string home_path; + static auto GetService = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::SERVICE); + return GetService ? GetService() : 0; +} - std::call_once(flag, [] { - std::string path = util::w2s(util::get_pp_wstring(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; - home_path = std::filesystem::absolute(path).string(); - }); +static std::string get_string_value(uint64_t base_addr, uint64_t offset) +{ + uint64_t type = util::get_qword(base_addr + offset + 0x18); + return (type == 0xF) ? util::get_p_string(base_addr + offset) : util::get_pp_string(base_addr + offset); +} - return home_path; +bool is_logged_in() +{ + uint64_t service_addr = get_account_service(); + return service_addr && util::get_qword(service_addr + OsAcc::LOGIN) != 0; } std::string get_self_wxid() { - static std::once_flag flag; - static std::string wxid; + uint64_t service_addr = get_account_service(); + if (!service_addr) return ""; - std::call_once(flag, [] { - UINT64 wxid_type = 0; - try { - wxid_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); - if (wxid_type == 0xF) { - wxid = util::get_p_string(g_WeChatWinDllAddr + OS_USER_WXID); - } else { - wxid = util::get_pp_string(g_WeChatWinDllAddr + OS_USER_WXID); - } - - } catch (...) { - LOG_ERROR("Failed to get wxid, type: {:#x}", wxid_type); - LOG_BUFFER(reinterpret_cast(g_WeChatWinDllAddr + OS_USER_WXID), 20); - wxid = "获取wxid失败"; - } - }); - return wxid; + return get_string_value(service_addr, OsAcc::WXID); } UserInfo_t get_user_info() { UserInfo_t ui; + WxString home; + + auto GetDataPath = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::PATH); + + uint64_t service_addr = get_account_service(); + if (!service_addr) return ui; + ui.wxid = get_self_wxid(); - - UINT64 name_type = util::get_qword(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); - ui.name = (name_type == 0xF) ? util::get_p_string(g_WeChatWinDllAddr + OS_USER_NAME) - : util::get_pp_string(g_WeChatWinDllAddr + OS_USER_NAME); - - ui.mobile = util::get_p_string(g_WeChatWinDllAddr + OS_USER_MOBILE); - ui.home = get_home_path(); - + GetDataPath((QWORD)&home); + if (home.wptr) { + std::filesystem::path path = util::w2s(std::wstring(home.wptr, home.size)); + ui.home = path.generic_string(); + } + ui.name = get_string_value(service_addr, OsAcc::NAME); + ui.mobile = get_string_value(service_addr, OsAcc::MOBILE); return ui; } @@ -85,11 +78,11 @@ bool rpc_get_self_wxid(uint8_t *out, size_t *len) bool rpc_get_user_info(uint8_t *out, size_t *len) { return fill_response(out, len, [](Response &rsp) { - UserInfo_t ui = get_user_info(); - rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); - rsp.msg.ui.name = (char *)ui.name.c_str(); - rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); - rsp.msg.ui.home = (char *)ui.home.c_str(); + static UserInfo_t ui = get_user_info(); + rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); + rsp.msg.ui.name = (char *)ui.name.c_str(); + rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); + rsp.msg.ui.home = (char *)ui.home.c_str(); }); } diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h new file mode 100644 index 0000000..05f61e5 --- /dev/null +++ b/WeChatFerry/spy/offsets.h @@ -0,0 +1,21 @@ +#ifndef OFFSETS_H +#define OFFSETS_H + +#include + +namespace Offsets +{ + +namespace Account +{ + constexpr uint64_t SERVICE = 0x1B58B50; // 账户服务 + constexpr uint64_t PATH = 0x2250920; // 数据路径 + constexpr uint64_t WXID = 0x80; // WXID + constexpr uint64_t NAME = 0x1E8; // 昵称 + constexpr uint64_t MOBILE = 0x128; // 手机号 + constexpr uint64_t LOGIN = 0x7F8; // 登录状态 +} + +} + +#endif // OFFSETS_H diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index dbbc02a..ddfcd32 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -221,8 +221,8 @@ bool RpcServer::disableRecvMsg(uint8_t *out, size_t *len) const std::unordered_map RpcServer::rpcFunctionMap = { // clang-format off { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_is_logged_in(out, len); } }, - // { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, - // { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, + { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, + { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, // { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, From cdd9fe9a91cae2c37bf5cf3c21a91eae680c61dd Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 12 Feb 2025 00:53:20 +0800 Subject: [PATCH 049/132] feat(python): add disclaimer --- clients/python/wcferry/DISCLAIMER.md | 18 ++++++++++++++++++ clients/python/wcferry/client.py | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 clients/python/wcferry/DISCLAIMER.md diff --git a/clients/python/wcferry/DISCLAIMER.md b/clients/python/wcferry/DISCLAIMER.md new file mode 100644 index 0000000..520a4ac --- /dev/null +++ b/clients/python/wcferry/DISCLAIMER.md @@ -0,0 +1,18 @@ +# 免责声明 + +1. **本工具为开源项目,仅提供基础功能,供用户进行合法的学习、研究和非商业用途**。禁止将本工具用于任何违法或侵权行为。 + +2. **二次开发者的责任**: + - 任何基于本工具进行的二次开发、修改或衍生产品,其行为及后果由二次开发者独立承担,与本工具贡献者无关。 + - **禁止使用贡献者的姓名、项目名称或相关信息作为二次开发产品的背书或推广手段**。 + - 建议二次开发者在其衍生产品中添加自己的免责声明,明确责任归属。 + +3. **用户责任**: + - 使用本工具或其衍生产品的所有后果由用户自行承担。原贡献者不对因直接或间接使用本工具而导致的任何损失、责任或争议负责。 + +4. **法律约束**: + - 用户和二次开发者须遵守《中华人民共和国网络安全法》、《中华人民共和国著作权法》等相关法律法规。 + - 本工具涉及的所有第三方商标或产品名称,其权利归权利人所有,作者与第三方无任何直接或间接关联。 + +5. **作者保留权利**: + - 本工具作者保留随时修改、更新、删除或终止本工具的权利,无需事先通知或承担任何义务。 diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 6583014..507026e 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -13,8 +13,11 @@ import re import sys from queue import Queue from threading import Thread +import shutil from time import sleep from typing import Callable, Dict, List, Optional +from pathlib import Path +import importlib.resources as pkg_resources # Python 3.9+ import pynng import requests @@ -77,6 +80,7 @@ class Wcf(): if host is None: self._local_mode = True self.host = "127.0.0.1" + self._copy_disclaimer_to_cwd() self.sdk = ctypes.cdll.LoadLibrary(f"{self._wcf_root}/sdk.dll") if self.sdk.WxInitSDK(debug, port) != 0: self.LOG.error("初始化失败!") @@ -115,6 +119,17 @@ class Wcf(): def __del__(self) -> None: self.cleanup() + def _copy_disclaimer_to_cwd(self): + """复制免责声明到工作目录""" + try: + target_path = Path.cwd() / "DISCLAIMER.md" + with pkg_resources.path("wcferry", "DISCLAIMER.md") as disclaimer_path: + if not target_path.exists(): + shutil.copy(disclaimer_path, target_path) + except Exception as e: + self.LOG.error(f"复制免责声明失败:{e}") + os._exit(-3) + def cleanup(self) -> None: """关闭连接,回收资源""" if not self._is_running: From 61c6ad643fca3fe914a6cbf468d8cc5590bf3aeb Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 12 Feb 2025 01:15:05 +0800 Subject: [PATCH 050/132] chore(debug): add hex dump logging for received rpc messages --- clients/python/wcferry/client.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 507026e..a4ebe12 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -156,7 +156,9 @@ class Wcf(): data = req.SerializeToString() self.cmd_socket.send(data) rsp = wcf_pb2.Response() - rsp.ParseFromString(self.cmd_socket.recv_msg().bytes) + bs = self.cmd_socket.recv_msg().bytes + self.LOG.debug(bs.hex()) + rsp.ParseFromString(bs) return rsp def is_receiving_msg(self) -> bool: From 95e90e4afbb1f9cbe3472c3c3b661d74c31bc1b4 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 12 Feb 2025 01:19:19 +0800 Subject: [PATCH 051/132] refactor(rpc): add fill_response template --- WeChatFerry/spy/rpc_helper.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/WeChatFerry/spy/rpc_helper.h b/WeChatFerry/spy/rpc_helper.h index 963bcf7..feeb081 100644 --- a/WeChatFerry/spy/rpc_helper.h +++ b/WeChatFerry/spy/rpc_helper.h @@ -65,3 +65,28 @@ template bool fill_response(uint8_t *o *len = stream.bytes_written; return true; } + +template +bool fill_response(uint8_t *out, size_t *len, DataType &&data, AssignFunc &&assign) +{ + Response rsp = Response_init_default; + rsp.func = FuncType; + + auto it = rpc_tag_map.find(FuncType); + if (it == rpc_tag_map.end()) { + LOG_ERROR("Unknown function type: {}", magic_enum::enum_name(rsp.func)); + return false; + } + rsp.which_msg = it->second; + + assign(rsp, data); + + pb_ostream_t stream = pb_ostream_from_buffer(out, *len); + if (!pb_encode(&stream, Response_fields, &rsp)) { + LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream)); + return false; + } + *len = stream.bytes_written; + + return true; +} From e121000efa5c0e0c69681dd75c3e8a7771a37ee5 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 12 Feb 2025 01:32:26 +0800 Subject: [PATCH 052/132] refactor(account): update rpc_get_user_info with new fill_response template --- WeChatFerry/spy/account_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index 93b0b11..c0d3dc0 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -77,12 +77,12 @@ bool rpc_get_self_wxid(uint8_t *out, size_t *len) bool rpc_get_user_info(uint8_t *out, size_t *len) { - return fill_response(out, len, [](Response &rsp) { - static UserInfo_t ui = get_user_info(); - rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); - rsp.msg.ui.name = (char *)ui.name.c_str(); - rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); - rsp.msg.ui.home = (char *)ui.home.c_str(); + UserInfo_t ui = get_user_info(); + return fill_response(out, len, ui, [](Response &rsp, UserInfo_t &ui) { + rsp.msg.ui.wxid = (char *)ui.wxid.c_str(); + rsp.msg.ui.name = (char *)ui.name.c_str(); + rsp.msg.ui.mobile = (char *)ui.mobile.c_str(); + rsp.msg.ui.home = (char *)ui.home.c_str(); }); } From fdeda6d413c21aa1848f50d110c42f9fbffcd1b1 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 13 Feb 2025 00:10:04 +0800 Subject: [PATCH 053/132] feat(rpc_server): integrate MessageHandler into RpcServer --- WeChatFerry/spy/rpc_server.cpp | 29 +++++++++++++---------------- WeChatFerry/spy/rpc_server.h | 4 ++++ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index ddfcd32..8f01ab7 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -54,7 +54,8 @@ void RpcServer::destroyInstance() } } -RpcServer::RpcServer(int port) : port_(port) { LOG_DEBUG("RpcServer 构造: 端口 {}", port_); } +RpcServer::RpcServer(int port) : port_(port), handler_(message::Handler::getInstance() { + LOG_DEBUG("RpcServer 构造: 端口 {}", port_); } RpcServer::~RpcServer() { @@ -99,9 +100,8 @@ int RpcServer::stop() } isRunning_ = false; - auto &handler = message::Handler::getInstance(); - handler.UnListenPyq(); - handler.UnListenMsg(); + handler_.UnListenPyq(); + handler_.UnListenMsg(); #if ENABLE_WX_LOG DisableLog(); #endif @@ -149,16 +149,15 @@ void RpcServer::receiveMessageCallback() return; } - auto &handler = message::Handler::getInstance(); - while (handler.isMessageListening()) { - std::unique_lock lock(handler.getMutex()); + while (handler_.isMessageListening()) { + std::unique_lock lock(handler_.getMutex()); std::optional msgOpt; auto hasMessage = [&]() { - msgOpt = handler.popMessage(); + msgOpt = handler_.popMessage(); return msgOpt.has_value(); }; - if (handler.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { + if (handler_.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { WxMsg_t wxmsg = std::move(msgOpt.value()); rsp.msg.wxmsg.id = wxmsg.id; rsp.msg.wxmsg.is_self = wxmsg.is_self; @@ -193,11 +192,10 @@ void RpcServer::receiveMessageCallback() bool RpcServer::enableRecvMsg(bool pyq, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { - auto &handler = message::Handler::getInstance(); - rsp.msg.status = handler.ListenMsg(); + rsp.msg.status = handler_.ListenMsg(); if (rsp.msg.status == 0) { if (pyq) { - handler.ListenPyq(); + handler_.ListenPyq(); } msgThread_ = std::thread(&RpcServer::receiveMessageCallback, this); } @@ -207,10 +205,9 @@ bool RpcServer::enableRecvMsg(bool pyq, uint8_t *out, size_t *len) bool RpcServer::disableRecvMsg(uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { - auto &handler = message::Handler::getInstance(); - rsp.msg.status = handler.UnListenMsg(); + rsp.msg.status = handler_.UnListenMsg(); if (rsp.msg.status == 0) { - handler.UnListenPyq(); + handler_.UnListenPyq(); if (msgThread_.joinable()) { msgThread_.join(); } @@ -223,7 +220,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_is_logged_in(out, len); } }, { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, - // { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler.rpc_get_msg_types(out, len); } }, + // { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler_.rpc_get_msg_types(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, diff --git a/WeChatFerry/spy/rpc_server.h b/WeChatFerry/spy/rpc_server.h index 400bbff..468689e 100644 --- a/WeChatFerry/spy/rpc_server.h +++ b/WeChatFerry/spy/rpc_server.h @@ -9,6 +9,8 @@ #include "wcf.pb.h" +#include "message_handler.h" + class RpcServer { public: @@ -43,6 +45,8 @@ private: std::thread cmdThread_; std::thread msgThread_; + message::Handler &handler_; + struct Deleter { void operator()(RpcServer *server) const { delete server; } }; From f6ceb8139a32f3f1f2ed216aab8fad7fbd8e2c55 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 13 Feb 2025 00:13:29 +0800 Subject: [PATCH 054/132] feat(rpc_server): integrate MessageHandler into RpcServer --- WeChatFerry/spy/rpc_server.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 8f01ab7..418af65 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -54,8 +54,10 @@ void RpcServer::destroyInstance() } } -RpcServer::RpcServer(int port) : port_(port), handler_(message::Handler::getInstance() { - LOG_DEBUG("RpcServer 构造: 端口 {}", port_); } +RpcServer::RpcServer(int port) : port_(port), handler_(message::Handler::getInstance()) +{ + LOG_DEBUG("RpcServer 构造: 端口 {}", port_); +} RpcServer::~RpcServer() { From 2297afa25dbf2fc2d5a1016d12ca9dac2baaa6f9 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Feb 2025 13:52:31 +0800 Subject: [PATCH 055/132] fix(rpc_server): fix log function --- WeChatFerry/spy/rpc_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 418af65..7e2b33b 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -88,7 +88,7 @@ int RpcServer::start(int port) return -2; } #if ENABLE_WX_LOG - EnableLog(); + handler_.EnableLog(); #endif LOG_INFO("RPC 服务器成功启动,监听端口: {}", port_); return 0; @@ -105,7 +105,7 @@ int RpcServer::stop() handler_.UnListenPyq(); handler_.UnListenMsg(); #if ENABLE_WX_LOG - DisableLog(); + handler_.DisableLog(); #endif nng_fini(); if (cmdThread_.joinable()) { From 3658796a00fb415116337a293403cb9dd98ed67b Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 17 Feb 2025 00:42:41 +0800 Subject: [PATCH 056/132] feat(message): impl wechat log --- WeChatFerry/spy/message_handler.cpp | 20 +++++++++++++------- WeChatFerry/spy/message_handler.h | 1 + WeChatFerry/spy/offsets.h | 5 +++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 3bf9882..2039525 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -6,10 +6,11 @@ #include "framework.h" +#include "account_manager.h" #include "log.hpp" +#include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" -#include "account_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; @@ -37,6 +38,8 @@ extern QWORD g_WeChatWinDllAddr; namespace message { +namespace OsMsg = Offsets::Message; + QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2) { auto &handler = getInstance(); @@ -190,13 +193,15 @@ int Handler::EnableLog() { if (isLogging) return 1; - funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OS_WXLOG); + funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsMsg::LOG); + pLogLevel = reinterpret_cast(g_WeChatWinDllAddr + OsMsg::LOG_LEVEL); if (InitializeHook() != MH_OK) return -1; - if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -1; - if (MH_EnableHook(funcWxLog) != MH_OK) return -1; + if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -2; + if (MH_EnableHook(funcWxLog) != MH_OK) return -3; - isLogging = true; + *pLogLevel = 0; + isLogging = true; return 0; } @@ -204,8 +209,9 @@ int Handler::DisableLog() { if (!isLogging) return 1; if (MH_DisableHook(funcWxLog) != MH_OK) return -1; - if (UninitializeHook() != MH_OK) return -1; - isLogging = false; + if (UninitializeHook() != MH_OK) return -2; + *pLogLevel = 6; + isLogging = false; return 0; } diff --git a/WeChatFerry/spy/message_handler.h b/WeChatFerry/spy/message_handler.h index c4227ec..2937b11 100644 --- a/WeChatFerry/spy/message_handler.h +++ b/WeChatFerry/spy/message_handler.h @@ -55,6 +55,7 @@ private: using funcWxLog_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); using funcRecvPyq_t = QWORD (*)(QWORD, QWORD, QWORD); + uint32_t *pLogLevel; funcWxLog_t funcWxLog, realWxLog; funcRecvMsg_t funcRecvMsg, realRecvMsg; funcRecvPyq_t funcRecvPyq, realRecvPyq; diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 05f61e5..02625b9 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -16,6 +16,11 @@ namespace Account constexpr uint64_t LOGIN = 0x7F8; // 登录状态 } +namespace Message +{ + constexpr uint64_t LOG = 0x261B890; // 日志函数 + constexpr uint64_t LOG_LEVEL = 0x56E4244; // 日志级别 +} } #endif // OFFSETS_H From 36f30a9d0a171f62714c7e1b5c146954d55beb0e Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 17 Feb 2025 01:29:07 +0800 Subject: [PATCH 057/132] refactor(message_handler): split offset namespace --- WeChatFerry/spy/message_handler.cpp | 6 +++--- WeChatFerry/spy/offsets.h | 7 +++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 2039525..92adf64 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -38,7 +38,7 @@ extern QWORD g_WeChatWinDllAddr; namespace message { -namespace OsMsg = Offsets::Message; +namespace OsLog = Offsets::Message::Log; QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2) { @@ -193,8 +193,8 @@ int Handler::EnableLog() { if (isLogging) return 1; - funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsMsg::LOG); - pLogLevel = reinterpret_cast(g_WeChatWinDllAddr + OsMsg::LOG_LEVEL); + pLogLevel = reinterpret_cast(g_WeChatWinDllAddr + OsLog::LEVEL); + funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsLog::FUNCTION); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -2; diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 02625b9..447c077 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -18,8 +18,11 @@ namespace Account namespace Message { - constexpr uint64_t LOG = 0x261B890; // 日志函数 - constexpr uint64_t LOG_LEVEL = 0x56E4244; // 日志级别 + namespace Log + { + constexpr uint64_t FUNCTION = 0x261B890; // 日志函数 + constexpr uint64_t LEVEL = 0x56E4244; // 日志级别 + } } } From b6a550a165c559a69266774fc72bbfd23ab18f0d Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 17 Feb 2025 19:09:18 +0800 Subject: [PATCH 058/132] refactor(message_handler): rename log call offset --- WeChatFerry/spy/message_handler.cpp | 2 +- WeChatFerry/spy/offsets.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 92adf64..34b8adf 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -194,7 +194,7 @@ int Handler::EnableLog() if (isLogging) return 1; pLogLevel = reinterpret_cast(g_WeChatWinDllAddr + OsLog::LEVEL); - funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsLog::FUNCTION); + funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsLog::CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -2; diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 447c077..a394d0c 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -20,8 +20,8 @@ namespace Message { namespace Log { - constexpr uint64_t FUNCTION = 0x261B890; // 日志函数 - constexpr uint64_t LEVEL = 0x56E4244; // 日志级别 + constexpr uint64_t LEVEL = 0x56E4244; // 日志级别 + constexpr uint64_t CALL = 0x261B890; // 日志函数 } } } From 132fa51b2d353e3f6cd36da8b7820a70c656be96 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 17 Feb 2025 21:51:17 +0800 Subject: [PATCH 059/132] feat(spy): impl get message types --- WeChatFerry/spy/message_handler.cpp | 2 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 34b8adf..7f7e8df 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -277,8 +277,8 @@ MH_STATUS Handler::UninitializeHook() bool Handler::rpc_get_msg_types(uint8_t *out, size_t *len) { + MsgTypes_t types = GetMsgTypes(); return fill_response(out, len, [&](Response &rsp) { - MsgTypes_t types = GetMsgTypes(); rsp.msg.types.types.funcs.encode = encode_types; rsp.msg.types.types.arg = &types; }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 7e2b33b..0d6842b 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -222,7 +222,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_IS_LOGIN, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_is_logged_in(out, len); } }, { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, - // { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return handler_.rpc_get_msg_types(out, len); } }, + { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().handler_.rpc_get_msg_types(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, From 7c63ae904a36eadc19cfda7b5f0fa70fb8994efb Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 17 Feb 2025 21:59:59 +0800 Subject: [PATCH 060/132] feat(account): cache self wxid --- WeChatFerry/spy/account_manager.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index c0d3dc0..de4a6f6 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -17,6 +17,12 @@ namespace OsAcc = Offsets::Account; using get_account_service_t = QWORD (*)(); using get_current_data_path_t = QWORD (*)(QWORD); +// 缓存 wxid 避免重复查询 +static std::optional cachedWxid; + +// 清除缓存的 wxid +static void clear_cached_wxid() { cachedWxid.reset(); } + static uint64_t get_account_service() { static auto GetService = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::SERVICE); @@ -31,16 +37,21 @@ static std::string get_string_value(uint64_t base_addr, uint64_t offset) bool is_logged_in() { + clear_cached_wxid(); uint64_t service_addr = get_account_service(); return service_addr && util::get_qword(service_addr + OsAcc::LOGIN) != 0; } std::string get_self_wxid() { + if (cachedWxid) { + return *cachedWxid; + } uint64_t service_addr = get_account_service(); if (!service_addr) return ""; - return get_string_value(service_addr, OsAcc::WXID); + cachedWxid = get_string_value(service_addr, OsAcc::WXID); + return *cachedWxid; } UserInfo_t get_user_info() From fdef55bcf6be8ed9ba991d951652659d57ca5a89 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 00:33:35 +0800 Subject: [PATCH 061/132] fix(account): fix home path --- WeChatFerry/spy/account_manager.cpp | 38 +++++++++++++++++++---------- WeChatFerry/spy/offsets.h | 2 +- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index de4a6f6..44f8970 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -12,16 +12,19 @@ extern UINT64 g_WeChatWinDllAddr; namespace account { +namespace fs = std::filesystem; namespace OsAcc = Offsets::Account; using get_account_service_t = QWORD (*)(); -using get_current_data_path_t = QWORD (*)(QWORD); +using get_data_path_t = QWORD (*)(QWORD); -// 缓存 wxid 避免重复查询 +// 缓存避免重复查询 static std::optional cachedWxid; +static std::optional cachedHomePath; -// 清除缓存的 wxid +// 清除缓存 static void clear_cached_wxid() { cachedWxid.reset(); } +static void clear_cached_home_path() { cachedHomePath.reset(); } static uint64_t get_account_service() { @@ -38,10 +41,27 @@ static std::string get_string_value(uint64_t base_addr, uint64_t offset) bool is_logged_in() { clear_cached_wxid(); + clear_cached_home_path(); uint64_t service_addr = get_account_service(); return service_addr && util::get_qword(service_addr + OsAcc::LOGIN) != 0; } +std::string get_home_path() +{ + if (cachedHomePath) { + return *cachedHomePath; + } + WxString home; + auto GetDataPath = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::PATH); + int64_t service_addr = get_account_service(); + GetDataPath((QWORD)&home); + if (home.wptr) { + fs::path path = util::w2s(std::wstring(home.wptr, home.size)); + cachedHomePath = path.generic_string(); + } + return *cachedHomePath; +} + std::string get_self_wxid() { if (cachedWxid) { @@ -57,19 +77,11 @@ std::string get_self_wxid() UserInfo_t get_user_info() { UserInfo_t ui; - WxString home; - - auto GetDataPath = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::PATH); - uint64_t service_addr = get_account_service(); if (!service_addr) return ui; - ui.wxid = get_self_wxid(); - GetDataPath((QWORD)&home); - if (home.wptr) { - std::filesystem::path path = util::w2s(std::wstring(home.wptr, home.size)); - ui.home = path.generic_string(); - } + ui.wxid = get_self_wxid(); + ui.home = get_home_path(); ui.name = get_string_value(service_addr, OsAcc::NAME); ui.mobile = get_string_value(service_addr, OsAcc::MOBILE); return ui; diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index a394d0c..1a9d415 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -9,7 +9,7 @@ namespace Offsets namespace Account { constexpr uint64_t SERVICE = 0x1B58B50; // 账户服务 - constexpr uint64_t PATH = 0x2250920; // 数据路径 + constexpr uint64_t PATH = 0x25E9090; // 数据路径 constexpr uint64_t WXID = 0x80; // WXID constexpr uint64_t NAME = 0x1E8; // 昵称 constexpr uint64_t MOBILE = 0x128; // 手机号 From 48bf47f1e4b9c503dd94ae40001656f91cdea91d Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 00:35:41 +0800 Subject: [PATCH 062/132] style(account): reformat code --- WeChatFerry/spy/account_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index 44f8970..e7844a4 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -15,8 +15,8 @@ namespace account namespace fs = std::filesystem; namespace OsAcc = Offsets::Account; -using get_account_service_t = QWORD (*)(); -using get_data_path_t = QWORD (*)(QWORD); +using get_account_service_t = QWORD (*)(); +using get_data_path_t = QWORD (*)(QWORD); // 缓存避免重复查询 static std::optional cachedWxid; From f964dba48ac454e8215b39e522fc90d496285044 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 01:11:46 +0800 Subject: [PATCH 063/132] refactor(account): change home_path type to fs path --- WeChatFerry/spy/account_manager.cpp | 9 ++++----- WeChatFerry/spy/account_manager.h | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index e7844a4..3043c0f 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -20,7 +20,7 @@ using get_data_path_t = QWORD (*)(QWORD); // 缓存避免重复查询 static std::optional cachedWxid; -static std::optional cachedHomePath; +static std::optional cachedHomePath; // 清除缓存 static void clear_cached_wxid() { cachedWxid.reset(); } @@ -46,7 +46,7 @@ bool is_logged_in() return service_addr && util::get_qword(service_addr + OsAcc::LOGIN) != 0; } -std::string get_home_path() +fs::path get_home_path() { if (cachedHomePath) { return *cachedHomePath; @@ -56,8 +56,7 @@ std::string get_home_path() int64_t service_addr = get_account_service(); GetDataPath((QWORD)&home); if (home.wptr) { - fs::path path = util::w2s(std::wstring(home.wptr, home.size)); - cachedHomePath = path.generic_string(); + cachedHomePath = util::w2s(std::wstring(home.wptr, home.size)); } return *cachedHomePath; } @@ -81,7 +80,7 @@ UserInfo_t get_user_info() if (!service_addr) return ui; ui.wxid = get_self_wxid(); - ui.home = get_home_path(); + ui.home = get_home_path().generic_string(); ui.name = get_string_value(service_addr, OsAcc::NAME); ui.mobile = get_string_value(service_addr, OsAcc::MOBILE); return ui; diff --git a/WeChatFerry/spy/account_manager.h b/WeChatFerry/spy/account_manager.h index e184475..4eede78 100644 --- a/WeChatFerry/spy/account_manager.h +++ b/WeChatFerry/spy/account_manager.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -12,7 +13,7 @@ namespace account bool is_logged_in(); // 获取 WeChat 数据存储路径 -std::string get_home_path(); +std::filesystem::path get_home_path(); // 获取自身 wxid std::string get_self_wxid(); From 6371f83b872f04b90924b695e4075b723a2c7cae Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 01:14:20 +0800 Subject: [PATCH 064/132] feat(message): impl message handler --- WeChatFerry/spy/message_handler.cpp | 72 +++++++++++----------- WeChatFerry/spy/offsets.h | 16 +++++ WeChatFerry/spy/rpc_server.cpp | 92 ++++++++++++++++++----------- WeChatFerry/spy/rpc_server.h | 8 +-- 4 files changed, 113 insertions(+), 75 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 7f7e8df..db3cc1f 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -1,6 +1,7 @@ #include "message_handler.h" #include +#include #include #include @@ -15,60 +16,61 @@ extern QWORD g_WeChatWinDllAddr; -#define OS_RECV_MSG_ID 0x30 -#define OS_RECV_MSG_TYPE 0x38 -#define OS_RECV_MSG_SELF 0x3C -#define OS_RECV_MSG_TS 0x44 -#define OS_RECV_MSG_ROOMID 0x48 -#define OS_RECV_MSG_CONTENT 0x88 -#define OS_RECV_MSG_WXID 0x240 -#define OS_RECV_MSG_SIGN 0x260 -#define OS_RECV_MSG_THUMB 0x280 -#define OS_RECV_MSG_EXTRA 0x2A0 -#define OS_RECV_MSG_XML 0x308 -#define OS_RECV_MSG_CALL 0x213ED90 -#define OS_PYQ_MSG_START 0x30 -#define OS_PYQ_MSG_END 0x38 -#define OS_PYQ_MSG_TS 0x38 -#define OS_PYQ_MSG_XML 0x9B8 -#define OS_PYQ_MSG_SENDER 0x18 -#define OS_PYQ_MSG_CONTENT 0x48 -#define OS_PYQ_MSG_CALL 0x2E42C90 -#define OS_WXLOG 0x2613D20 +#define OS_PYQ_MSG_START 0x30 +#define OS_PYQ_MSG_END 0x38 +#define OS_PYQ_MSG_TS 0x38 +#define OS_PYQ_MSG_XML 0x9B8 +#define OS_PYQ_MSG_SENDER 0x18 +#define OS_PYQ_MSG_CONTENT 0x48 +#define OS_PYQ_MSG_CALL 0x2E42C90 namespace message { -namespace OsLog = Offsets::Message::Log; + +namespace fs = std::filesystem; + +namespace OsLog = Offsets::Message::Log; +namespace OsRecv = Offsets::Message::Receive; QWORD Handler::DispatchMsg(QWORD arg1, QWORD arg2) { auto &handler = getInstance(); WxMsg_t wxMsg = {}; try { - wxMsg.id = util::get_qword(arg2 + OS_RECV_MSG_ID); - wxMsg.type = util::get_dword(arg2 + OS_RECV_MSG_TYPE); - wxMsg.is_self = util::get_dword(arg2 + OS_RECV_MSG_SELF); - wxMsg.ts = util::get_dword(arg2 + OS_RECV_MSG_TS); - wxMsg.content = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_CONTENT); - wxMsg.sign = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_SIGN); - wxMsg.xml = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_XML); - wxMsg.roomid = util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_ROOMID); + wxMsg.id = util::get_qword(arg2 + OsRecv::ID); + wxMsg.type = util::get_dword(arg2 + OsRecv::TYPE); + wxMsg.is_self = util::get_dword(arg2 + OsRecv::SELF); + wxMsg.ts = util::get_dword(arg2 + OsRecv::TIMESTAMP); + wxMsg.content = util::get_str_by_wstr_addr(arg2 + OsRecv::CONTENT); + wxMsg.sign = util::get_str_by_wstr_addr(arg2 + OsRecv::SIGN); + wxMsg.xml = util::get_str_by_wstr_addr(arg2 + OsRecv::XML); + wxMsg.roomid = util::get_str_by_wstr_addr(arg2 + OsRecv::ROOMID); - if (wxMsg.roomid.find("@chatroom") != std::string::npos) { + if (wxMsg.roomid.find("@chatroom") != std::string::npos) { // 群 ID 的格式为 xxxxxxxxxxx@chatroom wxMsg.is_group = true; - wxMsg.sender - = wxMsg.is_self ? account::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OS_RECV_MSG_WXID); + wxMsg.sender = wxMsg.is_self ? account::get_self_wxid() : util::get_str_by_wstr_addr(arg2 + OsRecv::WXID); } else { wxMsg.is_group = false; wxMsg.sender = wxMsg.is_self ? account::get_self_wxid() : wxMsg.roomid; } + + fs::path thumb = util::get_str_by_wstr_addr(arg2 + OsRecv::THUMB); + if (!thumb.empty()) { + wxMsg.thumb = (account::get_home_path() / thumb).generic_string(); + } + + fs::path extra = util::get_str_by_wstr_addr(arg2 + OsRecv::EXTRA); + if (!extra.empty()) { + wxMsg.extra = (account::get_home_path() / extra).generic_string(); + } + LOG_DEBUG("{}", wxMsg.content); } catch (const std::exception &e) { LOG_ERROR(util::gb2312_to_utf8(e.what())); } { std::unique_lock lock(handler.mutex_); - handler.msgQueue_.push(wxMsg); + handler.msgQueue_.push(wxMsg); // 推送到队列 } handler.cv_.notify_all(); @@ -219,7 +221,7 @@ int Handler::ListenMsg() { if (isListeningMsg) return 1; - funcRecvMsg = reinterpret_cast(g_WeChatWinDllAddr + OS_RECV_MSG_CALL); + funcRecvMsg = reinterpret_cast(g_WeChatWinDllAddr + OsRecv::CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)) != MH_OK) return -1; if (MH_EnableHook(funcRecvMsg) != MH_OK) return -1; @@ -277,7 +279,7 @@ MH_STATUS Handler::UninitializeHook() bool Handler::rpc_get_msg_types(uint8_t *out, size_t *len) { - MsgTypes_t types = GetMsgTypes(); + MsgTypes_t types = GetMsgTypes(); return fill_response(out, len, [&](Response &rsp) { rsp.msg.types.types.funcs.encode = encode_types; rsp.msg.types.types.arg = &types; diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 1a9d415..ca7ba91 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -23,6 +23,22 @@ namespace Message constexpr uint64_t LEVEL = 0x56E4244; // 日志级别 constexpr uint64_t CALL = 0x261B890; // 日志函数 } + + namespace Receive + { + constexpr uint64_t CALL = 0x2141E80; // 接收消息 Call + constexpr uint64_t ID = 0x30; // 消息 ID + constexpr uint64_t TYPE = 0x38; // 消息类型 + constexpr uint64_t SELF = 0x3C; // 消息是否来自自己 + constexpr uint64_t TIMESTAMP = 0x44; // 消息时间戳 + constexpr uint64_t ROOMID = 0x48; // 群聊 ID(或者发送者 wxid) + constexpr uint64_t CONTENT = 0x88; // 消息内容 + constexpr uint64_t WXID = 0x240; // 发送者 wxid + constexpr uint64_t SIGN = 0x260; // 消息签名 + constexpr uint64_t THUMB = 0x280; // 缩略图路径 + constexpr uint64_t EXTRA = 0x2A0; // 原图路径 + constexpr uint64_t XML = 0x308; // 消息 XML + } } } diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 0d6842b..5f24d78 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -81,7 +81,7 @@ int RpcServer::start(int port) isRunning_ = true; try { - cmdThread_ = std::thread(&RpcServer::runRpcServer, this); + cmdThread_ = std::thread(&RpcServer::run_rpc_server, this); } catch (const std::exception &e) { LOG_ERROR("启动 RPC 服务器失败: {}", e.what()); isRunning_ = false; @@ -123,43 +123,56 @@ int RpcServer::stop() return 0; } -void RpcServer::receiveMessageCallback() +void RpcServer::on_message_callback() { - int rv; - nng_socket msgSock = NNG_SOCKET_INITIALIZER; - Response rsp = Response_init_default; - rsp.func = Functions_FUNC_ENABLE_RECV_TXT; - rsp.which_msg = Response_wxmsg_tag; - std::vector msgBuffer(DEFAULT_BUF_SIZE); + try { + int rv; + nng_socket msgSock = NNG_SOCKET_INITIALIZER; + Response rsp = Response_init_default; + rsp.func = Functions_FUNC_ENABLE_RECV_TXT; + rsp.which_msg = Response_wxmsg_tag; + std::vector msgBuffer(DEFAULT_BUF_SIZE); - pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); + pb_ostream_t stream = pb_ostream_from_buffer(msgBuffer.data(), msgBuffer.size()); - std::string url = build_url(port_ + 1); - if ((rv = nng_pair1_open(&msgSock)) != 0) { - LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); - return; - } + std::string url = build_url(port_ + 1); + if ((rv = nng_pair1_open(&msgSock)) != 0) { + LOG_ERROR("nng_pair0_open error {}", nng_strerror(rv)); + return; + } - if ((rv = nng_listen(msgSock, url.c_str(), NULL, 0)) != 0) { - LOG_ERROR("nng_listen error {}", nng_strerror(rv)); - return; - } + if ((rv = nng_listen(msgSock, url.c_str(), NULL, 0)) != 0) { + LOG_ERROR("nng_listen error {}", nng_strerror(rv)); + return; + } - LOG_INFO("MSG Server listening on {}", url.c_str()); - if ((rv = nng_setopt_ms(msgSock, NNG_OPT_SENDTIMEO, 5000)) != 0) { - LOG_ERROR("nng_setopt_ms: {}", nng_strerror(rv)); - return; - } + if ((rv = nng_setopt_ms(msgSock, NNG_OPT_SENDTIMEO, 5000)) != 0) { + LOG_ERROR("nng_setopt_ms: {}", nng_strerror(rv)); + return; + } - while (handler_.isMessageListening()) { - std::unique_lock lock(handler_.getMutex()); - std::optional msgOpt; - auto hasMessage = [&]() { - msgOpt = handler_.popMessage(); - return msgOpt.has_value(); - }; + while (handler_.isMessageListening()) { + std::optional msgOpt; + { + std::unique_lock lock(handler_.getMutex()); + bool hasMessage + = handler_.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), [&]() { + lock.unlock(); + msgOpt = handler_.popMessage(); + lock.lock(); + return msgOpt.has_value(); + }); + + if (!hasMessage) { + continue; + } + } + + if (!msgOpt.has_value()) { + LOG_WARN("popMessage returned empty after wait_for success."); + continue; + } - if (handler_.getConditionVariable().wait_for(lock, std::chrono::milliseconds(1000), hasMessage)) { WxMsg_t wxmsg = std::move(msgOpt.value()); rsp.msg.wxmsg.id = wxmsg.id; rsp.msg.wxmsg.is_self = wxmsg.is_self; @@ -187,11 +200,16 @@ void RpcServer::receiveMessageCallback() } LOG_DEBUG("Send data length {}", stream.bytes_written); } + nng_close(msgSock); + LOG_DEBUG("Leave MSG Server."); + } catch (const std::exception &e) { + LOG_ERROR("Fatal exception in on_message_callback: {}", e.what()); + } catch (...) { + LOG_ERROR("Unknown fatal exception in on_message_callback."); } - nng_close(msgSock); } -bool RpcServer::enableRecvMsg(bool pyq, uint8_t *out, size_t *len) +bool RpcServer::start_message_listener(bool pyq, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = handler_.ListenMsg(); @@ -199,12 +217,12 @@ bool RpcServer::enableRecvMsg(bool pyq, uint8_t *out, size_t *len) if (pyq) { handler_.ListenPyq(); } - msgThread_ = std::thread(&RpcServer::receiveMessageCallback, this); + msgThread_ = std::thread(&RpcServer::on_message_callback, this); } }); } -bool RpcServer::disableRecvMsg(uint8_t *out, size_t *len) +bool RpcServer::stop_message_listener(uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = handler_.UnListenMsg(); @@ -223,6 +241,8 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_GET_SELF_WXID, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_self_wxid(out, len); } }, { Functions_FUNC_GET_USER_INFO, [](const Request &r, uint8_t *out, size_t *len) { return account::rpc_get_user_info(out, len); } }, { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().handler_.rpc_get_msg_types(out, len); } }, + { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().start_message_listener(r.msg.flag, out, len); } }, + { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().stop_message_listener(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, @@ -277,7 +297,7 @@ bool RpcServer::dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out return ret; } -void RpcServer::runRpcServer() +void RpcServer::run_rpc_server() { int rv = 0; nng_socket cmdSock = NNG_SOCKET_INITIALIZER; diff --git a/WeChatFerry/spy/rpc_server.h b/WeChatFerry/spy/rpc_server.h index 468689e..0bb96b9 100644 --- a/WeChatFerry/spy/rpc_server.h +++ b/WeChatFerry/spy/rpc_server.h @@ -26,10 +26,10 @@ private: RpcServer(const RpcServer &) = delete; RpcServer &operator=(const RpcServer &) = delete; - void runRpcServer(); - void receiveMessageCallback(); - bool enableRecvMsg(bool pyq, uint8_t *out, size_t *len); - bool disableRecvMsg(uint8_t *out, size_t *len); + void run_rpc_server(); + void on_message_callback(); + bool start_message_listener(bool pyq, uint8_t *out, size_t *len); + bool stop_message_listener(uint8_t *out, size_t *len); bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len); static std::string build_url(int port); From ba8865d348b5c4678c12e58b758ca50cd9a8a646 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 01:54:05 +0800 Subject: [PATCH 065/132] fix(rpc): close cmdSock on error --- WeChatFerry/spy/rpc_server.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 5f24d78..b7a60e3 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -310,11 +310,15 @@ void RpcServer::run_rpc_server() if ((rv = nng_listen(cmdSock, url.c_str(), nullptr, 0)) != 0) { LOG_ERROR("nng_listen error: {}", nng_strerror(rv)); + nng_close(cmdSock); + nng_fini(); return; } if ((rv = nng_setopt_ms(cmdSock, NNG_OPT_SENDTIMEO, 1000)) != 0) { LOG_ERROR("nng_setopt_ms error: {}", nng_strerror(rv)); + nng_close(cmdSock); + nng_fini(); return; } From 89c676a1b81858b2018f022d83aa84344c1b6467 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 02:09:22 +0800 Subject: [PATCH 066/132] feat(message): impl receive pyq message --- WeChatFerry/spy/message_handler.cpp | 28 +++++++++++----------------- WeChatFerry/spy/message_handler.h | 2 +- WeChatFerry/spy/offsets.h | 8 ++++++++ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index db3cc1f..64c29cc 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -16,14 +16,6 @@ extern QWORD g_WeChatWinDllAddr; -#define OS_PYQ_MSG_START 0x30 -#define OS_PYQ_MSG_END 0x38 -#define OS_PYQ_MSG_TS 0x38 -#define OS_PYQ_MSG_XML 0x9B8 -#define OS_PYQ_MSG_SENDER 0x18 -#define OS_PYQ_MSG_CONTENT 0x48 -#define OS_PYQ_MSG_CALL 0x2E42C90 - namespace message { @@ -91,14 +83,14 @@ QWORD Handler::PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWOR return p; } -void Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) +QWORD Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) { auto &handler = getInstance(); - QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START); - QWORD endAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_END); + QWORD startAddr = *(QWORD *)(arg2 + OsRecv::PYQ_START); + QWORD endAddr = *(QWORD *)(arg2 + OsRecv::PYQ_END); if (startAddr == 0) { - return; + return 0; } while (startAddr < endAddr) { @@ -108,10 +100,10 @@ void Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) wxMsg.is_self = false; wxMsg.is_group = false; wxMsg.id = util::get_qword(startAddr); - wxMsg.ts = util::get_dword(startAddr + OS_PYQ_MSG_TS); - wxMsg.xml = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_XML); - wxMsg.sender = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_SENDER); - wxMsg.content = util::get_str_by_wstr_addr(startAddr + OS_PYQ_MSG_CONTENT); + wxMsg.ts = util::get_dword(startAddr + OsRecv::PYQ_TS); + wxMsg.xml = util::get_str_by_wstr_addr(startAddr + OsRecv::PYQ_XML); + wxMsg.sender = util::get_str_by_wstr_addr(startAddr + OsRecv::PYQ_SENDER); + wxMsg.content = util::get_str_by_wstr_addr(startAddr + OsRecv::PYQ_CONTENT); { std::unique_lock lock(handler.mutex_); @@ -121,6 +113,8 @@ void Handler::DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) handler.cv_.notify_all(); startAddr += 0x1618; } + + return handler.realRecvPyq(arg1, arg2, arg3); } Handler &Handler::getInstance() @@ -243,7 +237,7 @@ int Handler::ListenPyq() { if (isListeningPyq) return 1; - funcRecvPyq = reinterpret_cast(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL); + funcRecvPyq = reinterpret_cast(g_WeChatWinDllAddr + OsRecv::PYQ_CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)) != MH_OK) return -1; if (MH_EnableHook(funcRecvPyq) != MH_OK) return -1; diff --git a/WeChatFerry/spy/message_handler.h b/WeChatFerry/spy/message_handler.h index 2937b11..53e3d1a 100644 --- a/WeChatFerry/spy/message_handler.h +++ b/WeChatFerry/spy/message_handler.h @@ -68,7 +68,7 @@ private: static QWORD DispatchMsg(QWORD arg1, QWORD arg2); static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9, QWORD a10, QWORD a11, QWORD a12); - static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3); + static QWORD DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3); }; } // namespace message diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index ca7ba91..db446de 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -38,6 +38,14 @@ namespace Message constexpr uint64_t THUMB = 0x280; // 缩略图路径 constexpr uint64_t EXTRA = 0x2A0; // 原图路径 constexpr uint64_t XML = 0x308; // 消息 XML + + constexpr uint64_t PYQ_CALL = 0x2E56080; // 接收朋友圈 Call + constexpr uint64_t PYQ_START = 0x30; // 开始地址 + constexpr uint64_t PYQ_END = 0x38; // 结束地址 + constexpr uint64_t PYQ_SENDER = 0x18; // 发布者 + constexpr uint64_t PYQ_TS = 0x38; // 时间戳 + constexpr uint64_t PYQ_CONTENT = 0x48; // 文本内容 + constexpr uint64_t PYQ_XML = 0x9B8; // 其他内容 } } } From b9240a84d88b085245a7e47fe15d032e97f15a6b Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 18 Feb 2025 22:58:51 +0800 Subject: [PATCH 067/132] chore(rpc): remove unused code --- WeChatFerry/spy/rpc_server.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index b7a60e3..8998fd0 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -255,8 +255,6 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, - // { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_enable_recv_msg(r.msg.flag, out, len); } }, - // { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return rpc_disable_recv_msg(out, len); } }, // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, From d368e26318af9afd202201e6e220527e1f18e73d Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 19 Feb 2025 00:58:24 +0800 Subject: [PATCH 068/132] fix(spy): fix WxString memory map --- WeChatFerry/spy/spy_types.h | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/WeChatFerry/spy/spy_types.h b/WeChatFerry/spy/spy_types.h index 56f7c51..73c801e 100644 --- a/WeChatFerry/spy/spy_types.h +++ b/WeChatFerry/spy/spy_types.h @@ -7,40 +7,51 @@ typedef uint64_t QWORD; class WxString { - public: const wchar_t *wptr; DWORD size; - DWORD capacity; + DWORD length; const char *ptr; DWORD clen; - WxString() - { - wptr = nullptr; - size = 0; - capacity = 0; - ptr = nullptr; - clen = 0; - } + WxString() : wptr(nullptr), size(0), length(0), ptr(nullptr), clen(0) { } - WxString(std::wstring ws) : internal_ws(std::move(ws)) + explicit WxString(const std::wstring &ws) + : wptr(ws.c_str()), size(static_cast(ws.size())), length(static_cast(ws.length())), ptr(nullptr), + clen(0) { - wptr = internal_ws.c_str(); - size = static_cast(internal_ws.size()); - capacity = static_cast(internal_ws.capacity()); - ptr = nullptr; - clen = 0; } WxString(const WxString &) = delete; WxString &operator=(const WxString &) = delete; - WxString(WxString &&) noexcept = default; - WxString &operator=(WxString &&) noexcept = default; + WxString(WxString &&other) noexcept + : wptr(other.wptr), size(other.size), length(other.length), ptr(other.ptr), clen(other.clen) + { + other.wptr = nullptr; + other.size = 0; + other.length = 0; + other.ptr = nullptr; + other.clen = 0; + } -private: - std::wstring internal_ws; + WxString &operator=(WxString &&other) noexcept + { + if (this != &other) { + wptr = other.wptr; + size = other.size; + length = other.length; + ptr = other.ptr; + clen = other.clen; + + other.wptr = nullptr; + other.size = 0; + other.length = 0; + other.ptr = nullptr; + other.clen = 0; + } + return *this; + } }; typedef struct RawVector { From 5909205d6100f76839eece243b7ffe6c5d2515f4 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 19 Feb 2025 00:59:21 +0800 Subject: [PATCH 069/132] chore(spy): remove unused struct --- WeChatFerry/spy/spy_types.h | 9 --------- 1 file changed, 9 deletions(-) diff --git a/WeChatFerry/spy/spy_types.h b/WeChatFerry/spy/spy_types.h index 73c801e..4e0f764 100644 --- a/WeChatFerry/spy/spy_types.h +++ b/WeChatFerry/spy/spy_types.h @@ -53,12 +53,3 @@ public: return *this; } }; - -typedef struct RawVector { -#ifdef _DEBUG - QWORD head; -#endif - QWORD start; - QWORD finish; - QWORD end; -} RawVector_t; From 62d2703f568e8228fd35a03ae5b404c4dd1b0d0f Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 21 Feb 2025 00:04:54 +0800 Subject: [PATCH 070/132] feat(message): update send text offset --- WeChatFerry/spy/offsets.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index db446de..63f4d33 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -47,6 +47,13 @@ namespace Message constexpr uint64_t PYQ_CONTENT = 0x48; // 文本内容 constexpr uint64_t PYQ_XML = 0x9B8; // 其他内容 } + + namespace Send + { + constexpr uint64_t MGR = 0x1B57350; + constexpr uint64_t FREE = 0x1B58BD0; + constexpr uint64_t TEXT = 0x22C9CA0; + } } } From b282c9f9f57d9d9b0e73cda08922426f61204d4b Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 21 Feb 2025 00:25:29 +0800 Subject: [PATCH 071/132] feat(message): impl send text --- WeChatFerry/spy/message_sender.cpp | 61 +++++++++++++++++------------- WeChatFerry/spy/message_sender.h | 1 - WeChatFerry/spy/rpc_server.cpp | 5 ++- WeChatFerry/spy/rpc_server.h | 2 + 4 files changed, 39 insertions(+), 30 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index ac4452d..bc6cad5 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -3,19 +3,17 @@ #include #include +#include "account_manager.h" #include "database_executor.h" #include "log.hpp" +#include "offsets.h" #include "rpc_helper.h" #include "spy_types.h" -#include "account_manager.h" #include "util.h" extern QWORD g_WeChatWinDllAddr; #define OS_NEW 0x1B5E140 -#define OS_FREE 0x1B55850 -#define OS_SEND_MSG_MGR 0x1B53FD0 -#define OS_SEND_TEXT 0x22C6B60 #define OS_SEND_IMAGE 0x22BC2F0 #define OS_GET_APP_MSG_MGR 0x1B58F70 #define OS_SEND_FILE 0x20D0230 @@ -32,6 +30,8 @@ extern QWORD g_WeChatWinDllAddr; namespace message { +namespace OsSend = Offsets::Message::Send; + Sender &Sender::get_instance() { static Sender instance; @@ -41,9 +41,9 @@ Sender &Sender::get_instance() Sender::Sender() { func_new = reinterpret_cast(g_WeChatWinDllAddr + OS_NEW); - func_free = reinterpret_cast(g_WeChatWinDllAddr + OS_FREE); - func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); - func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_TEXT); + func_free = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); + func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); + func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_IMAGE); func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_FILE); func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); @@ -62,35 +62,42 @@ std::unique_ptr Sender::new_wx_string(const std::string &str) return std::make_unique(util::s2w(str)); } -std::vector Sender::parse_wxids(const string &wxids) +template struct WxStringHolder { + std::wstring ws; + WxString wx; + explicit WxStringHolder(const T &str) : ws(util::s2w(str)), wx(ws) { } +}; + +template struct AtWxidSplitResult { + std::vector wxids; + std::vector wxWxids; +}; + +AtWxidSplitResult<> parse_wxids(const std::string &atWxids) { - vector wx_members; - wstringstream wss(util::s2w(wxids)); - wstring wstr; - while (getline(wss, wstr, L',')) { - wx_members.emplace_back(wstr); + AtWxidSplitResult<> result; + if (!atWxids.empty()) { + std::wstringstream wss(util::s2w(atWxids)); + for (std::wstring wxid; std::getline(wss, wxid, L',');) { + result.wxids.push_back(wxid); + result.wxWxids.emplace_back(result.wxids.back()); + } } - return wx_members; + return result; } void Sender::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) { - auto wxWxid = new_wx_string(wxid); - auto wxMsg = new_wx_string(msg); + WxStringHolder holderMsg(msg); + WxStringHolder holderWxid(wxid); - std::vector wx_at_wxids; - if (!at_wxids.empty()) { - wx_at_wxids = parse_wxids(at_wxids); - } else { - wx_at_wxids.emplace_back(); - } + auto wxAtWxids = parse_wxids(at_wxids).wxWxids; + QWORD pWxAtWxids = wxAtWxids.empty() ? 0 : reinterpret_cast(&wxAtWxids); - QWORD wx_ater_list = reinterpret_cast(&(wx_at_wxids.front())); - - char buffer[0x460] = { 0 }; + char buffer[1104] = { 0 }; func_send_msg_mgr(); - func_send_text(reinterpret_cast(&buffer), reinterpret_cast(wxWxid.get()), - reinterpret_cast(wxMsg.get()), wx_ater_list, 1, 1, 0, 0); + func_send_text(reinterpret_cast(&buffer), reinterpret_cast(&holderWxid.wx), + reinterpret_cast(&holderMsg.wx), pWxAtWxids, 1, 1, 0, 0); func_free(reinterpret_cast(&buffer)); } diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 1f0a855..4453ba9 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -75,6 +75,5 @@ private: std::unique_ptr new_wx_string(const char *str); std::unique_ptr new_wx_string(const std::string &str); - std::vector parse_wxids(const std::string &wxids); }; } diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 8998fd0..6590897 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -54,7 +54,8 @@ void RpcServer::destroyInstance() } } -RpcServer::RpcServer(int port) : port_(port), handler_(message::Handler::getInstance()) +RpcServer::RpcServer(int port) + : port_(port), handler_(message::Handler::getInstance()), sender_(message::Sender::get_instance()) { LOG_DEBUG("RpcServer 构造: 端口 {}", port_); } @@ -247,7 +248,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, - // { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_text(r.msg.txt, out, len); } }, + { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, // { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, diff --git a/WeChatFerry/spy/rpc_server.h b/WeChatFerry/spy/rpc_server.h index 0bb96b9..76dbc1b 100644 --- a/WeChatFerry/spy/rpc_server.h +++ b/WeChatFerry/spy/rpc_server.h @@ -10,6 +10,7 @@ #include "wcf.pb.h" #include "message_handler.h" +#include "message_sender.h" class RpcServer { @@ -46,6 +47,7 @@ private: std::thread msgThread_; message::Handler &handler_; + message::Sender &sender_; struct Deleter { void operator()(RpcServer *server) const { delete server; } From b9345e69ee14500cdb7e5e8bcf2a2ed189db70bb Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 25 Feb 2025 00:49:32 +0800 Subject: [PATCH 072/132] feat(message): update send image offsets --- WeChatFerry/spy/offsets.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 63f4d33..a65a341 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -50,9 +50,11 @@ namespace Message namespace Send { - constexpr uint64_t MGR = 0x1B57350; - constexpr uint64_t FREE = 0x1B58BD0; - constexpr uint64_t TEXT = 0x22C9CA0; + constexpr uint64_t MGR = 0x1B57350; + constexpr uint64_t INSTANCE = 0x1B614C0; + constexpr uint64_t FREE = 0x1B58BD0; + constexpr uint64_t TEXT = 0x22C9CA0; + constexpr uint64_t IMAGE = 0x22BF430; } } } From 1e703f0f64dc6ae557c79e0240f9141bfa07a53c Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 25 Feb 2025 00:50:50 +0800 Subject: [PATCH 073/132] feat(message): impl send image --- WeChatFerry/spy/message_sender.cpp | 52 +++++++++++++++--------------- WeChatFerry/spy/message_sender.h | 4 +-- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index bc6cad5..64256d5 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -40,11 +40,11 @@ Sender &Sender::get_instance() Sender::Sender() { - func_new = reinterpret_cast(g_WeChatWinDllAddr + OS_NEW); - func_free = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); + func_get_instance = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); + func_free_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); - func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_IMAGE); + func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_FILE); func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); @@ -98,32 +98,32 @@ void Sender::send_text(const std::string &wxid, const std::string &msg, const st func_send_msg_mgr(); func_send_text(reinterpret_cast(&buffer), reinterpret_cast(&holderWxid.wx), reinterpret_cast(&holderMsg.wx), pWxAtWxids, 1, 1, 0, 0); - func_free(reinterpret_cast(&buffer)); + func_free_chat_msg(reinterpret_cast(&buffer)); } void Sender::send_image(const std::string &wxid, const std::string &path) { - auto wxWxid = new_wx_string(wxid); - auto wxPath = new_wx_string(path); + WxStringHolder holderWxid(wxid); + WxStringHolder holderPath(path); - char msg[0x460] = { 0 }; - char msgTmp[0x460] = { 0 }; - QWORD *flag[10] = { nullptr }; + char msg[1192] = { 0 }; + char msgTmp[1192] = { 0 }; + QWORD *flag[10] = { 0 }; - QWORD tmp1 = 0, tmp2 = 0; - QWORD pMsgTmp = func_new(reinterpret_cast(&msgTmp)); - flag[8] = &tmp1; - flag[9] = &tmp2; + QWORD tmp1 = 1, tmp2 = 0, tmp3 = 0; + QWORD pMsgTmp = func_get_instance((QWORD)(&msgTmp)); + flag[0] = reinterpret_cast(tmp1); flag[1] = reinterpret_cast(pMsgTmp); + flag[8] = &tmp2; + flag[9] = &tmp3; - QWORD pMsg = func_new(reinterpret_cast(&msg)); + QWORD pMsg = func_get_instance((QWORD)(&msg)); QWORD sendMgr = func_send_msg_mgr(); - - func_send_image(sendMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), + func_send_image(sendMgr, pMsg, reinterpret_cast(&holderWxid.wx), reinterpret_cast(&holderPath.wx), reinterpret_cast(&flag)); - func_free(pMsg); - func_free(pMsgTmp); + func_free_chat_msg(pMsg); + func_free_chat_msg(pMsgTmp); } void Sender::send_file(const std::string &wxid, const std::string &path) @@ -136,13 +136,13 @@ void Sender::send_file(const std::string &wxid, const std::string &path) QWORD tmp2[4] = { 0 }; QWORD tmp3[4] = { 0 }; - QWORD pMsg = func_new(reinterpret_cast(&msg)); + QWORD pMsg = func_get_instance(reinterpret_cast(&msg)); QWORD appMgr = func_get_app_mgr(); func_send_file(appMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), 1, tmp1, 0, tmp2, 0, tmp3, 0, 0); - func_free(pMsg); + func_free_chat_msg(pMsg); } void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type) @@ -151,8 +151,8 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::unique_ptr buff2(new char[0x500]()); char nullBuf[0x1C] = { 0 }; - func_new(reinterpret_cast(buff.get())); - func_new(reinterpret_cast(buff2.get())); + func_get_instance(reinterpret_cast(buff.get())); + func_get_instance(reinterpret_cast(buff2.get())); QWORD sbuf[4] = { 0, 0, 0, 0 }; QWORD sign = func_xml_buf_sign(reinterpret_cast(buff2.get()), reinterpret_cast(sbuf), 0x1); @@ -167,8 +167,8 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const reinterpret_cast(wxPath.get()), reinterpret_cast(nullBuf), type, 0x4, sign, reinterpret_cast(buff2.get())); - func_free(reinterpret_cast(buff.get())); - func_free(reinterpret_cast(buff2.get())); + func_free_chat_msg(reinterpret_cast(buff.get())); + func_free_chat_msg(reinterpret_cast(buff2.get())); } void Sender::send_emotion(const std::string &wxid, const std::string &path) @@ -201,7 +201,7 @@ int Sender::send_rich_text(const RichText &rt) } memset(buff, 0, SRTM_SIZE); - func_new(reinterpret_cast(buff)); + func_get_instance(reinterpret_cast(buff)); auto pReceiver = new_wx_string(rt.receiver); auto pTitle = new_wx_string(rt.title); @@ -220,7 +220,7 @@ int Sender::send_rich_text(const RichText &rt) QWORD mgr = func_get_app_mgr(); status = func_send_rich_text(mgr, reinterpret_cast(pReceiver.get()), reinterpret_cast(buff)); - func_free(reinterpret_cast(buff)); + func_free_chat_msg(reinterpret_cast(buff)); return static_cast(status); } diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 4453ba9..8ebfc46 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -58,8 +58,8 @@ private: using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD); using SendXml_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - New_t func_new; - Free_t func_free; + New_t func_get_instance; + Free_t func_free_chat_msg; SendMsgMgr_t func_send_msg_mgr; GetAppMgr_t func_get_app_mgr; SendText_t func_send_text; diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 6590897..c090b86 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -249,7 +249,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, - // { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_image(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, // { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, From b09c236833a48a21edaafa85d58ca1dd0779b06b Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 26 Feb 2025 21:45:26 +0800 Subject: [PATCH 074/132] feat(util): impl memory alloc/free --- WeChatFerry/com/util.cpp | 27 +++++++++++++++++++++++++++ WeChatFerry/com/util.h | 13 +++++++++++++ 2 files changed, 40 insertions(+) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index ed3473b..bff975a 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -279,4 +279,31 @@ std::vector parse_wxids(const std::string &wxids) return wx_members; } +WxString *CreateWxString(const std::string &s) +{ + std::wstring ws = util::s2w(s); + WxString *wxStr = reinterpret_cast(HeapAlloc(GetProcessHeap(), 8, sizeof(WxString))); + if (!wxStr) return nullptr; + size_t len = ws.length(); + wchar_t *ptr = reinterpret_cast(HeapAlloc(GetProcessHeap(), 8, (len + 1) * sizeof(wchar_t))); + if (!ptr) { + HeapFree(GetProcessHeap(), 8, wxStr); + return nullptr; + } + wmemcpy(ptr, ws.c_str(), len + 1); + wxStr->wptr = ptr; + wxStr->size = static_cast(ws.size()); + wxStr->length = static_cast(ws.length()); + wxStr->clen = 0; + wxStr->ptr = nullptr; + return wxStr; +} + +void FreeWxString(WxString *wxStr) +{ + if (wxStr) { + if (wxStr->wptr) HeapFree(GetProcessHeap(), 8, const_cast(wxStr->wptr)); + HeapFree(GetProcessHeap(), 8, wxStr); + } +} } // namespace util diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index de61c80..9031c27 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -65,6 +65,19 @@ inline std::wstring get_pp_len_wstring(uint64_t addr) return (addr && len) ? std::wstring(*reinterpret_cast(addr), len) : L""; } inline std::string get_str_by_wstr_addr(uint64_t addr) { return w2s(get_pp_len_wstring(addr)); } +inline void *AllocFromHeap(size_t size) { return HeapAlloc(GetProcessHeap(), 8, size); } +inline void FreeBuffer(void *buffer) +{ + if (buffer) HeapFree(GetProcessHeap(), 8, buffer); +} + +template static T *AllocBuffer(size_t count) +{ + return reinterpret_cast(HeapAlloc(GetProcessHeap(), 8, sizeof(T) * count)); +} + +WxString *CreateWxString(const std::string &s); +void FreeWxString(WxString *wxStr); std::unique_ptr new_wx_string(const char *str); std::unique_ptr new_wx_string(const wchar_t *wstr); From 3f07afee9aa7ed66d0a97254ba080d9ccc0b483c Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 26 Feb 2025 21:48:22 +0800 Subject: [PATCH 075/132] feat(message): update send file offsets --- WeChatFerry/spy/offsets.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index a65a341..655cdf4 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -55,6 +55,8 @@ namespace Message constexpr uint64_t FREE = 0x1B58BD0; constexpr uint64_t TEXT = 0x22C9CA0; constexpr uint64_t IMAGE = 0x22BF430; + constexpr uint64_t APP_MGR = 0x1B5C2F0; + constexpr uint64_t FILE = 0x20D30E0; } } } From c928b68ec42f82e02b0fcd777260e53e95d6fed9 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 26 Feb 2025 21:49:33 +0800 Subject: [PATCH 076/132] feat(message): impl send file --- WeChatFerry/spy/message_sender.cpp | 49 ++++++++++++++++++++++-------- WeChatFerry/spy/message_sender.h | 4 +-- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 64256d5..da5b9f3 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -45,7 +45,8 @@ Sender::Sender() func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); - func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_FILE); + func_get_app_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::APP_MGR); + func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FILE); func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); @@ -128,21 +129,45 @@ void Sender::send_image(const std::string &wxid, const std::string &path) void Sender::send_file(const std::string &wxid, const std::string &path) { - auto wxWxid = new_wx_string(wxid); - auto wxPath = new_wx_string(path); + WxString *wxWxid = util::CreateWxString(wxid); + WxString *wxPath = util::CreateWxString(path); + if (!wxWxid || !wxPath) { + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); + return; + } - char msg[0x460] = { 0 }; - QWORD tmp1[4] = { 0 }; - QWORD tmp2[4] = { 0 }; - QWORD tmp3[4] = { 0 }; + char *chat_msg = reinterpret_cast(util::AllocFromHeap(0x460)); + if (!chat_msg) { + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); + return; + } - QWORD pMsg = func_get_instance(reinterpret_cast(&msg)); - QWORD appMgr = func_get_app_mgr(); + QWORD *tmp1 = util::AllocBuffer(4); + QWORD *tmp2 = util::AllocBuffer(4); + QWORD *tmp3 = util::AllocBuffer(4); + if (!tmp1 || !tmp2 || !tmp3) { + func_free_chat_msg(reinterpret_cast(chat_msg)); + util::FreeBuffer(chat_msg); + util::FreeBuffer(tmp1); + util::FreeBuffer(tmp2); + util::FreeBuffer(tmp3); + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); + return; + } - func_send_file(appMgr, pMsg, reinterpret_cast(wxWxid.get()), reinterpret_cast(wxPath.get()), 1, tmp1, - 0, tmp2, 0, tmp3, 0, 0); + QWORD app_mgr = func_get_app_mgr(); + func_send_file(app_mgr, chat_msg, wxWxid, wxPath, 1, tmp1, 0, tmp2, 0, tmp3, 0, 0xC); + func_free_chat_msg(reinterpret_cast(chat_msg)); - func_free_chat_msg(pMsg); + util::FreeBuffer(chat_msg); + util::FreeBuffer(tmp1); + util::FreeBuffer(tmp2); + util::FreeBuffer(tmp3); + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); } void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type) diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 8ebfc46..3217146 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -48,8 +48,8 @@ private: using GetAppMgr_t = QWORD (*)(); using SendText_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); using SendImage_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD); - using SendFile_t - = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD); + using SendFile_t = QWORD (*)(QWORD, char *, WxString *, WxString *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, + QWORD, QWORD); using SendRichText_t = QWORD (*)(QWORD, QWORD, QWORD); using SendPat_t = QWORD (*)(QWORD, QWORD); using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index c090b86..9e2e9ee 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -250,7 +250,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, - // { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_file(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_file(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, // { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, From c4befd6fd16a7403a12a533683c8ceb7aef0f9c6 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 26 Feb 2025 22:39:56 +0800 Subject: [PATCH 077/132] feat(message): update xml offsets --- WeChatFerry/spy/offsets.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 655cdf4..d425a8a 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -50,13 +50,15 @@ namespace Message namespace Send { - constexpr uint64_t MGR = 0x1B57350; - constexpr uint64_t INSTANCE = 0x1B614C0; - constexpr uint64_t FREE = 0x1B58BD0; - constexpr uint64_t TEXT = 0x22C9CA0; - constexpr uint64_t IMAGE = 0x22BF430; - constexpr uint64_t APP_MGR = 0x1B5C2F0; - constexpr uint64_t FILE = 0x20D30E0; + constexpr uint64_t MGR = 0x1B57350; + constexpr uint64_t INSTANCE = 0x1B614C0; + constexpr uint64_t FREE = 0x1B58BD0; + constexpr uint64_t TEXT = 0x22C9CA0; + constexpr uint64_t IMAGE = 0x22BF430; + constexpr uint64_t APP_MGR = 0x1B5C2F0; + constexpr uint64_t FILE = 0x20D30E0; + constexpr uint64_t XML = 0x0; + constexpr uint64_t XML_BUF_SIGN = 0x0; } } } From 54be080fb37436f6d748c6d24f42bf200d21eee2 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 26 Feb 2025 22:40:47 +0800 Subject: [PATCH 078/132] feat(message): ignore send xml --- WeChatFerry/spy/message_sender.cpp | 9 ++++++--- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index da5b9f3..57089cb 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -51,7 +51,8 @@ Sender::Sender() func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_EMOTION); - func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_XML); + func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML); + func_xml_buf_sign = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML_BUF_SIGN); } std::unique_ptr Sender::new_wx_string(const char *str) @@ -172,6 +173,7 @@ void Sender::send_file(const std::string &wxid, const std::string &path) void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::string &path, uint64_t type) { +#if 0 std::unique_ptr buff(new char[0x500]()); std::unique_ptr buff2(new char[0x500]()); char nullBuf[0x1C] = { 0 }; @@ -194,6 +196,7 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const func_free_chat_msg(reinterpret_cast(buff.get())); func_free_chat_msg(reinterpret_cast(buff2.get())); +#endif } void Sender::send_emotion(const std::string &wxid, const std::string &path) @@ -346,8 +349,8 @@ bool Sender::rpc_send_xml(const XmlMsg &xml, uint8_t *out, size_t *len) LOG_ERROR("Empty content or receiver."); rsp.msg.status = -1; } else { - send_xml(xml.receiver, xml.content, xml.path, xml.type); - rsp.msg.status = 0; + // send_xml(xml.receiver, xml.content, xml.path, xml.type); + rsp.msg.status = -1; } }); } diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 9e2e9ee..8341289 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -251,7 +251,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_file(r.msg.file, out, len); } }, - // { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_xml(r.msg.xml, out, len); } }, + { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_xml(r.msg.xml, out, len); } }, // { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, From c1e597a9b4debfa49e0d6809db729ee7e2e6dd62 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 00:09:51 +0800 Subject: [PATCH 079/132] feat(message): update send emotion offsets --- WeChatFerry/spy/offsets.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index d425a8a..6e31721 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -59,6 +59,8 @@ namespace Message constexpr uint64_t FILE = 0x20D30E0; constexpr uint64_t XML = 0x0; constexpr uint64_t XML_BUF_SIGN = 0x0; + constexpr uint64_t EMOTION_MGR = 0x1BD2310; + constexpr uint64_t EMOTION = 0x21B8100; } } } From 2959502636d7d7800bac08ce39fbdc3b950f1b3b Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 00:10:46 +0800 Subject: [PATCH 080/132] feat(message): impl send emotion --- WeChatFerry/spy/message_sender.cpp | 48 ++++++++++++++++-------------- WeChatFerry/spy/message_sender.h | 2 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 57089cb..82a0efa 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -40,19 +40,20 @@ Sender &Sender::get_instance() Sender::Sender() { - func_get_instance = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); - func_free_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); - func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); - func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); - func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); - func_get_app_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::APP_MGR); - func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FILE); - func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); - func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); - func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); - func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_EMOTION); - func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML); - func_xml_buf_sign = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML_BUF_SIGN); + func_get_instance = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); + func_free_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); + func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); + func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); + func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); + func_get_app_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::APP_MGR); + func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FILE); + func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); + func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); + func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); + func_get_emotion_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION_MGR); + func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION); + func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML); + func_xml_buf_sign = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML_BUF_SIGN); } std::unique_ptr Sender::new_wx_string(const char *str) @@ -201,20 +202,21 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const void Sender::send_emotion(const std::string &wxid, const std::string &path) { - auto wxWxid = new_wx_string(wxid); - auto wxPath = new_wx_string(path); - - std::unique_ptr buff(new QWORD[8]()); // 0x20 bytes = 8 * QWORD - - if (!buff) { - LOG_ERROR("Out of Memory..."); + WxString *wxWxid = util::CreateWxString(wxid); + WxString *wxPath = util::CreateWxString(path); + QWORD *buff = util::AllocBuffer(8); + if (!wxWxid || !wxPath || !buff) { + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); + util::FreeBuffer(buff); return; } QWORD mgr = func_get_emotion_mgr(); - func_send_emotion(mgr, reinterpret_cast(wxPath.get()), reinterpret_cast(buff.get()), - reinterpret_cast(wxWxid.get()), 2, reinterpret_cast(buff.get()), 0, - reinterpret_cast(buff.get())); + func_send_emotion(mgr, wxPath, buff, wxWxid, 2, buff, 0, buff); + util::FreeBuffer(buff); + util::FreeWxString(wxWxid); + util::FreeWxString(wxPath); } int Sender::send_rich_text(const RichText &rt) diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 3217146..f25da25 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -54,7 +54,7 @@ private: using SendPat_t = QWORD (*)(QWORD, QWORD); using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using GetEmotionMgr_t = QWORD (*)(); - using SendEmotion_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); + using SendEmotion_t = QWORD (*)(QWORD, WxString *, QWORD *, WxString *, QWORD, QWORD *, QWORD, QWORD *); using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD); using SendXml_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 8341289..9658df8 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -252,7 +252,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_file(r.msg.file, out, len); } }, { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_xml(r.msg.xml, out, len); } }, - // { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_emotion(r.msg.file, out, len); } }, + { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_emotion(r.msg.file, out, len); } }, // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, From 686673add2404339b0e83b64dd4e5cd9d81aff68 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 00:21:51 +0800 Subject: [PATCH 081/132] feat(message): update send rich text offsets --- WeChatFerry/spy/offsets.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 6e31721..940d26e 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -61,6 +61,10 @@ namespace Message constexpr uint64_t XML_BUF_SIGN = 0x0; constexpr uint64_t EMOTION_MGR = 0x1BD2310; constexpr uint64_t EMOTION = 0x21B8100; + + constexpr uint64_t NEW_MM_READER = 0x1B60A10; + constexpr uint64_t FREE_MM_READER = 0x1B5FDE0; + constexpr uint64_t RICH_TEXT = 0x20DD0C0; } } } From 77a3938e1fa86a7680a2584885f9d41a5fac8532 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 00:23:44 +0800 Subject: [PATCH 082/132] feat(message): impl send rich text --- WeChatFerry/spy/message_sender.cpp | 63 ++++++++++++++++++------------ WeChatFerry/spy/message_sender.h | 4 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 82a0efa..efe79da 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -47,7 +47,9 @@ Sender::Sender() func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); func_get_app_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::APP_MGR); func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FILE); - func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); + func_new_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::NEW_MM_READER); + func_free_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE_MM_READER); + func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::RICH_TEXT); func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); func_get_emotion_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION_MGR); @@ -221,36 +223,49 @@ void Sender::send_emotion(const std::string &wxid, const std::string &path) int Sender::send_rich_text(const RichText &rt) { -#define SRTM_SIZE 0x3F0 QWORD status = -1; - char *buff = static_cast(HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE)); - if (!buff) { + char *buff = util::AllocBuffer(0x3F0); + WxString *pReceiver = util::CreateWxString(rt.receiver); + WxString *pTitle = util::CreateWxString(rt.title); + WxString *pUrl = util::CreateWxString(rt.url); + WxString *pThumburl = util::CreateWxString(rt.thumburl); + WxString *pDigest = util::CreateWxString(rt.digest); + WxString *pAccount = util::CreateWxString(rt.account); + WxString *pName = util::CreateWxString(rt.name); + if (!buff || !pReceiver || !pTitle || !pUrl || !pThumburl || !pDigest || !pAccount || !pName) { + util::FreeWxString(pReceiver); + util::FreeWxString(pTitle); + util::FreeWxString(pUrl); + util::FreeWxString(pThumburl); + util::FreeWxString(pDigest); + util::FreeWxString(pAccount); + util::FreeWxString(pName); + util::FreeBuffer(buff); LOG_ERROR("Out of Memory..."); - return -1; + return static_cast(status); } - memset(buff, 0, SRTM_SIZE); - func_get_instance(reinterpret_cast(buff)); + func_new_mmreader(reinterpret_cast(buff)); + memcpy(buff + 0x8, pTitle, sizeof(WxString)); + memcpy(buff + 0x48, pUrl, sizeof(WxString)); + memcpy(buff + 0xB0, pThumburl, sizeof(WxString)); + memcpy(buff + 0xF0, pDigest, sizeof(WxString)); + memcpy(buff + 0x2C0, pAccount, sizeof(WxString)); + memcpy(buff + 0x2E0, pName, sizeof(WxString)); - auto pReceiver = new_wx_string(rt.receiver); - auto pTitle = new_wx_string(rt.title); - auto pUrl = new_wx_string(rt.url); - auto pThumburl = new_wx_string(rt.thumburl); - auto pDigest = new_wx_string(rt.digest); - auto pAccount = new_wx_string(rt.account); - auto pName = new_wx_string(rt.name); + status = func_send_rich_text(func_get_app_mgr(), pReceiver, buff); + func_free_mmreader(reinterpret_cast(buff)); - memcpy(buff + 0x8, pTitle.get(), sizeof(WxString)); - memcpy(buff + 0x48, pUrl.get(), sizeof(WxString)); - memcpy(buff + 0xB0, pThumburl.get(), sizeof(WxString)); - memcpy(buff + 0xF0, pDigest.get(), sizeof(WxString)); - memcpy(buff + 0x2C0, pAccount.get(), sizeof(WxString)); - memcpy(buff + 0x2E0, pName.get(), sizeof(WxString)); - - QWORD mgr = func_get_app_mgr(); - status = func_send_rich_text(mgr, reinterpret_cast(pReceiver.get()), reinterpret_cast(buff)); - func_free_chat_msg(reinterpret_cast(buff)); + // TODO: 验证是否有内存泄露 + // util::FreeWxString(pReceiver); + // util::FreeWxString(pTitle); + // util::FreeWxString(pUrl); + // util::FreeWxString(pThumburl); + // util::FreeWxString(pDigest); + // util::FreeWxString(pAccount); + // util::FreeWxString(pName); + util::FreeBuffer(buff); return static_cast(status); } diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index f25da25..ee752a6 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -50,7 +50,7 @@ private: using SendImage_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD); using SendFile_t = QWORD (*)(QWORD, char *, WxString *, WxString *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD); - using SendRichText_t = QWORD (*)(QWORD, QWORD, QWORD); + using SendRichText_t = QWORD (*)(QWORD, WxString *, char *); using SendPat_t = QWORD (*)(QWORD, QWORD); using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using GetEmotionMgr_t = QWORD (*)(); @@ -65,6 +65,8 @@ private: SendText_t func_send_text; SendImage_t func_send_image; SendFile_t func_send_file; + New_t func_new_mmreader; + Free_t func_free_mmreader; SendRichText_t func_send_rich_text; SendPat_t func_send_pat; Forward_t func_forward; diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 9658df8..bdcde26 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -253,7 +253,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_file(r.msg.file, out, len); } }, { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_xml(r.msg.xml, out, len); } }, { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_emotion(r.msg.file, out, len); } }, - // { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_rich_text(r.msg.rt, out, len); } }, + { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_rich_text(r.msg.rt, out, len); } }, // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, From 49000bf65e62518189e04e6fc714278472af569d Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 01:11:32 +0800 Subject: [PATCH 083/132] feat(message): update send pat offset --- WeChatFerry/spy/offsets.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 940d26e..98ec109 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -65,6 +65,8 @@ namespace Message constexpr uint64_t NEW_MM_READER = 0x1B60A10; constexpr uint64_t FREE_MM_READER = 0x1B5FDE0; constexpr uint64_t RICH_TEXT = 0x20DD0C0; + + constexpr uint64_t PAT = 0x2CC1E90; } } } From e896468c7f48a30485d2e6c3b93b6778726aa8e3 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 01:12:32 +0800 Subject: [PATCH 084/132] feat(message): impl send pat --- WeChatFerry/spy/message_sender.cpp | 8 ++++---- WeChatFerry/spy/message_sender.h | 2 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index efe79da..103ea27 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -50,7 +50,7 @@ Sender::Sender() func_new_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::NEW_MM_READER); func_free_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE_MM_READER); func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::RICH_TEXT); - func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); + func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OsSend::PAT); func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); func_get_emotion_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION_MGR); func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION); @@ -274,10 +274,10 @@ int Sender::send_pat(const std::string &roomid, const std::string &wxid) { QWORD status = -1; - auto wxRoomid = new_wx_string(roomid); - auto wxWxid = new_wx_string(wxid); + WxStringHolder holderRoom(roomid); + WxStringHolder holderWxid(wxid); - status = func_send_pat(reinterpret_cast(wxRoomid.get()), reinterpret_cast(wxWxid.get())); + status = func_send_pat(&holderRoom.wx, &holderWxid.wx); return static_cast(status); } diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index ee752a6..1f509fd 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -51,7 +51,7 @@ private: using SendFile_t = QWORD (*)(QWORD, char *, WxString *, WxString *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD); using SendRichText_t = QWORD (*)(QWORD, WxString *, char *); - using SendPat_t = QWORD (*)(QWORD, QWORD); + using SendPat_t = QWORD (*)(WxString *, WxString *); using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using GetEmotionMgr_t = QWORD (*)(); using SendEmotion_t = QWORD (*)(QWORD, WxString *, QWORD *, WxString *, QWORD, QWORD *, QWORD, QWORD *); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index bdcde26..74afb5f 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -254,7 +254,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_XML, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_xml(r.msg.xml, out, len); } }, { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_emotion(r.msg.file, out, len); } }, { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_rich_text(r.msg.rt, out, len); } }, - // { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_send_pat(r.msg.pm, out, len); } }, + { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_pat(r.msg.pm, out, len); } }, // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, From a53bdeedb131834aa0c197a828873cbe41942eb1 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 01:35:49 +0800 Subject: [PATCH 085/132] feat(message): update forward message offset --- WeChatFerry/spy/offsets.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 98ec109..6219910 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -67,6 +67,8 @@ namespace Message constexpr uint64_t RICH_TEXT = 0x20DD0C0; constexpr uint64_t PAT = 0x2CC1E90; + + constexpr uint64_t FORWARD = 0x22C9220; } } } From 8ac85573b5cee2fe266b26aad56f18c18cae38dc Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 01:36:43 +0800 Subject: [PATCH 086/132] feat(message): impl forward message --- WeChatFerry/spy/message_sender.cpp | 7 ++++--- WeChatFerry/spy/message_sender.h | 2 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 103ea27..b92d639 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -51,7 +51,7 @@ Sender::Sender() func_free_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE_MM_READER); func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::RICH_TEXT); func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OsSend::PAT); - func_forward = reinterpret_cast(g_WeChatWinDllAddr + OS_FORWARD_MSG); + func_forward = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FORWARD); func_get_emotion_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION_MGR); func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION); func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML); @@ -295,9 +295,10 @@ int Sender::forward(QWORD msgid, const std::string &receiver) LARGE_INTEGER l; l.HighPart = dbIdx; l.LowPart = static_cast(localId); - auto wxReceiver = new_wx_string(receiver); - return static_cast(func_forward(reinterpret_cast(wxReceiver.get()), l.QuadPart, 0x4, 0x0)); + WxStringHolder holderReceiver(receiver); + + return static_cast(func_forward(&holderReceiver.wx, l.QuadPart, 0x4, 0x0)); } // RPC 方法 diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 1f509fd..44cc4ce 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -52,7 +52,7 @@ private: QWORD, QWORD); using SendRichText_t = QWORD (*)(QWORD, WxString *, char *); using SendPat_t = QWORD (*)(WxString *, WxString *); - using Forward_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); + using Forward_t = QWORD (*)(WxString *, QWORD, QWORD, QWORD); using GetEmotionMgr_t = QWORD (*)(); using SendEmotion_t = QWORD (*)(QWORD, WxString *, QWORD *, WxString *, QWORD, QWORD *, QWORD, QWORD *); using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 74afb5f..0c5224c 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -255,7 +255,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_EMOTION, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_emotion(r.msg.file, out, len); } }, { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_rich_text(r.msg.rt, out, len); } }, { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_pat(r.msg.pm, out, len); } }, - // { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return sender.rpc_forward(r.msg.fm, out, len); } }, + { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_forward(r.msg.fm, out, len); } }, // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, From 1d9dcf448c185baefe46c00e2ff01ef81901f809 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 27 Feb 2025 01:47:41 +0800 Subject: [PATCH 087/132] refactor(message): refactoring --- WeChatFerry/spy/message_sender.cpp | 34 ++++++++---------------------- WeChatFerry/spy/message_sender.h | 6 +++--- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index b92d639..25ea084 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -13,20 +13,6 @@ extern QWORD g_WeChatWinDllAddr; -#define OS_NEW 0x1B5E140 -#define OS_SEND_IMAGE 0x22BC2F0 -#define OS_GET_APP_MSG_MGR 0x1B58F70 -#define OS_SEND_FILE 0x20D0230 -#define OS_RTM_NEW 0x1B5D690 -#define OS_RTM_FREE 0x1B5CA60 -#define OS_SEND_RICH_TEXT 0x20DA210 -#define OS_SEND_PAT_MSG 0x2CAEC00 -#define OS_FORWARD_MSG 0x22C60E0 -#define OS_GET_EMOTION_MGR 0x1BCEF10 -#define OS_SEND_EMOTION 0x21B52D5 -#define OS_XML_BUFSIGN 0x24F0D70 -#define OS_SEND_XML 0x20CF360 - namespace message { @@ -40,7 +26,7 @@ Sender &Sender::get_instance() Sender::Sender() { - func_get_instance = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); + func_new_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); func_free_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); @@ -101,8 +87,7 @@ void Sender::send_text(const std::string &wxid, const std::string &msg, const st char buffer[1104] = { 0 }; func_send_msg_mgr(); - func_send_text(reinterpret_cast(&buffer), reinterpret_cast(&holderWxid.wx), - reinterpret_cast(&holderMsg.wx), pWxAtWxids, 1, 1, 0, 0); + func_send_text(reinterpret_cast(&buffer), &holderWxid.wx, &holderMsg.wx, pWxAtWxids, 1, 1, 0, 0); func_free_chat_msg(reinterpret_cast(&buffer)); } @@ -116,16 +101,15 @@ void Sender::send_image(const std::string &wxid, const std::string &path) QWORD *flag[10] = { 0 }; QWORD tmp1 = 1, tmp2 = 0, tmp3 = 0; - QWORD pMsgTmp = func_get_instance((QWORD)(&msgTmp)); + QWORD pMsgTmp = func_new_chat_msg((QWORD)(&msgTmp)); flag[0] = reinterpret_cast(tmp1); flag[1] = reinterpret_cast(pMsgTmp); flag[8] = &tmp2; flag[9] = &tmp3; - QWORD pMsg = func_get_instance((QWORD)(&msg)); + QWORD pMsg = func_new_chat_msg((QWORD)(&msg)); QWORD sendMgr = func_send_msg_mgr(); - func_send_image(sendMgr, pMsg, reinterpret_cast(&holderWxid.wx), reinterpret_cast(&holderPath.wx), - reinterpret_cast(&flag)); + func_send_image(sendMgr, pMsg, &holderWxid.wx, &holderPath.wx, reinterpret_cast(&flag)); func_free_chat_msg(pMsg); func_free_chat_msg(pMsgTmp); @@ -181,8 +165,8 @@ void Sender::send_xml(const std::string &receiver, const std::string &xml, const std::unique_ptr buff2(new char[0x500]()); char nullBuf[0x1C] = { 0 }; - func_get_instance(reinterpret_cast(buff.get())); - func_get_instance(reinterpret_cast(buff2.get())); + func_new_chat_msg(reinterpret_cast(buff.get())); + func_new_chat_msg(reinterpret_cast(buff2.get())); QWORD sbuf[4] = { 0, 0, 0, 0 }; QWORD sign = func_xml_buf_sign(reinterpret_cast(buff2.get()), reinterpret_cast(sbuf), 0x1); @@ -293,8 +277,8 @@ int Sender::forward(QWORD msgid, const std::string &receiver) } LARGE_INTEGER l; - l.HighPart = dbIdx; - l.LowPart = static_cast(localId); + l.HighPart = dbIdx; + l.LowPart = static_cast(localId); WxStringHolder holderReceiver(receiver); diff --git a/WeChatFerry/spy/message_sender.h b/WeChatFerry/spy/message_sender.h index 44cc4ce..7811395 100644 --- a/WeChatFerry/spy/message_sender.h +++ b/WeChatFerry/spy/message_sender.h @@ -46,8 +46,8 @@ private: using Free_t = QWORD (*)(QWORD); using SendMsgMgr_t = QWORD (*)(); using GetAppMgr_t = QWORD (*)(); - using SendText_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - using SendImage_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD); + using SendText_t = QWORD (*)(QWORD, WxString *, WxString *, QWORD, QWORD, QWORD, QWORD, QWORD); + using SendImage_t = QWORD (*)(QWORD, QWORD, WxString *, WxString *, QWORD); using SendFile_t = QWORD (*)(QWORD, char *, WxString *, WxString *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD); using SendRichText_t = QWORD (*)(QWORD, WxString *, char *); @@ -58,7 +58,7 @@ private: using XmlBufSign_t = QWORD (*)(QWORD, QWORD, QWORD); using SendXml_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - New_t func_get_instance; + New_t func_new_chat_msg; Free_t func_free_chat_msg; SendMsgMgr_t func_send_msg_mgr; GetAppMgr_t func_get_app_mgr; From 0281b2cb66e958b06d8255c76629adcd638b7f3a Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 28 Feb 2025 22:55:47 +0800 Subject: [PATCH 088/132] feat(db): update db offsets --- WeChatFerry/spy/offsets.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 6219910..c484731 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -16,6 +16,20 @@ namespace Account constexpr uint64_t LOGIN = 0x7F8; // 登录状态 } +namespace Db +{ + constexpr uint64_t INSTANCE = 0x59226C8; // 数据库实例地址 + constexpr uint64_t MSG_I = 0x5980420; // MSGi.db & MediaMsgi.db + constexpr uint64_t MICROMSG = 0xB8; + constexpr uint64_t CHAT_MSG = 0x2C8; + constexpr uint64_t MISC = 0x5F0; + constexpr uint64_t EMOTION = 0x15F0; + constexpr uint64_t MEDIA = 0xF48; + constexpr uint64_t BIZCHAT_MSG = 0x1AC0; + constexpr uint64_t FUNCTION_MSG = 0x1B98; + constexpr uint64_t NAME = 0x28; +} + namespace Message { namespace Log From 962a935880cff362429f78b394e60cf9434bf4d0 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 28 Feb 2025 23:57:39 +0800 Subject: [PATCH 089/132] feat(db): update db offsets --- WeChatFerry/spy/offsets.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index c484731..6554809 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -28,6 +28,26 @@ namespace Db constexpr uint64_t BIZCHAT_MSG = 0x1AC0; constexpr uint64_t FUNCTION_MSG = 0x1B98; constexpr uint64_t NAME = 0x28; + + // SQLITE3 + constexpr uint64_t EXEC = 0x3A76430; + constexpr uint64_t BACKUP_INIT = EXEC - 0x1D113E0; + constexpr uint64_t PREPARE = EXEC + 0x7CB0; + constexpr uint64_t OPEN = EXEC - 0x1CA2430; + constexpr uint64_t BACKUP_STEP = EXEC - 0x1D110A0; + constexpr uint64_t BACKUP_REMAINING = EXEC - 0x1D10880; + constexpr uint64_t BACKUP_PAGECOUNT = EXEC - 0x1D10890; + constexpr uint64_t BACKUP_FINISH = EXEC - 0x1D10940; + constexpr uint64_t SLEEP = EXEC - 0x1CA1BB0; + constexpr uint64_t ERRCODE = EXEC - 0x1CA3770; + constexpr uint64_t CLOSE = EXEC - 0x1CA4FD0; + constexpr uint64_t STEP = EXEC - 0x40000; + constexpr uint64_t COLUMN_COUNT = EXEC - 0x3B7E0; + constexpr uint64_t COLUMN_NAME = EXEC - 0x3ACE0; + constexpr uint64_t COLUMN_TYPE = EXEC - 0x30AF9; + constexpr uint64_t COLUMN_BLOB = EXEC - 0x3B790; + constexpr uint64_t COLUMN_BYTES = EXEC - 0x3B6A0; + constexpr uint64_t FINALIZE = EXEC - 0x3CF30; } namespace Message From c471f749e5e45997db9a8199c652fa269651cd97 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 1 Mar 2025 00:02:30 +0800 Subject: [PATCH 090/132] feat(db): impl get db names --- WeChatFerry/spy/database_executor.cpp | 55 +++++++++++---------------- WeChatFerry/spy/rpc_server.cpp | 2 +- WeChatFerry/spy/sqlite3.h | 19 --------- 3 files changed, 24 insertions(+), 52 deletions(-) diff --git a/WeChatFerry/spy/database_executor.cpp b/WeChatFerry/spy/database_executor.cpp index bd5a7f8..50c24fa 100644 --- a/WeChatFerry/spy/database_executor.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -4,6 +4,7 @@ #include #include "log.hpp" +#include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" #include "sqlite3.h" @@ -13,23 +14,14 @@ extern UINT64 g_WeChatWinDllAddr; namespace db { -#define OFFSET_DB_INSTANCE 0x5902000 -#define OFFSET_DB_MICROMSG 0xB8 -#define OFFSET_DB_CHAT_MSG 0x2C8 -#define OFFSET_DB_MISC 0x5F0 -#define OFFSET_DB_EMOTION 0x15F0 -#define OFFSET_DB_MEDIA 0xF48 -#define OFFSET_DB_BIZCHAT_MSG 0x1A70 -#define OFFSET_DB_FUNCTION_MSG 0x1B98 -#define OFFSET_DB_NAME 0x28 -#define OFFSET_DB_MSG_MGR 0x595F900 +namespace OsDb = Offsets::Db; using db_map_t = std::map; static db_map_t db_map; static void get_db_handle(QWORD base, QWORD offset) { - auto *wsp = reinterpret_cast(*(QWORD *)(base + offset + OFFSET_DB_NAME)); + auto *wsp = reinterpret_cast(*(QWORD *)(base + offset + OsDb::NAME)); std::string dbname = util::w2s(std::wstring(wsp)); db_map[dbname] = util::get_qword(base + offset); } @@ -56,17 +48,16 @@ static void get_msg_db_handle(QWORD msg_mgr_addr) db_map_t get_db_handles() { db_map.clear(); - QWORD db_instance_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); + QWORD db_instance_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::INSTANCE); - get_db_handle(db_instance_addr, OFFSET_DB_MICROMSG); // MicroMsg.db - get_db_handle(db_instance_addr, OFFSET_DB_CHAT_MSG); // ChatMsg.db - get_db_handle(db_instance_addr, OFFSET_DB_MISC); // Misc.db - get_db_handle(db_instance_addr, OFFSET_DB_EMOTION); // Emotion.db - get_db_handle(db_instance_addr, OFFSET_DB_MEDIA); // Media.db - get_db_handle(db_instance_addr, OFFSET_DB_FUNCTION_MSG); // Function.db - - get_msg_db_handle(util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db + get_db_handle(db_instance_addr, OsDb::MICROMSG); // MicroMsg.db + get_db_handle(db_instance_addr, OsDb::CHAT_MSG); // ChatMsg.db + get_db_handle(db_instance_addr, OsDb::MISC); // Misc.db + get_db_handle(db_instance_addr, OsDb::EMOTION); // Emotion.db + get_db_handle(db_instance_addr, OsDb::MEDIA); // Media.db + get_db_handle(db_instance_addr, OsDb::FUNCTION_MSG); // Function.db + get_msg_db_handle(util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I)); // MSGi.db & MediaMsgi.db return db_map; } @@ -113,7 +104,7 @@ DbTables_t get_db_tables(const std::string &db) } constexpr const char *sql = "SELECT name FROM sqlite_master WHERE type='table';"; - Sqlite3_exec p_sqlite3_exec = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET); + Sqlite3_exec p_sqlite3_exec = reinterpret_cast(g_WeChatWinDllAddr + OsDb::EXEC); p_sqlite3_exec(it->second, sql, (Sqlite3_callback)cb_get_tables, (void *)&tables, nullptr); return tables; @@ -123,19 +114,19 @@ DbRows_t exec_db_query(const std::string &db, const std::string &sql) { DbRows_t rows; - Sqlite3_prepare func_prepare = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET); - Sqlite3_step func_step = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET); + Sqlite3_prepare func_prepare = reinterpret_cast(g_WeChatWinDllAddr + OsDb::PREPARE); + Sqlite3_step func_step = reinterpret_cast(g_WeChatWinDllAddr + OsDb::STEP); Sqlite3_column_count func_column_count - = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET); + = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_COUNT); Sqlite3_column_name func_column_name - = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET); + = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_NAME); Sqlite3_column_type func_column_type - = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET); + = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_TYPE); Sqlite3_column_blob func_column_blob - = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET); + = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_BLOB); Sqlite3_column_bytes func_column_bytes - = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET); - Sqlite3_finalize func_finalize = reinterpret_cast(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET); + = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_BYTES); + Sqlite3_finalize func_finalize = reinterpret_cast(g_WeChatWinDllAddr + OsDb::FINALIZE); if (db_map.empty()) { db_map = get_db_handles(); @@ -189,7 +180,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) return -1; } - QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I); int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧? QWORD p_start = util::get_qword(msg_mgr_addr + 0x50); @@ -232,7 +223,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) std::vector get_audio_data(uint64_t id) { - QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); + QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I); int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); std::string sql = "SELECT Buf FROM Media WHERE Reserved0=" + std::to_string(id) + ";"; @@ -262,8 +253,8 @@ std::vector get_audio_data(uint64_t id) bool rpc_get_db_names(uint8_t *out, size_t *len) { + DbNames_t names = get_db_names(); return fill_response(out, len, [&](Response &rsp) { - DbNames_t names = get_db_names(); rsp.msg.dbs.names.funcs.encode = encode_dbnames; rsp.msg.dbs.names.arg = &names; }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 0c5224c..6a082fa 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -245,7 +245,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().start_message_listener(r.msg.flag, out, len); } }, { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().stop_message_listener(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, - // { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, + { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, diff --git a/WeChatFerry/spy/sqlite3.h b/WeChatFerry/spy/sqlite3.h index b50ec2a..a680be7 100644 --- a/WeChatFerry/spy/sqlite3.h +++ b/WeChatFerry/spy/sqlite3.h @@ -138,25 +138,6 @@ #define SQLITE_NULL 5 #define SQLITE_TEXT 3 -#define SQLITE3_EXEC_OFFSET 0x3A5EDA0 -#define SQLITE3_BACKUP_INIT_OFFSET 0x3A18EA0 -#define SQLITE3_PREPARE_OFFSET 0x3A66A20 -#define SQLITE3_OPEN_OFFSET 0x3A9E210 -#define SQLITE3_BACKUP_STEP_OFFSET 0x3A193F0 -#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1B26EB0 -#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1B26EE0 -#define SQLITE3_BACKUP_FINISH_OFFSET 0x3A19AF0 -#define SQLITE3_SLEEP_OFFSET 0x3A9EE70 -#define SQLITE3_ERRCODE_OFFSET 0x3A9CB10 -#define SQLITE3_CLOSE_OFFSET 0x3A9AC70 -#define SQLITE3_STEP_OFFSET 0x3A22DA0 -#define SQLITE3_COLUMN_COUNT_OFFSET 0x3A235C0 -#define SQLITE3_COLUMN_NAME_OFFSET 0x3A23FC0 -#define SQLITE3_COLUMN_TYPE_OFFSET 0x3A23E10 -#define SQLITE3_COLUMN_BLOB_OFFSET 0x3A235F0 -#define SQLITE3_COLUMN_BYTES_OFFSET 0x3A236E0 -#define SQLITE3_FINALIZE_OFFSET 0x3A21E50 - typedef int (*Sqlite3_callback)(void *, int, char **, char **); typedef int(__cdecl *Sqlite3_exec)(QWORD, /* An open database */ From 89bca5c20cc7801f03adf4b03701201588a72f19 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 1 Mar 2025 00:34:01 +0800 Subject: [PATCH 091/132] fix(db): fix offsets --- WeChatFerry/spy/offsets.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 6554809..ef5deba 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -31,23 +31,23 @@ namespace Db // SQLITE3 constexpr uint64_t EXEC = 0x3A76430; - constexpr uint64_t BACKUP_INIT = EXEC - 0x1D113E0; + // constexpr uint64_t BACKUP_INIT = EXEC - 0x1D113E0; constexpr uint64_t PREPARE = EXEC + 0x7CB0; - constexpr uint64_t OPEN = EXEC - 0x1CA2430; - constexpr uint64_t BACKUP_STEP = EXEC - 0x1D110A0; - constexpr uint64_t BACKUP_REMAINING = EXEC - 0x1D10880; - constexpr uint64_t BACKUP_PAGECOUNT = EXEC - 0x1D10890; - constexpr uint64_t BACKUP_FINISH = EXEC - 0x1D10940; - constexpr uint64_t SLEEP = EXEC - 0x1CA1BB0; - constexpr uint64_t ERRCODE = EXEC - 0x1CA3770; - constexpr uint64_t CLOSE = EXEC - 0x1CA4FD0; - constexpr uint64_t STEP = EXEC - 0x40000; + // constexpr uint64_t OPEN = EXEC - 0x1CA2430; + // constexpr uint64_t BACKUP_STEP = EXEC - 0x1D110A0; + // constexpr uint64_t BACKUP_REMAINING = EXEC - 0x1D10880; + // constexpr uint64_t BACKUP_PAGECOUNT = EXEC - 0x1D10890; + // constexpr uint64_t BACKUP_FINISH = EXEC - 0x1D10940; + // constexpr uint64_t SLEEP = EXEC - 0x1CA1BB0; + // constexpr uint64_t ERRCODE = EXEC - 0x1CA3770; + // constexpr uint64_t CLOSE = EXEC - 0x1CA4FD0; + constexpr uint64_t STEP = EXEC - 0x3C000; constexpr uint64_t COLUMN_COUNT = EXEC - 0x3B7E0; - constexpr uint64_t COLUMN_NAME = EXEC - 0x3ACE0; - constexpr uint64_t COLUMN_TYPE = EXEC - 0x30AF9; - constexpr uint64_t COLUMN_BLOB = EXEC - 0x3B790; - constexpr uint64_t COLUMN_BYTES = EXEC - 0x3B6A0; - constexpr uint64_t FINALIZE = EXEC - 0x3CF30; + constexpr uint64_t COLUMN_NAME = EXEC - 0x3ADE0; + constexpr uint64_t COLUMN_TYPE = EXEC - 0x3AF90; + constexpr uint64_t COLUMN_BLOB = EXEC - 0x3B7B0; + constexpr uint64_t COLUMN_BYTES = EXEC - 0x3B6C0; + constexpr uint64_t FINALIZE = EXEC - 0x3CF50; } namespace Message From 0aefbbca989fd9172e09a0c9029cda7bf678bbb5 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 1 Mar 2025 00:35:23 +0800 Subject: [PATCH 092/132] feat(db): impl db functions --- WeChatFerry/spy/database_executor.cpp | 4 ++-- WeChatFerry/spy/rpc_server.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WeChatFerry/spy/database_executor.cpp b/WeChatFerry/spy/database_executor.cpp index 50c24fa..30a4944 100644 --- a/WeChatFerry/spy/database_executor.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -262,8 +262,8 @@ bool rpc_get_db_names(uint8_t *out, size_t *len) bool rpc_get_db_tables(const std::string &db, uint8_t *out, size_t *len) { + DbTables_t tables = get_db_tables(db); return fill_response(out, len, [&](Response &rsp) { - DbTables_t tables = get_db_tables(db); rsp.msg.tables.tables.funcs.encode = encode_tables; rsp.msg.tables.tables.arg = &tables; }); @@ -273,8 +273,8 @@ bool rpc_exec_db_query(const DbQuery query, uint8_t *out, size_t *len) { const std::string db(query.db); const std::string sql(query.sql); + DbRows_t rows = exec_db_query(db, sql); return fill_response(out, len, [&](Response &rsp) { - DbRows_t rows = exec_db_query(db, sql); rsp.msg.rows.rows.funcs.encode = encode_rows; rsp.msg.rows.rows.arg = &rows; }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 6a082fa..314b00c 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -246,7 +246,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().stop_message_listener(out, len); } }, // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, - // { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, + { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, @@ -256,7 +256,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_RICH_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_rich_text(r.msg.rt, out, len); } }, { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_pat(r.msg.pm, out, len); } }, { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_forward(r.msg.fm, out, len); } }, - // { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, + { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, // { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, From 68b04d85de106fcaee2f8bd0d17586bf36fd1d38 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 2 Mar 2025 00:50:26 +0800 Subject: [PATCH 093/132] fix(message): fix forward message --- WeChatFerry/spy/message_sender.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 25ea084..5e041d7 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -280,9 +280,9 @@ int Sender::forward(QWORD msgid, const std::string &receiver) l.HighPart = dbIdx; l.LowPart = static_cast(localId); - WxStringHolder holderReceiver(receiver); + WxString *pReceiver = util::CreateWxString(receiver); - return static_cast(func_forward(&holderReceiver.wx, l.QuadPart, 0x4, 0x0)); + return static_cast(func_forward(pReceiver, l.QuadPart, 0x4, 0x0)); } // RPC 方法 From ffe55bbbc44fdbc589ee048784c97cdb5da06063 Mon Sep 17 00:00:00 2001 From: liotherm Date: Sun, 2 Mar 2025 22:17:23 +0800 Subject: [PATCH 094/132] Update partial offset for 3.9.12.45 --- WeChatFerry/spy/chatroom_mgmt.cpp | 10 ++--- WeChatFerry/spy/contact_mgmt.cpp | 6 +-- WeChatFerry/spy/exec_sql.cpp | 6 +-- WeChatFerry/spy/funcs.cpp | 66 +++++++++++++++++++++++-------- WeChatFerry/spy/receive_msg.cpp | 8 ++-- WeChatFerry/spy/send_msg.cpp | 34 ++++++++-------- WeChatFerry/spy/spy.cpp | 4 +- WeChatFerry/spy/spy.h | 2 +- WeChatFerry/spy/sqlite3.h | 36 ++++++++--------- WeChatFerry/spy/user_info.cpp | 8 ++-- 10 files changed, 107 insertions(+), 73 deletions(-) diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp index 0950d45..06f353b 100644 --- a/WeChatFerry/spy/chatroom_mgmt.cpp +++ b/WeChatFerry/spy/chatroom_mgmt.cpp @@ -3,16 +3,16 @@ #include #include "chatroom_mgmt.h" -#include "log.hpp" +#include "log.h" #include "util.h" using namespace std; extern QWORD g_WeChatWinDllAddr; -#define OS_GET_CHATROOM_MGR 0x1B83BD0 -#define OS_ADD_MEMBERS 0x2155100 -#define OS_DELETE_MEMBERS 0x2155740 -#define OS_INVITE_MEMBERS 0x2154AE0 +#define OS_GET_CHATROOM_MGR 0x1B894E0 +#define OS_ADD_MEMBERS 0x215A820 +#define OS_DELETE_MEMBERS 0x215AE60 +#define OS_INVITE_MEMBERS 0x215A200 typedef QWORD (*GetChatRoomMgr_t)(); typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD); diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp index 84f916a..16d077d 100644 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ b/WeChatFerry/spy/contact_mgmt.cpp @@ -1,14 +1,14 @@ #pragma execution_character_set("utf-8") #include "contact_mgmt.h" -#include "log.hpp" +#include "log.h" #include "util.h" using namespace std; extern QWORD g_WeChatWinDllAddr; -#define OS_GET_CONTACT_MGR 0x1B417A0 -#define OS_GET_CONTACT_LIST 0x219ED10 +#define OS_GET_CONTACT_MGR 0x1B470B0 +#define OS_GET_CONTACT_LIST 0x21A49D0 #define OS_CONTACT_BIN 0x200 #define OS_CONTACT_BIN_LEN 0x208 #define OS_CONTACT_WXID 0x10 diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp index 834c856..2f48510 100644 --- a/WeChatFerry/spy/exec_sql.cpp +++ b/WeChatFerry/spy/exec_sql.cpp @@ -1,11 +1,11 @@ #include #include "exec_sql.h" -#include "log.hpp" +#include "log.h" #include "sqlite3.h" #include "util.h" -#define OFFSET_DB_INSTANCE 0x5902000 +#define OFFSET_DB_INSTANCE 0x59C5B48 #define OFFSET_DB_MICROMSG 0xB8 #define OFFSET_DB_CHAT_MSG 0x2C8 #define OFFSET_DB_MISC 0x5F0 @@ -14,7 +14,7 @@ #define OFFSET_DB_BIZCHAT_MSG 0x1A70 #define OFFSET_DB_FUNCTION_MSG 0x1B98 #define OFFSET_DB_NAME 0x28 -#define OFFSET_DB_MSG_MGR 0x595F900 +#define OFFSET_DB_MSG_MGR 0x5A23888 extern UINT64 g_WeChatWinDllAddr; diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp index c12f9b5..e3249e7 100644 --- a/WeChatFerry/spy/funcs.cpp +++ b/WeChatFerry/spy/funcs.cpp @@ -3,11 +3,13 @@ #include "framework.h" #include #include +#include +#include #include "codec.h" #include "exec_sql.h" #include "funcs.h" -#include "log.hpp" +#include "log.h" #include "spy_types.h" #include "util.h" @@ -24,18 +26,18 @@ extern QWORD g_WeChatWinDllAddr; #define HEADER_GIF1 0x47 #define HEADER_GIF2 0x49 -#define OS_LOGIN_STATUS 0x595C9E8 -#define OS_GET_SNS_DATA_MGR 0x21E2200 -#define OS_GET_SNS_FIRST_PAGE 0x2E212D0 -#define OS_GET_SNS_TIMELINE_MGR 0x2DB3390 -#define OS_GET_SNS_NEXT_PAGE 0x2EC8970 -#define OS_NEW_CHAT_MSG 0x1B5E140 -#define OS_FREE_CHAT_MSG 0x1B55850 -#define OS_GET_CHAT_MGR 0x1B876C0 -#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x213FB00 -#define OS_GET_PRE_DOWNLOAD_MGR 0x1C0EE70 -#define OS_PUSH_ATTACH_TASK 0x1CDF4E0 -#define OS_LOGIN_QR_CODE 0x59620D8 +#define OS_LOGIN_STATUS 0x5A20978 +#define OS_GET_SNS_DATA_MGR 0x21E7EC0 +#define OS_GET_SNS_FIRST_PAGE 0x2E37960 +#define OS_GET_SNS_TIMELINE_MGR 0x2DC9470 +#define OS_GET_SNS_NEXT_PAGE 0x2EDF4D0 +#define OS_NEW_CHAT_MSG 0x1B63A50 +#define OS_FREE_CHAT_MSG 0x1B5B160 +#define OS_GET_CHAT_MGR 0x1B8CFD0 +#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x2145220 +#define OS_GET_PRE_DOWNLOAD_MGR 0x1C14930 +#define OS_PUSH_ATTACH_TASK 0x1CE57B0 +#define OS_LOGIN_QR_CODE 0x5A26440 typedef QWORD (*GetSNSDataMgr_t)(); typedef QWORD (*GetSnsTimeLineMgr_t)(); @@ -75,6 +77,37 @@ static string get_key(uint8_t header1, uint8_t header2, uint8_t *key) return ""; // 错误 } +// 创建多级目录 +bool CreateDir(const char* dir) +{ + int m = 0, n; + string str1, str2; + str1 = dir; + str2 = str1.substr(0, 2); + str1 = str1.substr(3, str1.size()); + while (m >= 0) + { + m = str1.find('/'); + + str2 += '/' + str1.substr(0, m); + //判断该目录是否存在 + n = _access(str2.c_str(), 0); + if (n == -1) + { + //创建目录文件 + int flag = _mkdir(str2.c_str()); + if (flag != 0) { //创建失败 + LOG_ERROR("Failed to CreateDir:{}", dir); + return false; + } + } + + str1 = str1.substr(m + 1, str1.size()); + } + LOG_DEBUG("CreateDir {} success.", dir); + return true; +} + string DecryptImage(string src, string dir) { if (!fs::exists(src)) { @@ -116,10 +149,11 @@ string DecryptImage(string src, string dir) dst = fs::path(src).replace_extension(ext).string(); } else { dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - + replace(dst.begin(), dst.end(), '\\', '/'); + // 判断dir文件夹是否存在,若不存在则创建(否则将无法创建出文件) - if (!fs::exists(dst)) {//判断该文件夹是否存在 - bool success = fs::create_directories(dst); //Windows创建文件夹 + if (_access(dst.c_str(), 0) == -1) {//判断该文件夹是否存在 + bool success = CreateDir(dst.c_str()); //Windows创建文件夹 if (!success) { //创建失败 LOG_ERROR("Failed to mkdir:{}", dst); return ""; diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index e283ce0..cff2706 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -6,7 +6,7 @@ #include #include -#include "log.hpp" +#include "log.h" #include "receive_msg.h" #include "user_info.h" #include "util.h" @@ -31,15 +31,15 @@ extern QWORD g_WeChatWinDllAddr; #define OS_RECV_MSG_THUMB 0x280 #define OS_RECV_MSG_EXTRA 0x2A0 #define OS_RECV_MSG_XML 0x308 -#define OS_RECV_MSG_CALL 0x213ED90 +#define OS_RECV_MSG_CALL 0x21444B0 #define OS_PYQ_MSG_START 0x30 #define OS_PYQ_MSG_END 0x38 #define OS_PYQ_MSG_TS 0x38 #define OS_PYQ_MSG_XML 0x9B8 #define OS_PYQ_MSG_SENDER 0x18 #define OS_PYQ_MSG_CONTENT 0x48 -#define OS_PYQ_MSG_CALL 0x2E42C90 -#define OS_WXLOG 0x2613D20 +#define OS_PYQ_MSG_CALL 0x2E59320 +#define OS_WXLOG 0x261E760 typedef QWORD (*RecvMsg_t)(QWORD, QWORD); typedef QWORD (*WxLog_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp index dca37f7..ed69865 100644 --- a/WeChatFerry/spy/send_msg.cpp +++ b/WeChatFerry/spy/send_msg.cpp @@ -4,7 +4,7 @@ #include #include "exec_sql.h" -#include "log.hpp" +#include "log.h" #include "send_msg.h" #include "spy_types.h" #include "util.h" @@ -15,22 +15,22 @@ extern string GetSelfWxid(); // Defined in spy.cpp #define SRTM_SIZE 0x3F0 -#define OS_NEW 0x1B5E140 -#define OS_FREE 0x1B55850 -#define OS_SEND_MSG_MGR 0x1B53FD0 -#define OS_SEND_TEXT 0x22C6B60 -#define OS_SEND_IMAGE 0x22BC2F0 -#define OS_GET_APP_MSG_MGR 0x1B58F70 -#define OS_SEND_FILE 0x20D0230 -#define OS_RTM_NEW 0x1B5D690 -#define OS_RTM_FREE 0x1B5CA60 -#define OS_SEND_RICH_TEXT 0x20DA210 -#define OS_SEND_PAT_MSG 0x2CAEC00 -#define OS_FORWARD_MSG 0x22C60E0 -#define OS_GET_EMOTION_MGR 0x1BCEF10 -#define OS_SEND_EMOTION 0x21B52D5 -#define OS_XML_BUFSIGN 0x24F0D70 -#define OS_SEND_XML 0x20CF360 +#define OS_NEW 0x1B63A50 +#define OS_FREE 0x1B5B160 +#define OS_SEND_MSG_MGR 0x1B598E0 +#define OS_SEND_TEXT 0x22CC660 +#define OS_SEND_IMAGE 0x22C1E70 +#define OS_GET_APP_MSG_MGR 0x1B5E880 +#define OS_SEND_FILE 0x20D5FF0 +#define OS_RTM_NEW 0x1B62FA0 +#define OS_RTM_FREE 0x1B62370 +#define OS_SEND_RICH_TEXT 0x20DFFD0 +#define OS_SEND_PAT_MSG 0x2CC4F10 +#define OS_FORWARD_MSG 0x22CBBE0 +#define OS_GET_EMOTION_MGR 0x1BD49A0 +#define OS_SEND_EMOTION 0x21BACD0 +#define OS_XML_BUFSIGN 0x24FB330 +#define OS_SEND_XML 0x20D5120 typedef QWORD (*New_t)(QWORD); typedef QWORD (*Free_t)(QWORD); diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index e179aa0..576276c 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -1,6 +1,6 @@ #include -#include "log.hpp" +#include "log.h" #include "rpc_server.h" #include "spy.h" #include "util.h" @@ -21,7 +21,7 @@ void InitSpy(LPVOID args) wchar_t version[16] = { 0 }; PortPath_t *pp = (PortPath_t *)args; - Log::InitLogger(pp->path); + InitLogger(pp->path); g_WeChatWinDllAddr = (UINT64)GetModuleHandle(L"WeChatWin.dll"); // 获取wechatWin模块地址 if (g_WeChatWinDllAddr == 0) { LOG_ERROR("获取 wechatWin.dll 模块地址失败"); diff --git a/WeChatFerry/spy/spy.h b/WeChatFerry/spy/spy.h index 525109a..8a7d61c 100644 --- a/WeChatFerry/spy/spy.h +++ b/WeChatFerry/spy/spy.h @@ -2,7 +2,7 @@ #include "framework.h" -#define SUPPORT_VERSION L"3.9.11.25" +#define SUPPORT_VERSION L"3.9.12.45" void InitSpy(int port); void CleanupSpy(); diff --git a/WeChatFerry/spy/sqlite3.h b/WeChatFerry/spy/sqlite3.h index b50ec2a..176de80 100644 --- a/WeChatFerry/spy/sqlite3.h +++ b/WeChatFerry/spy/sqlite3.h @@ -138,24 +138,24 @@ #define SQLITE_NULL 5 #define SQLITE_TEXT 3 -#define SQLITE3_EXEC_OFFSET 0x3A5EDA0 -#define SQLITE3_BACKUP_INIT_OFFSET 0x3A18EA0 -#define SQLITE3_PREPARE_OFFSET 0x3A66A20 -#define SQLITE3_OPEN_OFFSET 0x3A9E210 -#define SQLITE3_BACKUP_STEP_OFFSET 0x3A193F0 -#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1B26EB0 -#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1B26EE0 -#define SQLITE3_BACKUP_FINISH_OFFSET 0x3A19AF0 -#define SQLITE3_SLEEP_OFFSET 0x3A9EE70 -#define SQLITE3_ERRCODE_OFFSET 0x3A9CB10 -#define SQLITE3_CLOSE_OFFSET 0x3A9AC70 -#define SQLITE3_STEP_OFFSET 0x3A22DA0 -#define SQLITE3_COLUMN_COUNT_OFFSET 0x3A235C0 -#define SQLITE3_COLUMN_NAME_OFFSET 0x3A23FC0 -#define SQLITE3_COLUMN_TYPE_OFFSET 0x3A23E10 -#define SQLITE3_COLUMN_BLOB_OFFSET 0x3A235F0 -#define SQLITE3_COLUMN_BYTES_OFFSET 0x3A236E0 -#define SQLITE3_FINALIZE_OFFSET 0x3A21E50 +#define SQLITE3_EXEC_OFFSET 0x3A791F0 +#define SQLITE3_BACKUP_INIT_OFFSET 0x3A332F0 +#define SQLITE3_PREPARE_OFFSET 0x3A80E70 +#define SQLITE3_OPEN_OFFSET 0x3AB8660 +#define SQLITE3_BACKUP_STEP_OFFSET 0x3A33840 +#define SQLITE3_BACKUP_REMAINING_OFFSET 0x1B2C480 +#define SQLITE3_BACKUP_PAGECOUNT_OFFSET 0x1B2C4C0 +#define SQLITE3_BACKUP_FINISH_OFFSET 0x3A33F40 +#define SQLITE3_SLEEP_OFFSET 0x3AB92C0 +#define SQLITE3_ERRCODE_OFFSET 0x3AB6F60 +#define SQLITE3_CLOSE_OFFSET 0x3AB50C0 +#define SQLITE3_STEP_OFFSET 0x3A3D1F0 +#define SQLITE3_COLUMN_COUNT_OFFSET 0x3A3DA10 +#define SQLITE3_COLUMN_NAME_OFFSET 0x3A3E410 +#define SQLITE3_COLUMN_TYPE_OFFSET 0x3A3E260 +#define SQLITE3_COLUMN_BLOB_OFFSET 0x3A3DA40 +#define SQLITE3_COLUMN_BYTES_OFFSET 0x3A3DB30 +#define SQLITE3_FINALIZE_OFFSET 0x3A3C2A0 typedef int (*Sqlite3_callback)(void *, int, char **, char **); diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp index a273592..134b78c 100644 --- a/WeChatFerry/spy/user_info.cpp +++ b/WeChatFerry/spy/user_info.cpp @@ -1,12 +1,12 @@ #include "user_info.h" -#include "log.hpp" +#include "log.h" #include "util.h" extern UINT64 g_WeChatWinDllAddr; -#define OS_USER_HOME 0x5932770 -#define OS_USER_WXID 0x595C270 -#define OS_USER_NAME 0x595C3D8 +#define OS_USER_HOME 0x59F6330 +#define OS_USER_WXID 0x5A20200 +#define OS_USER_NAME 0x5A20368 #define OS_USER_MOBILE 0x595C318 static char home[MAX_PATH] = { 0 }; From bc7f91fc54c10152460b66e8817ea733323b11b4 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 21:42:33 +0800 Subject: [PATCH 095/132] feat(misc): impl refresh pyq --- WeChatFerry/spy/offsets.h | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index ef5deba..2c88610 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -30,9 +30,9 @@ namespace Db constexpr uint64_t NAME = 0x28; // SQLITE3 - constexpr uint64_t EXEC = 0x3A76430; + constexpr uint64_t EXEC = 0x3A76430; // constexpr uint64_t BACKUP_INIT = EXEC - 0x1D113E0; - constexpr uint64_t PREPARE = EXEC + 0x7CB0; + constexpr uint64_t PREPARE = EXEC + 0x7CB0; // constexpr uint64_t OPEN = EXEC - 0x1CA2430; // constexpr uint64_t BACKUP_STEP = EXEC - 0x1D110A0; // constexpr uint64_t BACKUP_REMAINING = EXEC - 0x1D10880; @@ -41,13 +41,13 @@ namespace Db // constexpr uint64_t SLEEP = EXEC - 0x1CA1BB0; // constexpr uint64_t ERRCODE = EXEC - 0x1CA3770; // constexpr uint64_t CLOSE = EXEC - 0x1CA4FD0; - constexpr uint64_t STEP = EXEC - 0x3C000; - constexpr uint64_t COLUMN_COUNT = EXEC - 0x3B7E0; - constexpr uint64_t COLUMN_NAME = EXEC - 0x3ADE0; - constexpr uint64_t COLUMN_TYPE = EXEC - 0x3AF90; - constexpr uint64_t COLUMN_BLOB = EXEC - 0x3B7B0; - constexpr uint64_t COLUMN_BYTES = EXEC - 0x3B6C0; - constexpr uint64_t FINALIZE = EXEC - 0x3CF50; + constexpr uint64_t STEP = EXEC - 0x3C000; + constexpr uint64_t COLUMN_COUNT = EXEC - 0x3B7E0; + constexpr uint64_t COLUMN_NAME = EXEC - 0x3ADE0; + constexpr uint64_t COLUMN_TYPE = EXEC - 0x3AF90; + constexpr uint64_t COLUMN_BLOB = EXEC - 0x3B7B0; + constexpr uint64_t COLUMN_BYTES = EXEC - 0x3B6C0; + constexpr uint64_t FINALIZE = EXEC - 0x3CF50; } namespace Message @@ -105,6 +105,17 @@ namespace Message constexpr uint64_t FORWARD = 0x22C9220; } } + +namespace Misc +{ + namespace Sns + { + constexpr uint64_t DATA_MGR = 0x21E52F0; + constexpr uint64_t TIMELINE = 0x2DC6180; + constexpr uint64_t FIRST = 0x2E346C0; + constexpr uint64_t NEXT = 0x2E5A270; + } +} } #endif // OFFSETS_H From 3039599905c328df9f3136d7f3a3ddca074be7ca Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 21:43:15 +0800 Subject: [PATCH 096/132] feat(misc): impl refresh pyq --- WeChatFerry/spy/misc_manager.cpp | 23 ++++++++++------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 17e1e8e..eed0c2a 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -10,17 +10,19 @@ #include "database_executor.h" #include "log.hpp" #include "message_handler.h" +#include "offsets.h" #include "rpc_helper.h" #include "spy_types.h" #include "util.h" -using namespace std; -namespace fs = std::filesystem; - extern QWORD g_WeChatWinDllAddr; namespace misc { +using namespace std; +namespace fs = std::filesystem; +namespace OsSns = Offsets::Misc::Sns; + #define HEADER_PNG1 0x89 #define HEADER_PNG2 0x50 #define HEADER_JPG1 0xFF @@ -28,10 +30,6 @@ namespace misc #define HEADER_GIF1 0x47 #define HEADER_GIF2 0x49 -#define OS_GET_SNS_DATA_MGR 0x21E2200 -#define OS_GET_SNS_FIRST_PAGE 0x2E212D0 -#define OS_GET_SNS_TIMELINE_MGR 0x2DB3390 -#define OS_GET_SNS_NEXT_PAGE 0x2EC8970 #define OS_NEW_CHAT_MSG 0x1B5E140 #define OS_FREE_CHAT_MSG 0x1B55850 #define OS_GET_CHAT_MGR 0x1B876C0 @@ -103,12 +101,12 @@ static int get_first_page() { int status = -1; - get_sns_data_mgr_t GetSNSDataMgr = (get_sns_data_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR); - get_sns_first_page_t GetSNSFirstPage = (get_sns_first_page_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE); + get_sns_data_mgr_t GetSNSDataMgr = (get_sns_data_mgr_t)(g_WeChatWinDllAddr + OsSns::DATA_MGR); + get_sns_first_page_t GetSNSFirstPage = (get_sns_first_page_t)(g_WeChatWinDllAddr + OsSns::FIRST); QWORD buff[16] = { 0 }; QWORD mgr = GetSNSDataMgr(); - status = (int)GetSNSFirstPage(mgr, (QWORD)buff, 1); + status = (int)GetSNSFirstPage(mgr, (QWORD)&buff, 1); return status; } @@ -117,9 +115,8 @@ static int get_next_page(QWORD id) { int status = -1; - get_sns_timeline_mgr_t GetSnsTimeLineMgr = (get_sns_timeline_mgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR); - get_sns_next_page_scene_t GetSNSNextPageScene - = (get_sns_next_page_scene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE); + get_sns_timeline_mgr_t GetSnsTimeLineMgr = (get_sns_timeline_mgr_t)(g_WeChatWinDllAddr + OsSns::TIMELINE); + get_sns_next_page_scene_t GetSNSNextPageScene = (get_sns_next_page_scene_t)(g_WeChatWinDllAddr + OsSns::NEXT); QWORD mgr = GetSnsTimeLineMgr(); status = (int)GetSNSNextPageScene(mgr, id); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 314b00c..0657884 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -259,7 +259,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, - // { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, + { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, From b665db572f9b581ffbfc4a7374de970a6a80c0e2 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 21:47:31 +0800 Subject: [PATCH 097/132] refactor(misc): replace macro with constexpr --- WeChatFerry/spy/misc_manager.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index eed0c2a..6ae22ad 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -23,12 +23,12 @@ using namespace std; namespace fs = std::filesystem; namespace OsSns = Offsets::Misc::Sns; -#define HEADER_PNG1 0x89 -#define HEADER_PNG2 0x50 -#define HEADER_JPG1 0xFF -#define HEADER_JPG2 0xD8 -#define HEADER_GIF1 0x47 -#define HEADER_GIF2 0x49 +constexpr uint8_t HEADER_PNG1 = 0x89; +constexpr uint8_t HEADER_PNG2 = 0x50; +constexpr uint8_t HEADER_JPG1 = 0xFF; +constexpr uint8_t HEADER_JPG2 = 0xD8; +constexpr uint8_t HEADER_GIF1 = 0x47; +constexpr uint8_t HEADER_GIF2 = 0x49; #define OS_NEW_CHAT_MSG 0x1B5E140 #define OS_FREE_CHAT_MSG 0x1B55850 From ba0a8b33b84d96a4a46919bcef68b4a2f59a7228 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 21:53:14 +0800 Subject: [PATCH 098/132] feat(misc): impl decrypt image --- WeChatFerry/spy/rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 0657884..459ccc3 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -264,7 +264,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, // { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, - // { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, + { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, // { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, // { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, From 09da3044be648c122a39cd518ba6fae860ea482e Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 22:11:34 +0800 Subject: [PATCH 099/132] fix(misc): fix decrypt image --- WeChatFerry/spy/misc_manager.cpp | 33 ++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 6ae22ad..1b4626d 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -51,11 +51,28 @@ using push_attach_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using get_ocr_manager_t = QWORD (*)(); using do_ocr_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -static std::string detect_image_extension(uint8_t header1, uint8_t header2, uint8_t &key) +struct ImagePattern { + uint8_t header1_candidate; + uint8_t header2_expected; + const char *extension; +}; + +static constexpr ImagePattern patterns[] = { + { 0x89, 0x50, ".png" }, + { 0xFF, 0xD8, ".jpg" }, + { 0x47, 0x49, ".gif" }, +}; + +static std::string detect_image_extension(uint8_t header1, uint8_t header2, uint8_t *key) { - if ((key = HEADER_PNG1 ^ header1) && (HEADER_PNG2 ^ key) == header2) return ".png"; - if ((key = HEADER_JPG1 ^ header1) && (HEADER_JPG2 ^ key) == header2) return ".jpg"; - if ((key = HEADER_GIF1 ^ header1) && (HEADER_GIF2 ^ key) == header2) return ".gif"; + + for (const auto &pat : patterns) { + *key = pat.header1_candidate ^ header1; + if ((pat.header2_expected ^ *key) == header2) { + return pat.extension; + } + } + LOG_ERROR("未知类型:{:02x} {:02x}", header1, header2); return ""; } @@ -76,8 +93,8 @@ std::string decrypt_image(const fs::path &src, const fs::path &dst_dir) if (buffer.size() < 2) return ""; uint8_t key = 0x00; - auto ext = detect_image_extension(buffer[0], buffer[1], key); - if (!ext.empty()) { + auto ext = detect_image_extension(buffer[0], buffer[1], &key); + if (ext.empty()) { LOG_ERROR("无法检测文件类型."); return ""; } @@ -89,12 +106,12 @@ std::string decrypt_image(const fs::path &src, const fs::path &dst_dir) std::ofstream out(dst_path, std::ios::binary); if (!out) { - LOG_ERROR("写入文件失败: {}", dst_path.string()); + LOG_ERROR("写入文件失败: {}", dst_path.generic_string()); return ""; } out.write(buffer.data(), buffer.size()); - return dst_path.string(); + return dst_path.generic_string(); } static int get_first_page() From fd25a8e0f2aefc83770a7d1d0c419b3bcb2a491a Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 22:12:52 +0800 Subject: [PATCH 100/132] feat(misc): impl get audio --- WeChatFerry/spy/rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 459ccc3..0bbaae0 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -247,7 +247,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, - // { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, + { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, { Functions_FUNC_SEND_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_text(r.msg.txt, out, len); } }, { Functions_FUNC_SEND_IMG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_image(r.msg.file, out, len); } }, { Functions_FUNC_SEND_FILE, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_file(r.msg.file, out, len); } }, From 1bf431590a8ee3aa1a38983b301879e6d2305624 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 22:17:33 +0800 Subject: [PATCH 101/132] refacor(misc): refactoring --- WeChatFerry/spy/misc_manager.cpp | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 1b4626d..90dad0d 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -23,13 +23,6 @@ using namespace std; namespace fs = std::filesystem; namespace OsSns = Offsets::Misc::Sns; -constexpr uint8_t HEADER_PNG1 = 0x89; -constexpr uint8_t HEADER_PNG2 = 0x50; -constexpr uint8_t HEADER_JPG1 = 0xFF; -constexpr uint8_t HEADER_JPG2 = 0xD8; -constexpr uint8_t HEADER_GIF1 = 0x47; -constexpr uint8_t HEADER_GIF2 = 0x49; - #define OS_NEW_CHAT_MSG 0x1B5E140 #define OS_FREE_CHAT_MSG 0x1B55850 #define OS_GET_CHAT_MGR 0x1B876C0 @@ -252,7 +245,7 @@ std::string get_audio(uint64_t id, const fs::path &dir) if (!fs::exists(dir)) fs::create_directories(dir); fs::path mp3path = dir / (std::to_string(id) + ".mp3"); - if (fs::exists(mp3path)) return mp3path.string(); + if (fs::exists(mp3path)) return mp3path.generic_string(); auto silk = db::get_audio_data(id); if (silk.empty()) { @@ -260,8 +253,8 @@ std::string get_audio(uint64_t id, const fs::path &dir) return ""; } - Silk2Mp3(silk, mp3path.string(), 24000); - return mp3path.string(); + Silk2Mp3(silk, mp3path.generic_string(), 24000); + return mp3path.generic_string(); } std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) @@ -269,7 +262,7 @@ std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) if (!fs::exists(dir)) fs::create_directories(dir); fs::path pcmpath = dir / (std::to_string(id) + ".pcm"); - if (fs::exists(pcmpath)) return pcmpath.string(); + if (fs::exists(pcmpath)) return pcmpath.generic_string(); auto silk = db::get_audio_data(id); if (silk.empty()) { @@ -282,15 +275,15 @@ std::string get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr) std::ofstream out(pcmpath, std::ios::binary); if (!out) { - LOG_ERROR("创建文件失败: {}", pcmpath.string()); + LOG_ERROR("创建文件失败: {}", pcmpath.generic_string()); return ""; } out.write(reinterpret_cast(pcm.data()), pcm.size()); - return pcmpath.string(); + return pcmpath.generic_string(); } -OcrResult_t get_ocr_result(const std::filesystem::path &path) +OcrResult_t get_ocr_result(const fs::path &path) { OcrResult_t ret = { -1, "" }; #if 0 // 参数没调好,会抛异常,看看有没有好心人来修复 @@ -368,10 +361,7 @@ bool rpc_get_audio(const AudioMsg &am, uint8_t *out, size_t *len) out, len, [&](Response &rsp) { rsp.msg.str = (char *)get_audio(am.id, am.dir).c_str(); }); } -bool rpc_get_pcm_audio(uint64_t id, const std::filesystem::path &dir, int32_t sr, uint8_t *out, size_t *len) -{ - return false; -} +bool rpc_get_pcm_audio(uint64_t id, const fs::path &dir, int32_t sr, uint8_t *out, size_t *len) { return false; } bool rpc_decrypt_image(const DecPath &dec, uint8_t *out, size_t *len) { @@ -403,7 +393,7 @@ bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len) [&](Response &rsp) { rsp.msg.status = revoke_message(id); }); } -bool rpc_get_ocr_result(const std::filesystem::path &path, uint8_t *out, size_t *len) +bool rpc_get_ocr_result(const fs::path &path, uint8_t *out, size_t *len) { return fill_response(out, len, [&](Response &rsp) { OcrResult_t ret = { -1, "" }; From 841dadbb19c338bb19a78afd02c32cb0a4996203 Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 22:22:04 +0800 Subject: [PATCH 102/132] feat(misc): ignore receive transfer --- WeChatFerry/spy/misc_manager.cpp | 2 +- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 90dad0d..1eb95ad 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -351,7 +351,7 @@ std::string get_login_url() int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid) { - // 别想了,这个不实现了 + LOG_ERROR("技术太菜,实现不了。"); return -1; } diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 0bbaae0..2613231 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -258,7 +258,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_forward(r.msg.fm, out, len); } }, { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, - // { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, + { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, From 6d6ff21992a76d9db44dbe97ab397b16b14c150b Mon Sep 17 00:00:00 2001 From: Changhua Date: Mon, 3 Mar 2025 22:25:02 +0800 Subject: [PATCH 103/132] feat(misc): ignore get ocr result --- WeChatFerry/spy/misc_manager.cpp | 3 +-- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 1eb95ad..f674d1d 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -395,9 +395,8 @@ bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len) bool rpc_get_ocr_result(const fs::path &path, uint8_t *out, size_t *len) { + auto ret = get_ocr_result(path); return fill_response(out, len, [&](Response &rsp) { - OcrResult_t ret = { -1, "" }; - ret = get_ocr_result(path); rsp.msg.ocr.status = ret.status; rsp.msg.ocr.result = (char *)ret.result.c_str(); }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 2613231..148540a 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -265,7 +265,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, // { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, - // { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, + { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, // { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, // { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, From 47cdc65acac3fb69c0281572df0a81def797cdf7 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Mar 2025 00:01:29 +0800 Subject: [PATCH 104/132] feat(misc): update qr code offset --- WeChatFerry/spy/offsets.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 2c88610..26113fa 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -108,6 +108,8 @@ namespace Message namespace Misc { + constexpr uint64_t QR_CODE = 0x2025A80; + namespace Sns { constexpr uint64_t DATA_MGR = 0x21E52F0; From 8c47f096c92dcd1b035542f867653938a92876cb Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Mar 2025 00:18:43 +0800 Subject: [PATCH 105/132] feat(misc): impl get qr code --- WeChatFerry/spy/misc_manager.cpp | 22 ++++++++++++++++------ WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index f674d1d..d7a1662 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -29,7 +29,6 @@ namespace OsSns = Offsets::Misc::Sns; #define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x213FB00 #define OS_GET_PRE_DOWNLOAD_MGR 0x1C0EE70 #define OS_PUSH_ATTACH_TASK 0x1CDF4E0 -#define OS_LOGIN_QR_CODE 0x59620D8 using get_sns_data_mgr_t = QWORD (*)(); using get_sns_timeline_mgr_t = QWORD (*)(); @@ -43,6 +42,7 @@ using get_mgr_by_prefix_localid_t = QWORD (*)(QWORD, QWORD); using push_attach_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); using get_ocr_manager_t = QWORD (*)(); using do_ocr_task_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); +using get_qr_code_mgr_t = QWORD (*)(); struct ImagePattern { uint8_t header1_candidate; @@ -340,13 +340,23 @@ int revoke_message(uint64_t id) std::string get_login_url() { - LPVOID targetAddress = reinterpret_cast(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE; - char *dataPtr = *reinterpret_cast(targetAddress); - if (!dataPtr) { + std::string uri; + get_qr_code_mgr_t get_qr_code_mgr = (get_qr_code_mgr_t)(g_WeChatWinDllAddr + Offsets::Misc::QR_CODE); + + uint64_t addr = get_qr_code_mgr() + 0x68; + uint64_t len = *(uint64_t *)(addr + 0x10); + if (len == 0) { LOG_ERROR("获取二维码失败."); - return ""; + return uri; } - return "http://weixin.qq.com/x/" + std::string(dataPtr, 22); + + if (*(uint64_t *)(addr + 0x18) == 0xF) { + uri = std::string((char *)addr, len); + } else { + uri = std::string(*(char **)(addr), len); + } + + return "http://weixin.qq.com/x/" + uri; } int receive_transfer(const std::string &wxid, const std::string &transferid, const std::string &transactionid) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 148540a..e39bda6 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -263,7 +263,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, - // { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, + { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, From 2362f2ae8a626b480b74855a25afa8a44be73479 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Mar 2025 00:26:52 +0800 Subject: [PATCH 106/132] feat(misc): ignore revoke message --- WeChatFerry/spy/rpc_server.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index e39bda6..11807ad 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -262,7 +262,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, - // { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, + { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, From 908c3d3e8691282a211e5dc23af1fdcf45394636 Mon Sep 17 00:00:00 2001 From: Changhua Date: Tue, 4 Mar 2025 01:05:15 +0800 Subject: [PATCH 107/132] refactor(misc): refactoring --- WeChatFerry/spy/misc_manager.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index d7a1662..06e81ee 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -20,8 +20,9 @@ extern QWORD g_WeChatWinDllAddr; namespace misc { using namespace std; -namespace fs = std::filesystem; -namespace OsSns = Offsets::Misc::Sns; +namespace fs = std::filesystem; +namespace OsMisc = Offsets::Misc; +namespace OsSns = Offsets::Misc::Sns; #define OS_NEW_CHAT_MSG 0x1B5E140 #define OS_FREE_CHAT_MSG 0x1B55850 @@ -341,7 +342,7 @@ int revoke_message(uint64_t id) std::string get_login_url() { std::string uri; - get_qr_code_mgr_t get_qr_code_mgr = (get_qr_code_mgr_t)(g_WeChatWinDllAddr + Offsets::Misc::QR_CODE); + get_qr_code_mgr_t get_qr_code_mgr = (get_qr_code_mgr_t)(g_WeChatWinDllAddr + OsMisc::QR_CODE); uint64_t addr = get_qr_code_mgr() + 0x68; uint64_t len = *(uint64_t *)(addr + 0x10); From b9d2c6bb3b32c8dffa80a535eba55e670494764a Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 00:36:03 +0800 Subject: [PATCH 108/132] feat(misc): update download attachment offsets --- WeChatFerry/spy/offsets.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 26113fa..aa3a77d 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -110,6 +110,13 @@ namespace Misc { constexpr uint64_t QR_CODE = 0x2025A80; + constexpr uint64_t INSATNCE = Message::Send::INSTANCE; + constexpr uint64_t FREE = Message::Send::FREE; + constexpr uint64_t CHAT_MGR = 0x1B8AA50; + constexpr uint64_t PRE_LOCAL_ID_MGR = 0x2142BF0; + constexpr uint64_t PRE_DOWNLOAD_MGR = 0x1C12260; + constexpr uint64_t PUSH_ATTACH_TASK = 0x1CE3050; + namespace Sns { constexpr uint64_t DATA_MGR = 0x21E52F0; From 4b9c68072f9901c2b2c7ea1c792fa0fb27340e62 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 00:57:01 +0800 Subject: [PATCH 109/132] feat(misc): impl download attachment --- WeChatFerry/spy/misc_manager.cpp | 52 +++++++++++++++++--------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 28 insertions(+), 26 deletions(-) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index 06e81ee..dbfd47b 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -24,19 +24,12 @@ namespace fs = std::filesystem; namespace OsMisc = Offsets::Misc; namespace OsSns = Offsets::Misc::Sns; -#define OS_NEW_CHAT_MSG 0x1B5E140 -#define OS_FREE_CHAT_MSG 0x1B55850 -#define OS_GET_CHAT_MGR 0x1B876C0 -#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x213FB00 -#define OS_GET_PRE_DOWNLOAD_MGR 0x1C0EE70 -#define OS_PUSH_ATTACH_TASK 0x1CDF4E0 - using get_sns_data_mgr_t = QWORD (*)(); using get_sns_timeline_mgr_t = QWORD (*)(); using get_sns_first_page_t = QWORD (*)(QWORD, QWORD, QWORD); using get_sns_next_page_scene_t = QWORD (*)(QWORD, QWORD); using get_chat_mgr_t = QWORD (*)(); -using new_chat_msg_t = QWORD (*)(QWORD); +using new_chat_msg_t = QWORD (*)(char *); using free_chat_msg_t = QWORD (*)(QWORD); using get_pre_download_mgr_t = QWORD (*)(); using get_mgr_by_prefix_localid_t = QWORD (*)(QWORD, QWORD); @@ -165,6 +158,7 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr uint32_t dbIdx; if (fs::exists(extra)) { // 第一道,不重复下载。TODO: 通过文件大小来判断 + LOG_WARN("文件已存在:{}", extra.generic_string()); return 0; } @@ -173,28 +167,27 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr return status; } - new_chat_msg_t NewChatMsg = (new_chat_msg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG); - free_chat_msg_t FreeChatMsg = (free_chat_msg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG); - get_chat_mgr_t GetChatMgr = (get_chat_mgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR); - get_pre_download_mgr_t GetPreDownLoadMgr = (get_pre_download_mgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR); - push_attach_task_t PushAttachTask = (push_attach_task_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK); + new_chat_msg_t NewChatMsg = (new_chat_msg_t)(g_WeChatWinDllAddr + OsMisc::INSATNCE); + free_chat_msg_t FreeChatMsg = (free_chat_msg_t)(g_WeChatWinDllAddr + OsMisc::FREE); + get_chat_mgr_t GetChatMgr = (get_chat_mgr_t)(g_WeChatWinDllAddr + OsMisc::CHAT_MGR); + get_pre_download_mgr_t GetPreDownLoadMgr = (get_pre_download_mgr_t)(g_WeChatWinDllAddr + OsMisc::PRE_DOWNLOAD_MGR); + push_attach_task_t PushAttachTask = (push_attach_task_t)(g_WeChatWinDllAddr + OsMisc::PUSH_ATTACH_TASK); get_mgr_by_prefix_localid_t GetMgrByPrefixLocalId - = (get_mgr_by_prefix_localid_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID); + = (get_mgr_by_prefix_localid_t)(g_WeChatWinDllAddr + OsMisc::PRE_LOCAL_ID_MGR); LARGE_INTEGER l; l.HighPart = dbIdx; l.LowPart = (DWORD)localId; - char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460); + char *buff = util::AllocBuffer(0x460); if (buff == nullptr) { LOG_ERROR("申请内存失败."); return status; } - QWORD pChatMsg = NewChatMsg((QWORD)buff); + QWORD pChatMsg = NewChatMsg(buff); GetChatMgr(); GetMgrByPrefixLocalId(l.QuadPart, pChatMsg); - QWORD type = util::get_qword(reinterpret_cast(buff) + 0x38); fs::path save_path, thumb_path; @@ -206,7 +199,7 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr case 0x3E: case 0x2B: { // Video: thumb thumb_path = thumb; - save_path = fs::path(thumb).replace_extension("mp4").string(); + save_path = fs::path(thumb).replace_extension("mp4"); break; } case 0x31: { // File: extra @@ -222,21 +215,22 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr return 0; } - LOG_DEBUG("保存路径: {}", save_path.string()); + LOG_DEBUG("保存路径: {}", save_path.generic_string()); // 创建父目录,由于路径来源于微信,不做检查 fs::create_directory(save_path.parent_path()); int temp = 1; - auto wx_save_path = util::new_wx_string(save_path.string()); - auto wx_thumb_path = util::new_wx_string(thumb_path.string()); + auto wx_save_path = util::CreateWxString(save_path.make_preferred().string()); + auto wx_thumb_path = util::CreateWxString(thumb_path.make_preferred().string()); - memcpy(&buff[0x280], wx_thumb_path.get(), sizeof(WxString)); - memcpy(&buff[0x2A0], wx_save_path.get(), sizeof(WxString)); + memcpy(&buff[0x280], wx_thumb_path, sizeof(WxString)); + memcpy(&buff[0x2A0], wx_save_path, sizeof(WxString)); memcpy(&buff[0x40C], &temp, sizeof(temp)); QWORD mgr = GetPreDownLoadMgr(); status = (int)PushAttachTask(mgr, pChatMsg, 0, 1); FreeChatMsg(pChatMsg); + util::FreeBuffer(buff); return status; } @@ -394,8 +388,16 @@ bool rpc_refresh_pyq(uint64_t id, uint8_t *out, size_t *len) bool rpc_download_attachment(const AttachMsg &att, uint8_t *out, size_t *len) { - return fill_response( - out, len, [&](Response &rsp) { rsp.msg.status = download_attachment(att.id, att.thumb, att.extra); }); + int status = -1; + if (att.thumb || att.extra) { + std::string thumb = att.thumb ? att.thumb : ""; + std::string extra = att.extra ? att.extra : ""; + status = download_attachment(att.id, thumb, extra); + } else { + LOG_ERROR("文件地址不能全为空"); + } + + return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); } bool rpc_revoke_message(uint64_t id, uint8_t *out, size_t *len) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 11807ad..b088e95 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -260,7 +260,7 @@ const std::unordered_map RpcServer::rpcFu // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, - // { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, + { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, From 82e9b8c61bfba595eb21a80fe9db91609f4ea3cf Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:00:46 +0800 Subject: [PATCH 110/132] feat(contact): update contact offsets --- WeChatFerry/spy/offsets.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index aa3a77d..85528d7 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -16,6 +16,20 @@ namespace Account constexpr uint64_t LOGIN = 0x7F8; // 登录状态 } +namespace Contact +{ + constexpr uint64_t MGR = 0x1B44B20; + constexpr uint64_t LIST = 0x21A1E00; + constexpr uint64_t BIN = 0x200; + constexpr uint64_t BIN_LEN = 0x208; + constexpr uint64_t WXID = 0x10; + constexpr uint64_t CODE = 0x30; + constexpr uint64_t REMARK = 0x80; + constexpr uint64_t NAME = 0xA0; + constexpr uint64_t GENDER = 0x0E; + constexpr uint64_t STEP = 0x6A8; +} + namespace Db { constexpr uint64_t INSTANCE = 0x59226C8; // 数据库实例地址 From 6d31cb4c97566be4d4b272c648a91393b07c6d3f Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:06:01 +0800 Subject: [PATCH 111/132] feat(contact): impl get contacts --- WeChatFerry/spy/contact_manager.cpp | 38 +++++++++++------------------ WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index 0e1ecb3..7c63561 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -3,6 +3,7 @@ #include "contact_manager.h" #include "log.hpp" +#include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" #include "util.h" @@ -13,16 +14,7 @@ extern QWORD g_WeChatWinDllAddr; namespace contact { -#define OS_GET_CONTACT_MGR 0x1B417A0 -#define OS_GET_CONTACT_LIST 0x219ED10 -#define OS_CONTACT_BIN 0x200 -#define OS_CONTACT_BIN_LEN 0x208 -#define OS_CONTACT_WXID 0x10 -#define OS_CONTACT_CODE 0x30 -#define OS_CONTACT_REMARK 0x80 -#define OS_CONTACT_NAME 0xA0 -#define OS_CONTACT_GENDER 0x0E -#define OS_CONTACT_STEP 0x6A8 +namespace OsCon = Offsets::Contact; using get_contact_mgr_t = QWORD (*)(); using get_contact_list_t = QWORD (*)(QWORD, QWORD); @@ -62,10 +54,8 @@ static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t vector get_contacts() { vector contacts; - get_contact_mgr_t func_get_contact_mgr - = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR); - get_contact_list_t func_get_contact_list - = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST); + get_contact_mgr_t func_get_contact_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsCon::MGR); + get_contact_list_t func_get_contact_list = reinterpret_cast(g_WeChatWinDllAddr + OsCon::LIST); QWORD mgr = func_get_contact_mgr(); QWORD addr[3] = { 0 }; @@ -78,22 +68,22 @@ vector get_contacts() QWORD pend = addr[2]; while (pstart < pend) { RpcContact_t cnt; - QWORD pbin = util::get_qword(pstart + OS_CONTACT_BIN); - QWORD lenbin = util::get_dword(pstart + OS_CONTACT_BIN_LEN); + QWORD pbin = util::get_qword(pstart + OsCon::BIN); + QWORD lenbin = util::get_dword(pstart + OsCon::BIN_LEN); - cnt.wxid = util::get_str_by_wstr_addr(pstart + OS_CONTACT_WXID); - cnt.code = util::get_str_by_wstr_addr(pstart + OS_CONTACT_CODE); - cnt.remark = util::get_str_by_wstr_addr(pstart + OS_CONTACT_REMARK); - cnt.name = util::get_str_by_wstr_addr(pstart + OS_CONTACT_NAME); + cnt.wxid = util::get_str_by_wstr_addr(pstart + OsCon::WXID); + cnt.code = util::get_str_by_wstr_addr(pstart + OsCon::CODE); + cnt.remark = util::get_str_by_wstr_addr(pstart + OsCon::REMARK); + cnt.name = util::get_str_by_wstr_addr(pstart + OsCon::NAME); cnt.country = get_cnt_string(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN); cnt.province = get_cnt_string(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN); cnt.city = get_cnt_string(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN); - cnt.gender = (pbin == 0) ? 0 : static_cast(*(uint8_t *)(pbin + OS_CONTACT_GENDER)); + cnt.gender = (pbin == 0) ? 0 : static_cast(*(uint8_t *)(pbin + OsCon::GENDER)); contacts.push_back(cnt); - pstart += OS_CONTACT_STEP; + pstart += OsCon::STEP; } return contacts; @@ -196,8 +186,8 @@ RpcContact_t get_contact_by_wxid(const string &wxid) bool rpc_get_contacts(uint8_t *out, size_t *len) { - return fill_response(out, len, [](Response &rsp) { - vector contacts = get_contacts(); + vector contacts = get_contacts(); + return fill_response(out, len, [&](Response &rsp) { rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index b088e95..6ad47a9 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -244,7 +244,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_GET_MSG_TYPES, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().handler_.rpc_get_msg_types(out, len); } }, { Functions_FUNC_ENABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().start_message_listener(r.msg.flag, out, len); } }, { Functions_FUNC_DISABLE_RECV_TXT, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().stop_message_listener(out, len); } }, - // { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, + { Functions_FUNC_GET_CONTACTS, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contacts(out, len); } }, { Functions_FUNC_GET_DB_NAMES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_names(out, len); } }, { Functions_FUNC_GET_DB_TABLES, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_get_db_tables(r.msg.str, out, len); } }, { Functions_FUNC_GET_AUDIO_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_audio(r.msg.am, out, len); } }, From 82ec98b8520073825c82e28b48760509f00a9eac Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:07:12 +0800 Subject: [PATCH 112/132] refactor(pb): remove debug log --- WeChatFerry/rpc/pb_util.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/WeChatFerry/rpc/pb_util.cpp b/WeChatFerry/rpc/pb_util.cpp index 5f8c4bd..5bde0f5 100644 --- a/WeChatFerry/rpc/pb_util.cpp +++ b/WeChatFerry/rpc/pb_util.cpp @@ -68,7 +68,6 @@ bool encode_contacts(pb_ostream_t *stream, const pb_field_t *field, void *const vector *v = (vector *)*arg; RpcContact message = RpcContact_init_default; - LOG_DEBUG("encode_contacts[{}]", v->size()); for (auto it = v->begin(); it != v->end(); it++) { message.wxid.funcs.encode = &encode_string; message.wxid.arg = (void *)(*it).wxid.c_str(); From e20ca0e61fdbf7830bfab912f139cbc46db3ab04 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:11:51 +0800 Subject: [PATCH 113/132] feat(contact): ignore accept friend request --- WeChatFerry/spy/contact_manager.cpp | 53 +++-------------------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 5 insertions(+), 50 deletions(-) diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index 7c63561..ba34af9 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -91,53 +91,8 @@ vector get_contacts() int accept_new_friend(const std::string &v3, const std::string &v4, int scene) { - int success = -1; -#if 0 - DWORD accept_new_friend_call1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1; - DWORD accept_new_friend_call2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2; - DWORD accept_new_friend_call3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3; - DWORD accept_new_friend_call4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4; - - char buffer[0x40] = { 0 }; - char nullbuffer[0x3CC] = { 0 }; - - LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene); - - wstring ws_v3 = util::s2w(v3); - wstring ws_v4 = util::s2w(v4); - WxString wx_v3(ws_v3); - WxString wx_v4(ws_v4); - - __asm { - pushad; - pushfd; - lea ecx, buffer; - call accept_new_friend_call1; - mov esi, 0x0; - mov edi, scene; - push esi; - push edi; - sub esp, 0x14; - mov ecx, esp; - lea eax, wx_v4; - push eax; - call accept_new_friend_call2; - sub esp, 0x8; - push 0x0; - lea eax, nullbuffer; - push eax; - lea eax, wx_v3; - push eax; - lea ecx, buffer; - call accept_new_friend_call3; - mov success, eax; - lea ecx, buffer; - call accept_new_friend_call4; - popfd; - popad; - } -#endif - return success; // 成功返回 1 + LOG_ERROR("技术太菜,实现不了。"); + return -1; // 成功返回 1 } RpcContact_t get_contact_by_wxid(const string &wxid) @@ -204,8 +159,8 @@ bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len) bool rpc_accept_friend(const Verification &v, uint8_t *out, size_t *len) { - const string v3 = v.v3; - const string v4 = v.v4; + const string v3 = v.v3 ? v.v3 : ""; + const string v4 = v.v4 ? v.v4 : ""; int scene = v.scene; return fill_response( out, len, [&](Response &rsp) { rsp.msg.status = accept_new_friend(v3, v4, scene); }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 6ad47a9..4e5b6bf 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -257,7 +257,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_SEND_PAT_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_send_pat(r.msg.pm, out, len); } }, { Functions_FUNC_FORWARD_MSG, [](const Request &r, uint8_t *out, size_t *len) { return RpcServer::getInstance().sender_.rpc_forward(r.msg.fm, out, len); } }, { Functions_FUNC_EXEC_DB_QUERY, [](const Request &r, uint8_t *out, size_t *len) { return db::rpc_exec_db_query(r.msg.query, out, len); } }, - // { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, + { Functions_FUNC_ACCEPT_FRIEND, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_accept_friend(r.msg.v, out, len); } }, { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, From e9c64e4ef45c2e6169b8aad2cfe13e1fbcda655b Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:15:07 +0800 Subject: [PATCH 114/132] feat(contact): ignore get contact info --- WeChatFerry/spy/contact_manager.cpp | 41 ++--------------------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index ba34af9..591bbc9 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -98,44 +98,7 @@ int accept_new_friend(const std::string &v3, const std::string &v4, int scene) RpcContact_t get_contact_by_wxid(const string &wxid) { RpcContact_t contact; -#if 0 - char buff[0x440] = { 0 }; - wstring ws_wxid = util::s2w(wxid); - WxString pri(ws_wxid); - - DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0; - DWORD get_contact_addr = g_WeChatWinDllAddr + 0xC04E00; - DWORD free_contact_addr = g_WeChatWinDllAddr + 0xEA7880; - - __asm { - PUSHAD - PUSHFD - CALL contact_mgr_addr - LEA ECX,buff - PUSH ECX - LEA ECX,pri - PUSH ECX - MOV ECX,EAX - CALL get_contact_addr - POPFD - POPAD - } - - contact.wxid = wxid; - contact.code = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxCode); - contact.remark = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxRemark); - contact.name = util::get_str_by_wstr_addr(reinterpret_cast(buff) + g_WxCalls.contact.wxName); - contact.gender = util::get_dword(reinterpret_cast(buff) + 0x148); - - __asm { - PUSHAD - PUSHFD - LEA ECX,buff - CALL free_contact_addr - POPFD - POPAD - } -#endif + LOG_ERROR("技术太菜,实现不了。"); return contact; } @@ -150,8 +113,8 @@ bool rpc_get_contacts(uint8_t *out, size_t *len) bool rpc_get_contact_info(const string &wxid, uint8_t *out, size_t *len) { + vector contacts = { get_contact_by_wxid(wxid) }; return fill_response(out, len, [&](Response &rsp) { - vector contacts = { get_contact_by_wxid(wxid) }; rsp.msg.contacts.contacts.funcs.encode = encode_contacts; rsp.msg.contacts.contacts.arg = &contacts; }); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 4e5b6bf..8c4a6ee 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -261,7 +261,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_RECV_TRANSFER, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_receive_transfer(r.msg.tf, out, len); } }, { Functions_FUNC_REFRESH_PYQ, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_refresh_pyq(r.msg.ui64, out, len); } }, { Functions_FUNC_DOWNLOAD_ATTACH, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_download_attachment(r.msg.att, out, len); } }, - // { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, + { Functions_FUNC_GET_CONTACT_INFO, [](const Request &r, uint8_t *out, size_t *len) { return contact::rpc_get_contact_info(r.msg.str, out, len); } }, { Functions_FUNC_REVOKE_MSG, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_revoke_message(r.msg.ui64, out, len); } }, { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, From ccc8dcbbcdfcc2fd76c272d8514d6dca3cce1537 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 01:19:05 +0800 Subject: [PATCH 115/132] refactor(offset): replace include guard with #pragma once --- WeChatFerry/spy/offsets.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 85528d7..1e483c0 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -1,5 +1,4 @@ -#ifndef OFFSETS_H -#define OFFSETS_H +#pragma once #include @@ -140,5 +139,3 @@ namespace Misc } } } - -#endif // OFFSETS_H From 2dc1f25b7582963a65e2bbc46ffb84eea351555d Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 21:10:08 +0800 Subject: [PATCH 116/132] refactor(util): extract common functions from message_sender to util --- WeChatFerry/com/util.cpp | 16 +++++----- WeChatFerry/com/util.h | 14 +++++++-- WeChatFerry/spy/message_sender.cpp | 47 +++++------------------------- 3 files changed, 28 insertions(+), 49 deletions(-) diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp index bff975a..b82e82f 100644 --- a/WeChatFerry/com/util.cpp +++ b/WeChatFerry/com/util.cpp @@ -268,15 +268,17 @@ std::unique_ptr new_wx_string(const wchar_t *wstr) std::unique_ptr new_wx_string(const std::wstring &wstr) { return std::make_unique(wstr); } -std::vector parse_wxids(const std::string &wxids) +AtWxidSplitResult<> parse_wxids(const std::string &atWxids) { - std::vector wx_members; - std::wstringstream wss(s2w(wxids)); - std::wstring wstr; - while (getline(wss, wstr, L',')) { - wx_members.emplace_back(wstr); + AtWxidSplitResult<> result; + if (!atWxids.empty()) { + std::wstringstream wss(util::s2w(atWxids)); + for (std::wstring wxid; std::getline(wss, wxid, L',');) { + result.wxids.push_back(wxid); + result.wxWxids.emplace_back(result.wxids.back()); + } } - return wx_members; + return result; } WxString *CreateWxString(const std::string &s) diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index 9031c27..e2a0e4a 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -76,14 +76,24 @@ template static T *AllocBuffer(size_t count) return reinterpret_cast(HeapAlloc(GetProcessHeap(), 8, sizeof(T) * count)); } +template struct WxStringHolder { + std::wstring ws; + WxString wx; + explicit WxStringHolder(const T &str) : ws(util::s2w(str)), wx(ws) { } +}; + +template struct AtWxidSplitResult { + std::vector wxids; + std::vector wxWxids; +}; + WxString *CreateWxString(const std::string &s); void FreeWxString(WxString *wxStr); +AtWxidSplitResult<> parse_wxids(const std::string &atWxids); std::unique_ptr new_wx_string(const char *str); std::unique_ptr new_wx_string(const wchar_t *wstr); std::unique_ptr new_wx_string(const std::string &str); std::unique_ptr new_wx_string(const std::wstring &wstr); -std::vector parse_wxids(const std::string &wxids); - } // namespace util diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 5e041d7..411da62 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -44,45 +44,12 @@ Sender::Sender() func_xml_buf_sign = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML_BUF_SIGN); } -std::unique_ptr Sender::new_wx_string(const char *str) -{ - return new_wx_string(str ? std::string(str) : std::string()); -} -std::unique_ptr Sender::new_wx_string(const std::string &str) -{ - return std::make_unique(util::s2w(str)); -} - -template struct WxStringHolder { - std::wstring ws; - WxString wx; - explicit WxStringHolder(const T &str) : ws(util::s2w(str)), wx(ws) { } -}; - -template struct AtWxidSplitResult { - std::vector wxids; - std::vector wxWxids; -}; - -AtWxidSplitResult<> parse_wxids(const std::string &atWxids) -{ - AtWxidSplitResult<> result; - if (!atWxids.empty()) { - std::wstringstream wss(util::s2w(atWxids)); - for (std::wstring wxid; std::getline(wss, wxid, L',');) { - result.wxids.push_back(wxid); - result.wxWxids.emplace_back(result.wxids.back()); - } - } - return result; -} - void Sender::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) { - WxStringHolder holderMsg(msg); - WxStringHolder holderWxid(wxid); + util::WxStringHolder holderMsg(msg); + util::WxStringHolder holderWxid(wxid); - auto wxAtWxids = parse_wxids(at_wxids).wxWxids; + auto wxAtWxids = util::parse_wxids(at_wxids).wxWxids; QWORD pWxAtWxids = wxAtWxids.empty() ? 0 : reinterpret_cast(&wxAtWxids); char buffer[1104] = { 0 }; @@ -93,8 +60,8 @@ void Sender::send_text(const std::string &wxid, const std::string &msg, const st void Sender::send_image(const std::string &wxid, const std::string &path) { - WxStringHolder holderWxid(wxid); - WxStringHolder holderPath(path); + util::WxStringHolder holderWxid(wxid); + util::WxStringHolder holderPath(path); char msg[1192] = { 0 }; char msgTmp[1192] = { 0 }; @@ -258,8 +225,8 @@ int Sender::send_pat(const std::string &roomid, const std::string &wxid) { QWORD status = -1; - WxStringHolder holderRoom(roomid); - WxStringHolder holderWxid(wxid); + util::WxStringHolder holderRoom(roomid); + util::WxStringHolder holderWxid(wxid); status = func_send_pat(&holderRoom.wx, &holderWxid.wx); From da3357cb3b9db69bb5c8a0f864e1d9ea1608f556 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 21:59:36 +0800 Subject: [PATCH 117/132] feat(chatroom): update delete chatroom member offsets --- WeChatFerry/spy/offsets.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index 1e483c0..a9a4274 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -15,6 +15,12 @@ namespace Account constexpr uint64_t LOGIN = 0x7F8; // 登录状态 } +namespace Chatroom +{ + constexpr uint64_t MGR = 0x1B86F60; + constexpr uint64_t DEL = 0x2158830; +} + namespace Contact { constexpr uint64_t MGR = 0x1B44B20; From 51312d1bbeb512983b1ce5b058877484818fb334 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 22:01:57 +0800 Subject: [PATCH 118/132] feat(chatroom): impl delete chatroom members --- WeChatFerry/spy/chatroom_manager.cpp | 39 +++++++++++++++------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 4ce74ce..89b0a1e 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -2,6 +2,7 @@ #include "chatroom_manager.h" #include "log.hpp" +#include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" #include "util.h" @@ -11,14 +12,15 @@ extern QWORD g_WeChatWinDllAddr; namespace chatroom { +namespace OsRoom = Offsets::Chatroom; + #define OS_GET_CHATROOM_MGR 0x1B83BD0 #define OS_ADD_MEMBERS 0x2155100 -#define OS_DELETE_MEMBERS 0x2155740 #define OS_INVITE_MEMBERS 0x2154AE0 using get_chatroom_mgr_t = QWORD (*)(); using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); -using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD); +using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *); using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); static vector parse_wxids(const string &wxids) @@ -53,21 +55,15 @@ int add_chatroom_member(const string &roomid, const string &wxids) int del_chatroom_member(const string &roomid, const string &wxids) { - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return -1; - } - - get_chatroom_mgr_t get_chatroom_mgr - = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); + get_chatroom_mgr_t get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); del_member_from_chatroom_t del_members - = reinterpret_cast(g_WeChatWinDllAddr + OS_DELETE_MEMBERS); + = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::DEL); - vector wx_members = parse_wxids(wxids); - auto wx_roomid = util::new_wx_string(roomid); - QWORD p_members = reinterpret_cast(&wx_members.front()); + WxString *wx_roomid = util::CreateWxString(roomid); + auto wx_members = util::parse_wxids(wxids).wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); - return static_cast(del_members(get_chatroom_mgr(), p_members, reinterpret_cast(wx_roomid.get()))); + return static_cast(del_members(get_chatroom_mgr(), p_members, wx_roomid)); } int invite_chatroom_member(const string &roomid, const string &wxids) @@ -98,10 +94,17 @@ bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - return fill_response( - out, len, [&](Response &rsp) { rsp.msg.status = del_chatroom_member(roomid, wxids); }); + int status = -1; + if (m.wxids && m.roomid) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; + + status = del_chatroom_member(roomid, wxids); + } else { + LOG_ERROR("wxid 和 roomid 不能为空"); + } + + return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); } bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 8c4a6ee..6490e1a 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -267,7 +267,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, - // { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, // { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, // clang-format on }; From 132cd08f082ee97c854e1277d1b0c27b66fde380 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 22:37:29 +0800 Subject: [PATCH 119/132] feat(chatroom): update add chatroom members offset --- WeChatFerry/spy/offsets.h | 1 + 1 file changed, 1 insertion(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index a9a4274..c94780a 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -19,6 +19,7 @@ namespace Chatroom { constexpr uint64_t MGR = 0x1B86F60; constexpr uint64_t DEL = 0x2158830; + constexpr uint64_t ADD = 0x21581F0; } namespace Contact From 0dd6556099ab34109fe82a2fdd1b0ede2e00b16d Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 22:39:09 +0800 Subject: [PATCH 120/132] feat(chatroom): impl add chatroom members --- WeChatFerry/spy/chatroom_manager.cpp | 37 ++++++++++++++-------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 89b0a1e..8de85ea 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -15,11 +15,10 @@ namespace chatroom namespace OsRoom = Offsets::Chatroom; #define OS_GET_CHATROOM_MGR 0x1B83BD0 -#define OS_ADD_MEMBERS 0x2155100 #define OS_INVITE_MEMBERS 0x2154AE0 using get_chatroom_mgr_t = QWORD (*)(); -using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); +using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *); using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); @@ -36,21 +35,16 @@ static vector parse_wxids(const string &wxids) int add_chatroom_member(const string &roomid, const string &wxids) { - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return -1; - } + get_chatroom_mgr_t get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); + add_member_to_chatroom_t add_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::ADD); - get_chatroom_mgr_t get_chatroom_mgr - = reinterpret_cast(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR); - add_member_to_chatroom_t add_members - = reinterpret_cast(g_WeChatWinDllAddr + OS_ADD_MEMBERS); + WxString *wx_roomid = util::CreateWxString(roomid); - vector wx_members = parse_wxids(wxids); - auto wx_roomid = util::new_wx_string(roomid); - QWORD p_members = reinterpret_cast(&wx_members.front()); + QWORD tmp[2] = { 0 }; + auto wx_members = util::parse_wxids(wxids).wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); - return static_cast(add_members(get_chatroom_mgr(), p_members, reinterpret_cast(wx_roomid.get()), 0)); + return static_cast(add_members(get_chatroom_mgr(), p_members, wx_roomid, reinterpret_cast(tmp))); } int del_chatroom_member(const string &roomid, const string &wxids) @@ -86,10 +80,17 @@ int invite_chatroom_member(const string &roomid, const string &wxids) bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - return fill_response( - out, len, [&](Response &rsp) { rsp.msg.status = add_chatroom_member(roomid, wxids); }); + int status = -1; + if (m.wxids && m.roomid) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; + + status = add_chatroom_member(roomid, wxids); + } else { + LOG_ERROR("wxid 和 roomid 不能为空"); + } + + return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); } bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 6490e1a..a9add00 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -266,7 +266,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_REFRESH_QRCODE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_login_url(out, len); } }, { Functions_FUNC_DECRYPT_IMAGE, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_decrypt_image(r.msg.dec, out, len); } }, { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, - // { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, // { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, // clang-format on From 84338c85d1f20df97cb7d33d899edf870a8e0775 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 22:40:22 +0800 Subject: [PATCH 121/132] feat(chatroom): update invite chatroom members offset --- WeChatFerry/spy/offsets.h | 1 + 1 file changed, 1 insertion(+) diff --git a/WeChatFerry/spy/offsets.h b/WeChatFerry/spy/offsets.h index c94780a..15d3222 100644 --- a/WeChatFerry/spy/offsets.h +++ b/WeChatFerry/spy/offsets.h @@ -20,6 +20,7 @@ namespace Chatroom constexpr uint64_t MGR = 0x1B86F60; constexpr uint64_t DEL = 0x2158830; constexpr uint64_t ADD = 0x21581F0; + constexpr uint64_t INV = 0x2157BD0; } namespace Contact From aefea53bddab435d5fb3e1af0dcdf6b0f36f4ab8 Mon Sep 17 00:00:00 2001 From: Changhua Date: Wed, 5 Mar 2025 22:42:36 +0800 Subject: [PATCH 122/132] feat(chatroom): impl invite chatroom members --- WeChatFerry/spy/chatroom_manager.cpp | 55 +++++++++++----------------- WeChatFerry/spy/rpc_server.cpp | 2 +- 2 files changed, 23 insertions(+), 34 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 8de85ea..2b2798c 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -14,24 +14,10 @@ namespace chatroom { namespace OsRoom = Offsets::Chatroom; -#define OS_GET_CHATROOM_MGR 0x1B83BD0 -#define OS_INVITE_MEMBERS 0x2154AE0 - -using get_chatroom_mgr_t = QWORD (*)(); -using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); -using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *); -using invite_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, QWORD, QWORD); - -static vector parse_wxids(const string &wxids) -{ - vector wx_members; - wstringstream wss(util::s2w(wxids)); - wstring wstr; - while (getline(wss, wstr, L',')) { - wx_members.emplace_back(wstr); - } - return wx_members; -} +using get_chatroom_mgr_t = QWORD (*)(); +using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); +using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *); +using invite_members_t = QWORD (*)(const wchar_t *, QWORD, WxString *, QWORD); int add_chatroom_member(const string &roomid, const string &wxids) { @@ -62,20 +48,16 @@ int del_chatroom_member(const string &roomid, const string &wxids) int invite_chatroom_member(const string &roomid, const string &wxids) { - if (roomid.empty() || wxids.empty()) { - LOG_ERROR("Empty roomid or wxids."); - return -1; - } + invite_members_t invite_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::INV); - invite_member_to_chatroom_t invite_members - = reinterpret_cast(g_WeChatWinDllAddr + OS_INVITE_MEMBERS); + wstring ws_roomid = util::s2w(roomid); + WxString *wx_roomid = util::CreateWxString(roomid); - vector wx_members = parse_wxids(wxids); - auto wx_roomid = util::new_wx_string(roomid); - QWORD p_members = reinterpret_cast(&wx_members.front()); + QWORD tmp[2] = { 0 }; + auto wx_members = util::parse_wxids(wxids).wxWxids; + QWORD p_members = reinterpret_cast(&wx_members); - return static_cast(invite_members(reinterpret_cast(wx_roomid.get()->wptr), p_members, - reinterpret_cast(wx_roomid.get()), 0)); + return static_cast(invite_members(ws_roomid.c_str(), p_members, wx_roomid, reinterpret_cast(tmp))); } bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) @@ -110,10 +92,17 @@ bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - return fill_response( - out, len, [&](Response &rsp) { rsp.msg.status = invite_chatroom_member(roomid, wxids); }); + int status = -1; + if (m.wxids && m.roomid) { + const std::string wxids = m.wxids; + const std::string roomid = m.roomid; + + status = invite_chatroom_member(roomid, wxids); + } else { + LOG_ERROR("wxid 和 roomid 不能为空"); + } + + return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); } } // namespace chatroom diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index a9add00..094420b 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -268,7 +268,7 @@ const std::unordered_map RpcServer::rpcFu { Functions_FUNC_EXEC_OCR, [](const Request &r, uint8_t *out, size_t *len) { return misc::rpc_get_ocr_result(r.msg.str, out, len); } }, { Functions_FUNC_ADD_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_add_chatroom_member(r.msg.m, out, len); } }, { Functions_FUNC_DEL_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_delete_chatroom_member(r.msg.m, out, len); } }, - // { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, + { Functions_FUNC_INV_ROOM_MEMBERS, [](const Request &r, uint8_t *out, size_t *len) { return chatroom::rpc_invite_chatroom_member(r.msg.m, out, len); } }, // clang-format on }; From e00958cf33a8da0f6cd2994e256bc8f9fcee3753 Mon Sep 17 00:00:00 2001 From: Changhua Date: Thu, 6 Mar 2025 06:54:05 +0800 Subject: [PATCH 123/132] rafctor(chatroom): refactoring --- WeChatFerry/spy/chatroom_manager.cpp | 68 ++++++++++------------------ 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index 2b2798c..bd47ad5 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -7,22 +7,33 @@ #include "rpc_helper.h" #include "util.h" -using namespace std; extern QWORD g_WeChatWinDllAddr; namespace chatroom { namespace OsRoom = Offsets::Chatroom; -using get_chatroom_mgr_t = QWORD (*)(); -using add_member_to_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); -using del_member_from_chatroom_t = QWORD (*)(QWORD, QWORD, WxString *); -using invite_members_t = QWORD (*)(const wchar_t *, QWORD, WxString *, QWORD); +using get_mgr_t = QWORD (*)(); +using add_member_t = QWORD (*)(QWORD, QWORD, WxString *, QWORD); +using delete_member_t = QWORD (*)(QWORD, QWORD, WxString *); +using invite_members_t = QWORD (*)(const wchar_t *, QWORD, WxString *, QWORD); + +template +bool rpc_chatroom_common(const MemberMgmt &m, uint8_t *out, size_t *len, Func func) +{ + int status = -1; + if (m.wxids && m.roomid) { + status = func(m.roomid, m.wxids); + } else { + LOG_ERROR("wxid 和 roomid 不能为空"); + } + return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); +} int add_chatroom_member(const string &roomid, const string &wxids) { - get_chatroom_mgr_t get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); - add_member_to_chatroom_t add_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::ADD); + auto get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); + auto add_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::ADD); WxString *wx_roomid = util::CreateWxString(roomid); @@ -35,9 +46,8 @@ int add_chatroom_member(const string &roomid, const string &wxids) int del_chatroom_member(const string &roomid, const string &wxids) { - get_chatroom_mgr_t get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); - del_member_from_chatroom_t del_members - = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::DEL); + auto get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); + auto del_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::DEL); WxString *wx_roomid = util::CreateWxString(roomid); auto wx_members = util::parse_wxids(wxids).wxWxids; @@ -48,7 +58,7 @@ int del_chatroom_member(const string &roomid, const string &wxids) int invite_chatroom_member(const string &roomid, const string &wxids) { - invite_members_t invite_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::INV); + auto invite_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::INV); wstring ws_roomid = util::s2w(roomid); WxString *wx_roomid = util::CreateWxString(roomid); @@ -62,47 +72,17 @@ int invite_chatroom_member(const string &roomid, const string &wxids) bool rpc_add_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - int status = -1; - if (m.wxids && m.roomid) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - - status = add_chatroom_member(roomid, wxids); - } else { - LOG_ERROR("wxid 和 roomid 不能为空"); - } - - return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); + return rpc_chatroom_common(m, out, len, add_chatroom_member); } bool rpc_delete_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - int status = -1; - if (m.wxids && m.roomid) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - - status = del_chatroom_member(roomid, wxids); - } else { - LOG_ERROR("wxid 和 roomid 不能为空"); - } - - return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); + return rpc_chatroom_common(m, out, len, del_chatroom_member); } bool rpc_invite_chatroom_member(const MemberMgmt &m, uint8_t *out, size_t *len) { - int status = -1; - if (m.wxids && m.roomid) { - const std::string wxids = m.wxids; - const std::string roomid = m.roomid; - - status = invite_chatroom_member(roomid, wxids); - } else { - LOG_ERROR("wxid 和 roomid 不能为空"); - } - - return fill_response(out, len, [&](Response &rsp) { rsp.msg.status = status; }); + return rpc_chatroom_common(m, out, len, invite_chatroom_member); } } // namespace chatroom From 19a34d45bd888bd3bda3635177267ccc468818bb Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Mar 2025 00:07:12 +0800 Subject: [PATCH 124/132] refactor(spy): refactoring --- WeChatFerry/spy/account_manager.cpp | 7 +++--- WeChatFerry/spy/chatroom_manager.cpp | 13 +++++----- WeChatFerry/spy/contact_manager.cpp | 7 +++--- WeChatFerry/spy/database_executor.cpp | 36 +++++++++++---------------- WeChatFerry/spy/dllmain.cpp | 4 --- WeChatFerry/spy/message_handler.cpp | 11 ++++---- WeChatFerry/spy/message_sender.cpp | 35 +++++++++++++------------- WeChatFerry/spy/misc_manager.cpp | 26 +++++++++---------- WeChatFerry/spy/spy.cpp | 20 +++++++++------ WeChatFerry/spy/spy.h | 20 ++++++++++++--- 10 files changed, 90 insertions(+), 89 deletions(-) diff --git a/WeChatFerry/spy/account_manager.cpp b/WeChatFerry/spy/account_manager.cpp index 3043c0f..b7c0306 100644 --- a/WeChatFerry/spy/account_manager.cpp +++ b/WeChatFerry/spy/account_manager.cpp @@ -5,10 +5,9 @@ #include "log.hpp" #include "offsets.h" #include "rpc_helper.h" +#include "spy.h" #include "util.h" -extern UINT64 g_WeChatWinDllAddr; - namespace account { @@ -28,7 +27,7 @@ static void clear_cached_home_path() { cachedHomePath.reset(); } static uint64_t get_account_service() { - static auto GetService = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::SERVICE); + static auto GetService = Spy::getFunction(OsAcc::SERVICE); return GetService ? GetService() : 0; } @@ -52,7 +51,7 @@ fs::path get_home_path() return *cachedHomePath; } WxString home; - auto GetDataPath = reinterpret_cast(g_WeChatWinDllAddr + OsAcc::PATH); + auto GetDataPath = Spy::getFunction(OsAcc::PATH); int64_t service_addr = get_account_service(); GetDataPath((QWORD)&home); if (home.wptr) { diff --git a/WeChatFerry/spy/chatroom_manager.cpp b/WeChatFerry/spy/chatroom_manager.cpp index bd47ad5..912bda2 100644 --- a/WeChatFerry/spy/chatroom_manager.cpp +++ b/WeChatFerry/spy/chatroom_manager.cpp @@ -5,10 +5,9 @@ #include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" +#include "spy.h" #include "util.h" -extern QWORD g_WeChatWinDllAddr; - namespace chatroom { namespace OsRoom = Offsets::Chatroom; @@ -32,8 +31,8 @@ bool rpc_chatroom_common(const MemberMgmt &m, uint8_t *out, size_t *len, Func fu int add_chatroom_member(const string &roomid, const string &wxids) { - auto get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); - auto add_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::ADD); + auto get_chatroom_mgr = Spy::getFunction(OsRoom::MGR); + auto add_members = Spy::getFunction(OsRoom::ADD); WxString *wx_roomid = util::CreateWxString(roomid); @@ -46,8 +45,8 @@ int add_chatroom_member(const string &roomid, const string &wxids) int del_chatroom_member(const string &roomid, const string &wxids) { - auto get_chatroom_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::MGR); - auto del_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::DEL); + auto get_chatroom_mgr = Spy::getFunction(OsRoom::MGR); + auto del_members = Spy::getFunction(OsRoom::DEL); WxString *wx_roomid = util::CreateWxString(roomid); auto wx_members = util::parse_wxids(wxids).wxWxids; @@ -58,7 +57,7 @@ int del_chatroom_member(const string &roomid, const string &wxids) int invite_chatroom_member(const string &roomid, const string &wxids) { - auto invite_members = reinterpret_cast(g_WeChatWinDllAddr + OsRoom::INV); + auto invite_members = Spy::getFunction(OsRoom::INV); wstring ws_roomid = util::s2w(roomid); WxString *wx_roomid = util::CreateWxString(roomid); diff --git a/WeChatFerry/spy/contact_manager.cpp b/WeChatFerry/spy/contact_manager.cpp index 591bbc9..64728ea 100644 --- a/WeChatFerry/spy/contact_manager.cpp +++ b/WeChatFerry/spy/contact_manager.cpp @@ -6,12 +6,11 @@ #include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" +#include "spy.h" #include "util.h" using namespace std; -extern QWORD g_WeChatWinDllAddr; - namespace contact { namespace OsCon = Offsets::Contact; @@ -54,8 +53,8 @@ static string get_cnt_string(QWORD start, QWORD end, const uint8_t *feat, size_t vector get_contacts() { vector contacts; - get_contact_mgr_t func_get_contact_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsCon::MGR); - get_contact_list_t func_get_contact_list = reinterpret_cast(g_WeChatWinDllAddr + OsCon::LIST); + auto func_get_contact_mgr = Spy::getFunction(OsCon::MGR); + auto func_get_contact_list = Spy::getFunction(OsCon::LIST); QWORD mgr = func_get_contact_mgr(); QWORD addr[3] = { 0 }; diff --git a/WeChatFerry/spy/database_executor.cpp b/WeChatFerry/spy/database_executor.cpp index 30a4944..42e0d9a 100644 --- a/WeChatFerry/spy/database_executor.cpp +++ b/WeChatFerry/spy/database_executor.cpp @@ -7,11 +7,10 @@ #include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" +#include "spy.h" #include "sqlite3.h" #include "util.h" -extern UINT64 g_WeChatWinDllAddr; - namespace db { namespace OsDb = Offsets::Db; @@ -48,7 +47,7 @@ static void get_msg_db_handle(QWORD msg_mgr_addr) db_map_t get_db_handles() { db_map.clear(); - QWORD db_instance_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::INSTANCE); + QWORD db_instance_addr = util::get_qword(Spy::WeChatDll.load() + OsDb::INSTANCE); get_db_handle(db_instance_addr, OsDb::MICROMSG); // MicroMsg.db get_db_handle(db_instance_addr, OsDb::CHAT_MSG); // ChatMsg.db @@ -57,7 +56,7 @@ db_map_t get_db_handles() get_db_handle(db_instance_addr, OsDb::MEDIA); // Media.db get_db_handle(db_instance_addr, OsDb::FUNCTION_MSG); // Function.db - get_msg_db_handle(util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I)); // MSGi.db & MediaMsgi.db + get_msg_db_handle(util::get_qword(Spy::WeChatDll.load() + OsDb::MSG_I)); // MSGi.db & MediaMsgi.db return db_map; } @@ -103,8 +102,8 @@ DbTables_t get_db_tables(const std::string &db) return tables; } - constexpr const char *sql = "SELECT name FROM sqlite_master WHERE type='table';"; - Sqlite3_exec p_sqlite3_exec = reinterpret_cast(g_WeChatWinDllAddr + OsDb::EXEC); + constexpr const char *sql = "SELECT name FROM sqlite_master WHERE type='table';"; + auto p_sqlite3_exec = Spy::getFunction(OsDb::EXEC); p_sqlite3_exec(it->second, sql, (Sqlite3_callback)cb_get_tables, (void *)&tables, nullptr); return tables; @@ -114,19 +113,14 @@ DbRows_t exec_db_query(const std::string &db, const std::string &sql) { DbRows_t rows; - Sqlite3_prepare func_prepare = reinterpret_cast(g_WeChatWinDllAddr + OsDb::PREPARE); - Sqlite3_step func_step = reinterpret_cast(g_WeChatWinDllAddr + OsDb::STEP); - Sqlite3_column_count func_column_count - = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_COUNT); - Sqlite3_column_name func_column_name - = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_NAME); - Sqlite3_column_type func_column_type - = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_TYPE); - Sqlite3_column_blob func_column_blob - = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_BLOB); - Sqlite3_column_bytes func_column_bytes - = reinterpret_cast(g_WeChatWinDllAddr + OsDb::COLUMN_BYTES); - Sqlite3_finalize func_finalize = reinterpret_cast(g_WeChatWinDllAddr + OsDb::FINALIZE); + auto func_prepare = Spy::getFunction(OsDb::PREPARE); + auto func_step = Spy::getFunction(OsDb::STEP); + auto func_column_count = Spy::getFunction(OsDb::COLUMN_COUNT); + auto func_column_name = Spy::getFunction(OsDb::COLUMN_NAME); + auto func_column_type = Spy::getFunction(OsDb::COLUMN_TYPE); + auto func_column_blob = Spy::getFunction(OsDb::COLUMN_BLOB); + auto func_column_bytes = Spy::getFunction(OsDb::COLUMN_BYTES); + auto func_finalize = Spy::getFunction(OsDb::FINALIZE); if (db_map.empty()) { db_map = get_db_handles(); @@ -180,7 +174,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) return -1; } - QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I); + QWORD msg_mgr_addr = util::get_qword(Spy::WeChatDll.load() + OsDb::MSG_I); int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); // 总不能 int 还不够吧? QWORD p_start = util::get_qword(msg_mgr_addr + 0x50); @@ -223,7 +217,7 @@ int get_local_id_and_dbidx(uint64_t id, uint64_t *local_id, uint32_t *db_idx) std::vector get_audio_data(uint64_t id) { - QWORD msg_mgr_addr = util::get_qword(g_WeChatWinDllAddr + OsDb::MSG_I); + QWORD msg_mgr_addr = util::get_qword(Spy::WeChatDll.load() + OsDb::MSG_I); int db_index = static_cast(util::get_qword(msg_mgr_addr + 0x68)); std::string sql = "SELECT Buf FROM Media WHERE Reserved0=" + std::to_string(id) + ";"; diff --git a/WeChatFerry/spy/dllmain.cpp b/WeChatFerry/spy/dllmain.cpp index 580ec4c..66475e4 100644 --- a/WeChatFerry/spy/dllmain.cpp +++ b/WeChatFerry/spy/dllmain.cpp @@ -1,9 +1,5 @@ // dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "framework.h" -#include -#include - -#include "spy.h" BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { diff --git a/WeChatFerry/spy/message_handler.cpp b/WeChatFerry/spy/message_handler.cpp index 64c29cc..9537bf1 100644 --- a/WeChatFerry/spy/message_handler.cpp +++ b/WeChatFerry/spy/message_handler.cpp @@ -12,10 +12,9 @@ #include "offsets.h" #include "pb_util.h" #include "rpc_helper.h" +#include "spy.h" #include "util.h" -extern QWORD g_WeChatWinDllAddr; - namespace message { @@ -189,8 +188,8 @@ int Handler::EnableLog() { if (isLogging) return 1; - pLogLevel = reinterpret_cast(g_WeChatWinDllAddr + OsLog::LEVEL); - funcWxLog = reinterpret_cast(g_WeChatWinDllAddr + OsLog::CALL); + pLogLevel = reinterpret_cast(Spy::WeChatDll.load() + OsLog::LEVEL); + funcWxLog = Spy::getFunction(OsLog::CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)) != MH_OK) return -2; @@ -215,7 +214,7 @@ int Handler::ListenMsg() { if (isListeningMsg) return 1; - funcRecvMsg = reinterpret_cast(g_WeChatWinDllAddr + OsRecv::CALL); + funcRecvMsg = Spy::getFunction(OsRecv::CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)) != MH_OK) return -1; if (MH_EnableHook(funcRecvMsg) != MH_OK) return -1; @@ -237,7 +236,7 @@ int Handler::ListenPyq() { if (isListeningPyq) return 1; - funcRecvPyq = reinterpret_cast(g_WeChatWinDllAddr + OsRecv::PYQ_CALL); + funcRecvPyq = Spy::getFunction(OsRecv::PYQ_CALL); if (InitializeHook() != MH_OK) return -1; if (MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)) != MH_OK) return -1; if (MH_EnableHook(funcRecvPyq) != MH_OK) return -1; diff --git a/WeChatFerry/spy/message_sender.cpp b/WeChatFerry/spy/message_sender.cpp index 411da62..2cbfac3 100644 --- a/WeChatFerry/spy/message_sender.cpp +++ b/WeChatFerry/spy/message_sender.cpp @@ -8,11 +8,10 @@ #include "log.hpp" #include "offsets.h" #include "rpc_helper.h" +#include "spy.h" #include "spy_types.h" #include "util.h" -extern QWORD g_WeChatWinDllAddr; - namespace message { @@ -26,22 +25,22 @@ Sender &Sender::get_instance() Sender::Sender() { - func_new_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::INSTANCE); - func_free_chat_msg = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE); - func_send_msg_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::MGR); - func_send_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::TEXT); - func_send_image = reinterpret_cast(g_WeChatWinDllAddr + OsSend::IMAGE); - func_get_app_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::APP_MGR); - func_send_file = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FILE); - func_new_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::NEW_MM_READER); - func_free_mmreader = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FREE_MM_READER); - func_send_rich_text = reinterpret_cast(g_WeChatWinDllAddr + OsSend::RICH_TEXT); - func_send_pat = reinterpret_cast(g_WeChatWinDllAddr + OsSend::PAT); - func_forward = reinterpret_cast(g_WeChatWinDllAddr + OsSend::FORWARD); - func_get_emotion_mgr = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION_MGR); - func_send_emotion = reinterpret_cast(g_WeChatWinDllAddr + OsSend::EMOTION); - func_send_xml = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML); - func_xml_buf_sign = reinterpret_cast(g_WeChatWinDllAddr + OsSend::XML_BUF_SIGN); + func_new_chat_msg = Spy::getFunction(OsSend::INSTANCE); + func_free_chat_msg = Spy::getFunction(OsSend::FREE); + func_send_msg_mgr = Spy::getFunction(OsSend::MGR); + func_send_text = Spy::getFunction(OsSend::TEXT); + func_send_image = Spy::getFunction(OsSend::IMAGE); + func_get_app_mgr = Spy::getFunction(OsSend::APP_MGR); + func_send_file = Spy::getFunction(OsSend::FILE); + func_new_mmreader = Spy::getFunction(OsSend::NEW_MM_READER); + func_free_mmreader = Spy::getFunction(OsSend::FREE_MM_READER); + func_send_rich_text = Spy::getFunction(OsSend::RICH_TEXT); + func_send_pat = Spy::getFunction(OsSend::PAT); + func_forward = Spy::getFunction(OsSend::FORWARD); + func_get_emotion_mgr = Spy::getFunction(OsSend::EMOTION_MGR); + func_send_emotion = Spy::getFunction(OsSend::EMOTION); + func_send_xml = Spy::getFunction(OsSend::XML); + func_xml_buf_sign = Spy::getFunction(OsSend::XML_BUF_SIGN); } void Sender::send_text(const std::string &wxid, const std::string &msg, const std::string &at_wxids) diff --git a/WeChatFerry/spy/misc_manager.cpp b/WeChatFerry/spy/misc_manager.cpp index dbfd47b..fa968c4 100644 --- a/WeChatFerry/spy/misc_manager.cpp +++ b/WeChatFerry/spy/misc_manager.cpp @@ -12,11 +12,10 @@ #include "message_handler.h" #include "offsets.h" #include "rpc_helper.h" +#include "spy.h" #include "spy_types.h" #include "util.h" -extern QWORD g_WeChatWinDllAddr; - namespace misc { using namespace std; @@ -105,8 +104,8 @@ static int get_first_page() { int status = -1; - get_sns_data_mgr_t GetSNSDataMgr = (get_sns_data_mgr_t)(g_WeChatWinDllAddr + OsSns::DATA_MGR); - get_sns_first_page_t GetSNSFirstPage = (get_sns_first_page_t)(g_WeChatWinDllAddr + OsSns::FIRST); + auto GetSNSDataMgr = Spy::getFunction(OsSns::DATA_MGR); + auto GetSNSFirstPage = Spy::getFunction(OsSns::FIRST); QWORD buff[16] = { 0 }; QWORD mgr = GetSNSDataMgr(); @@ -119,8 +118,8 @@ static int get_next_page(QWORD id) { int status = -1; - get_sns_timeline_mgr_t GetSnsTimeLineMgr = (get_sns_timeline_mgr_t)(g_WeChatWinDllAddr + OsSns::TIMELINE); - get_sns_next_page_scene_t GetSNSNextPageScene = (get_sns_next_page_scene_t)(g_WeChatWinDllAddr + OsSns::NEXT); + auto GetSnsTimeLineMgr = Spy::getFunction(OsSns::TIMELINE); + auto GetSNSNextPageScene = Spy::getFunction(OsSns::NEXT); QWORD mgr = GetSnsTimeLineMgr(); status = (int)GetSNSNextPageScene(mgr, id); @@ -167,13 +166,12 @@ int download_attachment(uint64_t id, const fs::path &thumb, const fs::path &extr return status; } - new_chat_msg_t NewChatMsg = (new_chat_msg_t)(g_WeChatWinDllAddr + OsMisc::INSATNCE); - free_chat_msg_t FreeChatMsg = (free_chat_msg_t)(g_WeChatWinDllAddr + OsMisc::FREE); - get_chat_mgr_t GetChatMgr = (get_chat_mgr_t)(g_WeChatWinDllAddr + OsMisc::CHAT_MGR); - get_pre_download_mgr_t GetPreDownLoadMgr = (get_pre_download_mgr_t)(g_WeChatWinDllAddr + OsMisc::PRE_DOWNLOAD_MGR); - push_attach_task_t PushAttachTask = (push_attach_task_t)(g_WeChatWinDllAddr + OsMisc::PUSH_ATTACH_TASK); - get_mgr_by_prefix_localid_t GetMgrByPrefixLocalId - = (get_mgr_by_prefix_localid_t)(g_WeChatWinDllAddr + OsMisc::PRE_LOCAL_ID_MGR); + auto NewChatMsg = Spy::getFunction(OsMisc::INSATNCE); + auto FreeChatMsg = Spy::getFunction(OsMisc::FREE); + auto GetChatMgr = Spy::getFunction(OsMisc::CHAT_MGR); + auto GetPreDownLoadMgr = Spy::getFunction(OsMisc::PRE_DOWNLOAD_MGR); + auto PushAttachTask = Spy::getFunction(OsMisc::PUSH_ATTACH_TASK); + auto GetMgrByPrefixLocalId = Spy::getFunction(OsMisc::PRE_LOCAL_ID_MGR); LARGE_INTEGER l; l.HighPart = dbIdx; @@ -336,7 +334,7 @@ int revoke_message(uint64_t id) std::string get_login_url() { std::string uri; - get_qr_code_mgr_t get_qr_code_mgr = (get_qr_code_mgr_t)(g_WeChatWinDllAddr + OsMisc::QR_CODE); + auto get_qr_code_mgr = Spy::getFunction(OsMisc::QR_CODE); uint64_t addr = get_qr_code_mgr() + 0x68; uint64_t len = *(uint64_t *)(addr + 0x10); diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index b721e9f..ae5ee39 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -1,23 +1,21 @@ #include "spy.h" #include -#include #include "log.hpp" #include "rpc_server.h" +#include "spy.h" #include "util.h" -constexpr std::string_view SUPPORT_VERSION = "3.9.12.17"; - -UINT64 g_WeChatWinDllAddr = 0; - -int InitSpy(LPVOID args) +namespace Spy +{ +int Init(void *args) { auto *pp = static_cast(args); Log::InitLogger(pp->path); if (auto dll_addr = GetModuleHandle(L"WeChatWin.dll")) { - g_WeChatWinDllAddr = reinterpret_cast(dll_addr); + WeChatDll.store(reinterpret_cast(dll_addr)); } else { LOG_ERROR("获取 WeChatWin.dll 模块地址失败"); return -1; @@ -37,8 +35,14 @@ int InitSpy(LPVOID args) return 0; } -void CleanupSpy() +void Cleanup() { LOG_DEBUG("CleanupSpy"); RpcServer::destroyInstance(); } +} + +extern "C" { +__declspec(dllexport) int InitSpy(void *args) { return Spy::Init(args); } +__declspec(dllexport) void CleanupSpy() { Spy::Cleanup(); } +} diff --git a/WeChatFerry/spy/spy.h b/WeChatFerry/spy/spy.h index 07825e6..ae8f402 100644 --- a/WeChatFerry/spy/spy.h +++ b/WeChatFerry/spy/spy.h @@ -1,6 +1,20 @@ #pragma once -#include "framework.h" +#include +#include +#include -int InitSpy(int port); -void CleanupSpy(); +namespace Spy +{ +constexpr std::string_view SUPPORT_VERSION = "3.9.12.17"; +inline std::atomic WeChatDll { 0 }; + +template inline T getFunction(std::uintptr_t offset) { return reinterpret_cast(WeChatDll + offset); } +template inline T getFunction(std::uintptr_t base, std::uintptr_t offset) +{ + return reinterpret_cast(base + offset); +} + +int Init(void *args); +void Cleanup(); +} From 6233697e019f1d34fa6fe08d5cea922e5da376da Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Mar 2025 17:04:13 +0800 Subject: [PATCH 125/132] chore(all): resolve conflicts --- WeChatFerry/spy/spy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index be4feab..ae5ee39 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -2,7 +2,7 @@ #include -#include "log.h" +#include "log.hpp" #include "rpc_server.h" #include "spy.h" #include "util.h" From ab4824946426fc3d1603fc16b463dc76ad245839 Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Mar 2025 17:37:49 +0800 Subject: [PATCH 126/132] chore: bump to v39.4.0 --- README.MD | 9 ++++++--- WeChatFerry/spy/spy.aps | Bin 2612 -> 2612 bytes WeChatFerry/spy/spy.rc | 12 ++++++------ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/README.MD b/README.MD index 6913e72..49d5157 100644 --- a/README.MD +++ b/README.MD @@ -32,7 +32,6 @@ * 发送图片消息 * 发送文件消息 * 发送卡片消息 -* 发送 XML 消息 * 发送 GIF 消息 * 拍一拍群友 * 转发消息 @@ -205,9 +204,9 @@ WeChatFerry ## 版本更新 -### v39.3.5 +### v39.4.0 -* 代码优化 +* 重构代码,适配 `3.9.12.17`。
点击查看更多 @@ -219,6 +218,10 @@ WeChatFerry * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 +### v39.3.5 + +* 代码优化 + ### v39.3.4 * 实现获取登录二维码 diff --git a/WeChatFerry/spy/spy.aps b/WeChatFerry/spy/spy.aps index 9a2d14615a1ade6b64b35523c2ca58ff7c926d61..19965705ba7c957de4b79f6f4870e7d7a42c8cfa 100644 GIT binary patch delta 46 zcmdlYvPEP88$Sz!Is*uBGB7g;GVo01XZy}*vYDM-osr#$L65 Date: Fri, 7 Mar 2025 18:21:08 +0800 Subject: [PATCH 127/132] chore(python): bump to v39.4.0.0 --- clients/python/MANIFEST.in | 2 +- clients/python/wcferry/client.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clients/python/MANIFEST.in b/clients/python/MANIFEST.in index e228aeb..bc25aee 100644 --- a/clients/python/MANIFEST.in +++ b/clients/python/MANIFEST.in @@ -1,2 +1,2 @@ include wcferry/*.dll -include wcferry/*.exe +include wcferry/*.md diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index a4ebe12..bc6527b 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -__version__ = "39.3.3.3" +__version__ = "39.4.0.0" import atexit import base64 From ce0fb49956e5abf997023c8bb9024848b065287d Mon Sep 17 00:00:00 2001 From: Changhua Date: Fri, 7 Mar 2025 18:32:55 +0800 Subject: [PATCH 128/132] chore(spy): remove unsed files --- WeChatFerry/spy/contact_mgmt.cpp | 193 -------------- WeChatFerry/spy/exec_sql.cpp | 233 ---------------- WeChatFerry/spy/funcs.cpp | 445 ------------------------------- WeChatFerry/spy/receive_msg.cpp | 379 -------------------------- WeChatFerry/spy/send_msg.cpp | 274 ------------------- WeChatFerry/spy/user_info.cpp | 59 ---- 6 files changed, 1583 deletions(-) delete mode 100644 WeChatFerry/spy/contact_mgmt.cpp delete mode 100644 WeChatFerry/spy/exec_sql.cpp delete mode 100644 WeChatFerry/spy/funcs.cpp delete mode 100644 WeChatFerry/spy/receive_msg.cpp delete mode 100644 WeChatFerry/spy/send_msg.cpp delete mode 100644 WeChatFerry/spy/user_info.cpp diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp deleted file mode 100644 index b00152d..0000000 --- a/WeChatFerry/spy/contact_mgmt.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#pragma execution_character_set("utf-8") - -#include "contact_mgmt.h" -#include "log.h" -#include "util.h" - -using namespace std; -extern QWORD g_WeChatWinDllAddr; - -#define OS_GET_CONTACT_MGR 0x1B470B0 -#define OS_GET_CONTACT_LIST 0x21A49D0 -#define OS_CONTACT_BIN 0x200 -#define OS_CONTACT_BIN_LEN 0x208 -#define OS_CONTACT_WXID 0x10 -#define OS_CONTACT_CODE 0x30 -#define OS_CONTACT_REMARK 0x80 -#define OS_CONTACT_NAME 0xA0 -#define OS_CONTACT_GENDER 0x0E -#define OS_CONTACT_STEP 0x6A8 - -typedef QWORD (*GetContactMgr_t)(); -typedef QWORD (*GetContactList_t)(QWORD, QWORD); - -#define FEAT_LEN 5 -static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 }; -static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 }; -static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 }; - -static QWORD FindMem(QWORD start, QWORD end, const void *target, size_t len) -{ - uint8_t *p = (uint8_t *)start; - while ((QWORD)p < end) { - if (memcmp((void *)p, target, len) == 0) { - return (QWORD)p; - } - p++; - } - - return 0; -} - -static string GetCntString(QWORD start, QWORD end, const uint8_t *feat, size_t len) -{ - QWORD pfeat = FindMem(start, end, feat, len); - if (pfeat == 0) { - return ""; - } - - DWORD lfeat = GET_DWORD(pfeat + len); - if (lfeat <= 2) { - return ""; - } - - return Wstring2String(wstring(GET_WSTRING_FROM_P(pfeat + FEAT_LEN + 4), lfeat)); -} - -vector GetContacts() -{ - vector contacts; - GetContactMgr_t funcGetContactMgr = (GetContactMgr_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR); - GetContactList_t funcGetContactList = (GetContactList_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST); - - QWORD mgr = funcGetContactMgr(); - QWORD addr[3] = { 0 }; - if (funcGetContactList(mgr, (QWORD)addr) != 1) { - LOG_ERROR("GetContacts failed"); - return contacts; - } - - QWORD pstart = (QWORD)addr[0]; - QWORD pend = (QWORD)addr[2]; - while (pstart < pend) { - RpcContact_t cnt; - QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN); - QWORD lenbin = GET_DWORD(pstart + OS_CONTACT_BIN_LEN); - - cnt.wxid = GetStringByWstrAddr(pstart + OS_CONTACT_WXID); - cnt.code = GetStringByWstrAddr(pstart + OS_CONTACT_CODE); - cnt.remark = GetStringByWstrAddr(pstart + OS_CONTACT_REMARK); - cnt.name = GetStringByWstrAddr(pstart + OS_CONTACT_NAME); - - cnt.country = GetCntString(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN); - cnt.province = GetCntString(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN); - cnt.city = GetCntString(pbin, pbin + lenbin, FEAT_CITY, FEAT_LEN); - - if (pbin == 0) { - cnt.gender = 0; - } else { - cnt.gender = (DWORD) * (uint8_t *)(pbin + OS_CONTACT_GENDER); - } - - contacts.push_back(cnt); - pstart += OS_CONTACT_STEP; - } - - return contacts; -} - -#if 0 -int AcceptNewFriend(string v3, string v4, int scene) -{ - int success = 0; - - DWORD acceptNewFriendCall1 = g_WeChatWinDllAddr + g_WxCalls.anf.call1; - DWORD acceptNewFriendCall2 = g_WeChatWinDllAddr + g_WxCalls.anf.call2; - DWORD acceptNewFriendCall3 = g_WeChatWinDllAddr + g_WxCalls.anf.call3; - DWORD acceptNewFriendCall4 = g_WeChatWinDllAddr + g_WxCalls.anf.call4; - - char buffer[0x40] = { 0 }; - char nullbuffer[0x3CC] = { 0 }; - - LOG_DEBUG("\nv3: {}\nv4: {}\nscene: {}", v3, v4, scene); - - wstring wsV3 = String2Wstring(v3); - wstring wsV4 = String2Wstring(v4); - WxString wxV3(wsV3); - WxString wxV4(wsV4); - - __asm { - pushad; - pushfd; - lea ecx, buffer; - call acceptNewFriendCall1; - mov esi, 0x0; - mov edi, scene; - push esi; - push edi; - sub esp, 0x14; - mov ecx, esp; - lea eax, wxV4; - push eax; - call acceptNewFriendCall2; - sub esp, 0x8; - push 0x0; - lea eax, nullbuffer; - push eax; - lea eax, wxV3; - push eax; - lea ecx, buffer; - call acceptNewFriendCall3; - mov success, eax; - lea ecx, buffer; - call acceptNewFriendCall4; - popfd; - popad; - } - - return success; // 成功返回 1 -} - -/*没啥用,非好友获取不到*/ -RpcContact_t GetContactByWxid(string wxid) -{ - RpcContact_t contact; - char buff[0x440] = { 0 }; - wstring wsWxid = String2Wstring(wxid); - WxString pri(wsWxid); - DWORD contact_mgr_addr = g_WeChatWinDllAddr + 0x75A4A0; - DWORD get_contact_addr = g_WeChatWinDllAddr + 0xC04E00; - DWORD free_contact_addr = g_WeChatWinDllAddr + 0xEA7880; - __asm { - PUSHAD - PUSHFD - CALL contact_mgr_addr - LEA ECX,buff - PUSH ECX - LEA ECX,pri - PUSH ECX - MOV ECX,EAX - CALL get_contact_addr - POPFD - POPAD - } - - contact.wxid = wxid; - contact.code = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxCode); - contact.remark = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxRemark); - contact.name = GetStringByWstrAddr((DWORD)buff + g_WxCalls.contact.wxName); - contact.gender = GET_DWORD((DWORD)buff + 0x148); - - __asm { - PUSHAD - PUSHFD - LEA ECX,buff - CALL free_contact_addr - POPFD - POPAD - } - - return contact; -} -#endif - diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp deleted file mode 100644 index 5e8b857..0000000 --- a/WeChatFerry/spy/exec_sql.cpp +++ /dev/null @@ -1,233 +0,0 @@ -#include - -#include "exec_sql.h" -#include "log.h" -#include "sqlite3.h" -#include "util.h" - -#define OFFSET_DB_INSTANCE 0x59C5B48 -#define OFFSET_DB_MICROMSG 0xB8 -#define OFFSET_DB_CHAT_MSG 0x2C8 -#define OFFSET_DB_MISC 0x5F0 -#define OFFSET_DB_EMOTION 0x15F0 -#define OFFSET_DB_MEDIA 0xF48 -#define OFFSET_DB_BIZCHAT_MSG 0x1A70 -#define OFFSET_DB_FUNCTION_MSG 0x1B98 -#define OFFSET_DB_NAME 0x28 -#define OFFSET_DB_MSG_MGR 0x5A23888 - -extern UINT64 g_WeChatWinDllAddr; - -typedef map dbMap_t; -static dbMap_t dbMap; - -static void GetDbHandle(QWORD base, QWORD offset) -{ - wchar_t *wsp = (wchar_t *)(*(QWORD *)(base + offset + OFFSET_DB_NAME)); - string dbname = Wstring2String(wstring(wsp)); - dbMap[dbname] = GET_QWORD(base + offset); -} - -static void GetMsgDbHandle(QWORD msgMgrAddr) -{ - QWORD dbIndex = GET_QWORD(msgMgrAddr + 0x68); - QWORD pStart = GET_QWORD(msgMgrAddr + 0x50); - for (uint32_t i = 0; i < dbIndex; i++) { - QWORD dbAddr = GET_QWORD(pStart + i * 0x08); - if (dbAddr) { - // MSGi.db - string dbname = Wstring2String(GET_WSTRING(dbAddr)); - dbMap[dbname] = GET_QWORD(dbAddr + 0x78); - - // MediaMsgi.db - QWORD mmdbAddr = GET_QWORD(dbAddr + 0x20); - string mmdbname = Wstring2String(GET_WSTRING(mmdbAddr + 0x78)); - dbMap[mmdbname] = GET_QWORD(mmdbAddr + 0x50); - } - } -} - -dbMap_t GetDbHandles() -{ - dbMap.clear(); - - QWORD dbInstanceAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE); - - GetDbHandle(dbInstanceAddr, OFFSET_DB_MICROMSG); // MicroMsg.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_CHAT_MSG); // ChatMsg.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_MISC); // Misc.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_EMOTION); // Emotion.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_MEDIA); // Media.db - GetDbHandle(dbInstanceAddr, OFFSET_DB_FUNCTION_MSG); // Function.db - - GetMsgDbHandle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db - - return dbMap; -} - -DbNames_t GetDbNames() -{ - DbNames_t names; - if (dbMap.empty()) { - dbMap = GetDbHandles(); - } - - for (auto &[k, v] : dbMap) { - names.push_back(k); - } - - return names; -} - -static int cbGetTables(void *ret, int argc, char **argv, char **azColName) -{ - DbTables_t *tbls = (DbTables_t *)ret; - DbTable_t tbl; - for (int i = 0; i < argc; i++) { - if (strcmp(azColName[i], "name") == 0) { - tbl.name = argv[i] ? argv[i] : ""; - } else if (strcmp(azColName[i], "sql") == 0) { - string sql(argv[i]); - sql.erase(std::remove(sql.begin(), sql.end(), '\t'), sql.end()); - tbl.sql = sql.c_str(); - } - } - tbls->push_back(tbl); - return 0; -} - -DbTables_t GetDbTables(const string db) -{ - DbTables_t tables; - if (dbMap.empty()) { - dbMap = GetDbHandles(); - } - - auto it = dbMap.find(db); - if (it == dbMap.end()) { - return tables; // DB not found - } - - const char *sql = "select name, sql from sqlite_master where type=\"table\";"; - Sqlite3_exec p_Sqlite3_exec = (Sqlite3_exec)(g_WeChatWinDllAddr + SQLITE3_EXEC_OFFSET); - - p_Sqlite3_exec(it->second, sql, (Sqlite3_callback)cbGetTables, (void *)&tables, 0); - - return tables; -} - -DbRows_t ExecDbQuery(const string db, const string sql) -{ - DbRows_t rows; - Sqlite3_prepare func_prepare = (Sqlite3_prepare)(g_WeChatWinDllAddr + SQLITE3_PREPARE_OFFSET); - Sqlite3_step func_step = (Sqlite3_step)(g_WeChatWinDllAddr + SQLITE3_STEP_OFFSET); - Sqlite3_column_count func_column_count = (Sqlite3_column_count)(g_WeChatWinDllAddr + SQLITE3_COLUMN_COUNT_OFFSET); - Sqlite3_column_name func_column_name = (Sqlite3_column_name)(g_WeChatWinDllAddr + SQLITE3_COLUMN_NAME_OFFSET); - Sqlite3_column_type func_column_type = (Sqlite3_column_type)(g_WeChatWinDllAddr + SQLITE3_COLUMN_TYPE_OFFSET); - Sqlite3_column_blob func_column_blob = (Sqlite3_column_blob)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BLOB_OFFSET); - Sqlite3_column_bytes func_column_bytes = (Sqlite3_column_bytes)(g_WeChatWinDllAddr + SQLITE3_COLUMN_BYTES_OFFSET); - Sqlite3_finalize func_finalize = (Sqlite3_finalize)(g_WeChatWinDllAddr + SQLITE3_FINALIZE_OFFSET); - - if (dbMap.empty()) { - dbMap = GetDbHandles(); - } - - QWORD *stmt; - QWORD handle = dbMap[db]; - if (handle == 0) { - LOG_WARN("Empty handle, retrying..."); - dbMap = GetDbHandles(); - } - - int rc = func_prepare(dbMap[db], sql.c_str(), -1, &stmt, 0); - if (rc != SQLITE_OK) { - return rows; - } - - while (func_step(stmt) == SQLITE_ROW) { - DbRow_t row; - int col_count = func_column_count(stmt); - for (int i = 0; i < col_count; i++) { - DbField_t field; - field.type = func_column_type(stmt, i); - field.column = func_column_name(stmt, i); - - int length = func_column_bytes(stmt, i); - const void *blob = func_column_blob(stmt, i); - if (length && (field.type != 5)) { - field.content.reserve(length); - copy((uint8_t *)blob, (uint8_t *)blob + length, back_inserter(field.content)); - } - row.push_back(field); - } - rows.push_back(row); - } - return rows; -} - -int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx) -{ - QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); // 总不能 int 还不够吧? - QWORD pStart = GET_QWORD(msgMgrAddr + 0x50); - - *dbIdx = 0; - for (int i = dbIndex - 1; i >= 0; i--) { // 从后往前遍历 - QWORD dbAddr = GET_QWORD(pStart + i * 0x08); - if (dbAddr) { - string dbname = Wstring2String(GET_WSTRING(dbAddr)); - dbMap[dbname] = GET_QWORD(dbAddr + 0x78); - string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + to_string(id) + ";"; - DbRows_t rows = ExecDbQuery(dbname, sql); - if (rows.empty()) { - continue; - } - DbRow_t row = rows.front(); - if (row.empty()) { - continue; - } - DbField_t field = row.front(); - if ((field.column.compare("localId") != 0) && (field.type != 1)) { - continue; - } - - *localId = strtoull((const char *)(field.content.data()), NULL, 10); - *dbIdx = (uint32_t)(GET_QWORD(GET_QWORD(dbAddr + 0x28) + 0x1E8) >> 32); - - return 0; - } - } - - return -1; -} - -vector GetAudioData(uint64_t id) -{ - QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR); - int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); - - string sql = "SELECT Buf FROM Media WHERE Reserved0=" + to_string(id) + ";"; - for (int i = dbIndex - 1; i >= 0; i--) { - string dbname = "MediaMSG" + to_string(i) + ".db"; - DbRows_t rows = ExecDbQuery(dbname, sql); - if (rows.empty()) { - continue; - } - DbRow_t row = rows.front(); - if (row.empty()) { - continue; - } - DbField_t field = row.front(); - if (field.column.compare("Buf") != 0) { - continue; - } - - // 首字节为 0x02,估计是混淆用的?去掉。 - vector rv(field.content.begin() + 1, field.content.end()); - - return rv; - } - - return vector(); -} - diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp deleted file mode 100644 index 7eefed7..0000000 --- a/WeChatFerry/spy/funcs.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#pragma warning(disable : 4244) - -#include "framework.h" -#include -#include -#include -#include - -#include "codec.h" -#include "exec_sql.h" -#include "funcs.h" -#include "log.h" -#include "spy_types.h" -#include "util.h" - -using namespace std; -namespace fs = std::filesystem; - -extern bool gIsListeningPyq; -extern QWORD g_WeChatWinDllAddr; - -#define HEADER_PNG1 0x89 -#define HEADER_PNG2 0x50 -#define HEADER_JPG1 0xFF -#define HEADER_JPG2 0xD8 -#define HEADER_GIF1 0x47 -#define HEADER_GIF2 0x49 - -#define OS_LOGIN_STATUS 0x5A20978 -#define OS_GET_SNS_DATA_MGR 0x21E7EC0 -#define OS_GET_SNS_FIRST_PAGE 0x2E37960 -#define OS_GET_SNS_TIMELINE_MGR 0x2DC9470 -#define OS_GET_SNS_NEXT_PAGE 0x2EDF4D0 -#define OS_NEW_CHAT_MSG 0x1B63A50 -#define OS_FREE_CHAT_MSG 0x1B5B160 -#define OS_GET_CHAT_MGR 0x1B8CFD0 -#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x2145220 -#define OS_GET_PRE_DOWNLOAD_MGR 0x1C14930 -#define OS_PUSH_ATTACH_TASK 0x1CE57B0 -#define OS_LOGIN_QR_CODE 0x5A26440 - -typedef QWORD (*GetSNSDataMgr_t)(); -typedef QWORD (*GetSnsTimeLineMgr_t)(); -typedef QWORD (*GetSNSFirstPage_t)(QWORD, QWORD, QWORD); -typedef QWORD (*GetSNSNextPageScene_t)(QWORD, QWORD); -typedef QWORD (*GetChatMgr_t)(); -typedef QWORD (*NewChatMsg_t)(QWORD); -typedef QWORD (*FreeChatMsg_t)(QWORD); -typedef QWORD (*GetPreDownLoadMgr_t)(); -typedef QWORD (*GetMgrByPrefixLocalId_t)(QWORD, QWORD); -typedef QWORD (*PushAttachTask_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*GetOCRManager_t)(); -typedef QWORD (*DoOCRTask_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - -int IsLogin(void) { return (int)GET_QWORD(g_WeChatWinDllAddr + OS_LOGIN_STATUS); } - -static string get_key(uint8_t header1, uint8_t header2, uint8_t *key) -{ - // PNG? - *key = HEADER_PNG1 ^ header1; - if ((HEADER_PNG2 ^ *key) == header2) { - return ".png"; - } - - // JPG? - *key = HEADER_JPG1 ^ header1; - if ((HEADER_JPG2 ^ *key) == header2) { - return ".jpg"; - } - - // GIF? - *key = HEADER_GIF1 ^ header1; - if ((HEADER_GIF2 ^ *key) == header2) { - return ".gif"; - } - - return ""; // 错误 -} - -// 创建多级目录 -bool CreateDir(const char* dir) -{ - int m = 0, n; - string str1, str2; - str1 = dir; - str2 = str1.substr(0, 2); - str1 = str1.substr(3, str1.size()); - while (m >= 0) - { - m = str1.find('/'); - - str2 += '/' + str1.substr(0, m); - //判断该目录是否存在 - n = _access(str2.c_str(), 0); - if (n == -1) - { - //创建目录文件 - int flag = _mkdir(str2.c_str()); - if (flag != 0) { //创建失败 - LOG_ERROR("Failed to CreateDir:{}", dir); - return false; - } - } - - str1 = str1.substr(m + 1, str1.size()); - } - LOG_DEBUG("CreateDir {} success.", dir); - return true; -} - -string DecryptImage(string src, string dir) -{ - if (!fs::exists(src)) { - LOG_ERROR("File not exists: {}", src); - return ""; - } - - ifstream in(src.c_str(), ios::binary); - if (!in.is_open()) { - LOG_ERROR("Failed to read file {}", src); - return ""; - } - - filebuf *pfb = in.rdbuf(); - size_t size = pfb->pubseekoff(0, ios::end, ios::in); - pfb->pubseekpos(0, ios::in); - - vector buff; - buff.reserve(size); - char *pBuf = buff.data(); - pfb->sgetn(pBuf, size); - in.close(); - - uint8_t key = 0x00; - string ext = get_key(pBuf[0], pBuf[1], &key); - if (ext.empty()) { - LOG_ERROR("Failed to get key."); - return ""; - } - - for (size_t i = 0; i < size; i++) { - pBuf[i] ^= key; - } - - string dst = ""; - - try { - if (dir.empty()) { - dst = fs::path(src).replace_extension(ext).string(); - } else { - dst = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - replace(dst.begin(), dst.end(), '\\', '/'); - - // 判断dir文件夹是否存在,若不存在则创建(否则将无法创建出文件) - if (_access(dst.c_str(), 0) == -1) {//判断该文件夹是否存在 - bool success = CreateDir(dst.c_str()); //Windows创建文件夹 - if (!success) { //创建失败 - LOG_ERROR("Failed to mkdir:{}", dst); - return ""; - } - } - - dst += fs::path(src).stem().string() + ext; - } - - replace(dst.begin(), dst.end(), '\\', '/'); - } catch (const std::exception &e) { - LOG_ERROR(GB2312ToUtf8(e.what())); - } catch (...) { - LOG_ERROR("Unknow exception."); - return ""; - } - - ofstream out(dst.c_str(), ios::binary); - if (!out.is_open()) { - LOG_ERROR("Failed to write file {}", dst); - return ""; - } - - out.write(pBuf, size); - out.close(); - - return dst; -} - -static int GetFirstPage() -{ - int status = -1; - - GetSNSDataMgr_t GetSNSDataMgr = (GetSNSDataMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR); - GetSNSFirstPage_t GetSNSFirstPage = (GetSNSFirstPage_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE); - - QWORD buff[16] = { 0 }; - QWORD mgr = GetSNSDataMgr(); - status = (int)GetSNSFirstPage(mgr, (QWORD)buff, 1); - - return status; -} - -static int GetNextPage(QWORD id) -{ - int status = -1; - - GetSnsTimeLineMgr_t GetSnsTimeLineMgr = (GetSnsTimeLineMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR); - GetSNSNextPageScene_t GetSNSNextPageScene = (GetSNSNextPageScene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE); - - QWORD mgr = GetSnsTimeLineMgr(); - status = (int)GetSNSNextPageScene(mgr, id); - - return status; -} - -int RefreshPyq(QWORD id) -{ - if (!gIsListeningPyq) { - LOG_ERROR("没有启动朋友圈消息接收,参考:enable_receiving_msg"); - return -1; - } - - if (id == 0) { - return GetFirstPage(); - } - - return GetNextPage(id); -} - -/******************************************************************************* - * 都说我不写注释,写一下吧 - * 其实也没啥好写的,就是下载资源 - * 主要介绍一下几个参数: - * id:好理解,消息 id - * thumb:图片或者视频的缩略图路径;如果是视频,后缀为 mp4 后就是存在路径了 - * extra:图片、文件的路径 - *******************************************************************************/ -int DownloadAttach(QWORD id, string thumb, string extra) -{ - int status = -1; - QWORD localId; - uint32_t dbIdx; - - if (fs::exists(extra)) { // 第一道,不重复下载。TODO: 通过文件大小来判断 - return 0; - } - - if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) { - LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id)); - return status; - } - - NewChatMsg_t NewChatMsg = (NewChatMsg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG); - FreeChatMsg_t FreeChatMsg = (FreeChatMsg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG); - GetChatMgr_t GetChatMgr = (GetChatMgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR); - GetPreDownLoadMgr_t GetPreDownLoadMgr = (GetPreDownLoadMgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR); - PushAttachTask_t PushAttachTask = (PushAttachTask_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK); - GetMgrByPrefixLocalId_t GetMgrByPrefixLocalId - = (GetMgrByPrefixLocalId_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID); - - LARGE_INTEGER l; - l.HighPart = dbIdx; - l.LowPart = (DWORD)localId; - - char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460); - if (buff == nullptr) { - LOG_ERROR("Failed to allocate memory."); - return status; - } - - QWORD pChatMsg = NewChatMsg((QWORD)buff); - GetChatMgr(); - GetMgrByPrefixLocalId(l.QuadPart, pChatMsg); - - QWORD type = GET_QWORD(buff + 0x38); - - string save_path = ""; - string thumb_path = ""; - - switch (type) { - case 0x03: { // Image: extra - save_path = extra; - break; - } - case 0x3E: - case 0x2B: { // Video: thumb - thumb_path = thumb; - save_path = fs::path(thumb).replace_extension("mp4").string(); - break; - } - case 0x31: { // File: extra - save_path = extra; - break; - } - default: - break; - } - - if (fs::exists(save_path)) { // 不重复下载。TODO: 通过文件大小来判断 - return 0; - } - - LOG_DEBUG("path: {}", save_path); - // 创建父目录,由于路径来源于微信,不做检查 - fs::create_directory(fs::path(save_path).parent_path().string()); - - int temp = 1; - WxString *pSavePath = NewWxStringFromStr(save_path); - WxString *pThumbPath = NewWxStringFromStr(thumb_path); - - memcpy(&buff[0x280], pThumbPath, sizeof(WxString)); - memcpy(&buff[0x2A0], pSavePath, sizeof(WxString)); - memcpy(&buff[0x40C], &temp, sizeof(temp)); - - QWORD mgr = GetPreDownLoadMgr(); - status = (int)PushAttachTask(mgr, pChatMsg, 0, 1); - FreeChatMsg(pChatMsg); - - return status; -} - -string GetAudio(QWORD id, string dir) -{ - string mp3path = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - mp3path += to_string(id) + ".mp3"; - replace(mp3path.begin(), mp3path.end(), '\\', '/'); - if (fs::exists(mp3path)) { // 不重复下载 - return mp3path; - } - - vector silk = GetAudioData(id); - if (silk.size() == 0) { - LOG_ERROR("Empty audio data."); - return ""; - } - - Silk2Mp3(silk, mp3path, 24000); - - return mp3path; -} - -string GetPCMAudio(uint64_t id, string dir, int32_t sr) -{ - string pcmpath = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/"); - pcmpath += to_string(id) + ".pcm"; - replace(pcmpath.begin(), pcmpath.end(), '\\', '/'); - if (fs::exists(pcmpath)) { // 不重复下载 - return pcmpath; - } - vector pcm; - vector silk = GetAudioData(id); - if (silk.size() == 0) { - LOG_ERROR("Empty audio data."); - return ""; - } - - SilkDecode(silk, pcm, sr); - errno_t err; - FILE* fPCM; - err = fopen_s(&fPCM, pcmpath.c_str(), "wb"); - if (err != 0) { - printf("Error: could not open input file %s\n", pcmpath.c_str()); - exit(0); - } - - fwrite(pcm.data(), sizeof(uint8_t), pcm.size(), fPCM); - fclose(fPCM); - - return pcmpath; -} - - -OcrResult_t GetOcrResult(string path) -{ - OcrResult_t ret = { -1, "" }; -#if 0 // 参数没调好,会抛异常,看看有没有好心人来修复 - if (!fs::exists(path)) { - LOG_ERROR("Can not find: {}", path); - return ret; - } - - GetOCRManager_t GetOCRManager = (GetOCRManager_t)(g_WeChatWinDllAddr + 0x1D6C3C0); - DoOCRTask_t DoOCRTask = (DoOCRTask_t)(g_WeChatWinDllAddr + 0x2D10BC0); - - QWORD unk1 = 0, unk2 = 0, unused = 0; - QWORD *pUnk1 = &unk1; - QWORD *pUnk2 = &unk2; - // 路径分隔符有要求,必须为 `\` - wstring wsPath = String2Wstring(fs::path(path).make_preferred().string()); - WxString wxPath(wsPath); - vector *pv = (vector *)HeapAlloc(GetProcessHeap(), 0, 0x20); - RawVector_t *pRv = (RawVector_t *)pv; - pRv->finish = pRv->start; - char buff[0x98] = { 0 }; - memcpy(buff, &pRv->start, sizeof(QWORD)); - - QWORD mgr = GetOCRManager(); - ret.status = (int)DoOCRTask(mgr, (QWORD)&wxPath, unused, (QWORD)buff, (QWORD)&pUnk1, (QWORD)&pUnk2); - - QWORD count = GET_QWORD(buff + 0x8); - if (count > 0) { - QWORD header = GET_QWORD(buff); - for (QWORD i = 0; i < count; i++) { - QWORD content = GET_QWORD(header); - ret.result += Wstring2String(GET_WSTRING(content + 0x28)); - ret.result += "\n"; - header = content; - } - } -#endif - return ret; -} - -int RevokeMsg(QWORD id) -{ - int status = -1; -#if 0 // 这个挺鸡肋的,因为自己发的消息没法直接获得 msgid,就这样吧 - QWORD localId; - uint32_t dbIdx; - if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) { - LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id)); - return status; - } -#endif - return status; -} - -string GetLoginUrl() -{ - LPVOID targetAddress = reinterpret_cast(g_WeChatWinDllAddr) + OS_LOGIN_QR_CODE; - - char *dataPtr = *reinterpret_cast(targetAddress); // 读取指针内容 - if (!dataPtr) { - LOG_ERROR("Failed to get login url"); - return "error"; - } - - // 读取字符串内容 - std::string data(dataPtr, 22); - return "http://weixin.qq.com/x/" + data; -} - -int ReceiveTransfer(string wxid, string transferid, string transactionid) -{ - // 别想了,这个不实现了 - return -1; -} - diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp deleted file mode 100644 index 5a7867a..0000000 --- a/WeChatFerry/spy/receive_msg.cpp +++ /dev/null @@ -1,379 +0,0 @@ -#pragma execution_character_set("utf-8") - -#include "MinHook.h" -#include "framework.h" -#include -#include -#include - -#include "log.h" -#include "receive_msg.h" -#include "user_info.h" -#include "util.h" - -// Defined in rpc_server.cpp -extern bool gIsLogging, gIsListening, gIsListeningPyq; -extern mutex gMutex; -extern condition_variable gCV; -extern queue gMsgQueue; - -// Defined in spy.cpp -extern QWORD g_WeChatWinDllAddr; - -#define OS_RECV_MSG_ID 0x30 -#define OS_RECV_MSG_TYPE 0x38 -#define OS_RECV_MSG_SELF 0x3C -#define OS_RECV_MSG_TS 0x44 -#define OS_RECV_MSG_ROOMID 0x48 -#define OS_RECV_MSG_CONTENT 0x88 -#define OS_RECV_MSG_WXID 0x240 -#define OS_RECV_MSG_SIGN 0x260 -#define OS_RECV_MSG_THUMB 0x280 -#define OS_RECV_MSG_EXTRA 0x2A0 -#define OS_RECV_MSG_XML 0x308 -#define OS_RECV_MSG_CALL 0x21444B0 -#define OS_PYQ_MSG_START 0x30 -#define OS_PYQ_MSG_END 0x38 -#define OS_PYQ_MSG_TS 0x38 -#define OS_PYQ_MSG_XML 0x9B8 -#define OS_PYQ_MSG_SENDER 0x18 -#define OS_PYQ_MSG_CONTENT 0x48 -#define OS_PYQ_MSG_CALL 0x2E59320 -#define OS_WXLOG 0x261E760 - -typedef QWORD (*RecvMsg_t)(QWORD, QWORD); -typedef QWORD (*WxLog_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*RecvPyq_t)(QWORD, QWORD, QWORD); - -static RecvMsg_t funcRecvMsg = nullptr; -static RecvMsg_t realRecvMsg = nullptr; -static WxLog_t funcWxLog = nullptr; -static WxLog_t realWxLog = nullptr; -static RecvPyq_t funcRecvPyq = nullptr; -static RecvPyq_t realRecvPyq = nullptr; -static bool isMH_Initialized = false; - -MsgTypes_t GetMsgTypes() -{ - const MsgTypes_t m = { - { 0x00, "朋友圈消息" }, - { 0x01, "文字" }, - { 0x03, "图片" }, - { 0x22, "语音" }, - { 0x25, "好友确认" }, - { 0x28, "POSSIBLEFRIEND_MSG" }, - { 0x2A, "名片" }, - { 0x2B, "视频" }, - { 0x2F, "石头剪刀布 | 表情图片" }, - { 0x30, "位置" }, - { 0x31, "共享实时位置、文件、转账、链接" }, - { 0x32, "VOIPMSG" }, - { 0x33, "微信初始化" }, - { 0x34, "VOIPNOTIFY" }, - { 0x35, "VOIPINVITE" }, - { 0x3E, "小视频" }, - { 0x42, "微信红包" }, - { 0x270F, "SYSNOTICE" }, - { 0x2710, "红包、系统消息" }, - { 0x2712, "撤回消息" }, - { 0x100031, "搜狗表情" }, - { 0x1000031, "链接" }, - { 0x1A000031, "微信红包" }, - { 0x20010031, "红包封面" }, - { 0x2D000031, "视频号视频" }, - { 0x2E000031, "视频号名片" }, - { 0x31000031, "引用消息" }, - { 0x37000031, "拍一拍" }, - { 0x3A000031, "视频号直播" }, - { 0x3A100031, "商品链接" }, - { 0x3A200031, "视频号直播" }, - { 0x3E000031, "音乐链接" }, - { 0x41000031, "文件" }, - }; - - return m; -} - -static QWORD DispatchMsg(QWORD arg1, QWORD arg2) -{ - WxMsg_t wxMsg = { 0 }; - try { - wxMsg.id = GET_QWORD(arg2 + OS_RECV_MSG_ID); - wxMsg.type = GET_DWORD(arg2 + OS_RECV_MSG_TYPE); - wxMsg.is_self = GET_DWORD(arg2 + OS_RECV_MSG_SELF); - wxMsg.ts = GET_DWORD(arg2 + OS_RECV_MSG_TS); - wxMsg.content = GetStringByWstrAddr(arg2 + OS_RECV_MSG_CONTENT); - wxMsg.sign = GetStringByWstrAddr(arg2 + OS_RECV_MSG_SIGN); - wxMsg.xml = GetStringByWstrAddr(arg2 + OS_RECV_MSG_XML); - - string roomid = GetStringByWstrAddr(arg2 + OS_RECV_MSG_ROOMID); - wxMsg.roomid = roomid; - if (roomid.find("@chatroom") != string::npos) { // 群 ID 的格式为 xxxxxxxxxxx@chatroom - wxMsg.is_group = true; - if (wxMsg.is_self) { - wxMsg.sender = GetSelfWxid(); - } else { - wxMsg.sender = GetStringByWstrAddr(arg2 + OS_RECV_MSG_WXID); - } - } else { - wxMsg.is_group = false; - if (wxMsg.is_self) { - wxMsg.sender = GetSelfWxid(); - } else { - wxMsg.sender = roomid; - } - } - - wxMsg.thumb = GetStringByWstrAddr(arg2 + OS_RECV_MSG_THUMB); - if (!wxMsg.thumb.empty()) { - wxMsg.thumb = GetHomePath() + wxMsg.thumb; - replace(wxMsg.thumb.begin(), wxMsg.thumb.end(), '\\', '/'); - } - - wxMsg.extra = GetStringByWstrAddr(arg2 + OS_RECV_MSG_EXTRA); - if (!wxMsg.extra.empty()) { - wxMsg.extra = GetHomePath() + wxMsg.extra; - replace(wxMsg.extra.begin(), wxMsg.extra.end(), '\\', '/'); - } - } catch (const std::exception &e) { - LOG_ERROR(GB2312ToUtf8(e.what())); - } catch (...) { - LOG_ERROR("Unknow exception."); - } - - { - unique_lock lock(gMutex); - gMsgQueue.push(wxMsg); // 推送到队列 - } - - gCV.notify_all(); // 通知各方消息就绪 - return realRecvMsg(arg1, arg2); -} - -static QWORD PrintWxLog(QWORD a1, QWORD a2, QWORD a3, QWORD a4, QWORD a5, QWORD a6, QWORD a7, QWORD a8, QWORD a9, - QWORD a10, QWORD a11, QWORD a12) -{ - QWORD p = realWxLog(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12); - if (p == 0 || p == 1) { - return p; - } - - LOG_INFO("【WX】\n{}", GB2312ToUtf8((char *)p)); - - return p; -} - -static void DispatchPyq(QWORD arg1, QWORD arg2, QWORD arg3) -{ - QWORD startAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_START); - QWORD endAddr = *(QWORD *)(arg2 + OS_PYQ_MSG_END); - - if (startAddr == 0) { - return; - } - - while (startAddr < endAddr) { - WxMsg_t wxMsg; - - wxMsg.type = 0x00; // 朋友圈消息 - wxMsg.is_self = false; - wxMsg.is_group = false; - wxMsg.id = GET_QWORD(startAddr); - wxMsg.ts = GET_DWORD(startAddr + OS_PYQ_MSG_TS); - wxMsg.xml = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_XML); - wxMsg.sender = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_SENDER); - wxMsg.content = GetStringByWstrAddr(startAddr + OS_PYQ_MSG_CONTENT); - - { - unique_lock lock(gMutex); - gMsgQueue.push(wxMsg); // 推送到队列 - } - - gCV.notify_all(); // 通知各方消息就绪 - - startAddr += 0x1618; - } -} - -static MH_STATUS InitializeHook() -{ - if (isMH_Initialized) { - return MH_OK; - } - MH_STATUS status = MH_Initialize(); - if (status == MH_OK) { - isMH_Initialized = true; - } - return status; -} - -static MH_STATUS UninitializeHook() -{ - if (!isMH_Initialized) { - return MH_OK; - } - if (gIsLogging || gIsListening || gIsListeningPyq) { - return MH_OK; - } - MH_STATUS status = MH_Uninitialize(); - if (status == MH_OK) { - isMH_Initialized = false; - } - return status; -} - -void EnableLog() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsLogging) { - LOG_WARN("gIsLogging"); - return; - } - WxLog_t funcWxLog = (WxLog_t)(g_WeChatWinDllAddr + OS_WXLOG); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcWxLog, &PrintWxLog, reinterpret_cast(&realWxLog)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcWxLog); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - gIsLogging = true; -} - -void DisableLog() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsLogging) { - return; - } - - status = MH_DisableHook(funcWxLog); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsLogging = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} - -void ListenMessage() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsListening) { - LOG_WARN("gIsListening"); - return; - } - funcRecvMsg = (RecvMsg_t)(g_WeChatWinDllAddr + OS_RECV_MSG_CALL); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcRecvMsg, &DispatchMsg, reinterpret_cast(&realRecvMsg)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcRecvMsg); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - - gIsListening = true; -} - -void UnListenMessage() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsListening) { - return; - } - - status = MH_DisableHook(funcRecvMsg); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsListening = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} - -void ListenPyq() -{ - MH_STATUS status = MH_UNKNOWN; - if (gIsListeningPyq) { - LOG_WARN("gIsListeningPyq"); - return; - } - funcRecvPyq = (RecvPyq_t)(g_WeChatWinDllAddr + OS_PYQ_MSG_CALL); - - status = InitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Initialize failed: {}", to_string(status)); - return; - } - - status = MH_CreateHook(funcRecvPyq, &DispatchPyq, reinterpret_cast(&realRecvPyq)); - if (status != MH_OK) { - LOG_ERROR("MH_CreateHook failed: {}", to_string(status)); - return; - } - - status = MH_EnableHook(funcRecvPyq); - if (status != MH_OK) { - LOG_ERROR("MH_EnableHook failed: {}", to_string(status)); - return; - } - - gIsListeningPyq = true; -} - -void UnListenPyq() -{ - MH_STATUS status = MH_UNKNOWN; - if (!gIsListeningPyq) { - return; - } - - status = MH_DisableHook(funcRecvPyq); - if (status != MH_OK) { - LOG_ERROR("MH_DisableHook failed: {}", to_string(status)); - return; - } - - gIsListeningPyq = false; - - status = UninitializeHook(); - if (status != MH_OK) { - LOG_ERROR("MH_Uninitialize failed: {}", to_string(status)); - return; - } -} - diff --git a/WeChatFerry/spy/send_msg.cpp b/WeChatFerry/spy/send_msg.cpp deleted file mode 100644 index 75cf6d6..0000000 --- a/WeChatFerry/spy/send_msg.cpp +++ /dev/null @@ -1,274 +0,0 @@ - -#include "framework.h" -#include -#include - -#include "exec_sql.h" -#include "log.h" -#include "send_msg.h" -#include "spy_types.h" -#include "util.h" - -extern HANDLE g_hEvent; -extern QWORD g_WeChatWinDllAddr; -extern string GetSelfWxid(); // Defined in spy.cpp - -#define SRTM_SIZE 0x3F0 - -#define OS_NEW 0x1B63A50 -#define OS_FREE 0x1B5B160 -#define OS_SEND_MSG_MGR 0x1B598E0 -#define OS_SEND_TEXT 0x22CC660 -#define OS_SEND_IMAGE 0x22C1E70 -#define OS_GET_APP_MSG_MGR 0x1B5E880 -#define OS_SEND_FILE 0x20D5FF0 -#define OS_RTM_NEW 0x1B62FA0 -#define OS_RTM_FREE 0x1B62370 -#define OS_SEND_RICH_TEXT 0x20DFFD0 -#define OS_SEND_PAT_MSG 0x2CC4F10 -#define OS_FORWARD_MSG 0x22CBBE0 -#define OS_GET_EMOTION_MGR 0x1BD49A0 -#define OS_SEND_EMOTION 0x21BACD0 -#define OS_XML_BUFSIGN 0x24FB330 -#define OS_SEND_XML 0x20D5120 - -typedef QWORD (*New_t)(QWORD); -typedef QWORD (*Free_t)(QWORD); -typedef QWORD (*SendMsgMgr_t)(); -typedef QWORD (*GetAppMsgMgr_t)(); -typedef QWORD (*SendTextMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*SendImageMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*SendFileMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD *, QWORD, QWORD *, QWORD, QWORD *, QWORD, - QWORD); -typedef QWORD (*SendRichTextMsg_t)(QWORD, QWORD, QWORD); -typedef QWORD (*SendPatMsg_t)(QWORD, QWORD); -typedef QWORD (*ForwardMsg_t)(QWORD, QWORD, QWORD, QWORD); -typedef QWORD (*GetEmotionMgr_t)(); -typedef QWORD (*SendEmotion_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - -typedef QWORD (*XmlBufSign_t)(QWORD, QWORD, QWORD); -typedef QWORD (*SendXmlMsg_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD, QWORD); - -void SendTextMessage(string wxid, string msg, string atWxids) -{ - QWORD success = 0; - wstring wsWxid = String2Wstring(wxid); - wstring wsMsg = String2Wstring(msg); - WxString wxMsg(wsMsg); - WxString wxWxid(wsWxid); - - vector vAtWxids; - vector vWxAtWxids; - if (!atWxids.empty()) { - wstringstream wss(String2Wstring(atWxids)); - while (wss.good()) { - wstring wstr; - getline(wss, wstr, L','); - vAtWxids.push_back(wstr); - WxString wxAtWxid(vAtWxids.back()); - vWxAtWxids.push_back(wxAtWxid); - } - } else { - WxString wxEmpty = WxString(); - vWxAtWxids.push_back(wxEmpty); - } - - QWORD wxAters = (QWORD) & ((RawVector_t *)&vWxAtWxids)->start; - - char buffer[0x460] = { 0 }; - SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); - SendTextMsg_t funcSendTextMsg = (SendTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_TEXT); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - funcSendMsgMgr(); - success = funcSendTextMsg((QWORD)(&buffer), (QWORD)(&wxWxid), (QWORD)(&wxMsg), wxAters, 1, 1, 0, 0); - funcFree((QWORD)(&buffer)); -} - -void SendImageMessage(string wxid, string path) -{ - wstring wsWxid = String2Wstring(wxid); - wstring wsPath = String2Wstring(path); - - WxString wxWxid(wsWxid); - WxString wxPath(wsPath); - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - SendMsgMgr_t funcSendMsgMgr = (SendMsgMgr_t)(g_WeChatWinDllAddr + OS_SEND_MSG_MGR); - SendImageMsg_t funcSendImage = (SendImageMsg_t)(g_WeChatWinDllAddr + OS_SEND_IMAGE); - - char msg[0x460] = { 0 }; - char msgTmp[0x460] = { 0 }; - QWORD *flag[10] = { 0 }; - - QWORD tmp1 = 0, tmp2 = 0; - QWORD pMsgTmp = funcNew((QWORD)(&msgTmp)); - flag[8] = &tmp1; - flag[9] = &tmp2; - flag[1] = (QWORD *)(pMsgTmp); - - QWORD pMsg = funcNew((QWORD)(&msg)); - QWORD sendMgr = funcSendMsgMgr(); - funcSendImage(sendMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), (QWORD)(&flag)); - funcFree(pMsg); - funcFree(pMsgTmp); -} - -void SendFileMessage(string wxid, string path) -{ - wstring wsWxid = String2Wstring(wxid); - wstring wsPath = String2Wstring(path); - - WxString wxWxid(wsWxid); - WxString wxPath(wsPath); - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR); - SendFileMsg_t funcSendFile = (SendFileMsg_t)(g_WeChatWinDllAddr + OS_SEND_FILE); - - char msg[0x460] = { 0 }; - QWORD tmp1[4] = { 0 }; - QWORD tmp2[4] = { 0 }; - QWORD tmp3[4] = { 0 }; - - QWORD pMsg = funcNew((QWORD)(&msg)); - QWORD appMgr = funcGetAppMsgMgr(); - funcSendFile(appMgr, pMsg, (QWORD)(&wxWxid), (QWORD)(&wxPath), 1, tmp1, 0, tmp2, 0, tmp3, 0, 0); - funcFree(pMsg); -} - -int SendRichTextMessage(RichText_t &rt) -{ // TODO: Fix memory leak - QWORD status = -1; - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_RTM_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_RTM_FREE); - GetAppMsgMgr_t funcGetAppMsgMgr = (GetAppMsgMgr_t)(g_WeChatWinDllAddr + OS_GET_APP_MSG_MGR); - SendRichTextMsg_t funcForwordPublicMsg = (SendRichTextMsg_t)(g_WeChatWinDllAddr + OS_SEND_RICH_TEXT); - - char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, SRTM_SIZE); - if (buff == NULL) { - LOG_ERROR("Out of Memory..."); - return -1; - } - - memset(buff, 0, SRTM_SIZE); - funcNew((QWORD)buff); - WxString *pReceiver = NewWxStringFromStr(rt.receiver); - WxString *pTitle = NewWxStringFromStr(rt.title); - WxString *pUrl = NewWxStringFromStr(rt.url); - WxString *pThumburl = NewWxStringFromStr(rt.thumburl); - WxString *pDigest = NewWxStringFromStr(rt.digest); - WxString *pAccount = NewWxStringFromStr(rt.account); - WxString *pName = NewWxStringFromStr(rt.name); - - memcpy(buff + 0x8, pTitle, sizeof(WxString)); - memcpy(buff + 0x48, pUrl, sizeof(WxString)); - memcpy(buff + 0xB0, pThumburl, sizeof(WxString)); - memcpy(buff + 0xF0, pDigest, sizeof(WxString)); - memcpy(buff + 0x2C0, pAccount, sizeof(WxString)); - memcpy(buff + 0x2E0, pName, sizeof(WxString)); - - QWORD mgr = funcGetAppMsgMgr(); - status = funcForwordPublicMsg(mgr, (QWORD)(pReceiver), (QWORD)(buff)); - funcFree((QWORD)buff); - - return (int)status; -} - -int SendPatMessage(string roomid, string wxid) -{ - QWORD status = -1; - - wstring wsRoomid = String2Wstring(roomid); - wstring wsWxid = String2Wstring(wxid); - WxString wxRoomid(wsRoomid); - WxString wxWxid(wsWxid); - - SendPatMsg_t funcSendPatMsg = (SendPatMsg_t)(g_WeChatWinDllAddr + OS_SEND_PAT_MSG); - - status = funcSendPatMsg((QWORD)(&wxRoomid), (QWORD)(&wxWxid)); - return (int)status; -} - -int ForwardMessage(QWORD msgid, string receiver) -{ - int status = -1; - uint32_t dbIdx = 0; - QWORD localId = 0; - - ForwardMsg_t funcForwardMsg = (ForwardMsg_t)(g_WeChatWinDllAddr + OS_FORWARD_MSG); - if (GetLocalIdandDbidx(msgid, &localId, &dbIdx) != 0) { - LOG_ERROR("Failed to get localId, Please check id: {}", to_string(msgid)); - return status; - } - - WxString *pReceiver = NewWxStringFromStr(receiver); - - LARGE_INTEGER l; - l.HighPart = dbIdx; - l.LowPart = (DWORD)localId; - - status = (int)funcForwardMsg((QWORD)pReceiver, l.QuadPart, 0x4, 0x0); - - return status; -} - -void SendEmotionMessage(string wxid, string path) -{ - GetEmotionMgr_t GetEmotionMgr = (GetEmotionMgr_t)(g_WeChatWinDllAddr + OS_GET_EMOTION_MGR); - SendEmotion_t SendEmotion = (SendEmotion_t)(g_WeChatWinDllAddr + OS_SEND_EMOTION); - - WxString *pWxPath = NewWxStringFromStr(path); - WxString *pWxWxid = NewWxStringFromStr(wxid); - - QWORD *buff = (QWORD *)HeapAlloc(GetProcessHeap(), 0, 0x20); - if (buff == NULL) { - LOG_ERROR("Out of Memory..."); - return; - } - - memset(buff, 0, 0x20); - QWORD mgr = GetEmotionMgr(); - SendEmotion(mgr, (QWORD)pWxPath, (QWORD)buff, (QWORD)pWxWxid, 2, (QWORD)buff, 0, (QWORD)buff); -} - -void SendXmlMessage(string receiver, string xml, string path, QWORD type) -{ - if (g_WeChatWinDllAddr == 0) { - return; - } - - New_t funcNew = (New_t)(g_WeChatWinDllAddr + OS_NEW); - Free_t funcFree = (Free_t)(g_WeChatWinDllAddr + OS_FREE); - - XmlBufSign_t xmlBufSign = (XmlBufSign_t)(g_WeChatWinDllAddr + OS_XML_BUFSIGN); - SendXmlMsg_t sendXmlMsg = (SendXmlMsg_t)(g_WeChatWinDllAddr + OS_SEND_XML); - - char buff[0x500] = { 0 }; - char buff2[0x500] = { 0 }; - char nullBuf[0x1C] = { 0 }; - - QWORD pBuf = (QWORD)(&buff); - QWORD pBuf2 = (QWORD)(&buff2); - - funcNew(pBuf); - funcNew(pBuf2); - - QWORD sbuf[4] = { 0, 0, 0, 0 }; - - QWORD sign = xmlBufSign(pBuf2, (QWORD)(&sbuf), 0x1); - - WxString *pReceiver = NewWxStringFromStr(receiver); - WxString *pXml = NewWxStringFromStr(xml); - WxString *pPath = NewWxStringFromStr(path); - WxString *pSender = NewWxStringFromStr(GetSelfWxid()); - - sendXmlMsg(pBuf, (QWORD)pSender, (QWORD)pReceiver, (QWORD)pXml, (QWORD)pPath, (QWORD)(&nullBuf), type, 0x4, sign, - pBuf2); - - funcFree((QWORD)&buff); - funcFree((QWORD)&buff2); -} - diff --git a/WeChatFerry/spy/user_info.cpp b/WeChatFerry/spy/user_info.cpp deleted file mode 100644 index a5c132e..0000000 --- a/WeChatFerry/spy/user_info.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "user_info.h" -#include "log.h" -#include "util.h" - -extern UINT64 g_WeChatWinDllAddr; - -#define OS_USER_HOME 0x59F6330 -#define OS_USER_WXID 0x5A20200 -#define OS_USER_NAME 0x5A20368 -#define OS_USER_MOBILE 0x595C318 - -static char home[MAX_PATH] = { 0 }; - -string GetHomePath() -{ - if (home[0] == 0) { - string path = Wstring2String(GET_WSTRING(g_WeChatWinDllAddr + OS_USER_HOME)) + "\\WeChat Files\\"; - strncpy_s(home, path.c_str(), path.size()); - } - - return string(home); -} - -string GetSelfWxid() -{ - UINT64 wxidType = 0; - try { - wxidType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_WXID + 0x18); - if (wxidType == 0xF) { - return GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_WXID); - } else { - return GET_STRING(g_WeChatWinDllAddr + OS_USER_WXID); - } - } catch (...) { - LOG_ERROR("wxid type: {:#x}", wxidType); - LOG_BUFFER((uint8_t *)(g_WeChatWinDllAddr + OS_USER_WXID), 20); - return "empty_wxid"; - } -} - -UserInfo_t GetUserInfo() -{ - UserInfo_t ui; - - ui.wxid = GetSelfWxid(); - - UINT64 nameType = GET_UINT64(g_WeChatWinDllAddr + OS_USER_NAME + 0x18); - if (nameType == 0xF) { - ui.name = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_NAME); - } else { // 0x1F - ui.name = GET_STRING(g_WeChatWinDllAddr + OS_USER_NAME); - } - - ui.mobile = GET_STRING_FROM_P(g_WeChatWinDllAddr + OS_USER_MOBILE); - ui.home = GetHomePath(); - - return ui; -} - From 4a4914743fd534230c261d86b7689f825a77ef41 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 8 Mar 2025 11:41:44 +0800 Subject: [PATCH 129/132] fix(encoding): fix Chinese garbling --- WeChatFerry/com/util.h | 6 ++++ WeChatFerry/sdk/injector.cpp | 20 +++++++------ WeChatFerry/sdk/sdk.cpp | 54 +++++++++++++++++++++--------------- WeChatFerry/spy/spy.cpp | 2 +- 4 files changed, 50 insertions(+), 32 deletions(-) diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h index e2a0e4a..1998fbb 100644 --- a/WeChatFerry/com/util.h +++ b/WeChatFerry/com/util.h @@ -70,6 +70,12 @@ inline void FreeBuffer(void *buffer) { if (buffer) HeapFree(GetProcessHeap(), 8, buffer); } +inline int MsgBox(HWND hWnd, const std::string &text, const std::string &caption = "WCF", UINT uType = MB_OK) +{ + std::wstring wText = s2w(text); + std::wstring wCaption = s2w(caption); + return MessageBoxW(nullptr, wText.c_str(), wCaption.c_str(), uType); +} template static T *AllocBuffer(size_t count) { diff --git a/WeChatFerry/sdk/injector.cpp b/WeChatFerry/sdk/injector.cpp index d990854..913ba87 100644 --- a/WeChatFerry/sdk/injector.cpp +++ b/WeChatFerry/sdk/injector.cpp @@ -4,11 +4,13 @@ #include "psapi.h" +#include "util.h" + using namespace std; static void handle_injection_error(HANDLE process, LPVOID remote_address, const std::string &error_msg) { - MessageBoxA(NULL, error_msg.c_str(), "Error", MB_ICONERROR); + util::MsgBox(NULL, error_msg.c_str(), "Error", MB_ICONERROR); if (remote_address) { VirtualFreeEx(process, remote_address, 0, MEM_RELEASE); } @@ -22,7 +24,7 @@ HMODULE get_target_module_base(HANDLE process, const string &dll) DWORD needed; HMODULE modules[512]; if (!EnumProcessModulesEx(process, modules, sizeof(modules), &needed, LIST_MODULES_64BIT)) { - MessageBoxA(NULL, "获取模块失败", "get_target_module_base", 0); + util::MsgBox(NULL, "获取模块失败", "get_target_module_base", 0); return NULL; } @@ -44,7 +46,7 @@ HANDLE inject_dll(DWORD pid, const string &dll_path, HMODULE *injected_base) // 1. 打开目标进程 HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); if (!hProcess) { - MessageBoxA(NULL, "打开进程失败", "inject_dll", 0); + util::MsgBox(NULL, "打开进程失败", "inject_dll", 0); return NULL; } @@ -90,19 +92,19 @@ bool eject_dll(HANDLE process, HMODULE dll_base) { HMODULE k32 = GetModuleHandleA("kernel32.dll"); if (!k32) { - MessageBoxA(NULL, "获取 kernel32 失败", "eject_dll", 0); + util::MsgBox(NULL, "获取 kernel32 失败", "eject_dll", 0); return false; } FARPROC libAddr = GetProcAddress(k32, "FreeLibraryAndExitThread"); if (!libAddr) { - MessageBoxA(NULL, "获取 FreeLibrary 失败", "eject_dll", 0); + util::MsgBox(NULL, "获取 FreeLibrary 失败", "eject_dll", 0); return false; } HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)libAddr, (LPVOID)dll_base, 0, NULL); if (!hThread) { - MessageBoxA(NULL, "FreeLibrary 调用失败!", "eject_dll", 0); + util::MsgBox(NULL, "FreeLibrary 调用失败!", "eject_dll", 0); return false; } @@ -116,7 +118,7 @@ static uint64_t get_func_offset(const string &dll_path, const string &func_name) { HMODULE dll = LoadLibraryA(dll_path.c_str()); if (!dll) { - MessageBoxA(NULL, "获取 DLL 失败", "get_func_offset", 0); + util::MsgBox(NULL, "获取 DLL 失败", "get_func_offset", 0); return 0; } @@ -157,7 +159,7 @@ bool call_dll_func_ex(HANDLE process, const string &dll_path, HMODULE dll_base, uint64_t pFunc = reinterpret_cast(dll_base) + offset; LPVOID pRemoteAddress = VirtualAllocEx(process, NULL, size, MEM_COMMIT, PAGE_READWRITE); if (!pRemoteAddress) { - MessageBoxA(NULL, "申请内存失败", "call_dll_func_ex", 0); + util::MsgBox(NULL, "申请内存失败", "call_dll_func_ex", 0); return false; } @@ -166,7 +168,7 @@ bool call_dll_func_ex(HANDLE process, const string &dll_path, HMODULE dll_base, HANDLE hThread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)pFunc, pRemoteAddress, 0, NULL); if (!hThread) { VirtualFreeEx(process, pRemoteAddress, 0, MEM_RELEASE); - MessageBoxA(NULL, "远程调用失败", "call_dll_func_ex", 0); + util::MsgBox(NULL, "远程调用失败", "call_dll_func_ex", 0); return false; } diff --git a/WeChatFerry/sdk/sdk.cpp b/WeChatFerry/sdk/sdk.cpp index e1dbdc8..8363414 100644 --- a/WeChatFerry/sdk/sdk.cpp +++ b/WeChatFerry/sdk/sdk.cpp @@ -14,6 +14,8 @@ #include "injector.h" #include "util.h" +extern "C" IMAGE_DOS_HEADER __ImageBase; + static bool injected = false; static HANDLE wcProcess = NULL; static HMODULE spyBase = NULL; @@ -26,38 +28,46 @@ constexpr char WCFSPYDLL_DEBUG[] = "spy_debug.dll"; constexpr std::string_view DISCLAIMER_FLAG = ".license_accepted.flag"; constexpr std::string_view DISCLAIMER_TEXT_FILE = "DISCLAIMER.md"; -static std::optional read_disclaimer_text(const std::string &path) -{ - std::ifstream file(path, std::ios::binary); - if (!file.is_open()) { - return std::nullopt; // 文件打开失败 - } +namespace fs = std::filesystem; - return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); +static fs::path get_module_directory() +{ + char buffer[MAX_PATH] = { 0 }; + HMODULE hModule = reinterpret_cast(&__ImageBase); + GetModuleFileNameA(hModule, buffer, MAX_PATH); + fs::path modulePath(buffer); + return modulePath.parent_path(); } static bool show_disclaimer() { - if (std::filesystem::exists(DISCLAIMER_FLAG)) { + fs::path sdk_path = get_module_directory(); + if (fs::exists(sdk_path / DISCLAIMER_FLAG)) { return true; } - auto disclaimerTextOpt = read_disclaimer_text(std::string(DISCLAIMER_TEXT_FILE)); - if (!disclaimerTextOpt || disclaimerTextOpt->empty()) { - MessageBoxA(NULL, "免责声明文件为空或读取失败。", "错误", MB_ICONERROR); + fs::path path = sdk_path / DISCLAIMER_TEXT_FILE; + std::ifstream file(path, std::ios::binary); + if (!file.is_open()) { + util::MsgBox(NULL, "免责声明文件读取失败。", "错误", MB_ICONERROR); return false; } - int result - = MessageBoxA(NULL, disclaimerTextOpt->c_str(), "免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2); + auto disclaimerText = std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); + if (disclaimerText.empty()) { + util::MsgBox(NULL, "免责声明文件为空", "错误", MB_ICONERROR); + return false; + } + + int result = util::MsgBox(NULL, disclaimerText.c_str(), "免责声明", MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON2); if (result == IDCANCEL) { - MessageBoxA(NULL, "您拒绝了免责声明,程序将退出。", "提示", MB_ICONINFORMATION); + util::MsgBox(NULL, "您拒绝了免责声明,程序将退出。", "提示", MB_ICONINFORMATION); return false; } - std::ofstream flagFile(std::string(DISCLAIMER_FLAG), std::ios::out | std::ios::trunc); + std::ofstream flagFile(sdk_path / DISCLAIMER_FLAG, std::ios::out | std::ios::trunc); if (!flagFile) { - MessageBoxA(NULL, "无法创建协议标志文件。", "错误", MB_ICONERROR); + util::MsgBox(NULL, "无法创建协议标志文件。", "错误", MB_ICONERROR); return false; } flagFile << "User accepted the license agreement."; @@ -70,12 +80,12 @@ static std::string get_dll_path(bool debug) char buffer[MAX_PATH] = { 0 }; GetModuleFileNameA(GetModuleHandleA(WCFSDKDLL), buffer, MAX_PATH); - std::filesystem::path path(buffer); + fs::path path(buffer); path.remove_filename(); // 只保留目录路径 path /= debug ? WCFSPYDLL_DEBUG : WCFSPYDLL; - if (!std::filesystem::exists(path)) { - MessageBoxA(NULL, path.string().c_str(), "文件不存在", MB_ICONERROR); + if (!fs::exists(path)) { + util::MsgBox(NULL, path.string().c_str(), "文件不存在", MB_ICONERROR); return ""; } @@ -98,21 +108,21 @@ int WxInitSDK(bool debug, int port) status = util::open_wechat(wcPid); if (status != 0) { - MessageBoxA(NULL, "打开微信失败", "WxInitSDK", 0); + util::MsgBox(NULL, "打开微信失败", "WxInitSDK", 0); return status; } std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待微信打开 wcProcess = inject_dll(wcPid, spyDllPath, &spyBase); if (wcProcess == NULL) { - MessageBoxA(NULL, "注入失败", "WxInitSDK", 0); + util::MsgBox(NULL, "注入失败", "WxInitSDK", 0); return -1; } injected = true; util::PortPath pp = { 0 }; pp.port = port; - snprintf(pp.path, MAX_PATH, "%s", std::filesystem::current_path().string().c_str()); + snprintf(pp.path, MAX_PATH, "%s", fs::current_path().string().c_str()); status = -3; // TODO: 统一错误码 bool success = call_dll_func_ex(wcProcess, spyDllPath, spyBase, "InitSpy", (LPVOID)&pp, sizeof(util::PortPath), diff --git a/WeChatFerry/spy/spy.cpp b/WeChatFerry/spy/spy.cpp index ae5ee39..dc6a700 100644 --- a/WeChatFerry/spy/spy.cpp +++ b/WeChatFerry/spy/spy.cpp @@ -25,7 +25,7 @@ int Init(void *args) std::string msg = fmt::format("WCF 支持版本: {},当前版本: {}", SUPPORT_VERSION, version); if (version != SUPPORT_VERSION) { LOG_ERROR(msg); - MessageBoxA(NULL, msg.c_str(), "微信版本错误", MB_ICONERROR); + util::MsgBox(NULL, msg.c_str(), "微信版本错误", MB_ICONERROR); return -2; } From 68bac79d4a9fd8636b44364ebb7fa3df4cd2b2dd Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 8 Mar 2025 11:46:08 +0800 Subject: [PATCH 130/132] chore: add DISCLAIMER to release --- .github/workflows/Build-WeChatFerry.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Build-WeChatFerry.yml b/.github/workflows/Build-WeChatFerry.yml index 1991b84..1305750 100644 --- a/.github/workflows/Build-WeChatFerry.yml +++ b/.github/workflows/Build-WeChatFerry.yml @@ -92,7 +92,7 @@ jobs: - name: 打包输出文件及下载 WeChat 安装包 run: | New-Item -ItemType Directory -Force -Path "WeChatFerry/tmp" - Compress-Archive -Path "WeChatFerry/Out/sdk.dll", "WeChatFerry/Out/spy.dll", "WeChatFerry/Out/spy_debug.dll" -DestinationPath "WeChatFerry/tmp/v${{ env.version }}.zip" + Compress-Archive -Path "WeChatFerry/Out/sdk.dll", "WeChatFerry/Out/spy.dll", "WeChatFerry/Out/spy_debug.dll", "WeChatFerry/Out/DISCLAIMER.md" -DestinationPath "WeChatFerry/tmp/v${{ env.version }}.zip" Invoke-WebRequest -Uri "https://github.com/tom-snow/wechat-windows-versions/releases/download/v${{ env.wechat_version }}/WeChatSetup-${{ env.wechat_version }}.exe" -OutFile "WeChatFerry/tmp/WeChatSetup-${{ env.wechat_version }}.exe" shell: pwsh From d24e6137890db7f67c20e2b2ab734914f44a54cd Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 8 Mar 2025 11:50:33 +0800 Subject: [PATCH 131/132] chore: bump to v39.4.1 --- README.MD | 8 ++++++-- WeChatFerry/spy/spy.rc | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index 49d5157..741c9fa 100644 --- a/README.MD +++ b/README.MD @@ -204,9 +204,9 @@ WeChatFerry ## 版本更新 -### v39.4.0 +### v39.4.1 -* 重构代码,适配 `3.9.12.17`。 +* 修复乱码问题。
点击查看更多 @@ -218,6 +218,10 @@ WeChatFerry * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 +### v39.4.0 + +* 重构代码,适配 `3.9.12.17`。 + ### v39.3.5 * 代码优化 diff --git a/WeChatFerry/spy/spy.rc b/WeChatFerry/spy/spy.rc index e4fe09e..42d3264 100644 --- a/WeChatFerry/spy/spy.rc +++ b/WeChatFerry/spy/spy.rc @@ -51,7 +51,7 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 39,4,0,0 + FILEVERSION 39,4,1,0 PRODUCTVERSION 3,9,12,17 FILEFLAGSMASK 0x3fL #ifdef _DEBUG @@ -69,7 +69,7 @@ BEGIN BEGIN VALUE "CompanyName", "WeChatFerry" VALUE "FileDescription", "WeChatFerry" - VALUE "FileVersion", "39.4.0.0" + VALUE "FileVersion", "39.4.1.0" VALUE "InternalName", "spy.dll" VALUE "LegalCopyright", "Copyright (C) 2023" VALUE "OriginalFilename", "spy.dll" From 225f2e44ea7aa8b1e20edb468ea0a3e6f6f9bb29 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sat, 8 Mar 2025 11:57:08 +0800 Subject: [PATCH 132/132] chore(python): bump to v39.4.1.0 --- clients/python/README.MD | 5 ++--- clients/python/wcferry/client.py | 25 ++++++++++--------------- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/clients/python/README.MD b/clients/python/README.MD index 1f4c4b6..1b9ec75 100644 --- a/clients/python/README.MD +++ b/clients/python/README.MD @@ -44,8 +44,8 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc ## 版本更新 -### v39.3.3.1 -* 修复 `send_xml` +### v39.4.1.0 +* 修复乱码问题
点击查看更多 @@ -70,7 +70,6 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc * 发送图片消息 * 发送文件消息 * 发送卡片消息 -* 发送 XML 消息 * 发送 GIF 消息 * 拍一拍群友 * 转发消息 diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index bc6527b..99f5999 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -1,7 +1,7 @@ #! /usr/bin/env python3 # -*- coding: utf-8 -*- -__version__ = "39.4.0.0" +__version__ = "39.4.1.0" import atexit import base64 @@ -10,14 +10,12 @@ import logging import mimetypes import os import re +import subprocess import sys from queue import Queue from threading import Thread -import shutil from time import sleep from typing import Callable, Dict, List, Optional -from pathlib import Path -import importlib.resources as pkg_resources # Python 3.9+ import pynng import requests @@ -73,6 +71,7 @@ class Wcf(): self._dl_path = f"{self._wcf_root}/.dl" os.makedirs(self._dl_path, exist_ok=True) self.LOG = logging.getLogger("WCF") + self._set_console_utf8() self.LOG.info(f"wcferry version: {__version__}") self.port = port self.host = host @@ -80,7 +79,6 @@ class Wcf(): if host is None: self._local_mode = True self.host = "127.0.0.1" - self._copy_disclaimer_to_cwd() self.sdk = ctypes.cdll.LoadLibrary(f"{self._wcf_root}/sdk.dll") if self.sdk.WxInitSDK(debug, port) != 0: self.LOG.error("初始化失败!") @@ -119,16 +117,12 @@ class Wcf(): def __del__(self) -> None: self.cleanup() - def _copy_disclaimer_to_cwd(self): - """复制免责声明到工作目录""" + def _set_console_utf8(self): try: - target_path = Path.cwd() / "DISCLAIMER.md" - with pkg_resources.path("wcferry", "DISCLAIMER.md") as disclaimer_path: - if not target_path.exists(): - shutil.copy(disclaimer_path, target_path) - except Exception as e: - self.LOG.error(f"复制免责声明失败:{e}") - os._exit(-3) + subprocess.run("chcp 65001", shell=True, check=True, + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + except subprocess.CalledProcessError as e: + self.LOG.error(f"修改控制台代码页失败: {e}") def cleanup(self) -> None: """关闭连接,回收资源""" @@ -167,7 +161,6 @@ class Wcf(): def get_qrcode(self) -> str: """获取登录二维码,已经登录则返回空字符串""" - raise Exception("Not implemented, yet") req = wcf_pb2.Request() req.func = wcf_pb2.FUNC_REFRESH_QRCODE # FUNC_REFRESH_QRCODE rsp = self._send_request(req) @@ -415,6 +408,7 @@ class Wcf(): Returns: int: 0 为成功,其他失败 """ + raise Exception("Not implemented, yet") req = wcf_pb2.Request() req.func = wcf_pb2.FUNC_SEND_XML # FUNC_SEND_XML req.xml.receiver = receiver @@ -780,6 +774,7 @@ class Wcf(): Returns: int: 1 为成功,其他失败 """ + raise Exception("Not implemented, yet") req = wcf_pb2.Request() req.func = wcf_pb2.FUNC_REVOKE_MSG # FUNC_REVOKE_MSG req.ui64 = id