From d7a75eb37e1f8847e0e2e70e465251362b86009b Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 10:10:37 +0800 Subject: [PATCH 01/13] Update README --- clients/python/README.MD | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clients/python/README.MD b/clients/python/README.MD index de62e3b..ec2d9e0 100644 --- a/clients/python/README.MD +++ b/clients/python/README.MD @@ -114,12 +114,12 @@ pip install grpcio-tools pynng ### 重新生成 PB 文件 ```sh # CMD -cd python\wcferry -python -m grpc_tools.protoc --python_out=. --proto_path=..\..\rpc\proto\ wcf.proto +cd clients\python\wcferry +python -m grpc_tools.protoc --python_out=. --proto_path=..\..\..\WeChatFerry\rpc\proto\ wcf.proto # GitBash -cd python/wcferry -python -m grpc_tools.protoc --python_out=. --proto_path=../../rpc/proto/ wcf.proto +cd clients/python/wcferry +python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc/proto/ wcf.proto ``` ## 版本更新 From f2acc668bfa018d17892f1a1b1132d7e510a228c Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 14:25:46 +0800 Subject: [PATCH 02/13] Add more fields in WxMsg --- WeChatFerry/rpc/pb_types.h | 8 +++++--- WeChatFerry/rpc/proto/wcf.proto | 19 +++++++++++-------- WeChatFerry/spy/load_calls.cpp | 4 ++-- WeChatFerry/spy/receive_msg.cpp | 9 +++++---- WeChatFerry/spy/spy_types.h | 8 +++++--- WeChatFerry/spy/util.h | 1 + 6 files changed, 29 insertions(+), 20 deletions(-) diff --git a/WeChatFerry/rpc/pb_types.h b/WeChatFerry/rpc/pb_types.h index 447f53a..3e8488c 100644 --- a/WeChatFerry/rpc/pb_types.h +++ b/WeChatFerry/rpc/pb_types.h @@ -38,14 +38,16 @@ typedef vector DbRows_t; typedef struct { bool is_self; bool is_group; - int32_t type; - string id; - string xml; + uint32_t type; + uint32_t ts; + uint64_t id; string sender; string roomid; string content; + string sign; string thumb; string extra; + string xml; } WxMsg_t; typedef struct { diff --git a/WeChatFerry/rpc/proto/wcf.proto b/WeChatFerry/rpc/proto/wcf.proto index caf79f6..157b147 100644 --- a/WeChatFerry/rpc/proto/wcf.proto +++ b/WeChatFerry/rpc/proto/wcf.proto @@ -42,6 +42,7 @@ message Request XmlMsg xml = 9; DecPath dec = 10; Transfer tf = 11; + uint64 ui64 = 12; // 64 位整数,通用 } } @@ -68,14 +69,16 @@ message WxMsg { bool is_self = 1; // 是否自己发送的 bool is_group = 2; // 是否群消息 - int32 type = 3; // 消息类型 - string id = 4; // 消息 id - string xml = 5; // 消息 xml - string sender = 6; // 消息发送者 - string roomid = 7; // 群 id(如果是群消息的话) - string content = 8; // 消息内容 - string thumb = 9; // 缩略图 - string extra = 10; // 附加内容 + uint64 id = 3; // 消息 id + uint32 type = 4; // 消息类型 + uint32 ts = 5; // 消息类型 + string roomid = 6; // 群 id(如果是群消息的话) + string content = 7; // 消息内容 + string sender = 8; // 消息发送者 + string sign = 9; // Sign + string thumb = 10; // 缩略图 + string extra = 11; // 附加内容 + string xml = 12; // 消息 xml } message TextMsg diff --git a/WeChatFerry/spy/load_calls.cpp b/WeChatFerry/spy/load_calls.cpp index 9384492..014b951 100644 --- a/WeChatFerry/spy/load_calls.cpp +++ b/WeChatFerry/spy/load_calls.cpp @@ -9,8 +9,8 @@ WxCalls_t wxCalls = { { 0x2FFD484, 0x2FFD590, 0x2FFD500, 0x30238CC }, // User Info: wxid, nickname, mobile, home { 0x768140, 0xCE6C80, 0x756960 }, // Send Message /* Receive Message: - Hook, call, type, self, id, msgXml, roomId, wxId, content, thumb, extra */ - { 0xD19A0B, 0x756960, 0x38, 0x3C, 0x194, 0x1FC, 0x48, 0x180, 0x70, 0x1A8, 0x1BC }, + Hook, call, msgId, type, isSelf, ts, roomId, content, wxid, sign, thumb, extra, msgXml */ + { 0xD19A0B, 0x756960, 0x30, 0x38, 0x3C, 0x44, 0x48, 0x70, 0x180, 0x194, 0x1A8, 0x1BC, 0x1FC }, { 0x768140, 0XF59E40, 0XCE6640, 0x756960 }, // Send Image Message { 0x76AE20, 0xF59E40, 0xB6D1F0, 0x756960 }, // Send File Message { 0xB8A70, 0x3ED5E0, 0x107F00, 0x3ED7B0, 0x2386FE4 }, // Send xml Message diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index e495a63..1f69c1b 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -90,9 +90,12 @@ void DispatchMsg(DWORD reg) { WxMsg_t wxMsg; + wxMsg.id = GET_QWORD(reg + g_WxCalls.recvMsg.msgId); wxMsg.type = GET_DWORD(reg + g_WxCalls.recvMsg.type); wxMsg.is_self = GET_DWORD(reg + g_WxCalls.recvMsg.isSelf); - wxMsg.id = GetStringByStrAddr(reg + g_WxCalls.recvMsg.msgId); + wxMsg.ts = GET_DWORD(reg + g_WxCalls.recvMsg.ts); + wxMsg.content = GetStringByWstrAddr(reg + g_WxCalls.recvMsg.content); + wxMsg.sign = GetStringByStrAddr(reg + g_WxCalls.recvMsg.sign); wxMsg.xml = GetStringByStrAddr(reg + g_WxCalls.recvMsg.msgXml); string roomid = GetStringByWstrAddr(reg + g_WxCalls.recvMsg.roomId); @@ -102,7 +105,7 @@ void DispatchMsg(DWORD reg) if (wxMsg.is_self) { wxMsg.sender = GetSelfWxid(); } else { - wxMsg.sender = GetStringByStrAddr(reg + g_WxCalls.recvMsg.wxId); + wxMsg.sender = GetStringByStrAddr(reg + g_WxCalls.recvMsg.wxid); } } else { wxMsg.is_group = false; @@ -113,8 +116,6 @@ void DispatchMsg(DWORD reg) } } - wxMsg.content = GetStringByWstrAddr(reg + g_WxCalls.recvMsg.content); - wxMsg.thumb = GetStringByStrAddr(reg + g_WxCalls.recvMsg.thumb); if (!wxMsg.thumb.empty()) { wxMsg.thumb = GetHomePath() + wxMsg.thumb; diff --git a/WeChatFerry/spy/spy_types.h b/WeChatFerry/spy/spy_types.h index b26d546..bd495f7 100644 --- a/WeChatFerry/spy/spy_types.h +++ b/WeChatFerry/spy/spy_types.h @@ -12,15 +12,17 @@ typedef struct UserInfoCall { typedef struct RecvMsg { DWORD hook; // Hook地址 DWORD call; // Call地址 + DWORD msgId; // 消息ID地址 DWORD type; // 消息类型地址 DWORD isSelf; // 是否自己发送标志地址 - DWORD msgId; // 消息ID地址 - DWORD msgXml; // 消息xml内容地址 + DWORD ts; // TimeStamp DWORD roomId; // 群聊时,为群ID;私聊时,为微信ID - DWORD wxId; // 私聊时,为空;群聊时,为发送者微信ID DWORD content; // 消息内容地址 + DWORD wxid; // 私聊时,为空;群聊时,为发送者微信ID + DWORD sign; // Sign DWORD thumb; // 缩略图 DWORD extra; // 附加数据 + DWORD msgXml; // 消息xml内容地址 } RecvMsg_t; typedef struct SendText { diff --git a/WeChatFerry/spy/util.h b/WeChatFerry/spy/util.h index 5cf305f..11f03cd 100644 --- a/WeChatFerry/spy/util.h +++ b/WeChatFerry/spy/util.h @@ -9,6 +9,7 @@ #define WECHATINJECTDLL_DEBUG L"spy_debug.dll" #define GET_DWORD(addr) ((DWORD) * (DWORD *)(addr)) +#define GET_QWORD(addr) ((uint64_t) * (uint64_t *)(addr)) #define GET_STRING(addr) ((CHAR *)(*(DWORD *)(addr))) #define GET_WSTRING(addr) ((WCHAR *)(*(DWORD *)(addr))) #define GET_STRING_FROM_P(addr) ((CHAR *)(addr)) From fed3143b19843bff81af7a3421bc63b60d81c5ac Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 15:51:03 +0800 Subject: [PATCH 03/13] Impl FUNC_REFRESH_PYQ --- WeChatFerry/rpc/proto/wcf.proto | 1 + WeChatFerry/spy/Spy.vcxproj | 2 + WeChatFerry/spy/Spy.vcxproj.filters | 6 ++ WeChatFerry/spy/pyq.cpp | 70 +++++++++++++++++++++ WeChatFerry/spy/pyq.h | 5 ++ WeChatFerry/spy/receive_msg.cpp | 80 ++++++++++++++++++++++++ WeChatFerry/spy/receive_msg.h | 2 + WeChatFerry/spy/rpc_server.cpp | 32 +++++++++- clients/python/wcferry/client.py | 16 +++++ clients/python/wcferry/wcf_pb2.py | 96 ++++++++++++++--------------- clients/python/wcferry/wxmsg.py | 6 +- 11 files changed, 264 insertions(+), 52 deletions(-) create mode 100644 WeChatFerry/spy/pyq.cpp create mode 100644 WeChatFerry/spy/pyq.h diff --git a/WeChatFerry/rpc/proto/wcf.proto b/WeChatFerry/rpc/proto/wcf.proto index 157b147..d151296 100644 --- a/WeChatFerry/rpc/proto/wcf.proto +++ b/WeChatFerry/rpc/proto/wcf.proto @@ -22,6 +22,7 @@ enum Functions { FUNC_EXEC_DB_QUERY = 0x50; FUNC_ACCEPT_FRIEND = 0x51; FUNC_RECV_TRANSFER = 0x52; + FUNC_REFRESH_PYQ = 0x53; FUNC_DECRYPT_IMAGE = 0x60; FUNC_ADD_ROOM_MEMBERS = 0x70; FUNC_DEL_ROOM_MEMBERS = 0x71; diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj index e28f3f9..0b34661 100644 --- a/WeChatFerry/spy/Spy.vcxproj +++ b/WeChatFerry/spy/Spy.vcxproj @@ -233,6 +233,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto + @@ -257,6 +258,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto + diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters index 64edd64..967ac9d 100644 --- a/WeChatFerry/spy/Spy.vcxproj.filters +++ b/WeChatFerry/spy/Spy.vcxproj.filters @@ -90,6 +90,9 @@ 头文件 + + 头文件 + @@ -149,6 +152,9 @@ 源文件 + + 源文件 + diff --git a/WeChatFerry/spy/pyq.cpp b/WeChatFerry/spy/pyq.cpp new file mode 100644 index 0000000..2ef946c --- /dev/null +++ b/WeChatFerry/spy/pyq.cpp @@ -0,0 +1,70 @@ +#include "framework.h" + +#include "spy_types.h" +#include "util.h" + +extern WxCalls_t g_WxCalls; +extern DWORD g_WeChatWinDllAddr; + +typedef struct RawVector { + DWORD start; + DWORD finish; + DWORD end; +} RawVector_t; + +static int GetFirstPage() +{ + int rv = -1; + DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680; + DWORD pyqCall2 = g_WeChatWinDllAddr + 0x14E2140; + + char buf[0xB44] = { 0 }; + __asm { + pushad; + call pyqCall1; + push 0x1; + lea ecx, buf; + push ecx; + mov ecx, eax; + call pyqCall2; + mov rv, eax; + popad; + } + + return rv; +} + +static int GetNextPage(uint64_t id) +{ + int rv = -1; + DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680; + DWORD pyqCall3 = g_WeChatWinDllAddr + 0x14E21E0; + + RawVector_t tmp = { 0 }; + + __asm { + pushad; + call pyqCall1; + lea ecx, tmp; + push ecx; + mov ebx, dword ptr [id + 0x04]; + push ebx; + mov edi, dword ptr [id] + push edi; + mov ecx, eax; + call pyqCall3; + mov rv, eax; + popad; + } + + return rv; +} + +int RefreshPyq(uint64_t id) +{ + if (id == 0) { + return GetFirstPage(); + } + + return GetNextPage(id); +} diff --git a/WeChatFerry/spy/pyq.h b/WeChatFerry/spy/pyq.h new file mode 100644 index 0000000..13d7eeb --- /dev/null +++ b/WeChatFerry/spy/pyq.h @@ -0,0 +1,5 @@ +#pragma once + +#include "stdint.h" + +int RefreshPyq(uint64_t id); diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index 1f69c1b..f54eb7d 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -6,6 +6,7 @@ #include #include "load_calls.h" +#include "log.h" #include "receive_msg.h" #include "user_info.h" #include "util.h" @@ -26,9 +27,16 @@ static DWORD recvMsgCallAddr = 0; static DWORD recvMsgJumpBackAddr = 0; static CHAR recvMsgBackupCode[5] = { 0 }; +static DWORD recvPyqHookAddr = 0; +static DWORD recvPyqCallAddr = 0; +static DWORD recvPyqJumpBackAddr = 0; +static CHAR recvPyqBackupCode[5] = { 0 }; +static bool gIsListeningPyq = false; + MsgTypes_t GetMsgTypes() { const MsgTypes_t m = { + { 0x00, "朋友圈消息" }, { 0x01, "文字" }, { 0x03, "图片" }, { 0x22, "语音" }, @@ -164,6 +172,7 @@ void ListenMessage() HookAddress(recvMsgHookAddr, RecieveMsgFunc, recvMsgBackupCode); gIsListening = true; + ListenPyq(); } void UnListenMessage() @@ -173,4 +182,75 @@ void UnListenMessage() } UnHookAddress(recvMsgHookAddr, recvMsgBackupCode); gIsListening = false; + UnListenPyq(); +} + +void DispatchPyq(DWORD reg) +{ + DWORD startAddr = *(DWORD *)(reg + 0x20); + DWORD endAddr = *(DWORD *)(reg + 0x24); + + if (startAddr == 0) { + return; + } + + while (startAddr < endAddr) { + WxMsg_t wxMsg; + + wxMsg.type = 0x00; // 朋友圈消息 + wxMsg.is_self = 0x00; + wxMsg.id = GET_QWORD(startAddr); + wxMsg.ts = GET_DWORD(startAddr + 0x2C); + wxMsg.xml = GetStringByWstrAddr(startAddr + 0x384); + wxMsg.sender = GetStringByWstrAddr(startAddr + 0x18); + wxMsg.content = GetStringByWstrAddr(startAddr + 0x3C); + + { + unique_lock lock(gMutex); + gMsgQueue.push(wxMsg); // 推送到队列 + } + + gCV.notify_all(); // 通知各方消息就绪 + + startAddr += 0xB48; + } +} + +__declspec(naked) void RecievePyqFunc() +{ + __asm { + pushad + pushfd + push [esp + 0x24] + call DispatchPyq + add esp, 0x4 + popfd + popad + call recvPyqCallAddr // 这个为被覆盖的call + jmp recvPyqJumpBackAddr // 跳回被HOOK指令的下一条指令 + } +} + +void ListenPyq() +{ + if (gIsListeningPyq || (g_WeChatWinDllAddr == 0)) { + return; + } + + recvPyqHookAddr = g_WeChatWinDllAddr + 0x14F9E15; + recvPyqCallAddr = g_WeChatWinDllAddr + 0x14FA0A0; + recvPyqJumpBackAddr = recvPyqHookAddr + 5; + + HookAddress(recvPyqHookAddr, RecievePyqFunc, recvPyqBackupCode); + gIsListeningPyq = true; +} + +void UnListenPyq() +{ + if (!gIsListeningPyq) { + return; + } + + UnHookAddress(recvPyqHookAddr, recvPyqBackupCode); + gIsListeningPyq = false; } diff --git a/WeChatFerry/spy/receive_msg.h b/WeChatFerry/spy/receive_msg.h index f878c18..ff17170 100644 --- a/WeChatFerry/spy/receive_msg.h +++ b/WeChatFerry/spy/receive_msg.h @@ -2,6 +2,8 @@ #include "pb_types.h" +void ListenPyq(); +void UnListenPyq(); void ListenMessage(); void UnListenMessage(); MsgTypes_t GetMsgTypes(); diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 09de304..3682340 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -23,6 +23,7 @@ #include "log.h" #include "pb_types.h" #include "pb_util.h" +#include "pyq.h" #include "receive_msg.h" #include "receive_transfer.h" #include "rpc_server.h" @@ -345,16 +346,18 @@ static void PushMessage() 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.id = (char *)wxmsg.id.c_str(); - rsp.msg.wxmsg.xml = (char *)wxmsg.xml.c_str(); - rsp.msg.wxmsg.sender = (char *)wxmsg.sender.c_str(); + 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("Recv msg: {}", wxmsg.content); pb_ostream_t stream = pb_ostream_from_buffer(buffer, G_BUF_SIZE); @@ -486,6 +489,24 @@ bool func_receive_transfer(char *wxid, char *tfid, char *taid, uint8_t *out, siz return true; } +bool func_refresh_pyq(uint64_t id, 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; +} + bool func_decrypt_image(char *src, char *dst, uint8_t *out, size_t *len) { Response rsp = Response_init_default; @@ -652,6 +673,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len ret = func_receive_transfer(req.msg.tf.wxid, req.msg.tf.tfid, req.msg.tf.taid, out, out_len); break; } + case Functions_FUNC_REFRESH_PYQ: { + LOG_DEBUG("[Functions_FUNC_REFRESH_PYQ]"); + ret = func_refresh_pyq(req.msg.ui64, out, out_len); + break; + } case Functions_FUNC_DECRYPT_IMAGE: { LOG_DEBUG("[FUNCTIONS_FUNC_DECRYPT_IMAGE]"); ret = func_decrypt_image(req.msg.dec.src, req.msg.dec.dst, out, out_len); diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 5ef85b4..6919dca 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -526,6 +526,22 @@ class Wcf(): rsp = self._send_request(req) return rsp.status + def refresh_pyq(self, id: int) -> int: + """刷新朋友圈 + + Args: + id (int): 开始 id + + Returns: + int: 1 为成功,其他失败 + """ + req = wcf_pb2.Request() + req.func = wcf_pb2.FUNC_REFRESH_PYQ # FUNC_REFRESH_PYQ + req.ui64 = id + rsp = self._send_request(req) + return rsp.status + + def decrypt_image(self, src: str, dst: str) -> bool: """解密图片: diff --git a/clients/python/wcferry/wcf_pb2.py b/clients/python/wcferry/wcf_pb2.py index 94b36fc..c5a543f 100644 --- a/clients/python/wcferry/wcf_pb2.py +++ b/clients/python/wcferry/wcf_pb2.py @@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xc8\x02\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.AddMembersH\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\x42\x05\n\x03msg\"\xab\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\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xa0\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\x0c\n\x04type\x18\x03 \x01(\x05\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0b\n\x03xml\x18\x05 \x01(\t\x12\x0e\n\x06sender\x18\x06 \x01(\t\x12\x0e\n\x06roomid\x18\x07 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x08 \x01(\t\x12\r\n\x05thumb\x18\t \x01(\t\x12\r\n\x05\x65xtra\x18\n \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(\x05\"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\nAddMembers\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*\xee\x03\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\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\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\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xd8\x02\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.AddMembersH\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\x0e\n\x04ui64\x18\x0c \x01(\x04H\x00\x42\x05\n\x03msg\"\xab\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\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xba\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\n\n\x02id\x18\x03 \x01(\x04\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(\x05\"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\nAddMembers\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*\xb9\x04\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\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\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x18\n\x14\x46UNC_ENABLE_RECV_PYQ\x10\x31\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x19\n\x15\x46UNC_DISABLE_RECV_PYQ\x10\x41\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\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', globals()) @@ -23,52 +23,52 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._serialized_options = b'\n\013com.iamteer' _MSGTYPES_TYPESENTRY._options = None _MSGTYPES_TYPESENTRY._serialized_options = b'8\001' - _FUNCTIONS._serialized_start=1820 - _FUNCTIONS._serialized_end=2314 + _FUNCTIONS._serialized_start=1862 + _FUNCTIONS._serialized_end=2431 _REQUEST._serialized_start=19 - _REQUEST._serialized_end=347 - _RESPONSE._serialized_start=350 - _RESPONSE._serialized_end=649 - _EMPTY._serialized_start=651 - _EMPTY._serialized_end=658 - _WXMSG._serialized_start=661 - _WXMSG._serialized_end=821 - _TEXTMSG._serialized_start=823 - _TEXTMSG._serialized_end=878 - _PATHMSG._serialized_start=880 - _PATHMSG._serialized_end=921 - _XMLMSG._serialized_start=923 - _XMLMSG._serialized_end=994 - _MSGTYPES._serialized_start=996 - _MSGTYPES._serialized_end=1093 - _MSGTYPES_TYPESENTRY._serialized_start=1049 - _MSGTYPES_TYPESENTRY._serialized_end=1093 - _RPCCONTACT._serialized_start=1096 - _RPCCONTACT._serialized_end=1231 - _RPCCONTACTS._serialized_start=1233 - _RPCCONTACTS._serialized_end=1281 - _DBNAMES._serialized_start=1283 - _DBNAMES._serialized_end=1307 - _DBTABLE._serialized_start=1309 - _DBTABLE._serialized_end=1345 - _DBTABLES._serialized_start=1347 - _DBTABLES._serialized_end=1387 - _DBQUERY._serialized_start=1389 - _DBQUERY._serialized_end=1423 - _DBFIELD._serialized_start=1425 - _DBFIELD._serialized_end=1481 - _DBROW._serialized_start=1483 - _DBROW._serialized_end=1520 - _DBROWS._serialized_start=1522 - _DBROWS._serialized_end=1556 - _VERIFICATION._serialized_start=1558 - _VERIFICATION._serialized_end=1611 - _ADDMEMBERS._serialized_start=1613 - _ADDMEMBERS._serialized_end=1656 - _USERINFO._serialized_start=1658 - _USERINFO._serialized_end=1726 - _DECPATH._serialized_start=1728 - _DECPATH._serialized_end=1763 - _TRANSFER._serialized_start=1765 - _TRANSFER._serialized_end=1817 + _REQUEST._serialized_end=363 + _RESPONSE._serialized_start=366 + _RESPONSE._serialized_end=665 + _EMPTY._serialized_start=667 + _EMPTY._serialized_end=674 + _WXMSG._serialized_start=677 + _WXMSG._serialized_end=863 + _TEXTMSG._serialized_start=865 + _TEXTMSG._serialized_end=920 + _PATHMSG._serialized_start=922 + _PATHMSG._serialized_end=963 + _XMLMSG._serialized_start=965 + _XMLMSG._serialized_end=1036 + _MSGTYPES._serialized_start=1038 + _MSGTYPES._serialized_end=1135 + _MSGTYPES_TYPESENTRY._serialized_start=1091 + _MSGTYPES_TYPESENTRY._serialized_end=1135 + _RPCCONTACT._serialized_start=1138 + _RPCCONTACT._serialized_end=1273 + _RPCCONTACTS._serialized_start=1275 + _RPCCONTACTS._serialized_end=1323 + _DBNAMES._serialized_start=1325 + _DBNAMES._serialized_end=1349 + _DBTABLE._serialized_start=1351 + _DBTABLE._serialized_end=1387 + _DBTABLES._serialized_start=1389 + _DBTABLES._serialized_end=1429 + _DBQUERY._serialized_start=1431 + _DBQUERY._serialized_end=1465 + _DBFIELD._serialized_start=1467 + _DBFIELD._serialized_end=1523 + _DBROW._serialized_start=1525 + _DBROW._serialized_end=1562 + _DBROWS._serialized_start=1564 + _DBROWS._serialized_end=1598 + _VERIFICATION._serialized_start=1600 + _VERIFICATION._serialized_end=1653 + _ADDMEMBERS._serialized_start=1655 + _ADDMEMBERS._serialized_end=1698 + _USERINFO._serialized_start=1700 + _USERINFO._serialized_end=1768 + _DECPATH._serialized_start=1770 + _DECPATH._serialized_end=1805 + _TRANSFER._serialized_start=1807 + _TRANSFER._serialized_end=1859 # @@protoc_insertion_point(module_scope) diff --git a/clients/python/wcferry/wxmsg.py b/clients/python/wcferry/wxmsg.py index f71ed3f..37d521d 100644 --- a/clients/python/wcferry/wxmsg.py +++ b/clients/python/wcferry/wxmsg.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import re +from datetime import datetime from wcferry import wcf_pb2 @@ -24,6 +25,8 @@ class WxMsg(): self._is_group = msg.is_group self.type = msg.type self.id = msg.id + self.ts = msg.ts + self.sign = msg.sign self.xml = msg.xml self.sender = msg.sender self.roomid = msg.roomid @@ -33,7 +36,8 @@ class WxMsg(): def __str__(self) -> str: s = f"{'自己发的:' if self._is_self else ''}" - s += f"{self.sender}[{self.roomid}]:{self.id}:{self.type}:{self.xml.replace(chr(10), '').replace(chr(9),'')}\n" + s += f"{self.sender}[{self.roomid}]|{self.id}|{datetime.fromtimestamp(self.ts)}|{self.type}|{self.sign}" + s += f"\n{self.xml.replace(chr(10), '').replace(chr(9),'')}\n" s += self.content s += f"\n{self.thumb}" if self.thumb else "" s += f"\n{self.extra}" if self.extra else "" From 2d5097ee3d1b096619219855b79a5d7cb76ad5b9 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 18:35:53 +0800 Subject: [PATCH 04/13] Add PYQ control flag --- WeChatFerry/rpc/proto/wcf.proto | 1 + WeChatFerry/spy/pyq.cpp | 7 +++ WeChatFerry/spy/receive_msg.cpp | 5 +- WeChatFerry/spy/rpc_server.cpp | 12 +++- clients/python/wcferry/client.py | 6 +- clients/python/wcferry/wcf_pb2.py | 96 +++++++++++++++---------------- 6 files changed, 69 insertions(+), 58 deletions(-) diff --git a/WeChatFerry/rpc/proto/wcf.proto b/WeChatFerry/rpc/proto/wcf.proto index d151296..5cdc36b 100644 --- a/WeChatFerry/rpc/proto/wcf.proto +++ b/WeChatFerry/rpc/proto/wcf.proto @@ -44,6 +44,7 @@ message Request DecPath dec = 10; Transfer tf = 11; uint64 ui64 = 12; // 64 位整数,通用 + bool flag = 13; } } diff --git a/WeChatFerry/spy/pyq.cpp b/WeChatFerry/spy/pyq.cpp index 2ef946c..45c9d5a 100644 --- a/WeChatFerry/spy/pyq.cpp +++ b/WeChatFerry/spy/pyq.cpp @@ -1,8 +1,10 @@ #include "framework.h" +#include "log.h" #include "spy_types.h" #include "util.h" +extern bool gIsListeningPyq; extern WxCalls_t g_WxCalls; extern DWORD g_WeChatWinDllAddr; @@ -62,6 +64,11 @@ static int GetNextPage(uint64_t id) int RefreshPyq(uint64_t id) { + if (!gIsListeningPyq) { + LOG_ERROR("没有启动朋友圈消息接收,参考:enable_receiving_msg"); + return -1; + } + if (id == 0) { return GetFirstPage(); } diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index f54eb7d..48bf5d4 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -12,7 +12,7 @@ #include "util.h" // Defined in rpc_server.cpp -extern bool gIsListening; +extern bool gIsListening, gIsListeningPyq; extern mutex gMutex; extern condition_variable gCV; extern queue gMsgQueue; @@ -31,7 +31,6 @@ static DWORD recvPyqHookAddr = 0; static DWORD recvPyqCallAddr = 0; static DWORD recvPyqJumpBackAddr = 0; static CHAR recvPyqBackupCode[5] = { 0 }; -static bool gIsListeningPyq = false; MsgTypes_t GetMsgTypes() { @@ -172,7 +171,6 @@ void ListenMessage() HookAddress(recvMsgHookAddr, RecieveMsgFunc, recvMsgBackupCode); gIsListening = true; - ListenPyq(); } void UnListenMessage() @@ -182,7 +180,6 @@ void UnListenMessage() } UnHookAddress(recvMsgHookAddr, recvMsgBackupCode); gIsListening = false; - UnListenPyq(); } void DispatchPyq(DWORD reg) diff --git a/WeChatFerry/spy/rpc_server.cpp b/WeChatFerry/spy/rpc_server.cpp index 3682340..32a1686 100644 --- a/WeChatFerry/spy/rpc_server.cpp +++ b/WeChatFerry/spy/rpc_server.cpp @@ -39,7 +39,8 @@ extern int IsLogin(void); // Defined in spy.cpp -bool gIsListening; +bool gIsListening = false; +bool gIsListeningPyq = false; mutex gMutex; condition_variable gCV; queue gMsgQueue; @@ -377,7 +378,7 @@ static void PushMessage() nng_close(msg_sock); } -bool func_enable_recv_txt(uint8_t *out, size_t *len) +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; @@ -385,6 +386,9 @@ bool func_enable_recv_txt(uint8_t *out, size_t *len) rsp.msg.status = -1; ListenMessage(); + if (pyq) { + ListenPyq(); + } HANDLE msgThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PushMessage, NULL, NULL, NULL); if (msgThread != 0) { CloseHandle(msgThread); @@ -408,6 +412,7 @@ bool func_disable_recv_txt(uint8_t *out, size_t *len) rsp.which_msg = Response_status_tag; rsp.msg.status = 0; + UnListenPyq(); UnListenMessage(); // 可能需要1秒之后才能退出,见 PushMessage pb_ostream_t stream = pb_ostream_from_buffer(out, *len); @@ -650,7 +655,8 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len #endif case Functions_FUNC_ENABLE_RECV_TXT: { LOG_DEBUG("[Functions_FUNC_ENABLE_RECV_TXT]"); - ret = func_enable_recv_txt(out, out_len); + LOG_BUFFER(in, in_len); + ret = func_enable_recv_txt(req.msg.flag, out, out_len); break; } case Functions_FUNC_DISABLE_RECV_TXT: { diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 6919dca..7fd3b31 100644 --- a/clients/python/wcferry/client.py +++ b/clients/python/wcferry/client.py @@ -358,7 +358,7 @@ class Wcf(): """ return self.msgQ.get(block, timeout=1) - def enable_receiving_msg(self) -> bool: + def enable_receiving_msg(self, pyq=False) -> bool: """允许接收消息,成功后通过 `get_msg` 读取消息""" def listening_msg(): rsp = wcf_pb2.Response() @@ -379,13 +379,14 @@ class Wcf(): req = wcf_pb2.Request() req.func = wcf_pb2.FUNC_ENABLE_RECV_TXT # FUNC_ENABLE_RECV_TXT + req.flag = pyq rsp = self._send_request(req) if rsp.status != 0: return False self._is_receiving_msg = True # 阻塞,把控制权交给用户 - # self._rpc_get_message(callback) + # self.listening_msg(callback) # 不阻塞,启动一个新的线程来接收消息 Thread(target=listening_msg, name="GetMessage", daemon=True).start() @@ -541,7 +542,6 @@ class Wcf(): rsp = self._send_request(req) return rsp.status - def decrypt_image(self, src: str, dst: str) -> bool: """解密图片: diff --git a/clients/python/wcferry/wcf_pb2.py b/clients/python/wcferry/wcf_pb2.py index c5a543f..ba142c9 100644 --- a/clients/python/wcferry/wcf_pb2.py +++ b/clients/python/wcferry/wcf_pb2.py @@ -13,7 +13,7 @@ _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xd8\x02\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.AddMembersH\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\x0e\n\x04ui64\x18\x0c \x01(\x04H\x00\x42\x05\n\x03msg\"\xab\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\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xba\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\n\n\x02id\x18\x03 \x01(\x04\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(\x05\"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\nAddMembers\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*\xb9\x04\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\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\x18\n\x14\x46UNC_ENABLE_RECV_TXT\x10\x30\x12\x18\n\x14\x46UNC_ENABLE_RECV_PYQ\x10\x31\x12\x19\n\x15\x46UNC_DISABLE_RECV_TXT\x10@\x12\x19\n\x15\x46UNC_DISABLE_RECV_PYQ\x10\x41\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\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\twcf.proto\x12\x03wcf\"\xe8\x02\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.AddMembersH\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\x0e\n\x04ui64\x18\x0c \x01(\x04H\x00\x12\x0e\n\x04\x66lag\x18\r \x01(\x08H\x00\x42\x05\n\x03msg\"\xab\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\x42\x05\n\x03msg\"\x07\n\x05\x45mpty\"\xba\x01\n\x05WxMsg\x12\x0f\n\x07is_self\x18\x01 \x01(\x08\x12\x10\n\x08is_group\x18\x02 \x01(\x08\x12\n\n\x02id\x18\x03 \x01(\x04\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(\x05\"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\nAddMembers\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*\x84\x04\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\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\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\x16\n\x12\x46UNC_DECRYPT_IMAGE\x10`\x12\x19\n\x15\x46UNC_ADD_ROOM_MEMBERS\x10p\x12\x19\n\x15\x46UNC_DEL_ROOM_MEMBERS\x10qB\r\n\x0b\x63om.iamteerb\x06proto3') _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'wcf_pb2', globals()) @@ -23,52 +23,52 @@ if _descriptor._USE_C_DESCRIPTORS == False: DESCRIPTOR._serialized_options = b'\n\013com.iamteer' _MSGTYPES_TYPESENTRY._options = None _MSGTYPES_TYPESENTRY._serialized_options = b'8\001' - _FUNCTIONS._serialized_start=1862 - _FUNCTIONS._serialized_end=2431 + _FUNCTIONS._serialized_start=1878 + _FUNCTIONS._serialized_end=2394 _REQUEST._serialized_start=19 - _REQUEST._serialized_end=363 - _RESPONSE._serialized_start=366 - _RESPONSE._serialized_end=665 - _EMPTY._serialized_start=667 - _EMPTY._serialized_end=674 - _WXMSG._serialized_start=677 - _WXMSG._serialized_end=863 - _TEXTMSG._serialized_start=865 - _TEXTMSG._serialized_end=920 - _PATHMSG._serialized_start=922 - _PATHMSG._serialized_end=963 - _XMLMSG._serialized_start=965 - _XMLMSG._serialized_end=1036 - _MSGTYPES._serialized_start=1038 - _MSGTYPES._serialized_end=1135 - _MSGTYPES_TYPESENTRY._serialized_start=1091 - _MSGTYPES_TYPESENTRY._serialized_end=1135 - _RPCCONTACT._serialized_start=1138 - _RPCCONTACT._serialized_end=1273 - _RPCCONTACTS._serialized_start=1275 - _RPCCONTACTS._serialized_end=1323 - _DBNAMES._serialized_start=1325 - _DBNAMES._serialized_end=1349 - _DBTABLE._serialized_start=1351 - _DBTABLE._serialized_end=1387 - _DBTABLES._serialized_start=1389 - _DBTABLES._serialized_end=1429 - _DBQUERY._serialized_start=1431 - _DBQUERY._serialized_end=1465 - _DBFIELD._serialized_start=1467 - _DBFIELD._serialized_end=1523 - _DBROW._serialized_start=1525 - _DBROW._serialized_end=1562 - _DBROWS._serialized_start=1564 - _DBROWS._serialized_end=1598 - _VERIFICATION._serialized_start=1600 - _VERIFICATION._serialized_end=1653 - _ADDMEMBERS._serialized_start=1655 - _ADDMEMBERS._serialized_end=1698 - _USERINFO._serialized_start=1700 - _USERINFO._serialized_end=1768 - _DECPATH._serialized_start=1770 - _DECPATH._serialized_end=1805 - _TRANSFER._serialized_start=1807 - _TRANSFER._serialized_end=1859 + _REQUEST._serialized_end=379 + _RESPONSE._serialized_start=382 + _RESPONSE._serialized_end=681 + _EMPTY._serialized_start=683 + _EMPTY._serialized_end=690 + _WXMSG._serialized_start=693 + _WXMSG._serialized_end=879 + _TEXTMSG._serialized_start=881 + _TEXTMSG._serialized_end=936 + _PATHMSG._serialized_start=938 + _PATHMSG._serialized_end=979 + _XMLMSG._serialized_start=981 + _XMLMSG._serialized_end=1052 + _MSGTYPES._serialized_start=1054 + _MSGTYPES._serialized_end=1151 + _MSGTYPES_TYPESENTRY._serialized_start=1107 + _MSGTYPES_TYPESENTRY._serialized_end=1151 + _RPCCONTACT._serialized_start=1154 + _RPCCONTACT._serialized_end=1289 + _RPCCONTACTS._serialized_start=1291 + _RPCCONTACTS._serialized_end=1339 + _DBNAMES._serialized_start=1341 + _DBNAMES._serialized_end=1365 + _DBTABLE._serialized_start=1367 + _DBTABLE._serialized_end=1403 + _DBTABLES._serialized_start=1405 + _DBTABLES._serialized_end=1445 + _DBQUERY._serialized_start=1447 + _DBQUERY._serialized_end=1481 + _DBFIELD._serialized_start=1483 + _DBFIELD._serialized_end=1539 + _DBROW._serialized_start=1541 + _DBROW._serialized_end=1578 + _DBROWS._serialized_start=1580 + _DBROWS._serialized_end=1614 + _VERIFICATION._serialized_start=1616 + _VERIFICATION._serialized_end=1669 + _ADDMEMBERS._serialized_start=1671 + _ADDMEMBERS._serialized_end=1714 + _USERINFO._serialized_start=1716 + _USERINFO._serialized_end=1784 + _DECPATH._serialized_start=1786 + _DECPATH._serialized_end=1821 + _TRANSFER._serialized_start=1823 + _TRANSFER._serialized_end=1875 # @@protoc_insertion_point(module_scope) From 4a60e3b5f1a3b846303f4c5d3b14e54dbb5e140f Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 18:36:50 +0800 Subject: [PATCH 05/13] Remove remote demo --- clients/python/demo.py | 1 - 1 file changed, 1 deletion(-) diff --git a/clients/python/demo.py b/clients/python/demo.py index fd6d76f..4b42186 100644 --- a/clients/python/demo.py +++ b/clients/python/demo.py @@ -25,7 +25,6 @@ def process_msg(wcf: Wcf): def main(): LOG.info("Start demo...") wcf = Wcf(debug=True) # 默认连接本地服务 - # wcf = Wcf("tcp://127.0.0.1:10086") # 连接远端服务 sleep(5) # 等微信加载好,以免信息显示异常 LOG.info(f"已经登录: {True if wcf.is_login() else False}") From 85a31e21e4c5cff3a18de1a9fd740ff4d1379aa5 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 19:08:38 +0800 Subject: [PATCH 06/13] Refactoring --- WeChatFerry/spy/load_calls.cpp | 5 ++++- WeChatFerry/spy/pyq.cpp | 8 ++++---- WeChatFerry/spy/receive_msg.cpp | 18 +++++++++--------- WeChatFerry/spy/spy_types.h | 16 ++++++++++++++++ 4 files changed, 33 insertions(+), 14 deletions(-) diff --git a/WeChatFerry/spy/load_calls.cpp b/WeChatFerry/spy/load_calls.cpp index 014b951..b2551ad 100644 --- a/WeChatFerry/spy/load_calls.cpp +++ b/WeChatFerry/spy/load_calls.cpp @@ -24,7 +24,10 @@ WxCalls_t wxCalls = { { 0xA17D50, 0xF59E40, 0xA18BD0, 0xA17E70 }, // Accept New Friend application { 0x78CF20, 0xF59E40, 0xBD1DC0 }, // Add chatroom members { 0x78CF20, 0xF59E40, 0xBD22A0 }, // Delete chatroom members - { 0x7B2E60, 0x15E2C20, 0x79C250 } // Receive transfer + { 0x7B2E60, 0x15E2C20, 0x79C250 }, // Receive transfer + /* Receive PYQ + hook, call, call1, call2, call3, start, end, ts, wxid, content, xml, step*/ + { 0x14F9E15, 0x14FA0A0, 0xC39680, 0x14E2140, 0x14E21E0, 0x20, 0x24, 0x2C, 0x18, 0x3C, 0x384, 0xB48 } }; int LoadCalls(const wchar_t *version, WxCalls_t *calls) diff --git a/WeChatFerry/spy/pyq.cpp b/WeChatFerry/spy/pyq.cpp index 45c9d5a..7172318 100644 --- a/WeChatFerry/spy/pyq.cpp +++ b/WeChatFerry/spy/pyq.cpp @@ -17,8 +17,8 @@ typedef struct RawVector { static int GetFirstPage() { int rv = -1; - DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680; - DWORD pyqCall2 = g_WeChatWinDllAddr + 0x14E2140; + DWORD pyqCall1 = g_WeChatWinDllAddr + g_WxCalls.pyq.call1; + DWORD pyqCall2 = g_WeChatWinDllAddr + g_WxCalls.pyq.call2; char buf[0xB44] = { 0 }; __asm { @@ -39,8 +39,8 @@ static int GetFirstPage() static int GetNextPage(uint64_t id) { int rv = -1; - DWORD pyqCall1 = g_WeChatWinDllAddr + 0xC39680; - DWORD pyqCall3 = g_WeChatWinDllAddr + 0x14E21E0; + DWORD pyqCall1 = g_WeChatWinDllAddr + g_WxCalls.pyq.call1; + DWORD pyqCall3 = g_WeChatWinDllAddr + g_WxCalls.pyq.call3; RawVector_t tmp = { 0 }; diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index 48bf5d4..aa49ad1 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -184,8 +184,8 @@ void UnListenMessage() void DispatchPyq(DWORD reg) { - DWORD startAddr = *(DWORD *)(reg + 0x20); - DWORD endAddr = *(DWORD *)(reg + 0x24); + DWORD startAddr = *(DWORD *)(reg + g_WxCalls.pyq.start); + DWORD endAddr = *(DWORD *)(reg + g_WxCalls.pyq.end); if (startAddr == 0) { return; @@ -197,10 +197,10 @@ void DispatchPyq(DWORD reg) wxMsg.type = 0x00; // 朋友圈消息 wxMsg.is_self = 0x00; wxMsg.id = GET_QWORD(startAddr); - wxMsg.ts = GET_DWORD(startAddr + 0x2C); - wxMsg.xml = GetStringByWstrAddr(startAddr + 0x384); - wxMsg.sender = GetStringByWstrAddr(startAddr + 0x18); - wxMsg.content = GetStringByWstrAddr(startAddr + 0x3C); + wxMsg.ts = GET_DWORD(startAddr + g_WxCalls.pyq.ts); + wxMsg.xml = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.xml); + wxMsg.sender = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.wxid); + wxMsg.content = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.content); { unique_lock lock(gMutex); @@ -209,7 +209,7 @@ void DispatchPyq(DWORD reg) gCV.notify_all(); // 通知各方消息就绪 - startAddr += 0xB48; + startAddr += g_WxCalls.pyq.step; } } @@ -234,8 +234,8 @@ void ListenPyq() return; } - recvPyqHookAddr = g_WeChatWinDllAddr + 0x14F9E15; - recvPyqCallAddr = g_WeChatWinDllAddr + 0x14FA0A0; + recvPyqHookAddr = g_WeChatWinDllAddr + g_WxCalls.pyq.hook; + recvPyqCallAddr = g_WeChatWinDllAddr + g_WxCalls.pyq.call; recvPyqJumpBackAddr = recvPyqHookAddr + 5; HookAddress(recvPyqHookAddr, RecievePyqFunc, recvPyqBackupCode); diff --git a/WeChatFerry/spy/spy_types.h b/WeChatFerry/spy/spy_types.h index bd495f7..a279714 100644 --- a/WeChatFerry/spy/spy_types.h +++ b/WeChatFerry/spy/spy_types.h @@ -87,6 +87,21 @@ typedef struct TF { DWORD call3; } TF_t; +typedef struct Pyq { + DWORD hook; + DWORD call; + DWORD call1; + DWORD call2; + DWORD call3; + DWORD start; + DWORD end; + DWORD ts; + DWORD wxid; + DWORD content; + DWORD xml; + DWORD step; +} Pyq_t; + typedef struct WxCalls { DWORD login; // 登录状态 UserInfoCall_t ui; // 用户信息 @@ -102,6 +117,7 @@ typedef struct WxCalls { RoomMember_t arm; // 添加群成员 RoomMember_t drm; // 删除群成员 TF_t tf; // 接收转账 + Pyq_t pyq; // 接收朋友圈消息 } WxCalls_t; typedef struct WxString { From 3e3531d867371f68d111f85f5d797930c7abf0a3 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 19:30:32 +0800 Subject: [PATCH 07/13] v39.0.1 --- README.MD | 8 ++++++-- WeChatFerry/spy/spy.aps | Bin 2504 -> 2612 bytes WeChatFerry/spy/spy.rc | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.MD b/README.MD index c470ea5..5a6700a 100644 --- a/README.MD +++ b/README.MD @@ -182,6 +182,12 @@ Rust 客户端。 ## 版本更新 + +### v39.0.1 (2023.07.16) +* 获取朋友圈消息 + +
点击查看更多 + 客户端越来越多了,版本号开始混乱,所以重新定义了版本号:`w.x.y.z`。 其中: @@ -190,8 +196,6 @@ Rust 客户端。 * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 -
点击查看 - ### v39.0.0 (2023.07.14) 升级到 `3.9.2.23`。 diff --git a/WeChatFerry/spy/spy.aps b/WeChatFerry/spy/spy.aps index e053b40df7c6d93e48d0225c4f6fe8f7fb5aaa07..9d97a0307a5df0480afffaa13fb4ee63d5049329 100644 GIT binary patch delta 218 zcmX>hyhUV!0;9o1MP-Q^4h9BitC)bI{H)@b@KooF#1gmEqN2);0Sb(ay^{+WQ#B3E z7z`Q0g5$$ngMvN%{o+0S-2AcXnHV6!XUd?$@c%yuGcYiGntYehce40nADQvep1u#_ShZ8GzOaOrFmo3IL$8LO=ij delta 180 zcmdlYazc250;9@AMP(iV4h9BitC)bI{H)@QF$#=~Zj);nQ`uD*{{LrSxHZ|3$#?TK zCVgf`fyu{M9T*ulv$HWXG8$~=W}nK)s4{sWhdN`=#7cQqHwFfVDU;uF$g|!8vKS@{ zaw;=1Fi-YlmSdG*WMIgc90?TI!ZNvz(}?K{kYbgYe2!C$(P8pNPT9$`ScMeUFflNs VurdIx6#!zO79cPXU;(-g0sv+VD diff --git a/WeChatFerry/spy/spy.rc b/WeChatFerry/spy/spy.rc index abf36e5..4ed927e 100644 --- a/WeChatFerry/spy/spy.rc +++ b/WeChatFerry/spy/spy.rc @@ -51,7 +51,7 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 39,0,0,0 + FILEVERSION 39,0,1,0 PRODUCTVERSION 3,9,2,23 FILEFLAGSMASK 0x3fL #ifdef _DEBUG @@ -69,7 +69,7 @@ BEGIN BEGIN VALUE "CompanyName", "WeChatFerry" VALUE "FileDescription", "WeChatFerry" - VALUE "FileVersion", "39.0.0.0" + VALUE "FileVersion", "39.0.1.0" VALUE "InternalName", "spy.dll" VALUE "LegalCopyright", "Copyright (C) 2023" VALUE "OriginalFilename", "spy.dll" From 7f89bf7dd450c5eb2dd3b4a15859f77410aa96f8 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 19:42:45 +0800 Subject: [PATCH 08/13] v39.0.1.0 --- clients/python/README.MD | 21 +++++++++++++++------ clients/python/demo.py | 6 +++++- clients/python/wcferry/client.py | 6 +++--- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/clients/python/README.MD b/clients/python/README.MD index ec2d9e0..4150fe1 100644 --- a/clients/python/README.MD +++ b/clients/python/README.MD @@ -39,7 +39,7 @@ def process_msg(wcf: Wcf): def main(): LOG.info("Start demo...") - wcf = Wcf(debug=True) + wcf = Wcf(debug=True) # 默认连接本地服务 sleep(5) # 等微信加载好,以免信息显示异常 LOG.info(f"已经登录: {True if wcf.is_login() else False}") @@ -49,7 +49,7 @@ def main(): # wcf.enable_recv_msg(LOG.info) # deprecated # 允许接收消息 - wcf.enable_receiving_msg() + wcf.enable_receiving_msg(pyq=True) # 同时允许接收朋友圈消息 Thread(target=process_msg, name="GetMessage", args=(wcf,), daemon=True).start() # wcf.disable_recv_msg() # 当需要停止接收消息时调用 @@ -71,7 +71,7 @@ def main(): sleep(5) LOG.info(f"DBs:\n{wcf.get_dbs()}") - LOG.info(f"Tables:\n{wcf.get_tables('MicroMsg.db')}") + LOG.info(f"Tables:\n{wcf.get_tables('db')}") LOG.info(f"Results:\n{wcf.query_sql('MicroMsg.db', 'SELECT * FROM Contact LIMIT 1;')}") # 需要真正的 V3、V4 信息 @@ -85,6 +85,10 @@ def main(): # ret = wcf.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") # LOG.info(f"add_chatroom_members: {ret}") + sleep(5) + wcf.refresh_pyq(0) # 刷新朋友圈第一页 + # wcf.refresh_pyq(id) # 从 id 开始刷新朋友圈 + # 一直运行 wcf.keep_running() @@ -123,6 +127,11 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc ``` ## 版本更新 + +### 39.0.1.0 (2023.07.16) +* 获取朋友圈消息 + +
点击查看更多 版本号:`w.x.y.z`。 其中: @@ -131,9 +140,6 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 -### 39.0.0.1 (2023.07.15) -修复不能 @ 问题。 - 功能: * 检查登录状态 @@ -154,4 +160,7 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc * 添加群成员 * 删除群成员 * 解密图片 +* 获取朋友圈消息 * 某功能(Breaking Change) + +
diff --git a/clients/python/demo.py b/clients/python/demo.py index 4b42186..3363fe0 100644 --- a/clients/python/demo.py +++ b/clients/python/demo.py @@ -34,7 +34,7 @@ def main(): # wcf.enable_recv_msg(LOG.info) # deprecated # 允许接收消息 - wcf.enable_receiving_msg() + wcf.enable_receiving_msg(pyq=True) # 同时允许接收朋友圈消息 Thread(target=process_msg, name="GetMessage", args=(wcf,), daemon=True).start() # wcf.disable_recv_msg() # 当需要停止接收消息时调用 @@ -70,6 +70,10 @@ def main(): # ret = wcf.del_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...") # LOG.info(f"add_chatroom_members: {ret}") + sleep(5) + wcf.refresh_pyq(0) # 刷新朋友圈第一页 + # wcf.refresh_pyq(id) # 从 id 开始刷新朋友圈 + # 一直运行 wcf.keep_running() diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 7fd3b31..430eb1b 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.0.0.1" +__version__ = "39.0.1.0" import atexit import base64 @@ -527,11 +527,11 @@ class Wcf(): rsp = self._send_request(req) return rsp.status - def refresh_pyq(self, id: int) -> int: + def refresh_pyq(self, id: int = 0) -> int: """刷新朋友圈 Args: - id (int): 开始 id + id (int): 开始 id,0 为最新页 Returns: int: 1 为成功,其他失败 From a931204fe5be365c2379e67b0600cec8933043bc Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 21:27:38 +0800 Subject: [PATCH 09/13] Apply queue --- clients/http/wcfhttp/core.py | 62 +++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/clients/http/wcfhttp/core.py b/clients/http/wcfhttp/core.py index ec629dd..8a018eb 100644 --- a/clients/http/wcfhttp/core.py +++ b/clients/http/wcfhttp/core.py @@ -3,6 +3,8 @@ import base64 import logging +from queue import Empty +from threading import Thread from typing import Any import requests @@ -14,7 +16,9 @@ __version__ = "39.0.0.1" class Msg(BaseModel): - id: str + id: int + ts: int + sign: str type: int xml: str sender: str @@ -59,31 +63,45 @@ class Http(FastAPI): self.add_api_route("/transfer", self.receive_transfer, methods=["POST"], summary="接收转账") self.add_api_route("/dec-image", self.decrypt_image, methods=["POST"], summary="解密图片") - def _set_cb(self, cb): - def callback(msg: WxMsg): - data = {} - data["id"] = msg.id - data["type"] = msg.type - data["xml"] = msg.xml - data["sender"] = msg.sender - data["roomid"] = msg.roomid - data["content"] = msg.content - data["thumb"] = msg.thumb - data["extra"] = msg.extra - data["is_at"] = msg.is_at(self.wcf.self_wxid) - data["is_self"] = msg.from_self() - data["is_group"] = msg.from_group() + def _forward_msg(self, msg, cb): + data = {} + data["id"] = msg.id + data["ts"] = msg.ts + data["sign"] = msg.sign + data["type"] = msg.type + data["xml"] = msg.xml + data["sender"] = msg.sender + data["roomid"] = msg.roomid + data["content"] = msg.content + data["thumb"] = msg.thumb + data["extra"] = msg.extra + data["is_at"] = msg.is_at(self.wcf.self_wxid) + data["is_self"] = msg.from_self() + data["is_group"] = msg.from_group() - try: - rsp = requests.post(url=cb, json=data) - if rsp.status_code != 200: - self.LOG.error(f"消息转发失败,HTTP 状态码为: {rsp.status_code}") - except Exception as e: - self.LOG.error(f"消息转发异常: {e}") + try: + rsp = requests.post(url=cb, json=data) + if rsp.status_code != 200: + self.LOG.error(f"消息转发失败,HTTP 状态码为: {rsp.status_code}") + except Exception as e: + self.LOG.error(f"消息转发异常: {e}") + + def _set_cb(self, cb): + def callback(wcf: Wcf): + while wcf.is_receiving_msg(): + try: + msg = wcf.get_msg() + self.LOG.info(msg) + self._forward_msg(msg, cb) + except Empty: + continue # Empty message + except Exception as e: + self.LOG.error(f"Receiving message error: {e}") if cb: self.LOG.info(f"消息回调: {cb}") - self.wcf.enable_recv_msg(callback=callback) + self.wcf.enable_receiving_msg(pyq=True) # 同时允许接收朋友圈消息 + Thread(target=callback, name="GetMessage", args=(self.wcf,), daemon=True).start() else: self.LOG.info(f"没有设置回调,打印消息") self.wcf.enable_recv_msg(print) From e3ce7c037e370399050d39b38fa43267de462d24 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 21:59:27 +0800 Subject: [PATCH 10/13] v39.0.1.0 --- clients/http/setup.py | 2 +- clients/http/wcfhttp/core.py | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/clients/http/setup.py b/clients/http/setup.py index 68d0a17..5e3d4b4 100644 --- a/clients/http/setup.py +++ b/clients/http/setup.py @@ -33,7 +33,7 @@ setup( "setuptools", "fastapi", "uvicorn[standard]", - "wcferry>=39.0.0.0", + "wcferry>=39.0.1.0", ], classifiers=[ "Environment :: Win32 (MS Windows)", diff --git a/clients/http/wcfhttp/core.py b/clients/http/wcfhttp/core.py index 8a018eb..ea4e982 100644 --- a/clients/http/wcfhttp/core.py +++ b/clients/http/wcfhttp/core.py @@ -8,11 +8,11 @@ from threading import Thread from typing import Any import requests -from fastapi import Body, FastAPI +from fastapi import Body, Query, FastAPI from pydantic import BaseModel from wcferry import Wcf, WxMsg -__version__ = "39.0.0.1" +__version__ = "39.0.1.0" class Msg(BaseModel): @@ -50,6 +50,7 @@ class Http(FastAPI): self.add_api_route("/friends", self.get_friends, methods=["GET"], summary="获取好友列表") self.add_api_route("/dbs", self.get_dbs, methods=["GET"], summary="获取所有数据库") self.add_api_route("/{db}/tables", self.get_tables, methods=["GET"], summary="获取 db 中所有表") + self.add_api_route("/pyq/", self.refresh_pyq, methods=["GET"], summary="刷新朋友圈(数据从消息回调中查看)") self.add_api_route("/text", self.send_text, methods=["POST"], summary="发送文本消息") self.add_api_route("/image", self.send_image, methods=["POST"], summary="发送图片消息") @@ -59,10 +60,11 @@ class Http(FastAPI): self.add_api_route("/sql", self.query_sql, methods=["POST"], summary="执行 SQL,如果数据量大注意分页,以免 OOM") self.add_api_route("/new-friend", self.accept_new_friend, methods=["POST"], summary="通过好友申请") self.add_api_route("/chatroom-member", self.add_chatroom_members, methods=["POST"], summary="添加群成员") - self.add_api_route("/chatroom-member", self.del_chatroom_members, methods=["DELETE"], summary="删除群成员") self.add_api_route("/transfer", self.receive_transfer, methods=["POST"], summary="接收转账") self.add_api_route("/dec-image", self.decrypt_image, methods=["POST"], summary="解密图片") + self.add_api_route("/chatroom-member", self.del_chatroom_members, methods=["DELETE"], summary="删除群成员") + def _forward_msg(self, msg, cb): data = {} data["id"] = msg.id @@ -342,6 +344,18 @@ class Http(FastAPI): ret = self.wcf.receive_transfer(wxid, transferid, transactionid) return {"status": ret, "message": "成功"if ret == 1 else "失败"} + def refresh_pyq(self, id: int = Query(0, description="开始 id,0 为最新页")) -> dict: + """刷新朋友圈 + + Args: + id (int): 开始 id,0 为最新页 + + Returns: + int: 1 为成功,其他失败 + """ + ret = self.wcf.refresh_pyq(id) + return {"status": ret, "message": "成功"if ret == 1 else "失败"} + def decrypt_image(self, src: str = Body("C:\\...", description="加密的图片路径,从图片消息中获取"), dst: str = Body("C:\\...", description="解密的图片路径")) -> dict: From 60be22aca5bbdd29e6f88790d1cc7247a0cb49cc Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 23:27:43 +0800 Subject: [PATCH 11/13] Fix is_group --- WeChatFerry/spy/receive_msg.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/WeChatFerry/spy/receive_msg.cpp b/WeChatFerry/spy/receive_msg.cpp index aa49ad1..ca9abbf 100644 --- a/WeChatFerry/spy/receive_msg.cpp +++ b/WeChatFerry/spy/receive_msg.cpp @@ -194,13 +194,14 @@ void DispatchPyq(DWORD reg) while (startAddr < endAddr) { WxMsg_t wxMsg; - wxMsg.type = 0x00; // 朋友圈消息 - wxMsg.is_self = 0x00; - wxMsg.id = GET_QWORD(startAddr); - wxMsg.ts = GET_DWORD(startAddr + g_WxCalls.pyq.ts); - wxMsg.xml = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.xml); - wxMsg.sender = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.wxid); - wxMsg.content = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.content); + wxMsg.type = 0x00; // 朋友圈消息 + wxMsg.is_self = false; + wxMsg.is_group = false; + wxMsg.id = GET_QWORD(startAddr); + wxMsg.ts = GET_DWORD(startAddr + g_WxCalls.pyq.ts); + wxMsg.xml = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.xml); + wxMsg.sender = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.wxid); + wxMsg.content = GetStringByWstrAddr(startAddr + g_WxCalls.pyq.content); { unique_lock lock(gMutex); From bda368c4889d6672d031269a4e3d54cc81795ab9 Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 23:32:28 +0800 Subject: [PATCH 12/13] Bump to v39.0.2 --- README.MD | 7 +++++-- WeChatFerry/spy/spy.aps | Bin 2612 -> 2612 bytes WeChatFerry/spy/spy.rc | 4 ++-- clients/http/setup.py | 2 +- clients/http/wcfhttp/core.py | 2 +- clients/python/README.MD | 2 +- clients/python/wcferry/client.py | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/README.MD b/README.MD index 5a6700a..17c6b97 100644 --- a/README.MD +++ b/README.MD @@ -183,8 +183,8 @@ Rust 客户端。 ## 版本更新 -### v39.0.1 (2023.07.16) -* 获取朋友圈消息 +### v39.0.2 (2023.07.16) +* 修复朋友圈消息 `is_group` 为 `True` 问题
点击查看更多 @@ -196,6 +196,9 @@ Rust 客户端。 * `y` 是 `WeChatFerry` 的版本,从 0 开始 * `z` 是各客户端的版本,从 0 开始 +### v39.0.1 (2023.07.16) +* 获取朋友圈消息 + ### v39.0.0 (2023.07.14) 升级到 `3.9.2.23`。 diff --git a/WeChatFerry/spy/spy.aps b/WeChatFerry/spy/spy.aps index 9d97a0307a5df0480afffaa13fb4ee63d5049329..3c8ea0e12e229634bc1746f7c10e870406ca90c6 100644 GIT binary patch delta 21 ccmdlYvPEPA8yh3jW_C7aMn=39.0.1.0", + "wcferry>=39.0.2.0", ], classifiers=[ "Environment :: Win32 (MS Windows)", diff --git a/clients/http/wcfhttp/core.py b/clients/http/wcfhttp/core.py index ea4e982..ec95b60 100644 --- a/clients/http/wcfhttp/core.py +++ b/clients/http/wcfhttp/core.py @@ -12,7 +12,7 @@ from fastapi import Body, Query, FastAPI from pydantic import BaseModel from wcferry import Wcf, WxMsg -__version__ = "39.0.1.0" +__version__ = "39.0.2.0" class Msg(BaseModel): diff --git a/clients/python/README.MD b/clients/python/README.MD index 4150fe1..9e5b875 100644 --- a/clients/python/README.MD +++ b/clients/python/README.MD @@ -128,7 +128,7 @@ python -m grpc_tools.protoc --python_out=. --proto_path=../../../WeChatFerry/rpc ## 版本更新 -### 39.0.1.0 (2023.07.16) +### 39.0.2.0 (2023.07.16) * 获取朋友圈消息
点击查看更多 diff --git a/clients/python/wcferry/client.py b/clients/python/wcferry/client.py index 430eb1b..856c3ea 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.0.1.0" +__version__ = "39.0.2.0" import atexit import base64 From 5bdfb0b8d6ba04f756d88482bec927ac867d62ab Mon Sep 17 00:00:00 2001 From: Changhua Date: Sun, 16 Jul 2023 23:53:11 +0800 Subject: [PATCH 13/13] Update docs --- docs/source/_static/openapi-wcfhttp.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/_static/openapi-wcfhttp.json b/docs/source/_static/openapi-wcfhttp.json index 861e097..c1ff4d1 100644 --- a/docs/source/_static/openapi-wcfhttp.json +++ b/docs/source/_static/openapi-wcfhttp.json @@ -1 +1 @@ -{"openapi":"3.0.2","info":{"title":"WeChatFerry HTTP 客户端","description":"Github: WeChatFerry
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"碲矿\"\"赞赏\"
后台回复 WeChatFerry 加群交流如果你觉得有用
","version":"0.1.0"},"paths":{"/msg_cb":{"post":{"tags":["示例"],"summary":"接收消息回调样例","description":"示例回调方法,简单打印消息","operationId":"msg_cb_msg_cb_post","requestBody":{"content":{"application/json":{"schema":{"title":"Msg","allOf":[{"$ref":"#/components/schemas/Msg"}],"description":"微信消息"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/login":{"get":{"summary":"获取登录状态","description":"获取登录状态","operationId":"is_login_login_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Is Login Login Get","type":"object"}}}}}}},"/wxid":{"get":{"summary":"获取登录账号 wxid","description":"获取登录账号 wxid","operationId":"get_self_wxid_wxid_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Self Wxid Wxid Get","type":"object"}}}}}}},"/user-info":{"get":{"summary":"获取登录账号个人信息","description":"获取登录账号个人信息","operationId":"get_user_info_user_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get User Info User Info Get","type":"object"}}}}}}},"/msg-types":{"get":{"summary":"获取消息类型","description":"获取消息类型","operationId":"get_msg_types_msg_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Msg Types Msg Types Get","type":"object"}}}}}}},"/contacts":{"get":{"summary":"获取完整通讯录","description":"获取完整通讯录","operationId":"get_contacts_contacts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Contacts Contacts Get","type":"object"}}}}}}},"/friends":{"get":{"summary":"获取好友列表","description":"获取好友列表","operationId":"get_friends_friends_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Friends Friends Get","type":"object"}}}}}}},"/dbs":{"get":{"summary":"获取所有数据库","description":"获取所有数据库","operationId":"get_dbs_dbs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Dbs Dbs Get","type":"object"}}}}}}},"/{db}/tables":{"get":{"summary":"获取 db 中所有表","description":"获取 db 中所有表\n\nArgs:\n db (str): 数据库名(可通过 `get_dbs` 查询)\n\nReturns:\n List[dict]: `db` 下的所有表名及对应建表语句","operationId":"get_tables__db__tables_get","parameters":[{"required":true,"schema":{"title":"Db","type":"string"},"name":"db","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Tables Db Tables Get","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text":{"post":{"summary":"发送文本消息","description":"发送文本消息,可参考:https://github.com/lich0821/WeChatRobot/blob/master/robot.py 里 sendTextMsg\n\nArgs:\n msg (str): 要发送的消息,换行使用 `\\\\n`;如果 @ 人的话,需要带上跟 `aters` 里数量相同的 @\n receiver (str): 消息接收人,wxid 或者 roomid\n aters (str): 要 @ 的 wxid,多个用逗号分隔;`@所有人` 只需要 `notify@all`\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_text_text_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_text_text_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send Text Text Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/image":{"post":{"summary":"发送图片消息","description":"发送图片,非线程安全\n\nArgs:\n path (str): 图片路径,如:`C:/Projs/WeChatRobot/TEQuant.jpeg` 或 `https://raw.githubusercontent.com/lich0821/WeChatRobot/master/TEQuant.jpeg`\n receiver (str): 消息接收人,wxid 或者 roomid\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_image_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_image_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send Image Image Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/file":{"post":{"summary":"发送文件消息","description":"发送文件\n\nArgs:\n path (str): 本地文件路径,如:`C:/Projs/WeChatRobot/README.MD`\n receiver (str): 消息接收人,wxid 或者 roomid\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_file_file_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_file_file_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send File File Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sql":{"post":{"summary":"执行 SQL,如果数据量大注意分页,以免 OOM","description":"执行 SQL,如果数据量大注意分页,以免 OOM\n\nArgs:\n db (str): 要查询的数据库\n sql (str): 要执行的 SQL\n\nReturns:\n List[dict]: 查询结果","operationId":"query_sql_sql_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_query_sql_sql_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Query Sql Sql Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/new-friend":{"post":{"summary":"通过好友申请","description":"通过好友申请\n\nArgs:\n v3 (str): 加密用户名 (好友申请消息里 v3 开头的字符串)\n v4 (str): Ticket (好友申请消息里 v4 开头的字符串)\n scene: 申请方式 (好友申请消息里的 scene)\n\nReturns:\n int: 1 为成功,其他失败","operationId":"accept_new_friend_new_friend_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_accept_new_friend_new_friend_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Accept New Friend New Friend Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatroom-member":{"post":{"summary":"添加群成员","description":"添加群成员\n\nArgs:\n roomid (str): 待加群的 id\n wxids (str): 要加到群里的 wxid,多个用逗号分隔\n\nReturns:\n int: 1 为成功,其他失败","operationId":"add_chatroom_members_chatroom_member_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_add_chatroom_members_chatroom_member_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Add Chatroom Members Chatroom Member Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"删除群成员","description":"删除群成员\n\nArgs:\n roomid (str): 群的 id\n wxids (str): 要删除的 wxid,多个用逗号分隔\n\nReturns:\n int: 1 为成功,其他失败","operationId":"del_chatroom_members_chatroom_member_delete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_del_chatroom_members_chatroom_member_delete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Del Chatroom Members Chatroom Member Delete","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/transfer":{"post":{"summary":"接收转账","description":"接收转账\n\nArgs:\n wxid (str): 转账消息里的发送人 wxid\n transferid (str): 转账消息里的 transferid\n transactionid (str): 转账消息里的 transactionid\n\nReturns:\n int: 1 为成功,其他失败","operationId":"receive_transfer_transfer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_receive_transfer_transfer_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Receive Transfer Transfer Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dec-image":{"post":{"summary":"解密图片","description":"解密图片:\n\nArgs:\n src (str): 加密的图片路径\n dst (str): 解密的图片路径\n\nReturns:\n bool: 是否成功","operationId":"decrypt_image_dec_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_decrypt_image_dec_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Decrypt Image Dec Image Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_accept_new_friend_new_friend_post":{"title":"Body_accept_new_friend_new_friend_post","type":"object","properties":{"v3":{"title":"V3","type":"string","description":"加密用户名 (好友申请消息里 v3 开头的字符串)","default":"v3"},"v4":{"title":"V4","type":"string","description":"Ticket (好友申请消息里 v4 开头的字符串)","default":"v4"},"scene":{"title":"Scene","type":"integer","description":"申请方式 (好友申请消息里的 scene)","default":30}}},"Body_add_chatroom_members_chatroom_member_post":{"title":"Body_add_chatroom_members_chatroom_member_post","type":"object","properties":{"roomid":{"title":"Roomid","type":"string","description":"待加群的 id","default":"xxxxxxxx@chatroom"},"wxids":{"title":"Wxids","type":"string","description":"要加到群里的 wxid,多个用逗号分隔","default":"wxid_xxxxxxxxxxxxx"}}},"Body_decrypt_image_dec_image_post":{"title":"Body_decrypt_image_dec_image_post","type":"object","properties":{"src":{"title":"Src","type":"string","description":"加密的图片路径,从图片消息中获取","default":"C:\\..."},"dst":{"title":"Dst","type":"string","description":"解密的图片路径","default":"C:\\..."}}},"Body_del_chatroom_members_chatroom_member_delete":{"title":"Body_del_chatroom_members_chatroom_member_delete","type":"object","properties":{"roomid":{"title":"Roomid","type":"string","description":"群的 id","default":"xxxxxxxx@chatroom"},"wxids":{"title":"Wxids","type":"string","description":"要删除的 wxid,多个用逗号分隔","default":"wxid_xxxxxxxxxxxxx"}}},"Body_query_sql_sql_post":{"title":"Body_query_sql_sql_post","type":"object","properties":{"db":{"title":"Db","type":"string","description":"数据库","default":"MicroMsg.db"},"sql":{"title":"Sql","type":"string","description":"SQL 语句","default":"SELECT * FROM Contact LIMIT 1;"}}},"Body_receive_transfer_transfer_post":{"title":"Body_receive_transfer_transfer_post","type":"object","properties":{"wxid":{"title":"Wxid","type":"string","description":"转账消息里的发送人 wxid","default":"wxid_xxxxxxxxxxxxx"},"transferid":{"title":"Transferid","type":"string","description":"转账消息里的 transferid","default":"transferid"},"transactionid":{"title":"Transactionid","type":"string","description":"转账消息里的 transactionid","default":"transactionid"}}},"Body_send_file_file_post":{"title":"Body_send_file_file_post","type":"object","properties":{"path":{"title":"Path","type":"string","description":"本地文件路径,不支持网络路径","default":"C:\\Projs\\WeChatRobot\\TEQuant.jpeg"},"receiver":{"title":"Receiver","type":"string","description":"roomid 或者 wxid","default":"filehelper"}}},"Body_send_image_image_post":{"title":"Body_send_image_image_post","type":"object","properties":{"path":{"title":"Path","type":"string","description":"图片路径","default":"C:\\Projs\\WeChatRobot\\TEQuant.jpeg"},"receiver":{"title":"Receiver","type":"string","description":"消息接收者,roomid 或者 wxid","default":"filehelper"}}},"Body_send_text_text_post":{"title":"Body_send_text_text_post","required":["msg"],"type":"object","properties":{"msg":{"title":"Msg","type":"string","description":"要发送的消息,换行用\\n表示"},"receiver":{"title":"Receiver","type":"string","description":"消息接收者,roomid 或者 wxid","default":"filehelper"},"aters":{"title":"Aters","type":"string","description":"要 @ 的 wxid,多个用逗号分隔;@所有人 用 notify@all","default":""}}},"HTTPValidationError":{"title":"HTTPValidationError","type":"object","properties":{"detail":{"title":"Detail","type":"array","items":{"$ref":"#/components/schemas/ValidationError"}}}},"Msg":{"title":"Msg","required":["id","type","xml","sender","roomid","content","thumb","extra","is_at","is_self","is_group"],"type":"object","properties":{"id":{"title":"Id","type":"string"},"type":{"title":"Type","type":"integer"},"xml":{"title":"Xml","type":"string"},"sender":{"title":"Sender","type":"string"},"roomid":{"title":"Roomid","type":"string"},"content":{"title":"Content","type":"string"},"thumb":{"title":"Thumb","type":"string"},"extra":{"title":"Extra","type":"string"},"is_at":{"title":"Is At","type":"boolean"},"is_self":{"title":"Is Self","type":"boolean"},"is_group":{"title":"Is Group","type":"boolean"}}},"ValidationError":{"title":"ValidationError","required":["loc","msg","type"],"type":"object","properties":{"loc":{"title":"Location","type":"array","items":{"anyOf":[{"type":"string"},{"type":"integer"}]}},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}}}}}} \ No newline at end of file +{"openapi":"3.0.2","info":{"title":"WeChatFerry HTTP 客户端","description":"Github: WeChatFerry
\n\n\n\n\n\n\n\n\n\n\n\n\n
\"碲矿\"\"赞赏\"
后台回复 WeChatFerry 加群交流如果你觉得有用
","version":"0.1.0"},"paths":{"/msg_cb":{"post":{"tags":["示例"],"summary":"接收消息回调样例","description":"示例回调方法,简单打印消息","operationId":"msg_cb_msg_cb_post","requestBody":{"content":{"application/json":{"schema":{"title":"Msg","allOf":[{"$ref":"#/components/schemas/Msg"}],"description":"微信消息"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/login":{"get":{"summary":"获取登录状态","description":"获取登录状态","operationId":"is_login_login_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Is Login Login Get","type":"object"}}}}}}},"/wxid":{"get":{"summary":"获取登录账号 wxid","description":"获取登录账号 wxid","operationId":"get_self_wxid_wxid_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Self Wxid Wxid Get","type":"object"}}}}}}},"/user-info":{"get":{"summary":"获取登录账号个人信息","description":"获取登录账号个人信息","operationId":"get_user_info_user_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get User Info User Info Get","type":"object"}}}}}}},"/msg-types":{"get":{"summary":"获取消息类型","description":"获取消息类型","operationId":"get_msg_types_msg_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Msg Types Msg Types Get","type":"object"}}}}}}},"/contacts":{"get":{"summary":"获取完整通讯录","description":"获取完整通讯录","operationId":"get_contacts_contacts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Contacts Contacts Get","type":"object"}}}}}}},"/friends":{"get":{"summary":"获取好友列表","description":"获取好友列表","operationId":"get_friends_friends_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Friends Friends Get","type":"object"}}}}}}},"/dbs":{"get":{"summary":"获取所有数据库","description":"获取所有数据库","operationId":"get_dbs_dbs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Dbs Dbs Get","type":"object"}}}}}}},"/{db}/tables":{"get":{"summary":"获取 db 中所有表","description":"获取 db 中所有表\n\nArgs:\n db (str): 数据库名(可通过 `get_dbs` 查询)\n\nReturns:\n List[dict]: `db` 下的所有表名及对应建表语句","operationId":"get_tables__db__tables_get","parameters":[{"required":true,"schema":{"title":"Db","type":"string"},"name":"db","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Get Tables Db Tables Get","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/pyq/":{"get":{"summary":"刷新朋友圈(数据从消息回调中查看)","description":"刷新朋友圈\n\nArgs:\n id (int): 开始 id,0 为最新页\n\nReturns:\n int: 1 为成功,其他失败","operationId":"refresh_pyq_pyq__get","parameters":[{"description":"开始 id,0 为最新页","required":false,"schema":{"title":"Id","type":"integer","description":"开始 id,0 为最新页","default":0},"name":"id","in":"query"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Refresh Pyq Pyq Get","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/text":{"post":{"summary":"发送文本消息","description":"发送文本消息,可参考:https://github.com/lich0821/WeChatRobot/blob/master/robot.py 里 sendTextMsg\n\nArgs:\n msg (str): 要发送的消息,换行使用 `\\\\n`;如果 @ 人的话,需要带上跟 `aters` 里数量相同的 @\n receiver (str): 消息接收人,wxid 或者 roomid\n aters (str): 要 @ 的 wxid,多个用逗号分隔;`@所有人` 只需要 `notify@all`\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_text_text_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_text_text_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send Text Text Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/image":{"post":{"summary":"发送图片消息","description":"发送图片,非线程安全\n\nArgs:\n path (str): 图片路径,如:`C:/Projs/WeChatRobot/TEQuant.jpeg` 或 `https://raw.githubusercontent.com/lich0821/WeChatRobot/master/TEQuant.jpeg`\n receiver (str): 消息接收人,wxid 或者 roomid\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_image_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_image_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send Image Image Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/file":{"post":{"summary":"发送文件消息","description":"发送文件\n\nArgs:\n path (str): 本地文件路径,如:`C:/Projs/WeChatRobot/README.MD`\n receiver (str): 消息接收人,wxid 或者 roomid\n\nReturns:\n int: 0 为成功,其他失败","operationId":"send_file_file_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_send_file_file_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Send File File Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/sql":{"post":{"summary":"执行 SQL,如果数据量大注意分页,以免 OOM","description":"执行 SQL,如果数据量大注意分页,以免 OOM\n\nArgs:\n db (str): 要查询的数据库\n sql (str): 要执行的 SQL\n\nReturns:\n List[dict]: 查询结果","operationId":"query_sql_sql_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_query_sql_sql_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Query Sql Sql Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/new-friend":{"post":{"summary":"通过好友申请","description":"通过好友申请\n\nArgs:\n v3 (str): 加密用户名 (好友申请消息里 v3 开头的字符串)\n v4 (str): Ticket (好友申请消息里 v4 开头的字符串)\n scene: 申请方式 (好友申请消息里的 scene)\n\nReturns:\n int: 1 为成功,其他失败","operationId":"accept_new_friend_new_friend_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_accept_new_friend_new_friend_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Accept New Friend New Friend Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/chatroom-member":{"post":{"summary":"添加群成员","description":"添加群成员\n\nArgs:\n roomid (str): 待加群的 id\n wxids (str): 要加到群里的 wxid,多个用逗号分隔\n\nReturns:\n int: 1 为成功,其他失败","operationId":"add_chatroom_members_chatroom_member_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_add_chatroom_members_chatroom_member_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Add Chatroom Members Chatroom Member Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"删除群成员","description":"删除群成员\n\nArgs:\n roomid (str): 群的 id\n wxids (str): 要删除的 wxid,多个用逗号分隔\n\nReturns:\n int: 1 为成功,其他失败","operationId":"del_chatroom_members_chatroom_member_delete","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_del_chatroom_members_chatroom_member_delete"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Del Chatroom Members Chatroom Member Delete","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/transfer":{"post":{"summary":"接收转账","description":"接收转账\n\nArgs:\n wxid (str): 转账消息里的发送人 wxid\n transferid (str): 转账消息里的 transferid\n transactionid (str): 转账消息里的 transactionid\n\nReturns:\n int: 1 为成功,其他失败","operationId":"receive_transfer_transfer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_receive_transfer_transfer_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Receive Transfer Transfer Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dec-image":{"post":{"summary":"解密图片","description":"解密图片:\n\nArgs:\n src (str): 加密的图片路径\n dst (str): 解密的图片路径\n\nReturns:\n bool: 是否成功","operationId":"decrypt_image_dec_image_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Body_decrypt_image_dec_image_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Decrypt Image Dec Image Post","type":"object"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_accept_new_friend_new_friend_post":{"title":"Body_accept_new_friend_new_friend_post","type":"object","properties":{"v3":{"title":"V3","type":"string","description":"加密用户名 (好友申请消息里 v3 开头的字符串)","default":"v3"},"v4":{"title":"V4","type":"string","description":"Ticket (好友申请消息里 v4 开头的字符串)","default":"v4"},"scene":{"title":"Scene","type":"integer","description":"申请方式 (好友申请消息里的 scene)","default":30}}},"Body_add_chatroom_members_chatroom_member_post":{"title":"Body_add_chatroom_members_chatroom_member_post","type":"object","properties":{"roomid":{"title":"Roomid","type":"string","description":"待加群的 id","default":"xxxxxxxx@chatroom"},"wxids":{"title":"Wxids","type":"string","description":"要加到群里的 wxid,多个用逗号分隔","default":"wxid_xxxxxxxxxxxxx"}}},"Body_decrypt_image_dec_image_post":{"title":"Body_decrypt_image_dec_image_post","type":"object","properties":{"src":{"title":"Src","type":"string","description":"加密的图片路径,从图片消息中获取","default":"C:\\..."},"dst":{"title":"Dst","type":"string","description":"解密的图片路径","default":"C:\\..."}}},"Body_del_chatroom_members_chatroom_member_delete":{"title":"Body_del_chatroom_members_chatroom_member_delete","type":"object","properties":{"roomid":{"title":"Roomid","type":"string","description":"群的 id","default":"xxxxxxxx@chatroom"},"wxids":{"title":"Wxids","type":"string","description":"要删除的 wxid,多个用逗号分隔","default":"wxid_xxxxxxxxxxxxx"}}},"Body_query_sql_sql_post":{"title":"Body_query_sql_sql_post","type":"object","properties":{"db":{"title":"Db","type":"string","description":"数据库","default":"MicroMsg.db"},"sql":{"title":"Sql","type":"string","description":"SQL 语句","default":"SELECT * FROM Contact LIMIT 1;"}}},"Body_receive_transfer_transfer_post":{"title":"Body_receive_transfer_transfer_post","type":"object","properties":{"wxid":{"title":"Wxid","type":"string","description":"转账消息里的发送人 wxid","default":"wxid_xxxxxxxxxxxxx"},"transferid":{"title":"Transferid","type":"string","description":"转账消息里的 transferid","default":"transferid"},"transactionid":{"title":"Transactionid","type":"string","description":"转账消息里的 transactionid","default":"transactionid"}}},"Body_send_file_file_post":{"title":"Body_send_file_file_post","type":"object","properties":{"path":{"title":"Path","type":"string","description":"本地文件路径,不支持网络路径","default":"C:\\Projs\\WeChatRobot\\TEQuant.jpeg"},"receiver":{"title":"Receiver","type":"string","description":"roomid 或者 wxid","default":"filehelper"}}},"Body_send_image_image_post":{"title":"Body_send_image_image_post","type":"object","properties":{"path":{"title":"Path","type":"string","description":"图片路径","default":"C:\\Projs\\WeChatRobot\\TEQuant.jpeg"},"receiver":{"title":"Receiver","type":"string","description":"消息接收者,roomid 或者 wxid","default":"filehelper"}}},"Body_send_text_text_post":{"title":"Body_send_text_text_post","required":["msg"],"type":"object","properties":{"msg":{"title":"Msg","type":"string","description":"要发送的消息,换行用\\n表示"},"receiver":{"title":"Receiver","type":"string","description":"消息接收者,roomid 或者 wxid","default":"filehelper"},"aters":{"title":"Aters","type":"string","description":"要 @ 的 wxid,多个用逗号分隔;@所有人 用 notify@all","default":""}}},"HTTPValidationError":{"title":"HTTPValidationError","type":"object","properties":{"detail":{"title":"Detail","type":"array","items":{"$ref":"#/components/schemas/ValidationError"}}}},"Msg":{"title":"Msg","required":["id","ts","sign","type","xml","sender","roomid","content","thumb","extra","is_at","is_self","is_group"],"type":"object","properties":{"id":{"title":"Id","type":"integer"},"ts":{"title":"Ts","type":"integer"},"sign":{"title":"Sign","type":"string"},"type":{"title":"Type","type":"integer"},"xml":{"title":"Xml","type":"string"},"sender":{"title":"Sender","type":"string"},"roomid":{"title":"Roomid","type":"string"},"content":{"title":"Content","type":"string"},"thumb":{"title":"Thumb","type":"string"},"extra":{"title":"Extra","type":"string"},"is_at":{"title":"Is At","type":"boolean"},"is_self":{"title":"Is Self","type":"boolean"},"is_group":{"title":"Is Group","type":"boolean"}}},"ValidationError":{"title":"ValidationError","required":["loc","msg","type"],"type":"object","properties":{"loc":{"title":"Location","type":"array","items":{"anyOf":[{"type":"string"},{"type":"integer"}]}},"msg":{"title":"Message","type":"string"},"type":{"title":"Error Type","type":"string"}}}}}} \ No newline at end of file