diff --git a/python/demo.py b/python/demo.py
index aac393b..2fd8e42 100644
--- a/python/demo.py
+++ b/python/demo.py
@@ -3,6 +3,7 @@
import logging
from threading import Thread
+from time import sleep
from wcferry import Wcf
@@ -26,6 +27,7 @@ def main():
wcf = Wcf(debug=True) # 默认连接本地服务
# wcf = Wcf("tcp://127.0.0.1:10086") # 连接远端服务
+ sleep(5) # 等微信加载好,以免信息显示异常
LOG.info(f"已经登录: {True if wcf.is_login() else False}")
LOG.info(f"wxid: {wcf.get_self_wxid()}")
@@ -58,6 +60,10 @@ def main():
# ret = wcf.add_chatroom_members("chatroom id", "wxid1,wxid2,wxid3,...")
# LOG.info(f"add_chatroom_members: {ret}")
+ xml = '叮当药房,24小时服务,28分钟送药到家!叮当快药首家承诺范围内28分钟送药到家!叮当快药核心区域内7*24小时全天候服务,送药上门!叮当快药官网为您提供快捷便利,正品低价,安全放心的购药、送药服务体验。view330https://mp.weixin.qq.com/mp/waerrpage?appid=wxc2edadc87077fa2a&type=upgrade&upgradetype=3#wechat_redirect7f6f49d301ebf47100199b8a4fcf4de4gh_c2b88a38c424@app叮当快药 药店送药到家夜间买药0jpgda0e08f5c7259d03da150d5e7ca6d9503057020100044b30490201000204e4c0232702032f4ef20204a6bace6f02046401f62d042430326337303430352d333734332d343362652d623335322d6233333566623266376334620204012400030201000405004c5376000db26456caf243fbd4efb99058a01d660db26456caf243fbd4efb99058a01d66161558100100pages/index/index.htmlgh_c2b88a38c424@appwxc2edadc87077fa2a1972http://wx.qlogo.cn/mmhead/Q3auHgzwzM4727n0NQ0ZIPQPlfp15m1WLsnrXbo1kLhFGcolgLyc0A/9601_wxc2edadc87077fa2a_29177e9a9b918cb9e75964f80bb8f32e_1677849476_0wxid_eob5qfcrv4zd2201'
+ ret = wcf.send_xml("filehelper", xml, 0x21)
+ LOG.info(f"send_xml: {ret}")
+
# 一直运行
wcf.keep_running()
diff --git a/python/wcferry/client.py b/python/wcferry/client.py
index a15bd2a..c82d3a2 100644
--- a/python/wcferry/client.py
+++ b/python/wcferry/client.py
@@ -227,6 +227,18 @@ class Wcf():
rsp = self._send_request(req)
return rsp.status
+ def send_xml(self, receiver: str, xml: str, type: int, path: str = None) -> int:
+ """发送文件"""
+ req = wcf_pb2.Request()
+ req.func = wcf_pb2.FUNC_SEND_XML # FUNC_SEND_XML
+ req.xml.receiver = receiver
+ req.xml.content = xml
+ req.xml.type = type
+ if path:
+ req.xml.path = path
+ rsp = self._send_request(req)
+ return rsp.status
+
def get_msg(self, block=True) -> WxMsg:
return self.msgQ.get(block, timeout=1)
@@ -351,8 +363,8 @@ class Wcf():
friends = []
for cnt in self.get_contacts():
if (cnt.wxid.endswith("@chatroom") # 群聊
- or cnt.wxid.startswith("gh_") # 公众号
- or cnt.wxid in not_friends.keys() # 其他杂号
+ or cnt.wxid.startswith("gh_") # 公众号
+ or cnt.wxid in not_friends.keys() # 其他杂号
):
continue
friends.append(cnt)
diff --git a/rpc/proto/wcf.proto b/rpc/proto/wcf.proto
index b68bb61..8204025 100644
--- a/rpc/proto/wcf.proto
+++ b/rpc/proto/wcf.proto
@@ -31,6 +31,7 @@ enum Functions {
FUNC_SEND_TXT = 0x20;
FUNC_SEND_IMG = 0x21;
FUNC_SEND_FILE = 0x22;
+ FUNC_SEND_XML = 0x23;
FUNC_ENABLE_RECV_TXT = 0x30;
FUNC_DISABLE_RECV_TXT = 0x40;
FUNC_EXEC_DB_QUERY = 0x50;
@@ -50,6 +51,7 @@ message Request
DbQuery query = 6;
Verification v = 7;
AddMembers m = 8;
+ XmlMsg xml = 9;
}
}
@@ -96,6 +98,14 @@ message PathMsg
string receiver = 2; // 消息接收人
}
+message XmlMsg
+{
+ string receiver = 1; // 消息接收人
+ string content = 2; // xml 内容
+ string path = 3; // 图片路径
+ int32 type = 4; // 消息类型
+}
+
message MsgTypes { map types = 1; }
message RpcContact
diff --git a/spy/load_calls.cpp b/spy/load_calls.cpp
index 5efe9a5..529c237 100644
--- a/spy/load_calls.cpp
+++ b/spy/load_calls.cpp
@@ -11,8 +11,9 @@ WxCalls_t wxCalls = {
/* Receive Message:
Hook, call, type, self, id, msgXml, roomId, wxId, content */
{ 0x550F4C, 0xA96350, 0x38, 0x3C, 0x184, 0x1EC, 0x48, 0x170, 0x70 },
- { 0xBD780, 0x771980, 0x521640 }, // Send Image Message
- { 0xC3B70, 0x771980, 0x3ED8C0 }, // Send File Message
+ { 0xBD780, 0x771980, 0x521640 }, // Send Image Message
+ { 0xC3B70, 0x771980, 0x3ED8C0 }, // Send File Message
+ { 0xB8A70, 0x3ED5E0, 0x107F00, 0x3ED7B0, 0x2386FE4 }, // Send xml message
/* Get Contacts:
Base, head, wxId, Code, Name, Gender, Country, Province, City*/
{ 0x23668F4, 0x4C, 0x30, 0x44, 0x8C, 0x184, 0x1D0, 0x1E4, 0x1F8 },
diff --git a/spy/rpc_server.cpp b/spy/rpc_server.cpp
index b8c1c1a..a4dc2a4 100644
--- a/spy/rpc_server.cpp
+++ b/spy/rpc_server.cpp
@@ -234,6 +234,33 @@ bool func_send_file(char *path, char *receiver, uint8_t *out, size_t *len)
return true;
}
+bool func_send_xml(XmlMsg xml, uint8_t *out, size_t *len)
+{
+ Response rsp = Response_init_default;
+ rsp.func = Functions_FUNC_SEND_XML;
+ rsp.which_msg = Response_status_tag;
+ rsp.msg.status = 0;
+
+ if ((xml.content == NULL) || (xml.receiver == NULL)) {
+ rsp.msg.status = -1;
+ } else {
+ string receiver(xml.receiver);
+ string content(xml.content);
+ string path(xml.path ? xml.path : "");
+ uint32_t type = (uint32_t)xml.type;
+ SendXmlMessage(receiver, content, path, type);
+ }
+
+ pb_ostream_t stream = pb_ostream_from_buffer(out, *len);
+ if (!pb_encode(&stream, Response_fields, &rsp)) {
+ LOG_ERROR("Encoding failed: {}", PB_GET_ERROR(&stream));
+ return false;
+ }
+ *len = stream.bytes_written;
+
+ return true;
+}
+
static void PushMessage()
{
static nng_socket msg_sock;
@@ -417,7 +444,7 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
return false;
}
- LOG_DEBUG("Func: {}", (uint8_t)req.func);
+ LOG_DEBUG("Func: {:#x} Data: {}", (uint8_t)req.func, in_len);
switch (req.func) {
case Functions_FUNC_IS_LOGIN: {
LOG_DEBUG("[Functions_FUNC_IS_LOGIN]");
@@ -464,6 +491,11 @@ static bool dispatcher(uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len
ret = func_send_file(req.msg.file.path, req.msg.file.receiver, out, out_len);
break;
}
+ case Functions_FUNC_SEND_XML: {
+ LOG_DEBUG("[Functions_FUNC_SEND_XML]");
+ ret = func_send_xml(req.msg.xml, out, out_len);
+ break;
+ }
case Functions_FUNC_ENABLE_RECV_TXT: {
LOG_DEBUG("[Functions_FUNC_ENABLE_RECV_TXT]");
ret = func_enable_recv_txt(out, out_len);
@@ -527,7 +559,7 @@ static int RunServer()
break;
}
- LOG_BUFFER(in, in_len);
+ // LOG_BUFFER(in, in_len);
if (dispatcher(in, in_len, gBuffer, &out_len)) {
LOG_DEBUG("Send data length {}", out_len);
// LOG_BUFFER(gBuffer, out_len);
diff --git a/spy/send_msg.cpp b/spy/send_msg.cpp
index 95319a5..08b2849 100644
--- a/spy/send_msg.cpp
+++ b/spy/send_msg.cpp
@@ -9,12 +9,13 @@
extern HANDLE g_hEvent;
extern WxCalls_t g_WxCalls;
extern DWORD g_WeChatWinDllAddr;
+extern string GetSelfWxid(); // Defined in spy.cpp
void SendTextMessage(string wxid, string msg, string atWxids)
{
char buffer[0x3B0] = { 0 };
- WxString_t txtMsg = { 0 };
- WxString_t txtWxid = { 0 };
+ WxString_t wxMsg = { 0 };
+ WxString_t wxWxid = { 0 };
// 发送消息Call地址 = 微信基址 + 偏移
DWORD sendCallAddress = g_WeChatWinDllAddr + g_WxCalls.sendTextMsg;
@@ -22,13 +23,13 @@ void SendTextMessage(string wxid, string msg, string atWxids)
wstring wsWxid = String2Wstring(wxid);
wstring wsMsg = String2Wstring(msg);
- txtMsg.text = (wchar_t *)wsMsg.c_str();
- txtMsg.size = wsMsg.size();
- txtMsg.capacity = wsMsg.capacity();
+ wxMsg.text = (wchar_t *)wsMsg.c_str();
+ wxMsg.size = wsMsg.size();
+ wxMsg.capacity = wsMsg.capacity();
- txtWxid.text = (wchar_t *)wsWxid.c_str();
- txtWxid.size = wsWxid.size();
- txtWxid.capacity = wsWxid.capacity();
+ wxWxid.text = (wchar_t *)wsWxid.c_str();
+ wxWxid.size = wsWxid.size();
+ wxWxid.capacity = wsWxid.capacity();
vector vTxtAtWxids;
if (!atWxids.empty()) {
@@ -51,9 +52,9 @@ void SendTextMessage(string wxid, string msg, string atWxids)
lea eax, vTxtAtWxids;
push 0x01;
push eax;
- lea edi, txtMsg;
+ lea edi, wxMsg;
push edi;
- lea edx, txtWxid;
+ lea edx, wxWxid;
lea ecx, buffer;
call sendCallAddress;
add esp, 0xC;
@@ -173,3 +174,82 @@ void SendFileMessage(string wxid, string path)
popad;
}
}
+
+void SendXmlMessage(string receiver, string xml, string path, int type)
+{
+ if (g_WeChatWinDllAddr == 0) {
+ return;
+ }
+
+ // 发送消息Call地址 = 微信基址 + 偏移
+ DWORD sendXmlCall1 = g_WeChatWinDllAddr + g_WxCalls.sendXml.call1;
+ DWORD sendXmlCall2 = g_WeChatWinDllAddr + g_WxCalls.sendXml.call2;
+ DWORD sendXmlCall3 = g_WeChatWinDllAddr + g_WxCalls.sendXml.call3;
+ DWORD sendXmlCall4 = g_WeChatWinDllAddr + g_WxCalls.sendXml.call4;
+ DWORD sendXmlParam = g_WeChatWinDllAddr + g_WxCalls.sendXml.param;
+
+ char buffer[0xFF0] = { 0 };
+ char nullBuf[0x1C] = { 0 };
+ WxString_t wxReceiver = { 0 };
+ WxString_t wxXml = { 0 };
+ WxString_t wxPath = { 0 };
+ WxString_t wxNull = { 0 };
+ WxString_t wxSender = { 0 };
+
+ wstring wsSender = String2Wstring(GetSelfWxid());
+ wstring wsReceiver = String2Wstring(receiver);
+ wstring wsXml = String2Wstring(xml);
+
+ wxReceiver.text = (wchar_t *)wsReceiver.c_str();
+ wxReceiver.size = wsReceiver.size();
+ wxReceiver.capacity = wsReceiver.capacity();
+
+ wxXml.text = (wchar_t *)wsXml.c_str();
+ wxXml.size = wsXml.size();
+ wxXml.capacity = wsXml.capacity();
+
+ wxSender.text = (wchar_t *)wsSender.c_str();
+ wxSender.size = wsSender.size();
+ wxSender.capacity = wsSender.capacity();
+
+ if (!path.empty()) {
+ wstring wsPath = String2Wstring(path);
+ wxPath.text = (wchar_t *)wsPath.c_str();
+ wxPath.size = wsPath.size();
+ wxPath.capacity = wsPath.capacity();
+ }
+
+ DWORD sendtype = type;
+ __asm {
+ pushad;
+ pushfd;
+ lea ecx, buffer;
+ call sendXmlCall1;
+ mov eax, [sendtype];
+ push eax;
+ lea eax, nullBuf;
+ lea edx, wxSender;
+ push eax;
+ lea eax, wxPath;
+ push eax;
+ lea eax, wxXml;
+ push eax;
+ lea edi, wxReceiver;
+ push edi;
+ lea ecx, buffer;
+ call sendXmlCall2;
+ add esp, 0x14;
+ lea eax, wxNull;
+ push eax;
+ lea ecx, buffer;
+ call sendXmlCall3;
+ mov dl, 0x0;
+ lea ecx, buffer;
+ push sendXmlParam;
+ push sendXmlParam;
+ call sendXmlCall4;
+ add esp, 0x8;
+ popfd;
+ popad;
+ }
+}
diff --git a/spy/send_msg.h b/spy/send_msg.h
index 9abac22..6706e10 100644
--- a/spy/send_msg.h
+++ b/spy/send_msg.h
@@ -7,3 +7,4 @@ using namespace std;
void SendTextMessage(string wxid, string msg, string atWxids);
void SendImageMessage(string wxid, string path);
void SendFileMessage(string wxid, string path);
+void SendXmlMessage(string receiver, string xml, string path, int type);
diff --git a/spy/spy_types.h b/spy/spy_types.h
index 0330dc3..162180a 100644
--- a/spy/spy_types.h
+++ b/spy/spy_types.h
@@ -59,6 +59,14 @@ typedef struct RoomMember {
DWORD call3;
} RoomMember_t;
+typedef struct Xml {
+ DWORD call1;
+ DWORD call2;
+ DWORD call3;
+ DWORD call4;
+ DWORD param;
+} Xml_t;
+
typedef struct WxCalls {
DWORD login; // 登录状态
UserInfoCall_t ui; // 用户信息
@@ -66,6 +74,7 @@ typedef struct WxCalls {
RecvMsg_t recvMsg; // 接收消息
Sendfile_t sendImg; // 发送图片
Sendfile_t sendFile; // 发送文件
+ Xml_t sendXml; // 发送XML
Contact_t contact; // 获取联系人
Sql_t sql; // 执行 SQL
NewFriend_t anf; // 通过好友申请