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 ""