未验证 提交 f68111cf 编写于 作者: J Jack Li 提交者: GitHub

Merge pull request #94 from ljc545w/add/msg-extrabuf

修改接收消息格式,消息内容添加扩展信息
...@@ -24,5 +24,6 @@ repos: ...@@ -24,5 +24,6 @@ repos:
exclude: | exclude: |
(?x)^( (?x)^(
.*?_i\.h| .*?_i\.h|
.*?_p\.c .*?_p\.c|
.*?\.md
) )
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
#include <winsock2.h> #include <winsock2.h>
#include <Ws2tcpip.h> #include <Ws2tcpip.h>
#include <map> #include <map>
#include "json/json.hpp"
using namespace nlohmann;
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
...@@ -24,19 +26,6 @@ using namespace std; ...@@ -24,19 +26,6 @@ using namespace std;
static int SRVPORT = 0; static int SRVPORT = 0;
struct ScoketMsgStruct
{
DWORD pid;
int messagetype;
BOOL isSendMessage;
unsigned long long msgid;
wchar_t sender[80];
wchar_t wxid[80];
wchar_t message[0x1000B];
wchar_t filepath[MAX_PATH];
wchar_t time[30];
};
// 是否开启接收消息HOOK标志 // 是否开启接收消息HOOK标志
BOOL ReceiveMessageHooked = false; BOOL ReceiveMessageHooked = false;
// 保存HOOK前的字节码,用于恢复 // 保存HOOK前的字节码,用于恢复
...@@ -56,10 +45,23 @@ static DWORD SendMessageNextCall = WeChatWinBase + SendMessageNextCallOffset; ...@@ -56,10 +45,23 @@ static DWORD SendMessageNextCall = WeChatWinBase + SendMessageNextCallOffset;
// 发送HOOK的跳转地址 // 发送HOOK的跳转地址
static DWORD SendMessageJmpBackAddress = SendMessageHookAddress + 0x5; static DWORD SendMessageJmpBackAddress = SendMessageHookAddress + 0x5;
struct SocketMessageStruct
{
char *buffer;
int length;
~SocketMessageStruct()
{
if (this->buffer != NULL)
{
delete[] this->buffer;
this->buffer = NULL;
}
}
};
// 通过socket将消息发送给服务端 // 通过socket将消息发送给服务端
BOOL SendSocketMessage(ReceiveMsgStruct *ms) BOOL SendSocketMessage(const char *buffer, size_t len)
{ {
shared_ptr<ReceiveMsgStruct> shared_ms(ms);
if (SRVPORT == 0) if (SRVPORT == 0)
{ {
return false; return false;
...@@ -89,21 +91,7 @@ BOOL SendSocketMessage(ReceiveMsgStruct *ms) ...@@ -89,21 +91,7 @@ BOOL SendSocketMessage(ReceiveMsgStruct *ms)
return false; return false;
} }
char recvbuf[1024] = {0}; char recvbuf[1024] = {0};
auto sms = std::make_shared<ScoketMsgStruct>(); int ret = send(clientsocket, buffer, len, 0);
ZeroMemory(sms.get(), sizeof(ScoketMsgStruct));
sms->pid = shared_ms->pid;
sms->messagetype = shared_ms->messagetype;
sms->isSendMessage = shared_ms->isSendMessage;
sms->msgid = shared_ms->msgid;
memcpy(sms->wxid, shared_ms->wxid.c_str(), shared_ms->wxid.length() * 2);
memcpy(sms->sender, shared_ms->sender.c_str(), shared_ms->sender.length() * 2);
memcpy(sms->message, shared_ms->message.c_str(), shared_ms->message.length() * 2);
memcpy(sms->filepath, shared_ms->filepath.c_str(), shared_ms->filepath.length() * 2);
memcpy(sms->time, shared_ms->time.c_str(), shared_ms->time.length() * 2);
#ifdef _DEBUG
wcout << sms->time << endl;
#endif
int ret = send(clientsocket, (char *)sms.get(), sizeof(ScoketMsgStruct), 0);
if (ret == -1 || ret == 0) if (ret == -1 || ret == 0)
{ {
#ifdef _DEBUG #ifdef _DEBUG
...@@ -126,83 +114,43 @@ BOOL SendSocketMessage(ReceiveMsgStruct *ms) ...@@ -126,83 +114,43 @@ BOOL SendSocketMessage(ReceiveMsgStruct *ms)
return true; return true;
} }
// 创建广播消息数组 void SendSocketMessageInThread(SocketMessageStruct *param)
#ifndef USE_SOCKET
static SAFEARRAY *CreateMessageArray(map<wstring, _variant_t> msg)
{ {
HRESULT hr = S_OK; if (param == NULL)
SAFEARRAY *psaValue; return;
vector<wstring> MessageInfoKey = { unique_ptr<SocketMessageStruct> sms(param);
L"pid", string jstr(param->buffer, param->length);
L"type", SendSocketMessage(jstr.c_str(), jstr.size());
L"isSendMessage",
L"msgid",
msg[L"isSendMessage"].boolVal ? L"sendto" : L"from",
L"wxid",
L"message",
L"filepath",
L"time"};
SAFEARRAYBOUND rgsaBound[2] = {{MessageInfoKey.size(), 0}, {2, 0}};
psaValue = SafeArrayCreate(VT_VARIANT, 2, rgsaBound);
long keyIndex[2] = {0, 0};
keyIndex[0] = 0;
keyIndex[1] = 0;
for (unsigned int i = 0; i < MessageInfoKey.size(); i++)
{
keyIndex[0] = i;
keyIndex[1] = 0;
_variant_t key = MessageInfoKey[i].c_str();
hr = SafeArrayPutElement(psaValue, keyIndex, &key);
keyIndex[0] = i;
keyIndex[1] = 1;
hr = SafeArrayPutElement(psaValue, keyIndex, &msg[MessageInfoKey[i]]);
}
return psaValue;
} }
#endif
static void dealMessage(DWORD messageAddr) static void dealMessage(DWORD messageAddr)
{ {
BOOL isSendMessage = *(BOOL *)(messageAddr + 0x3C); json jMsg;
ReceiveMsgStruct *message = new ReceiveMsgStruct; unsigned long long msgid = *(unsigned long long *)(messageAddr + 0x30);
ZeroMemory(message, sizeof(ReceiveMsgStruct)); jMsg["pid"] = GetCurrentProcessId();
message->pid = GetCurrentProcessId(); jMsg["type"] = *(DWORD *)(messageAddr + 0x38);
message->isSendMessage = isSendMessage; jMsg["isSendMsg"] = *(BOOL *)(messageAddr + 0x3C);
message->time = GetTimeW(*(DWORD *)(messageAddr + 0x44)); jMsg["msgid"] = msgid;
message->messagetype = *(DWORD *)(messageAddr + 0x38); jMsg["sender"] = unicode_to_utf8((wchar_t *)READ_WSTRING(messageAddr, 0x48).c_str());
message->msgid = *(unsigned long long *)(messageAddr + 0x30);
message->sender = READ_WSTRING(messageAddr, 0x48);
int length = *(DWORD *)(messageAddr + 0x170 + 0x4); int length = *(DWORD *)(messageAddr + 0x170 + 0x4);
if (length == 0) jMsg["wxid"] = length == 0 ? jMsg["sender"].get<std::string>() : unicode_to_utf8((wchar_t *)READ_WSTRING(messageAddr, 0x170).c_str());
{ jMsg["message"] = unicode_to_utf8((wchar_t *)READ_WSTRING(messageAddr, 0x70).c_str());
message->wxid = message->sender; jMsg["filepath"] = unicode_to_utf8((wchar_t *)READ_WSTRING(messageAddr, 0x1AC).c_str());
} string extrabuf = base64_encode((BYTE *)(*(DWORD *)(messageAddr + 0x8C)), *(DWORD *)(messageAddr + 0x8C + 0x4));
else jMsg["extrainfo"] = extrabuf;
{ jMsg["time"] = unicode_to_utf8((wchar_t *)GetTimeW(*(DWORD *)(messageAddr + 0x44)).c_str());
message->wxid = READ_WSTRING(messageAddr, 0x170); string jstr = jMsg.dump() + '\n';
}
message->message = READ_WSTRING(messageAddr, 0x70);
message->filepath = READ_WSTRING(messageAddr, 0x1AC);
#ifdef USE_COM #ifdef USE_COM
// 通过连接点,将消息广播给客户端 // 通过连接点,将消息广播给客户端
map<wstring, _variant_t> msg_map; VARIANT vsaValue = (_variant_t)jstr.c_str();
msg_map[L"pid"] = message->pid; PostComMessage(jMsg["pid"].get<int>(), WX_MESSAGE, msgid, &vsaValue);
msg_map[L"isSendMessage"] = isSendMessage;
msg_map[L"time"] = message->time.c_str();
msg_map[L"type"] = message->messagetype;
msg_map[L"msgid"] = message->msgid;
msg_map[L"sendto"] = message->sender.c_str();
msg_map[L"from"] = message->sender.c_str();
msg_map[L"wxid"] = message->wxid.c_str();
msg_map[L"message"] = message->message.c_str();
msg_map[L"filepath"] = message->filepath.c_str();
SAFEARRAY *psaValue = CreateMessageArray(msg_map);
VARIANT vsaValue;
vsaValue.vt = VT_ARRAY | VT_VARIANT;
V_ARRAY(&vsaValue) = psaValue;
PostComMessage(message->pid, WX_MESSAGE, message->msgid, &vsaValue);
#endif #endif
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessage, message, NULL, 0); // 为保证线程安全,需要手动管理内存
SocketMessageStruct *sms = new SocketMessageStruct;
sms->buffer = new char[jstr.size() + 1];
memcpy(sms->buffer, jstr.c_str(), jstr.size() + 1);
sms->length = jstr.size();
HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendSocketMessageInThread, sms, NULL, 0);
if (hThread) if (hThread)
{ {
CloseHandle(hThread); CloseHandle(hThread);
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
#include <typeinfo> #include <typeinfo>
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include "json/json.hpp"
using namespace nlohmann;
// 获取好友信息CALL1偏移 // 获取好友信息CALL1偏移
#define GetUserInfoCall1Offset 0x100BD5C0 - 0x10000000 #define GetUserInfoCall1Offset 0x100BD5C0 - 0x10000000
...@@ -34,50 +37,29 @@ struct GetUserInfoStruct ...@@ -34,50 +37,29 @@ struct GetUserInfoStruct
*/ */
static wstring WxUserInfo(DWORD address) static wstring WxUserInfo(DWORD address)
{ {
wstring wUserInfo = L""; json jData;
vector<DWORD> InfoType{ map<string, DWORD> key_addr_map;
address + 0x10, key_addr_map["wxId"] = address + 0x10;
address + 0x24, key_addr_map["wxNumber"] = address + 0x24;
address + 0x38, key_addr_map["wxV3"] = address + 0x38;
address + 0x58, key_addr_map["wxRemark"] = address + 0x58;
address + 0x6C, key_addr_map["wxNickName"] = address + 0x6C;
address + 0xFC, key_addr_map["wxBigAvatar"] = address + 0xFC;
address + 0x110, key_addr_map["wxSmallAvatar"] = address + 0x110;
address + 0x19C, key_addr_map["wxSignature"] = address + 0x19C;
address + 0x1B0, key_addr_map["wxNation"] = address + 0x1B0;
address + 0x1C4, key_addr_map["wxProvince"] = address + 0x1C4;
address + 0x1D8, key_addr_map["wxCity"] = address + 0x1D8;
address + 0x27C}; key_addr_map["wxBackground"] = address + 0x27C;
vector<wchar_t *> InfoTypeName{ for (auto it = key_addr_map.begin(); it != key_addr_map.end(); it++)
(WCHAR *)L"\"wxId\"",
(WCHAR *)L"\"wxNumber\"",
(WCHAR *)L"\"wxV3\"",
(WCHAR *)L"\"wxRemark\"",
(WCHAR *)L"\"wxNickName\"",
(WCHAR *)L"\"wxBigAvatar\"",
(WCHAR *)L"\"wxSmallAvatar\"",
(WCHAR *)L"\"wxSignature\"",
(WCHAR *)L"\"wxNation\"",
(WCHAR *)L"\"wxProvince\"",
(WCHAR *)L"\"wxCity\"",
(WCHAR *)L"\"wxBackground\"",
};
wUserInfo += L"{";
for (unsigned int i = 0; i < InfoType.size(); i++)
{ {
wchar_t *wstemp = ((*((DWORD *)InfoType[i])) != 0) ? (WCHAR *)(*((LPVOID *)InfoType[i])) : (WCHAR *)L"null"; string key = it->first;
wstring wsrtemp = wreplace(wstemp, L'\"', L"\\\""); DWORD addr = it->second;
wUserInfo = wUserInfo + InfoTypeName[i] + L":\"" + wsrtemp + L"\""; wstring wstemp = ((*((DWORD *)addr)) != 0) ? (wstring)(WCHAR *)(*((LPVOID *)addr)) : L"null";
if (i != InfoType.size() - 1) string value = unicode_to_utf8((wchar_t *)wstemp.c_str());
{ jData[key] = value;
wUserInfo += L",";
}
} }
wUserInfo += L"}"; wstring wUserInfo = utf8_to_unicode(jData.dump().c_str());
#ifdef _DEBUG
wcout.imbue(locale("chs"));
wcout << wUserInfo.c_str() << endl;
#endif
return wUserInfo; return wUserInfo;
} }
......
#include "pch.h" #include "pch.h"
#include <vector> #include "json/json.hpp"
#include <map>
using namespace nlohmann;
#define CheckLoginOffset 0x2366538 #define CheckLoginOffset 0x2366538
// 个人WXID偏移 // 个人WXID偏移
...@@ -11,159 +13,141 @@ ...@@ -11,159 +13,141 @@
* length:selfinfo字符串长度 * length:selfinfo字符串长度
*/ */
#ifndef USE_SOCKET #ifndef USE_SOCKET
struct SelfInfoStruct { struct SelfInfoStruct
DWORD message; {
DWORD length; DWORD message;
DWORD length;
} ret; } ret;
#endif // !USE_SOCKET #endif // !USE_SOCKET
/* /*
* 供外部调用的获取个人信息接口 * 供外部调用的获取个人信息接口
* return:DWORD,ret的首地址 * return:DWORD,ret的首地址
*/ */
#ifndef USE_SOCKET #ifndef USE_SOCKET
DWORD GetSelfInfoRemote() { DWORD GetSelfInfoRemote()
ZeroMemory(&ret, sizeof(SelfInfoStruct)); {
wstring selfinfo = GetSelfInfo(); ZeroMemory(&ret, sizeof(SelfInfoStruct));
wchar_t* message = new wchar_t[selfinfo.length() + 1]; wstring selfinfo = GetSelfInfo();
memcpy(message,selfinfo.c_str(),(selfinfo.length() + 1) * 2); wchar_t *message = new wchar_t[selfinfo.length() + 1];
ret.message = (DWORD)message; memcpy(message, selfinfo.c_str(), (selfinfo.length() + 1) * 2);
ret.length = selfinfo.length(); ret.message = (DWORD)message;
return (DWORD)&ret; ret.length = selfinfo.length();
return (DWORD)&ret;
} }
#endif #endif
wstring GetSelfWxid() { wstring GetSelfWxid()
DWORD baseAddr = GetWeChatWinBase() + SelfWxidAddrOffset; {
char wxidbuffer[0x100] = { 0 }; DWORD baseAddr = GetWeChatWinBase() + SelfWxidAddrOffset;
DWORD SelfWxIdAddr = 0x0; char wxidbuffer[0x100] = {0};
sprintf_s(wxidbuffer, "%s", (char*)baseAddr); DWORD SelfWxIdAddr = 0x0;
if (strlen(wxidbuffer) < 0x6 || strlen(wxidbuffer) > 0x14) sprintf_s(wxidbuffer, "%s", (char *)baseAddr);
{ if (strlen(wxidbuffer) < 0x6 || strlen(wxidbuffer) > 0x14)
SelfWxIdAddr = *(DWORD*)baseAddr; {
} SelfWxIdAddr = *(DWORD *)baseAddr;
else }
{ else
SelfWxIdAddr = baseAddr; {
} SelfWxIdAddr = baseAddr;
if (SelfWxIdAddr == 0) { }
return L""; if (SelfWxIdAddr == 0)
} {
char* sselfwxid = (char*)SelfWxIdAddr; return L"";
wchar_t* wselfwxid = new wchar_t[strlen(sselfwxid) + 1]; }
MultiByteToWideChar(CP_ACP, 0, sselfwxid, -1, wselfwxid, strlen(sselfwxid) + 1); char *sselfwxid = (char *)SelfWxIdAddr;
wstring wxid(wselfwxid); wchar_t *wselfwxid = new wchar_t[strlen(sselfwxid) + 1];
delete[] wselfwxid; MultiByteToWideChar(CP_ACP, 0, sselfwxid, -1, wselfwxid, strlen(sselfwxid) + 1);
return wxid; wstring wxid(wselfwxid);
delete[] wselfwxid;
return wxid;
} }
/* /*
* 获取个人信息 * 获取个人信息
*/ */
wstring GetSelfInfo() { wstring GetSelfInfo()
wstring selfinfo = L""; {
DWORD WeChatWinBase = GetWeChatWinBase(); json jData;
vector<DWORD> SelfInfoAddr = { map<string, DWORD> self_info_addr;
WeChatWinBase + 0x236607C, DWORD WeChatWinBase = GetWeChatWinBase();
WeChatWinBase + 0x2366548, self_info_addr["wxId"] = WeChatWinBase + 0x236607C;
WeChatWinBase + 0x23660F4, self_info_addr["wxNumber"] = WeChatWinBase + 0x2366548;
WeChatWinBase + 0x23661F8, self_info_addr["wxNickName"] = WeChatWinBase + 0x23660F4;
*(DWORD*)(WeChatWinBase + 0x236622C), self_info_addr["Sex"] = WeChatWinBase + 0x23661F8;
*(DWORD*)(WeChatWinBase + 0x23A111C), self_info_addr["wxSignature"] = *(DWORD *)(WeChatWinBase + 0x236622C);
*(DWORD*)(WeChatWinBase + 0x23663D4), self_info_addr["wxBigAvatar"] = *(DWORD *)(WeChatWinBase + 0x23A111C);
WeChatWinBase + 0x23662E8, self_info_addr["wxSmallAvatar"] = *(DWORD *)(WeChatWinBase + 0x23663D4);
WeChatWinBase + 0x23661FC, self_info_addr["wxNation"] = WeChatWinBase + 0x23662E8;
WeChatWinBase + 0x2366214, self_info_addr["wxProvince"] = WeChatWinBase + 0x23661FC;
WeChatWinBase + 0x2366128 self_info_addr["wxCity"] = WeChatWinBase + 0x2366214;
}; self_info_addr["PhoneNumber"] = WeChatWinBase + 0x2366128;
for (auto it = self_info_addr.begin(); it != self_info_addr.end(); it++)
vector<wstring> SelfInfoKey = { {
L"\"wxId\"", string key = it->first;
L"\"wxNumber\"", DWORD addr = it->second;
L"\"wxNickName\"", string temp;
L"\"Sex\"", if (!key.compare("wxNickName"))
L"\"wxSignature\"", {
L"\"wxBigAvatar\"", if (*(DWORD *)(addr + 0x14) == 0xF)
L"\"wxSmallAvatar\"", {
L"\"wxNation\"", temp = (*((DWORD *)addr) != 0) ? string((char *)addr) : gb2312_to_utf8("null");
L"\"wxProvince\"", }
L"\"wxCity\"", else
L"\"PhoneNumber\"" {
}; temp = (*((DWORD *)addr) != 0) ? string((char *)(*(DWORD *)addr)) : gb2312_to_utf8("null");
}
selfinfo = selfinfo + L"{"; }
for (unsigned int i = 0; i < SelfInfoAddr.size(); i++) { else if (!key.compare("wxId"))
selfinfo = selfinfo + SelfInfoKey[i] + L":"; {
selfinfo = selfinfo + L"\""; char wxidbuffer[0x100] = {0};
char* temp = NULL; sprintf_s(wxidbuffer, "%s", (char *)addr);
if (!SelfInfoKey[i].compare(L"\"wxNickName\"")) { if (strlen(wxidbuffer) < 0x6 || strlen(wxidbuffer) > 0x14)
if (*(DWORD*)(SelfInfoAddr[i] + 0x14) == 0xF) { {
temp = (*((DWORD*)SelfInfoAddr[i]) != 0) ? (char*)SelfInfoAddr[i] : (char*)"null"; //新的微信号 微信ID用地址保存
} temp = string((char *)(*(DWORD *)addr));
else { }
temp = (*((DWORD*)SelfInfoAddr[i]) != 0) ? (char*)(*(DWORD*)SelfInfoAddr[i]) : (char*)"null"; else
} {
} temp = string((char *)addr);
else if (!SelfInfoKey[i].compare(L"\"wxId\"")) { }
char wxidbuffer[0x100] = { 0 }; }
sprintf_s(wxidbuffer, "%s", (char*)SelfInfoAddr[i]); else if (!key.compare("Sex"))
if (strlen(wxidbuffer) < 0x6 || strlen(wxidbuffer) > 0x14) {
{ int sex = *(int *)addr;
//新的微信号 微信ID用地址保存 switch (sex)
temp = (char*)(*(DWORD*)SelfInfoAddr[i]); {
} case 1:
else {
{ temp = gb2312_to_utf8("男");
temp = (char*)SelfInfoAddr[i]; break;
} }
} case 2:
else if (!SelfInfoKey[i].compare(L"\"Sex\"")) { {
int sex = *(int*)SelfInfoAddr[i]; temp = gb2312_to_utf8("女");
switch (sex) { break;
case 1: { }
selfinfo = selfinfo + L"男\","; default:
break; {
} temp = gb2312_to_utf8("未知");
case 2: { break;
selfinfo = selfinfo + L"女\","; }
break; }
} }
default: { else
selfinfo = selfinfo + L"未知\","; {
break; temp = addr != 0 ? string((char *)addr) : gb2312_to_utf8("null");
} }
} jData[key] = temp.c_str();
continue; }
} wstring selfinfo = utf8_to_unicode(jData.dump().c_str());
else { return selfinfo;
temp = (char*)SelfInfoAddr[i];
if (temp == NULL || strlen(temp) == 0) {
temp = (char*)"null";
}
}
wchar_t* wtemp = new wchar_t[strlen(temp) + 1];
ZeroMemory(wtemp, (strlen(temp) + 1) * 2);
MultiByteToWideChar(CP_UTF8, 0, temp, -1, wtemp, strlen(temp) + 1);
wstring wrtemp = wreplace(wtemp,L'\"',L"\\\"");
selfinfo = selfinfo + wrtemp;
selfinfo = selfinfo + L"\"";
if(i!= SelfInfoAddr.size() - 1)
selfinfo = selfinfo + L",";
delete[] wtemp;
wtemp = NULL;
}
selfinfo = selfinfo + L"}";
#ifdef _DEBUG
wcout.imbue(locale("chs"));
wcout << selfinfo << endl;
#endif
return selfinfo;
} }
BOOL isWxLogin() { BOOL isWxLogin()
DWORD CheckLoginAddr = GetWeChatWinBase() + CheckLoginOffset; {
return *(BOOL*)CheckLoginAddr; DWORD CheckLoginAddr = GetWeChatWinBase() + CheckLoginOffset;
return *(BOOL *)CheckLoginAddr;
} }
/* /*
...@@ -171,10 +155,12 @@ BOOL isWxLogin() { ...@@ -171,10 +155,12 @@ BOOL isWxLogin() {
* return:void * return:void
*/ */
#ifndef USE_SOCKET #ifndef USE_SOCKET
VOID DeleteSelfInfoCacheRemote() { VOID DeleteSelfInfoCacheRemote()
if (ret.length) { {
delete[] (wchar_t*)ret.message; if (ret.length)
ZeroMemory(&ret, sizeof(SelfInfoStruct)); {
} delete[](wchar_t *) ret.message;
ZeroMemory(&ret, sizeof(SelfInfoStruct));
}
} }
#endif #endif
\ No newline at end of file
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
Version: 2.rc.08 (release candidate) Version: 2.rc.08 (release candidate)
Copyright (C) 2004-2017, 2020, 2021 René Nyffenegger Copyright (C) 2004-2017, 2020, 2021 Ren¨¦ Nyffenegger
This source code is provided 'as-is', without any express or implied This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages warranty. In no event will the author be held liable for any damages
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch Ren¨¦ Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/ */
...@@ -182,7 +182,7 @@ template <typename String> ...@@ -182,7 +182,7 @@ template <typename String>
static std::string decode(String encoded_string, bool remove_linebreaks) static std::string decode(String encoded_string, bool remove_linebreaks)
{ {
// //
// decode(…) is templated so that it can be used with String = const std::string& // decode(¡­) is templated so that it can be used with String = const std::string&
// or std::string_view (requires at least C++17) // or std::string_view (requires at least C++17)
// //
......
...@@ -87,6 +87,23 @@ string utf8_to_gb2312(const char *strUTF8) ...@@ -87,6 +87,23 @@ string utf8_to_gb2312(const char *strUTF8)
return strTemp; return strTemp;
} }
string gb2312_to_utf8(const char *strGB2312)
{
int len = MultiByteToWideChar(CP_ACP, 0, strGB2312, -1, NULL, 0);
wchar_t *wszGBK = new wchar_t[len + 1];
memset(wszGBK, 0, len * 2 + 2);
MultiByteToWideChar(CP_ACP, 0, strGB2312, -1, wszGBK, len);
len = WideCharToMultiByte(CP_UTF8, 0, wszGBK, -1, NULL, 0, NULL, NULL);
char *szGBK = new char[len + 1];
memset(szGBK, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wszGBK, -1, szGBK, len, NULL, NULL);
string strTemp(szGBK);
delete[] szGBK;
delete[] wszGBK;
return strTemp;
}
/* /*
* 将UTF8编码数据转换为GBK编码 * 将UTF8编码数据转换为GBK编码
*/ */
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <string> #include <string>
#include "wxdata.h" #include "wxdata.h"
#include "wxapi.h" #include "wxapi.h"
#include "base64/base64.h"
#endif //PCH_H #endif //PCH_H
#ifdef USE_SOCKET #ifdef USE_SOCKET
......
...@@ -39,6 +39,7 @@ BOOL CreateConsole(void); ...@@ -39,6 +39,7 @@ BOOL CreateConsole(void);
DWORD GetWeChatWinBase(); DWORD GetWeChatWinBase();
string unicode_to_gb2312(wchar_t *wchar); string unicode_to_gb2312(wchar_t *wchar);
string utf8_to_gb2312(const char *strUTF8); string utf8_to_gb2312(const char *strUTF8);
string gb2312_to_utf8(const char *strGB2312);
string unicode_to_utf8(wchar_t *wstr); string unicode_to_utf8(wchar_t *wstr);
wstring utf8_to_unicode(const char *buffer); wstring utf8_to_unicode(const char *buffer);
void HookAnyAddress(DWORD dwHookAddr, LPVOID dwJmpAddress, char *originalRecieveCode); void HookAnyAddress(DWORD dwHookAddr, LPVOID dwJmpAddress, char *originalRecieveCode);
......
...@@ -65,6 +65,7 @@ struct ReceiveMsgStruct ...@@ -65,6 +65,7 @@ struct ReceiveMsgStruct
wstring message; wstring message;
wstring filepath; wstring filepath;
wstring time; wstring time;
wstring extrainfo;
~ReceiveMsgStruct() ~ReceiveMsgStruct()
{ {
} }
......
...@@ -28,7 +28,7 @@ json methods = {{"GET", HTTP_METHOD_GET}, {"POST", HTTP_METHOD_POST}}; ...@@ -28,7 +28,7 @@ json methods = {{"GET", HTTP_METHOD_GET}, {"POST", HTTP_METHOD_POST}};
#endif #endif
#define STOI_S(str) (is_digit_number(str) ? stoi(str) : 0) #define STOI_S(str) (is_digit_number(str) ? stoi(str) : 0)
#define POST_PARAM(jData, key) utf8_to_unicode(string(jData[key]).c_str()) #define POST_PARAM(jData, key) utf8_to_unicode(jData[key].get<string>().c_str())
#define GET_PARAM(hm, name) getMgVarW(hm, name) #define GET_PARAM(hm, name) getMgVarW(hm, name)
bool is_digit_number(string str) bool is_digit_number(string str)
...@@ -94,11 +94,11 @@ static int get_http_param_int(mg_http_message *hm, json jData, string key, int m ...@@ -94,11 +94,11 @@ static int get_http_param_int(mg_http_message *hm, json jData, string key, int m
{ {
try try
{ {
result = jData[key]; result = jData[key].get<int>();
} }
catch (json::exception) catch (json::exception)
{ {
result = STOI_S((string)jData[key]); result = STOI_S(jData[key].get<string>());
} }
break; break;
} }
...@@ -153,8 +153,13 @@ void request_event(mg_http_message *hm, string &ret) ...@@ -153,8 +153,13 @@ void request_event(mg_http_message *hm, string &ret)
} }
case WECHAT_GET_SELF_INFO: case WECHAT_GET_SELF_INFO:
{ {
wstring self_info = GetSelfInfo(); json ret_data;
json ret_data = {{"self_info", unicode_to_utf8(WS2LW(self_info))}, {"result", "OK"}}; string self_info = unicode_to_utf8(WS2LW(GetSelfInfo()));
json j_info = json::parse(self_info.c_str(), self_info.c_str() + self_info.size(), nullptr, false);
if (j_info.is_discarded() != true)
ret_data = {{"data", j_info}, {"result", "OK"}};
else
ret_data = {{"data", self_info}, {"result", "OK"}};
ret = ret_data.dump(); ret = ret_data.dump();
break; break;
} }
...@@ -308,8 +313,13 @@ void request_event(mg_http_message *hm, string &ret) ...@@ -308,8 +313,13 @@ void request_event(mg_http_message *hm, string &ret)
case WECHAT_CONTACT_SEARCH_BY_CACHE: case WECHAT_CONTACT_SEARCH_BY_CACHE:
{ {
wstring wxid = get_http_param_str(hm, jData, "wxid", method); wstring wxid = get_http_param_str(hm, jData, "wxid", method);
wstring userinfo = GetUserInfoByWxId(wxid); string userinfo = unicode_to_utf8(WS2LW(GetUserInfoByWxId(wxid)));
json ret_data = {{"userinfo", unicode_to_utf8(WS2LW(userinfo))}, {"result", "OK"}}; json ret_data;
json j_info = json::parse(userinfo.c_str(), userinfo.c_str() + userinfo.size(), nullptr, false);
if (j_info.is_discarded() != true)
ret_data = {{"data", j_info}, {"result", "OK"}};
else
ret_data = {{"data", userinfo}, {"result", "OK"}};
ret = ret_data.dump(); ret = ret_data.dump();
break; break;
} }
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
#include <signal.h> #include <signal.h>
// mongoose: https://github.com/cesanta/mongoose // mongoose: https://github.com/cesanta/mongoose
#include "mongoose/mongoose.h" #include "mongoose/mongoose.h"
#include "base64/base64.h"
#pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ws2_32.lib")
extern "C" __declspec(dllexport) void http_start(int port); extern "C" __declspec(dllexport) void http_start(int port);
extern "C" __declspec(dllexport) int http_close(); extern "C" __declspec(dllexport) int http_close();
......
...@@ -88,5 +88,5 @@ if __name__ == '__main__': ...@@ -88,5 +88,5 @@ if __name__ == '__main__':
wx = WeChatRobot(pid_list[0]) wx = WeChatRobot(pid_list[0])
wx.StartService() wx.StartService()
wx.StartReceiveMessage() wx.StartReceiveMessage()
wxRobot.start_socket_server() wxRobot.register_msg_event(pid_list[0])
wx.StopService() wx.StopService()
...@@ -6,9 +6,10 @@ Created on Thu Feb 24 16:19:48 2022 ...@@ -6,9 +6,10 @@ Created on Thu Feb 24 16:19:48 2022
""" """
# Before use,execute `CWeChatRobot.exe /regserver` in cmd by admin user # Before use,execute `CWeChatRobot.exe /regserver` in cmd by admin user
import ast
import os import os
import ctypes import ctypes
import json
import base64
import ctypes.wintypes import ctypes.wintypes
import socketserver import socketserver
import threading import threading
...@@ -51,6 +52,8 @@ class WeChatEventSink: ...@@ -51,6 +52,8 @@ class WeChatEventSink:
""" """
def OnGetMessageEvent(self, msg): def OnGetMessageEvent(self, msg):
msg = json.loads(msg)
msg['extrainfo'] = base64.b64decode(msg['extrainfo'])
print(msg) print(msg)
...@@ -58,65 +61,41 @@ class ReceiveMsgBaseServer(socketserver.BaseRequestHandler): ...@@ -58,65 +61,41 @@ class ReceiveMsgBaseServer(socketserver.BaseRequestHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
class ReceiveMsgStruct(ctypes.Structure):
_fields_ = [("pid", ctypes.wintypes.DWORD),
("type", ctypes.wintypes.DWORD),
("isSendMsg", ctypes.wintypes.DWORD),
("msgid", ctypes.c_ulonglong),
("sender", ctypes.c_wchar * 80),
("wxid", ctypes.c_wchar * 80),
("message", ctypes.c_wchar * 0x1000B),
("filepath", ctypes.c_wchar * 260),
("time", ctypes.c_wchar * 30)
]
def handle(self): def handle(self):
conn = self.request conn = self.request
comtypes.CoInitialize() comtypes.CoInitialize()
while True: while True:
try: try:
ptr_data = conn.recv(1024) ptr_data = b""
try: while True:
if ptr_data.decode() == 'bye':
break
except UnicodeDecodeError:
pass
while len(ptr_data) < ctypes.sizeof(self.ReceiveMsgStruct):
data = conn.recv(1024) data = conn.recv(1024)
if len(data) == 0:
break
ptr_data += data ptr_data += data
if ptr_data: if len(data) == 0 or data[-1] == 0xA:
ptr_receive_msg = ctypes.cast(ptr_data, ctypes.POINTER(self.ReceiveMsgStruct)) break
ReceiveMsgBaseServer.msg_callback(ptr_receive_msg.contents) msg = json.loads(ptr_data.decode('utf-8'))
response = "200 OK" ReceiveMsgBaseServer.msg_callback(msg)
conn.sendall(response.encode())
except OSError: except OSError:
break break
except Exception as e: except json.JSONDecodeError:
print(e) pass
conn.sendall("200 OK".encode()) conn.sendall("200 OK".encode())
conn.close() conn.close()
comtypes.CoUninitialize() comtypes.CoUninitialize()
@staticmethod @staticmethod
def msg_callback(data): def msg_callback(msg):
msg['extrainfo'] = base64.b64decode(msg['extrainfo'])
# 主线程中已经注入,此处禁止调用StartService和StopService # 主线程中已经注入,此处禁止调用StartService和StopService
msg = {'pid': data.pid, 'time': data.time, 'type': data.type,
'isSendMsg': data.isSendMsg, 'msgid': data.msgid,
'wxid': data.wxid,
'sendto' if data.isSendMsg else 'from': data.sender,
'message': data.message}
robot = comtypes.client.CreateObject("WeChatRobot.CWeChatRobot") robot = comtypes.client.CreateObject("WeChatRobot.CWeChatRobot")
event = comtypes.client.CreateObject("WeChatRobot.RobotEvent") event = comtypes.client.CreateObject("WeChatRobot.RobotEvent")
wx = WeChatRobot(data.pid, robot, event) wx = WeChatRobot(msg['pid'], robot, event)
userinfo = wx.GetWxUserInfo(data.wxid) userinfo = wx.GetWxUserInfo(msg['wxid'])
msg['alias'] = userinfo['wxNumber'] msg['alias'] = userinfo['wxNumber']
if data.isSendMsg == 0: if msg['isSendMsg'] == 0:
if '@chatroom' in data.sender: if '@chatroom' in msg['sender']:
chatroom_info = wx.GetWxUserInfo(data.sender) chatroom_info = wx.GetWxUserInfo(msg['sender'])
msg['chatroom_name'] = chatroom_info['wxNickName'] msg['chatroom_name'] = chatroom_info['wxNickName']
msg['nickname'] = wx.GetChatRoomMemberNickname(data.sender, data.wxid) msg['nickname'] = wx.GetChatRoomMemberNickname(msg['sender'], msg['wxid'])
else: else:
msg['nickname'] = userinfo['wxNickName'] msg['nickname'] = userinfo['wxNickName']
# TODO: 在这里写额外的消息处理逻辑 # TODO: 在这里写额外的消息处理逻辑
...@@ -330,12 +309,8 @@ class WeChatRobot: ...@@ -330,12 +309,8 @@ class WeChatRobot:
调用成功返回个人信息,否则返回空字典. 调用成功返回个人信息,否则返回空字典.
""" """
self_info = self.robot.CGetSelfInfo(self.pid).replace('\n', '\\n') self_info = self.robot.CGetSelfInfo(self.pid)
try: return json.loads(self_info)
self_info = ast.literal_eval(self_info)
except SyntaxError:
return {}
return self_info
def StopService(self) -> int: def StopService(self) -> int:
""" """
...@@ -519,8 +494,8 @@ class WeChatRobot: ...@@ -519,8 +494,8 @@ class WeChatRobot:
联系人信息. 联系人信息.
""" """
userinfo = self.robot.CGetWxUserInfo(self.pid, wxid).replace('\n', '\\n') userinfo = self.robot.CGetWxUserInfo(self.pid, wxid)
return ast.literal_eval(userinfo) return json.loads(userinfo)
def GetChatRoomMembers(self, chatroom_id: str) -> dict or None: def GetChatRoomMembers(self, chatroom_id: str) -> dict or None:
""" """
......
...@@ -49,11 +49,11 @@ CWeChatRobot.exe /unregserver ...@@ -49,11 +49,11 @@ CWeChatRobot.exe /unregserver
``` ```
# 调用 # 调用
**Python:** **Python:**
参考[wxRobot.py](/Python/wxRobot.py) 参考[wxRobot.py](/Python/wxRobot.py)
**C#:** **C#:**
参考[ComWechatRobotCsharp](https://github.com/RingoStudio/ComWechatRobotCsharp),感谢@RingoStudio 的贡献 参考[ComWechatRobotCsharp](https://github.com/RingoStudio/ComWechatRobotCsharp),感谢@RingoStudio 的贡献
**易语言:** **易语言:**
参考[ESDK](/ESDK),感谢@lovezm 的贡献 参考[ESDK](/ESDK),感谢@lovezm 的贡献
# 更多功能 # 更多功能
1. 尝试添加issue中的功能 1. 尝试添加issue中的功能
...@@ -119,11 +119,13 @@ CWeChatRobot.exe /unregserver ...@@ -119,11 +119,13 @@ CWeChatRobot.exe /unregserver
## 2022.08.13 ## 2022.08.13
1. 现在消息HOOK内容包含消息ID 1. 现在消息HOOK内容包含消息ID
2. 完成发送消息的http接口,可参考[wxDriver.py](/Release/socket/wxDriver.py),其他接口还需要一点时间 2. 完成发送消息的http接口,可参考[wxDriver.py](/Release/socket/wxDriver.py),其他接口还需要一点时间
3. 新增项目配置文件,感谢@amchii 提供的方法 3. 新增项目配置文件,感谢@amchii 提供的方法
## 2022.08.21 ## 2022.08.21
1. 所有功能http接口封装完成,可接受get、post请求 1. 所有功能http接口封装完成,可接受get、post请求
2. 提供http接口调用示例,参考[wxDriver.py](/Release/socket/wxDriver.py) 2. 提供http接口调用示例,参考[wxDriver.py](/Release/socket/wxDriver.py)
## 2022.08.25
1. 接收消息格式修改为json,现在也可以获取到扩展信息,可从扩展信息中获取到文件保存路径或被艾特人wxid
2. 优化获取个人信息,获取好友信息接口
# 打赏作者 # 打赏作者
请给作者一个star,感谢感谢 请给作者一个star,感谢感谢
......
...@@ -3,6 +3,7 @@ import json ...@@ -3,6 +3,7 @@ import json
import copy import copy
import threading import threading
import requests import requests
import base64
import socketserver import socketserver
if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulonglong): if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulonglong):
...@@ -190,47 +191,29 @@ class ReceiveMsgSocketServer(socketserver.BaseRequestHandler): ...@@ -190,47 +191,29 @@ class ReceiveMsgSocketServer(socketserver.BaseRequestHandler):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
class ReceiveMsgStruct(ctypes.Structure):
_fields_ = [("pid", ctypes.wintypes.DWORD),
("type", ctypes.wintypes.DWORD),
("isSendMsg", ctypes.wintypes.DWORD),
("msgid", ctypes.c_ulonglong),
("sender", ctypes.c_wchar * 80),
("wxid", ctypes.c_wchar * 80),
("message", ctypes.c_wchar * 0x1000B),
("filepath", ctypes.c_wchar * 260),
("time", ctypes.c_wchar * 30)
]
def handle(self): def handle(self):
conn = self.request conn = self.request
while True: while True:
try: try:
ptr_data = conn.recv(1024) ptr_data = b""
try: while True:
if ptr_data.decode() == 'bye': break
except UnicodeDecodeError:
pass
while len(ptr_data) < ctypes.sizeof(self.ReceiveMsgStruct):
data = conn.recv(1024) data = conn.recv(1024)
if len(data) == 0: break
ptr_data += data ptr_data += data
if ptr_data: if len(data) == 0 or data[-1] == 0xA:
ptr_receive_msg = ctypes.cast(ptr_data, ctypes.POINTER(self.ReceiveMsgStruct)) break
ReceiveMsgSocketServer.msg_callback(ptr_receive_msg.contents) msg = json.loads(ptr_data.decode('utf-8'))
ReceiveMsgSocketServer.msg_callback(msg)
except OSError: except OSError:
break break
except: except json.JSONDecodeError:
pass pass
conn.sendall("200 OK".encode()) conn.sendall("200 OK".encode())
conn.close() conn.close()
@staticmethod @staticmethod
def msg_callback(data): def msg_callback(msg):
msg = {'pid': data.pid, 'time': data.time, 'type': data.type, # 附加信息是protobuf格式,需要自行处理
'isSendMsg': data.isSendMsg, 'msgid': data.msgid, msg['extrainfo'] = base64.b64decode(msg['extrainfo'])
'wxid': data.wxid,'message': data.message,
'sendto' if data.isSendMsg else 'from': data.sender}
# TODO: 在这里写额外的消息处理逻辑 # TODO: 在这里写额外的消息处理逻辑
print(msg) print(msg)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册