diff --git a/README.MD b/README.MD
index 720aca6..114bc1a 100644
--- a/README.MD
+++ b/README.MD
@@ -16,6 +16,29 @@
+|[📖 Python 文档](https://wechatferry.readthedocs.io/)|[📺 Python 视频教程](https://mp.weixin.qq.com/s/APdjGyZ2hllXxyG_sNCfXQ)|[🙋 FAQ](https://mp.weixin.qq.com/s/eMQgP1TiEqAxmpxYljSgeg)|
+|:-:|:-:|:-:|
+
+👉 [WeChatRobot🤖](https://github.com/lich0821/WeChatRobot),一个基于 WeChatFerry 的 Python 机器人框架。
+
+点击查看功能清单
+
+* 检查登录状态
+* 获取登录账号信息(wxid、昵称、手机号、数据目录)
+* 获取消息类型
+* 发送文本消息(可 @)
+* 发送图片
+* 允许接收消息
+* 停止接收消息
+
+
+
+点击查看支持的客户端
+
+* Python
+
+
+
|||
|:-:|:-:|
|后台回复 `WCF` 加群交流|如果你觉得有用|
@@ -127,11 +150,16 @@ WeChatFerry
## 版本更新
-### v39.1.0 (2024.04.19)
+### v39.2.0
-* 适配 x64 环境
-* 重构项目
-* 开始适配 `3.9.10.19`
+* 开始适配 `3.9.10.27`
+* 实现检查登录状态
+* 实现获取登录账号信息(wxid、昵称、手机号、数据目录)
+* 实现获取消息类型
+* 实现开启接收消息
+* 实现停止接收消息
+* 实现发送文本消息(可 @)
+* 实现发送图片消息
点击查看更多
@@ -143,218 +171,10 @@ WeChatFerry
* `y` 是 `WeChatFerry` 的版本,从 0 开始
* `z` 是各客户端的版本,从 0 开始
-### v39.0.14 (2024.02.18)
+### v39.1.0 (2024.04.19)
-* 修复获取登录二维码问题
-
-### v39.0.13 (2024.02.14)
-
-* 修复若干问题
-* 撤回消息
-* 获取登录二维码
-
-### v39.0.12 (2023.12.20)
-
-* 修复一个问题
-* 消息转发
-
-### v39.0.11 (2023.12.16)
-
-* 修复 PB 消息类型(可能会导致非 Python 客户端崩溃)
-* 修复日志错误
-* 移除非必要依赖
-
-### v39.0.10 (2023.12.08)
-
-* 代码优化
-* 发送卡片消息
-* 拍一拍群友
-* 邀请群成员
-* 图片 OCR
-
-### v39.0.7 (2023.12.03)
-
-* 保存语音
-
-### v39.0.6 (2023.11.26)
-
-* 修复下载图片退出问题
-
-### v39.0.5 (2023.11.22)
-
-* 修复收到某些文件崩溃问题
-
-### v39.0.4 (2023.11.21)
-
-* 下载图片、文件和视频
-
-### v39.0.3 (2023.09.28)
-
-* 修复登录账号昵称超长报错问题
-
-### v39.0.2 (2023.07.16)
-
-* 修复朋友圈消息 `is_group` 为 `True` 问题
-
-### v39.0.1 (2023.07.16)
-
-* 获取朋友圈消息
-
-### v39.0.0 (2023.07.14)
-
-升级到 `3.9.2.23`。
-
-### v37.1.25 (2023.05.07)
-
-更新版本编码。
-
-根据新版本编码规则:
-* `WeChatFerry` 的 `v3.7.0.30.25` 应调整为:`v37.1.25`,因为此前曾适配过 `3.7.0.29`。
-* Python 客户端 `wcferry` 的 `v3.7.0.30.25` 应该调整为 `v37.1.25.0`
-* HTTP 客户端 `wcfhttp` 的 `v3.7.0.30.25` 应该调整为 `v37.1.25.0`
-
-### v3.7.0.30.25 (2023.05.05)
-
-* 修复群消息判断错误
-* 修复名片添加好友问题
-* 修复获取数据库多余字符问题
-* 添加 Python 文档
-* Python 客户端发送图片支持网络路径
-
-### v3.7.0.30.24 (2023.04.19)
-
-实现了一个功能。
-
-### v3.7.0.30.23 (2023.04.13)
-
-* 解密图片
-* 获取登录账号信息
-* 获取联系人备注
-
-### v3.7.0.30.22(2023.04.09)
-
-将监听端口调整为可配置。
-
-### v3.7.0.30.21(2023.03.15)
-
-* 发送表情
-
-### v3.7.0.30.20(2023.03.12)
-
-修复 wxid 获取问题。
-
-### v3.7.0.30.19(2023.03.06)
-
-修复重复消息问题。
-
-### v3.7.0.30.18(2023.03.05)
-
-修复添加好友问题。
-
-### v3.7.0.30.17(2023.03.05)
-
-修复获取登录账号 wxid 问题。
-
-### v3.7.0.30.16(2023.03.04)
-
-将错误码改成错误消息,方便调试。
-
-### v3.7.0.30.15(2023.03.01)
-
-* 发送 xml
-
-### v3.7.0.30.14(2023.02.28)
-
-* 添加群成员
-
-### v3.7.0.30.13(2023.02.27)
-
-去除 gRPC 框架,自定义更轻量的 RPC 轮子 `nnprc`。
-
-### v3.7.0.30.12(2023.01.20)
-
-* 更新 Python 客户端
-* 修改监听地址为 `0.0.0.0:10086`
-* 增加 `Launcher`,直接注入 `spy`
-
-### v3.7.0.30.11(2022.10.19)
-
-更新 Python 客户端。
-
-### v3.7.0.30-gRPC-2(2022.10.18)
-
-增加 Java 客户端。
-
-### v3.7.0.30-gRPC-1(2022.10.16)
-
-将 RPC 框架切换为 gRPC!
-
-### v3.7.0.30-8(2022.09.25)
-
-* 获取登录账号微信 ID
-
-### v3.7.0.30-7(2022.09.24)
-
-修复群聊有系统消息时会崩溃 bug。后续考虑把消息来源交还给客户端自己区别。
-
-### v3.7.0.30-6(2022.08.21)
-
-* 通过好友验证
-
-### v3.7.0.30-5(2022.08.20)
-
-* 执行 SQL 语句
-
-### v3.7.0.30-4(2022.08.20)
-
-修复群消息 @人 功能。有几点注意事项:
-1. `vAtWxids` 是要 `@` 的 `wxid` 清单,以 `,` 分隔。
-2. 只有群主才能 `@所有人`,非群主硬发 `@所有人` 会导致消息发不出去;`@所有人` 对应 `vAtWxids` 为 `"notify@all"`。
-3. 消息体里 `@` 的数量必须与 `vAtWxids` 里的数量一致,否则消息能发出但 `@` 功能失效。
-
-### v3.7.0.30-3(2022.08.20)
-
-修复可重入 bug。
-
-### v3.7.0.30-2(2022.08.14)
-
-优化 Hook 和 Inject 代码,实现可重入。
-
-### v3.7.0.30-1(2022.08.12)
-
-适配微信 `3.7.0.30`。
-
-### v3.7.0.29-3(2022.08.7)
-
-* 查询数据库,获取库、表。
-
-### v3.7.0.29-2(2022.08.7)
-
-优化 RPC。
-
-### v3.7.0.29-1(2022.08.7)
-
-适配微信 `3.7.0.29`。
-
-### v3.3.0.115-3(2021.08.28)
-
-适配微信 `3.3.0.115`,新增功能:
-* 获取所有联系人
-
-### v3.3.0.115-2(2021.08.22)
-
-适配微信 `3.3.0.115`,新增功能:
-* 发送图片消息
-
-### v3.3.0.115-1(2021.08.22)
-
-适配微信 `3.3.0.115`。
-
-### v3.0.0.57-1(2021.02.12)
-
-适配微信 `3.0.0.57`,支持功能:
-* 登录状态判断
-* 接收文本消息
-* 发送文本消息
+* 适配 x64 环境
+* 重构项目
+* 开始适配 `3.9.10.19`
diff --git a/WeChatFerry/WeChatFerry.sln b/WeChatFerry/WeChatFerry.sln
index 2122ba1..02ba879 100644
--- a/WeChatFerry/WeChatFerry.sln
+++ b/WeChatFerry/WeChatFerry.sln
@@ -13,14 +13,18 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
+ Dev|x64 = Dev|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Debug|x64.ActiveCfg = Debug|x64
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Debug|x64.Build.0 = Debug|x64
+ {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Dev|x64.ActiveCfg = Dev|x64
+ {4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Dev|x64.Build.0 = Dev|x64
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x64.ActiveCfg = Release|x64
{4DE80B82-5F6A-4C4C-9D16-1574308110FA}.Release|x64.Build.0 = Release|x64
{ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Debug|x64.ActiveCfg = Debug|x64
+ {ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Dev|x64.ActiveCfg = Dev|x64
{ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x64.ActiveCfg = Release|x64
{ABFCB647-137F-478B-A73E-F0B1E3ADC215}.Release|x64.Build.0 = Release|x64
EndGlobalSection
diff --git a/WeChatFerry/com/util.cpp b/WeChatFerry/com/util.cpp
index e52790c..273a1bd 100644
--- a/WeChatFerry/com/util.cpp
+++ b/WeChatFerry/com/util.cpp
@@ -8,6 +8,7 @@
#include
#include
+#include "log.h"
#include "util.h"
#pragma comment(lib, "shlwapi")
@@ -296,3 +297,23 @@ void DbgMsg(const char *zcFormat, ...)
OutputDebugStringA(strText.c_str());
}
+
+WxString *NewWxStringFromStr(const string &str) { return NewWxStringFromWstr(String2Wstring(str)); }
+
+WxString *NewWxStringFromWstr(const wstring &ws)
+{
+ WxString *p = (WxString *)HeapAlloc(GetProcessHeap(), 0, sizeof(WxString));
+ wchar_t *pWstring = (wchar_t *)HeapAlloc(GetProcessHeap(), 0, (ws.size() + 1) * 2);
+ if (p == NULL || pWstring == NULL) {
+ LOG_ERROR("Out of Memory...");
+ return NULL;
+ }
+
+ wmemcpy(pWstring, ws.c_str(), ws.size() + 1);
+ p->wptr = pWstring;
+ p->size = (DWORD)ws.size();
+ p->capacity = (DWORD)ws.size();
+ p->ptr = 0;
+ p->clen = 0;
+ return p;
+}
diff --git a/WeChatFerry/com/util.h b/WeChatFerry/com/util.h
index 8339464..00e50bb 100644
--- a/WeChatFerry/com/util.h
+++ b/WeChatFerry/com/util.h
@@ -2,6 +2,8 @@
#include
+#include "spy_types.h"
+
#define WECHAREXE L"WeChat.exe"
#define WECHATWINDLL L"WeChatWin.dll"
#define WCFSDKDLL L"sdk.dll"
@@ -34,3 +36,5 @@ std::string GetStringByAddress(UINT64 address);
std::string GetStringByStrAddr(UINT64 addr);
std::string GetStringByWstrAddr(UINT64 addr);
void DbgMsg(const char *zcFormat, ...);
+WxString *NewWxStringFromStr(const std::string &str);
+WxString *NewWxStringFromWstr(const std::wstring &ws);
diff --git a/WeChatFerry/sdk/SDK.vcxproj b/WeChatFerry/sdk/SDK.vcxproj
index 5c17179..af47a4b 100644
--- a/WeChatFerry/sdk/SDK.vcxproj
+++ b/WeChatFerry/sdk/SDK.vcxproj
@@ -1,18 +1,14 @@
-
- Debug
- Win32
-
-
- Release
- Win32
-
Debug
x64
+
+ Dev
+ x64
+
Release
x64
@@ -26,20 +22,13 @@
10.0
-
+
DynamicLibrary
true
v142
Unicode
-
- DynamicLibrary
- false
- v142
- true
- Unicode
-
-
+
DynamicLibrary
true
v142
@@ -57,44 +46,34 @@
-
-
-
-
-
-
+
+
+
-
+
true
-
- false
-
-
+
true
false
-
- true
- x86-windows-static
-
true
x64-windows-static
-
+
Level3
true
- WIN32;_DEBUG;SDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
+ _DEBUG;SDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
Use
pch.h
@@ -106,37 +85,7 @@
sdk.def
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;SDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
- true
- NotUsing
-
-
- stdcpp17
- $(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include
- MultiThreaded
-
-
- Windows
- true
- true
- true
- false
- sdk.def
-
-
- xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out
-
-
- Copy files
-
-
-
+
Level3
true
diff --git a/WeChatFerry/smc/Codec.lib b/WeChatFerry/smc/Codec.lib
old mode 100644
new mode 100755
index 96ce349..b994395
Binary files a/WeChatFerry/smc/Codec.lib and b/WeChatFerry/smc/Codec.lib differ
diff --git a/WeChatFerry/spy/Spy.vcxproj b/WeChatFerry/spy/Spy.vcxproj
index c81c7d0..0c44f9a 100644
--- a/WeChatFerry/spy/Spy.vcxproj
+++ b/WeChatFerry/spy/Spy.vcxproj
@@ -1,18 +1,14 @@
-
- Debug
- Win32
-
-
- Release
- Win32
-
Debug
x64
+
+ Dev
+ x64
+
Release
x64
@@ -24,24 +20,16 @@
{4de80b82-5f6a-4c4c-9d16-1574308110fa}
spy
10.0
- x86-windows-static
x64-windows-static
-
+
DynamicLibrary
true
v142
Unicode
-
- DynamicLibrary
- false
- v142
- true
- Unicode
-
-
+
DynamicLibrary
true
v142
@@ -59,141 +47,38 @@
-
-
-
-
-
-
+
+
+
-
+
true
$(ProjectName)_debug
- true
-
- false
- true
-
-
+
true
$(ProjectName)_debug
false
-
- true
-
-
+
true
Release
-
+
true
+ Release
true
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;SPY_EXPORTS;_WINDOWS;_USRDLL;ENABLE_DEBUG_LOG;%(PreprocessorDefinitions)
- true
- NotUsing
-
-
- $(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include
-
- 4251;4731;4819
- MultiThreaded
- stdcpp17
- /EHa %(AdditionalOptions)
-
-
- Windows
- true
- true
- true
- false
- iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies)
- spy.def
- $(SolutionDir)smc;%(AdditionalLibraryDirectories)
- /ignore:4099 %(AdditionalOptions)
-
-
- if not exist $(SolutionDir)Out md $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetName).exp $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetName).lib $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetName).pdb $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
-
-
- Copy spy.dll
-
-
- cd $(SolutionDir)rpc\proto
-$(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
-
-
- Generating PB files
-
-
-
-
- Level3
- true
- true
- true
- WIN32;NDEBUG;SPY_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
- true
- NotUsing
-
-
- $(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x86-windows-static\include
-
- 4251;4731;4819
- MultiThreaded
- stdcpp17
- /EHa %(AdditionalOptions)
-
-
- Windows
- true
- true
- true
- false
- iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies)
- spy.def
- $(SolutionDir)smc;%(AdditionalLibraryDirectories)
- /ignore:4099 %(AdditionalOptions)
-
-
- if not exist $(SolutionDir)Out md $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out
-xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
-
-
- Copy spy.dll
-
-
- cd $(SolutionDir)rpc\proto
-$(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
-
-
- Generating PB files
-
-
Level3
@@ -207,7 +92,7 @@ $(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
$(SolutionDir)com;$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x64-windows-static\include
true
false
- MultiThreadedDebug
+ MultiThreaded
true
4251;4731;4819
@@ -236,6 +121,55 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out
xcopy /y $(OutDir)$(TargetName).exp $(SolutionDir)Out
xcopy /y $(OutDir)$(TargetName).lib $(SolutionDir)Out
xcopy /y $(OutDir)$(TargetName).pdb $(SolutionDir)Out
+xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
+
+
+ Copy spy.dll
+
+
+
+
+ Level3
+ true
+ WIN32;NDEBUG;SPY_EXPORTS;_WINDOWS;_USRDLL;ENABLE_DEBUG_LOG;ENABLE_WX_LOG;%(PreprocessorDefinitions)
+ true
+ NotUsing
+
+
+ stdcpp17
+ $(SolutionDir)com;$(SolutionDir)rpc;$(SolutionDir)rpc\nanopb;$(SolutionDir)rpc\proto;$(SolutionDir)smc;$(SolutionDir)spy;C:\Tools\vcpkg\installed\x64-windows-static\include
+ true
+ false
+ MultiThreaded
+ true
+
+
+ 4251;4731;4819
+ /EHa %(AdditionalOptions)
+
+
+ Windows
+ true
+ false
+ spy.def
+ /ignore:4099 %(AdditionalOptions)
+ $(SolutionDir)smc;%(AdditionalLibraryDirectories)
+ iphlpapi.lib;wsock32.lib;ws2_32.lib;crypt32.lib;Codec.lib;%(AdditionalDependencies)
+ true
+
+
+ cd $(SolutionDir)rpc\proto
+$(SolutionDir)rpc\tool\protoc --nanopb_out=. wcf.proto
+
+
+ Generating PB files
+
+
+ if not exist $(SolutionDir)Out md $(SolutionDir)Out
+xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)Out
+xcopy /y $(OutDir)$(TargetName).exp $(SolutionDir)Out
+xcopy /y $(OutDir)$(TargetName).lib $(SolutionDir)Out
+xcopy /y $(OutDir)$(TargetName).pdb $(SolutionDir)Out
xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
@@ -303,9 +237,7 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
-
-
@@ -327,9 +259,7 @@ xcopy /y $(OutDir)$(TargetFileName) $(SolutionDir)..\clients\python\wcferry
-
-
diff --git a/WeChatFerry/spy/Spy.vcxproj.filters b/WeChatFerry/spy/Spy.vcxproj.filters
index 8bd6b31..f2b9096 100644
--- a/WeChatFerry/spy/Spy.vcxproj.filters
+++ b/WeChatFerry/spy/Spy.vcxproj.filters
@@ -30,9 +30,6 @@
头文件
-
- 头文件
-
头文件
@@ -78,9 +75,6 @@
头文件
-
- 头文件
-
头文件
@@ -107,9 +101,6 @@
源文件
-
- 源文件
-
源文件
@@ -143,9 +134,6 @@
源文件
-
- 源文件
-
源文件
diff --git a/WeChatFerry/spy/chatroom_mgmt.cpp b/WeChatFerry/spy/chatroom_mgmt.cpp
index 7a6e4b9..f26e351 100644
--- a/WeChatFerry/spy/chatroom_mgmt.cpp
+++ b/WeChatFerry/spy/chatroom_mgmt.cpp
@@ -3,122 +3,33 @@
#include
#include "chatroom_mgmt.h"
-#include "load_calls.h"
#include "log.h"
#include "util.h"
using namespace std;
+extern QWORD g_WeChatWinDllAddr;
+
+#define OS_GET_CHATROOM_MGR 0x1C4E200
+#define OS_ADD_MEMBERS 0x221B8A0
+#define OS_DELETE_MEMBERS 0x221BEE0
+#define OS_INVITE_MEMBERS 0x221B280
+
+typedef QWORD (*GetChatRoomMgr_t)();
+typedef QWORD (*AddMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD);
+typedef QWORD (*DelMemberFromChatRoom_t)(QWORD, QWORD, QWORD);
+typedef QWORD (*InviteMemberToChatRoom_t)(QWORD, QWORD, QWORD, QWORD);
-extern WxCalls_t g_WxCalls;
-extern UINT64 g_WeChatWinDllAddr;
-#if 0
int AddChatroomMember(string roomid, string wxids)
{
+ int status = -1;
+
if (roomid.empty() || wxids.empty()) {
LOG_ERROR("Empty roomid or wxids.");
- return -1;
+ return status;
}
- int rv = 0;
- DWORD armCall1 = g_WeChatWinDllAddr + g_WxCalls.arm.call1;
- DWORD armCall2 = g_WeChatWinDllAddr + g_WxCalls.arm.call2;
- DWORD armCall3 = g_WeChatWinDllAddr + g_WxCalls.arm.call3;
-
- DWORD temp = 0;
- wstring wsRoomid = String2Wstring(roomid);
- WxString wxRoomid(wsRoomid);
-
- vector vMembers;
- vector vWxMembers;
- wstringstream wss(String2Wstring(wxids));
- while (wss.good()) {
- wstring wstr;
- getline(wss, wstr, L',');
- vMembers.push_back(wstr);
- WxString txtMember(vMembers.back());
- vWxMembers.push_back(txtMember);
- }
-
- LOG_DEBUG("Adding {} members[{}] to {}", vWxMembers.size(), wxids.c_str(), roomid.c_str());
- __asm {
- pushad;
- pushfd;
- call armCall1;
- sub esp, 0x8;
- mov temp, eax;
- mov ecx, esp;
- mov dword ptr[ecx], 0x0;
- mov dword ptr[ecx + 4], 0x0;
- test esi, esi;
- sub esp, 0x14;
- mov ecx, esp;
- lea eax, wxRoomid;
- push eax;
- call armCall2;
- mov ecx, temp;
- lea eax, vWxMembers;
- push eax;
- call armCall3;
- mov rv, eax;
- popfd;
- popad;
- }
- return rv;
-}
-
-int DelChatroomMember(string roomid, string wxids)
-{
- if (roomid.empty() || wxids.empty()) {
- LOG_ERROR("Empty roomid or wxids.");
- return -1;
- }
-
- int rv = 0;
- DWORD drmCall1 = g_WeChatWinDllAddr + g_WxCalls.drm.call1;
- DWORD drmCall2 = g_WeChatWinDllAddr + g_WxCalls.drm.call2;
- DWORD drmCall3 = g_WeChatWinDllAddr + g_WxCalls.drm.call3;
-
- DWORD temp = 0;
- wstring wsRoomid = String2Wstring(roomid);
- WxString wxRoomid(wsRoomid);
-
- vector vMembers;
- vector vWxMembers;
- wstringstream wss(String2Wstring(wxids));
- while (wss.good()) {
- wstring wstr;
- getline(wss, wstr, L',');
- vMembers.push_back(wstr);
- WxString txtMember(vMembers.back());
- vWxMembers.push_back(txtMember);
- }
-
- LOG_DEBUG("Deleting {} members[{}] from {}", vWxMembers.size(), wxids.c_str(), roomid.c_str());
- __asm {
- pushad;
- pushfd;
- call drmCall1;
- sub esp, 0x14;
- mov esi, eax;
- mov ecx, esp;
- lea edi, wxRoomid;
- push edi;
- call drmCall2;
- mov ecx, esi;
- lea eax, vWxMembers;
- push eax;
- call drmCall3;
- mov rv, eax;
- popfd;
- popad;
- }
- return rv;
-}
-
-int InviteChatroomMember(string roomid, string wxids)
-{
- wstring wsRoomid = String2Wstring((roomid));
- WxString wxRoomid(wsRoomid);
+ GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
+ AddMemberToChatRoom_t AddMembers = (AddMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_ADD_MEMBERS);
vector vMembers;
vector vWxMembers;
@@ -131,51 +42,72 @@ int InviteChatroomMember(string roomid, string wxids)
vWxMembers.push_back(wxMember);
}
- LOG_DEBUG("Inviting {} members[{}] to {}", vWxMembers.size(), wxids.c_str(), roomid.c_str());
+ QWORD temp[2] = { 0 };
+ WxString *pWxRoomid = NewWxStringFromStr(roomid);
+ QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
- DWORD irmCall1 = g_WeChatWinDllAddr + g_WxCalls.irm.call1;
- DWORD irmCall2 = g_WeChatWinDllAddr + g_WxCalls.irm.call2;
- DWORD irmCall3 = g_WeChatWinDllAddr + g_WxCalls.irm.call3;
- DWORD irmCall4 = g_WeChatWinDllAddr + g_WxCalls.irm.call4;
- DWORD irmCall5 = g_WeChatWinDllAddr + g_WxCalls.irm.call5;
- DWORD irmCall6 = g_WeChatWinDllAddr + g_WxCalls.irm.call6;
- DWORD irmCall7 = g_WeChatWinDllAddr + g_WxCalls.irm.call7;
- DWORD irmCall8 = g_WeChatWinDllAddr + g_WxCalls.irm.call8;
-
- DWORD sys_addr = (DWORD)GetModuleHandleA("win32u.dll") + 0x116C;
- DWORD addr[2] = { sys_addr, 0 };
- __asm {
- pushad;
- pushfd;
- call irmCall1;
- lea ecx, addr;
- push ecx;
- mov ecx, eax;
- call irmCall2;
- call irmCall3;
- sub esp, 0x8;
- lea eax, addr;
- mov ecx, esp;
- push eax;
- call irmCall4;
- sub esp, 0x14;
- mov ecx, esp;
- lea eax, wxRoomid;
- push eax;
- call irmCall5;
- lea eax, vWxMembers;
- push eax;
- call irmCall6;
- call irmCall1;
- push 0x0;
- push 0x1;
- mov ecx, eax;
- call irmCall7;
- lea ecx, addr;
- call irmCall8;
- popfd;
- popad;
- }
- return 1;
+ QWORD mgr = GetChatRoomMgr();
+ status = (int)AddMembers(mgr, pMembers, (QWORD)pWxRoomid, (QWORD)temp);
+ return status;
+}
+
+int DelChatroomMember(string roomid, string wxids)
+{
+ int status = -1;
+
+ if (roomid.empty() || wxids.empty()) {
+ LOG_ERROR("Empty roomid or wxids.");
+ return status;
+ }
+
+ GetChatRoomMgr_t GetChatRoomMgr = (GetChatRoomMgr_t)(g_WeChatWinDllAddr + OS_GET_CHATROOM_MGR);
+ DelMemberFromChatRoom_t DelMembers = (DelMemberFromChatRoom_t)(g_WeChatWinDllAddr + OS_DELETE_MEMBERS);
+
+ vector vMembers;
+ vector vWxMembers;
+ wstringstream wss(String2Wstring(wxids));
+ while (wss.good()) {
+ wstring wstr;
+ getline(wss, wstr, L',');
+ vMembers.push_back(wstr);
+ WxString wxMember(vMembers.back());
+ vWxMembers.push_back(wxMember);
+ }
+
+ WxString *pWxRoomid = NewWxStringFromStr(roomid);
+ QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
+
+ QWORD mgr = GetChatRoomMgr();
+ status = (int)DelMembers(mgr, pMembers, (QWORD)pWxRoomid);
+ return status;
+}
+
+int InviteChatroomMember(string roomid, string wxids)
+{
+ int status = -1;
+
+ if (roomid.empty() || wxids.empty()) {
+ LOG_ERROR("Empty roomid or wxids.");
+ return status;
+ }
+
+ InviteMemberToChatRoom_t InviteMembers = (InviteMemberToChatRoom_t)(g_WeChatWinDllAddr + OS_INVITE_MEMBERS);
+
+ vector vMembers;
+ vector vWxMembers;
+ wstringstream wss(String2Wstring(wxids));
+ while (wss.good()) {
+ wstring wstr;
+ getline(wss, wstr, L',');
+ vMembers.push_back(wstr);
+ WxString wxMember(vMembers.back());
+ vWxMembers.push_back(wxMember);
+ }
+ QWORD temp[2] = { 0 };
+ wstring wsRoomid = String2Wstring(roomid);
+ WxString *pWxRoomid = NewWxStringFromWstr(wsRoomid);
+ QWORD pMembers = (QWORD) & ((RawVector_t *)&vWxMembers)->start;
+
+ status = (int)InviteMembers((QWORD)wsRoomid.c_str(), pMembers, (QWORD)pWxRoomid, (QWORD)temp);
+ return status;
}
-#endif
diff --git a/WeChatFerry/spy/contact_mgmt.cpp b/WeChatFerry/spy/contact_mgmt.cpp
index 2039a40..3b7b559 100644
--- a/WeChatFerry/spy/contact_mgmt.cpp
+++ b/WeChatFerry/spy/contact_mgmt.cpp
@@ -1,25 +1,37 @@
#pragma execution_character_set("utf-8")
#include "contact_mgmt.h"
-#include "load_calls.h"
#include "log.h"
#include "util.h"
using namespace std;
-extern WxCalls_t g_WxCalls;
-extern UINT64 g_WeChatWinDllAddr;
-#if 0
+extern QWORD g_WeChatWinDllAddr;
+
+#define OS_GET_CONTACT_MGR 0x1C0BDE0
+#define OS_GET_CONTACT_LIST 0x2265540
+#define OS_CONTACT_BIN 0x200
+#define OS_CONTACT_BIN_LEN 0x208
+#define OS_CONTACT_WXID 0x10
+#define OS_CONTACT_CODE 0x30
+#define OS_CONTACT_REMARK 0x80
+#define OS_CONTACT_NAME 0xA0
+#define OS_CONTACT_GENDER 0x0E
+#define OS_CONTACT_STEP 0x6A8
+
+typedef QWORD (*GetContactMgr_t)();
+typedef QWORD (*GetContactList_t)(QWORD, QWORD);
+
#define FEAT_LEN 5
static const uint8_t FEAT_COUNTRY[FEAT_LEN] = { 0xA4, 0xD9, 0x02, 0x4A, 0x18 };
static const uint8_t FEAT_PROVINCE[FEAT_LEN] = { 0xE2, 0xEA, 0xA8, 0xD1, 0x18 };
static const uint8_t FEAT_CITY[FEAT_LEN] = { 0x1D, 0x02, 0x5B, 0xBF, 0x18 };
-static DWORD FindMem(DWORD start, DWORD end, const void *target, size_t len)
+static QWORD FindMem(QWORD start, QWORD end, const void *target, size_t len)
{
uint8_t *p = (uint8_t *)start;
- while ((DWORD)p < end) {
+ while ((QWORD)p < end) {
if (memcmp((void *)p, target, len) == 0) {
- return (DWORD)p;
+ return (QWORD)p;
}
p++;
}
@@ -27,9 +39,9 @@ static DWORD FindMem(DWORD start, DWORD end, const void *target, size_t len)
return 0;
}
-static string GetCntString(DWORD start, DWORD end, const uint8_t *feat, size_t len)
+static string GetCntString(QWORD start, QWORD end, const uint8_t *feat, size_t len)
{
- DWORD pfeat = FindMem(start, end, feat, len);
+ QWORD pfeat = FindMem(start, end, feat, len);
if (pfeat == 0) {
return "";
}
@@ -45,34 +57,27 @@ static string GetCntString(DWORD start, DWORD end, const uint8_t *feat, size_t l
vector GetContacts()
{
vector contacts;
- DWORD call1 = g_WeChatWinDllAddr + g_WxCalls.contact.base;
- DWORD call2 = g_WeChatWinDllAddr + g_WxCalls.contact.head;
+ GetContactMgr_t funcGetContactMgr = (GetContactMgr_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_MGR);
+ GetContactList_t funcGetContactList = (GetContactList_t)(g_WeChatWinDllAddr + OS_GET_CONTACT_LIST);
- int success = 0;
- DWORD *addr[3] = { 0, 0, 0 };
- __asm {
- pushad
- call call1
- lea ecx,addr
- push ecx
- mov ecx,eax
- call call2
- mov success,eax
- popad
+ QWORD mgr = funcGetContactMgr();
+ QWORD addr[3] = { 0 };
+ if (funcGetContactList(mgr, (QWORD)addr) != 1) {
+ LOG_ERROR("GetContacts failed");
+ return contacts;
}
- DWORD pstart = (DWORD)addr[0];
- DWORD pend = (DWORD)addr[2];
-
+ QWORD pstart = (QWORD)addr[0];
+ QWORD pend = (QWORD)addr[2];
while (pstart < pend) {
RpcContact_t cnt;
- DWORD pbin = GET_DWORD(pstart + 0x150);
- DWORD lenbin = GET_DWORD(pstart + 0x154);
+ QWORD pbin = GET_QWORD(pstart + OS_CONTACT_BIN);
+ QWORD lenbin = GET_DWORD(pstart + OS_CONTACT_BIN_LEN);
- cnt.wxid = GetStringByAddress(pstart + g_WxCalls.contact.wxId);
- cnt.code = GetStringByAddress(pstart + g_WxCalls.contact.wxCode);
- cnt.remark = GetStringByAddress(pstart + g_WxCalls.contact.wxRemark);
- cnt.name = GetStringByAddress(pstart + g_WxCalls.contact.wxName);
+ cnt.wxid = GetStringByWstrAddr(pstart + OS_CONTACT_WXID);
+ cnt.code = GetStringByWstrAddr(pstart + OS_CONTACT_CODE);
+ cnt.remark = GetStringByWstrAddr(pstart + OS_CONTACT_REMARK);
+ cnt.name = GetStringByWstrAddr(pstart + OS_CONTACT_NAME);
cnt.country = GetCntString(pbin, pbin + lenbin, FEAT_COUNTRY, FEAT_LEN);
cnt.province = GetCntString(pbin, pbin + lenbin, FEAT_PROVINCE, FEAT_LEN);
@@ -81,16 +86,17 @@ vector GetContacts()
if (pbin == 0) {
cnt.gender = 0;
} else {
- cnt.gender = (DWORD) * (uint8_t *)(pbin + g_WxCalls.contact.wxGender);
+ cnt.gender = (DWORD) * (uint8_t *)(pbin + OS_CONTACT_GENDER);
}
contacts.push_back(cnt);
- pstart += 0x438;
+ pstart += OS_CONTACT_STEP;
}
return contacts;
}
+#if 0
int AcceptNewFriend(string v3, string v4, int scene)
{
int success = 0;
diff --git a/WeChatFerry/spy/exec_sql.cpp b/WeChatFerry/spy/exec_sql.cpp
index 9065a35..5de9628 100644
--- a/WeChatFerry/spy/exec_sql.cpp
+++ b/WeChatFerry/spy/exec_sql.cpp
@@ -1,49 +1,48 @@
#include
#include "exec_sql.h"
-#include "load_calls.h"
+#include "log.h"
#include "sqlite3.h"
#include "util.h"
-#define OFFSET_DB_INSTANCE 0x2FFDDC8
-#define OFFSET_DB_MICROMSG 0x68
-#define OFFSET_DB_CHAT_MSG 0x1C0
-#define OFFSET_DB_MISC 0x3D8
-#define OFFSET_DB_EMOTION 0x558
-#define OFFSET_DB_MEDIA 0x9B8
-#define OFFSET_DB_BIZCHAT_MSG 0x1120
-#define OFFSET_DB_FUNCTION_MSG 0x11B0
-#define OFFSET_DB_NAME 0x14
-#define OFFSET_DB_MSG_MGR 0x30403B8
+#define OFFSET_DB_INSTANCE 0x5A40598
+#define OFFSET_DB_MICROMSG 0xb8
+#define OFFSET_DB_CHAT_MSG 0x2c8
+#define OFFSET_DB_MISC 0x5f0
+#define OFFSET_DB_EMOTION 0x15f0
+#define OFFSET_DB_MEDIA 0xF48
+#define OFFSET_DB_BIZCHAT_MSG 0x1A70
+#define OFFSET_DB_FUNCTION_MSG 0x1b98
+#define OFFSET_DB_NAME 0x28
+#define OFFSET_DB_MSG_MGR 0x5ABB5D8
extern UINT64 g_WeChatWinDllAddr;
-typedef map dbMap_t;
+typedef map dbMap_t;
static dbMap_t dbMap;
-#if 0
-static void GetDbHandle(DWORD base, DWORD offset)
+
+static void GetDbHandle(QWORD base, QWORD offset)
{
- wchar_t *wsp;
- wsp = (wchar_t *)(*(DWORD *)(base + offset + OFFSET_DB_NAME));
+ wchar_t *wsp = (wchar_t *)(*(QWORD *)(base + offset + OFFSET_DB_NAME));
string dbname = Wstring2String(wstring(wsp));
- dbMap[dbname] = GET_DWORD(base + offset);
+ dbMap[dbname] = GET_QWORD(base + offset);
}
-static void GetMsgDbHandle(DWORD msgMgrAddr)
+static void GetMsgDbHandle(QWORD msgMgrAddr)
{
- DWORD dbIndex = GET_DWORD(msgMgrAddr + 0x38);
- DWORD pStart = GET_DWORD(msgMgrAddr + 0x2C);
+ QWORD dbIndex = GET_QWORD(msgMgrAddr + 0x68);
+ QWORD pStart = GET_QWORD(msgMgrAddr + 0x50);
for (uint32_t i = 0; i < dbIndex; i++) {
- DWORD dbAddr = GET_DWORD(pStart + i * 0x04);
+ QWORD dbAddr = GET_QWORD(pStart + i * 0x08);
if (dbAddr) {
// MSGi.db
string dbname = Wstring2String(GET_WSTRING(dbAddr));
- dbMap[dbname] = GET_DWORD(dbAddr + 0x60);
+ dbMap[dbname] = GET_QWORD(dbAddr + 0x78);
// MediaMsgi.db
- DWORD mmdbAddr = GET_DWORD(dbAddr + 0x14);
- string mmdbname = Wstring2String(GET_WSTRING(mmdbAddr + 0x4C));
- dbMap[mmdbname] = GET_DWORD(mmdbAddr + 0x38);
+ QWORD mmdbAddr = GET_QWORD(dbAddr + 0x20);
+ string mmdbname = Wstring2String(GET_WSTRING(mmdbAddr + 0x78));
+ dbMap[mmdbname] = GET_QWORD(mmdbAddr + 0x50);
}
}
}
@@ -52,7 +51,7 @@ dbMap_t GetDbHandles()
{
dbMap.clear();
- DWORD dbInstanceAddr = GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE);
+ QWORD dbInstanceAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_INSTANCE);
GetDbHandle(dbInstanceAddr, OFFSET_DB_MICROMSG); // MicroMsg.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_CHAT_MSG); // ChatMsg.db
@@ -61,7 +60,7 @@ dbMap_t GetDbHandles()
GetDbHandle(dbInstanceAddr, OFFSET_DB_MEDIA); // Media.db
GetDbHandle(dbInstanceAddr, OFFSET_DB_FUNCTION_MSG); // Function.db
- GetMsgDbHandle(GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db
+ GetMsgDbHandle(GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR)); // MSGi.db & MediaMsgi.db
return dbMap;
}
@@ -133,7 +132,13 @@ DbRows_t ExecDbQuery(const string db, const string sql)
dbMap = GetDbHandles();
}
- DWORD *stmt;
+ QWORD *stmt;
+ QWORD handle = dbMap[db];
+ if (handle == 0) {
+ LOG_WARN("Empty handle, retrying...");
+ dbMap = GetDbHandles();
+ }
+
int rc = func_prepare(dbMap[db], sql.c_str(), -1, &stmt, 0);
if (rc != SQLITE_OK) {
return rows;
@@ -162,16 +167,16 @@ DbRows_t ExecDbQuery(const string db, const string sql)
int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
{
- DWORD msgMgrAddr = GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
- DWORD dbIndex = GET_DWORD(msgMgrAddr + 0x38);
- DWORD pStart = GET_DWORD(msgMgrAddr + 0x2C);
+ QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
+ int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68); // 总不能 int 还不够吧?
+ QWORD pStart = GET_QWORD(msgMgrAddr + 0x50);
*dbIdx = 0;
for (int i = dbIndex - 1; i >= 0; i--) { // 从后往前遍历
- DWORD dbAddr = GET_DWORD(pStart + i * 0x04);
+ QWORD dbAddr = GET_QWORD(pStart + i * 0x08);
if (dbAddr) {
string dbname = Wstring2String(GET_WSTRING(dbAddr));
- dbMap[dbname] = GET_DWORD(dbAddr + 0x60);
+ dbMap[dbname] = GET_QWORD(dbAddr + 0x78);
string sql = "SELECT localId FROM MSG WHERE MsgSvrID=" + to_string(id) + ";";
DbRows_t rows = ExecDbQuery(dbname, sql);
if (rows.empty()) {
@@ -187,7 +192,7 @@ int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
}
*localId = strtoull((const char *)(field.content.data()), NULL, 10);
- *dbIdx = GET_DWORD(GET_DWORD(dbAddr + 0x18) + 0x144);
+ *dbIdx = (uint32_t)(GET_QWORD(GET_QWORD(dbAddr + 0x28) + 0x1E8) >> 32);
return 0;
}
@@ -198,10 +203,10 @@ int GetLocalIdandDbidx(uint64_t id, uint64_t *localId, uint32_t *dbIdx)
vector GetAudioData(uint64_t id)
{
- DWORD msgMgrAddr = GET_DWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
- DWORD dbIndex = GET_DWORD(msgMgrAddr + 0x38);
+ QWORD msgMgrAddr = GET_QWORD(g_WeChatWinDllAddr + OFFSET_DB_MSG_MGR);
+ int dbIndex = (int)GET_QWORD(msgMgrAddr + 0x68);
- string sql = "SELECT Buf from Media WHERE Reserved0=" + to_string(id) + ";";
+ string sql = "SELECT Buf FROM Media WHERE Reserved0=" + to_string(id) + ";";
for (int i = dbIndex - 1; i >= 0; i--) {
string dbname = "MediaMSG" + to_string(i) + ".db";
DbRows_t rows = ExecDbQuery(dbname, sql);
@@ -225,4 +230,3 @@ vector GetAudioData(uint64_t id)
return vector();
}
-#endif
diff --git a/WeChatFerry/spy/funcs.cpp b/WeChatFerry/spy/funcs.cpp
index b03bb51..9ff3edc 100644
--- a/WeChatFerry/spy/funcs.cpp
+++ b/WeChatFerry/spy/funcs.cpp
@@ -11,6 +11,12 @@
#include "spy_types.h"
#include "util.h"
+using namespace std;
+namespace fs = std::filesystem;
+
+extern bool gIsListeningPyq;
+extern QWORD g_WeChatWinDllAddr;
+
#define HEADER_PNG1 0x89
#define HEADER_PNG2 0x50
#define HEADER_JPG1 0xFF
@@ -18,16 +24,33 @@
#define HEADER_GIF1 0x47
#define HEADER_GIF2 0x49
-using namespace std;
-namespace fs = std::filesystem;
+#define OS_LOGIN_STATUS 0x5AB86A8
+#define OS_GET_SNS_DATA_MGR 0x22A91C0
+#define OS_GET_SNS_FIRST_PAGE 0x2ED9080
+#define OS_GET_SNS_TIMELINE_MGR 0x2E6B110
+#define OS_GET_SNS_NEXT_PAGE 0x2EFEC00
+#define OS_NEW_CHAT_MSG 0x1C28800
+#define OS_FREE_CHAT_MSG 0x1C1FF10
+#define OS_GET_CHAT_MGR 0x1C51CF0
+#define OS_GET_MGR_BY_PREFIX_LOCAL_ID 0x2206280
+#define OS_GET_PRE_DOWNLOAD_MGR 0x1CD87E0
+#define OS_PUSH_ATTACH_TASK 0x1DA69C0
-extern bool gIsListeningPyq;
-extern WxCalls_t g_WxCalls;
-extern UINT64 g_WeChatWinDllAddr;
+typedef QWORD (*GetSNSDataMgr_t)();
+typedef QWORD (*GetSnsTimeLineMgr_t)();
+typedef QWORD (*GetSNSFirstPage_t)(QWORD, QWORD, QWORD);
+typedef QWORD (*GetSNSNextPageScene_t)(QWORD, QWORD);
+typedef QWORD (*GetChatMgr_t)();
+typedef QWORD (*NewChatMsg_t)(QWORD);
+typedef QWORD (*FreeChatMsg_t)(QWORD);
+typedef QWORD (*GetPreDownLoadMgr_t)();
+typedef QWORD (*GetMgrByPrefixLocalId_t)(QWORD, QWORD);
+typedef QWORD (*PushAttachTask_t)(QWORD, QWORD, QWORD, QWORD);
+typedef QWORD (*GetOCRManager_t)();
+typedef QWORD (*DoOCRTask_t)(QWORD, QWORD, QWORD, QWORD, QWORD, QWORD);
-int IsLogin(void) { return (int)GET_UINT64(g_WeChatWinDllAddr + g_WxCalls.login); }
+int IsLogin(void) { return (int)GET_QWORD(g_WeChatWinDllAddr + OS_LOGIN_STATUS); }
-#if 0
static string get_key(uint8_t header1, uint8_t header2, uint8_t *key)
{
// PNG?
@@ -54,6 +77,7 @@ static string get_key(uint8_t header1, uint8_t header2, uint8_t *key)
string DecryptImage(string src, string dir)
{
if (!fs::exists(src)) {
+ LOG_ERROR("File not exists: {}", src);
return "";
}
@@ -116,53 +140,32 @@ string DecryptImage(string src, string dir)
static int GetFirstPage()
{
- int rv = -1;
- DWORD pyqCall1 = g_WeChatWinDllAddr + g_WxCalls.pyq.call1;
- DWORD pyqCall2 = g_WeChatWinDllAddr + g_WxCalls.pyq.call2;
+ int status = -1;
- char buf[0xB44] = { 0 };
- __asm {
- pushad;
- call pyqCall1;
- push 0x1;
- lea ecx, buf;
- push ecx;
- mov ecx, eax;
- call pyqCall2;
- mov rv, eax;
- popad;
- }
+ GetSNSDataMgr_t GetSNSDataMgr = (GetSNSDataMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_DATA_MGR);
+ GetSNSFirstPage_t GetSNSFirstPage = (GetSNSFirstPage_t)(g_WeChatWinDllAddr + OS_GET_SNS_FIRST_PAGE);
- return rv;
+ QWORD buff[16] = { 0 };
+ QWORD mgr = GetSNSDataMgr();
+ status = (int)GetSNSFirstPage(mgr, (QWORD)buff, 1);
+
+ return status;
}
-static int GetNextPage(uint64_t id)
+static int GetNextPage(QWORD id)
{
- int rv = -1;
- DWORD pyqCall1 = g_WeChatWinDllAddr + g_WxCalls.pyq.call1;
- DWORD pyqCall3 = g_WeChatWinDllAddr + g_WxCalls.pyq.call3;
+ int status = -1;
- RawVector_t tmp = { 0 };
+ GetSnsTimeLineMgr_t GetSnsTimeLineMgr = (GetSnsTimeLineMgr_t)(g_WeChatWinDllAddr + OS_GET_SNS_TIMELINE_MGR);
+ GetSNSNextPageScene_t GetSNSNextPageScene = (GetSNSNextPageScene_t)(g_WeChatWinDllAddr + OS_GET_SNS_NEXT_PAGE);
- __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;
- }
+ QWORD mgr = GetSnsTimeLineMgr();
+ status = (int)GetSNSNextPageScene(mgr, id);
- return rv;
+ return status;
}
-int RefreshPyq(uint64_t id)
+int RefreshPyq(QWORD id)
{
if (!gIsListeningPyq) {
LOG_ERROR("没有启动朋友圈消息接收,参考:enable_receiving_msg");
@@ -176,13 +179,21 @@ int RefreshPyq(uint64_t id)
return GetNextPage(id);
}
-int DownloadAttach(uint64_t id, string thumb, string extra)
+/*******************************************************************************
+ * 都说我不写注释,写一下吧
+ * 其实也没啥好写的,就是下载资源
+ * 主要介绍一下几个参数:
+ * id:好理解,消息 id
+ * thumb:图片或者视频的缩略图路径;如果是视频,后缀为 mp4 后就是存在路径了
+ * extra:图片、文件的路径
+ *******************************************************************************/
+int DownloadAttach(QWORD id, string thumb, string extra)
{
int status = -1;
- uint64_t localId;
+ QWORD localId;
uint32_t dbIdx;
- if (fs::exists(extra)) { // 第一道,不重复下载
+ if (fs::exists(extra)) { // 第一道,不重复下载。TODO: 通过文件大小来判断
return 0;
}
@@ -191,30 +202,29 @@ int DownloadAttach(uint64_t id, string thumb, string extra)
return status;
}
- char buff[0x2D8] = { 0 };
- DWORD dlCall1 = g_WeChatWinDllAddr + g_WxCalls.da.call1;
- DWORD dlCall2 = g_WeChatWinDllAddr + g_WxCalls.da.call2;
- DWORD dlCall3 = g_WeChatWinDllAddr + g_WxCalls.da.call3;
- DWORD dlCall4 = g_WeChatWinDllAddr + g_WxCalls.da.call4;
- DWORD dlCall5 = g_WeChatWinDllAddr + g_WxCalls.da.call5;
- DWORD dlCall6 = g_WeChatWinDllAddr + g_WxCalls.da.call6;
+ NewChatMsg_t NewChatMsg = (NewChatMsg_t)(g_WeChatWinDllAddr + OS_NEW_CHAT_MSG);
+ FreeChatMsg_t FreeChatMsg = (FreeChatMsg_t)(g_WeChatWinDllAddr + OS_FREE_CHAT_MSG);
+ GetChatMgr_t GetChatMgr = (GetChatMgr_t)(g_WeChatWinDllAddr + OS_GET_CHAT_MGR);
+ GetPreDownLoadMgr_t GetPreDownLoadMgr = (GetPreDownLoadMgr_t)(g_WeChatWinDllAddr + OS_GET_PRE_DOWNLOAD_MGR);
+ PushAttachTask_t PushAttachTask = (PushAttachTask_t)(g_WeChatWinDllAddr + OS_PUSH_ATTACH_TASK);
+ GetMgrByPrefixLocalId_t GetMgrByPrefixLocalId
+ = (GetMgrByPrefixLocalId_t)(g_WeChatWinDllAddr + OS_GET_MGR_BY_PREFIX_LOCAL_ID);
- __asm {
- pushad;
- pushfd;
- lea ecx, buff;
- call dlCall1;
- call dlCall2;
- push dword ptr [dbIdx];
- lea ecx, buff;
- push dword ptr [localId];
- call dlCall3;
- add esp, 0x8;
- popfd;
- popad;
+ LARGE_INTEGER l;
+ l.HighPart = dbIdx;
+ l.LowPart = (DWORD)localId;
+
+ char *buff = (char *)HeapAlloc(GetProcessHeap(), 0, 0x460);
+ if (buff == nullptr) {
+ LOG_ERROR("Failed to allocate memory.");
+ return status;
}
- DWORD type = GET_DWORD(buff + 0x38);
+ QWORD pChatMsg = NewChatMsg((QWORD)buff);
+ GetChatMgr();
+ GetMgrByPrefixLocalId(l.QuadPart, pChatMsg);
+
+ QWORD type = GET_QWORD(buff + 0x38);
string save_path = "";
string thumb_path = "";
@@ -238,7 +248,7 @@ int DownloadAttach(uint64_t id, string thumb, string extra)
break;
}
- if (fs::exists(save_path)) { // 不重复下载
+ if (fs::exists(save_path)) { // 不重复下载。TODO: 通过文件大小来判断
return 0;
}
@@ -246,84 +256,22 @@ int DownloadAttach(uint64_t id, string thumb, string extra)
// 创建父目录,由于路径来源于微信,不做检查
fs::create_directory(fs::path(save_path).parent_path().string());
- wstring wsSavePath = String2Wstring(save_path);
- wstring wsThumbPath = String2Wstring(thumb_path);
+ int temp = 1;
+ WxString *pSavePath = NewWxStringFromStr(save_path);
+ WxString *pThumbPath = NewWxStringFromStr(thumb_path);
- WxString wxSavePath(wsSavePath);
- WxString wxThumbPath(wsThumbPath);
+ memcpy(&buff[0x280], pThumbPath, sizeof(WxString));
+ memcpy(&buff[0x2A0], pSavePath, sizeof(WxString));
+ memcpy(&buff[0x40C], &temp, sizeof(temp));
- int temp = 1;
- memcpy(&buff[0x19C], &wxThumbPath, sizeof(wxThumbPath));
- memcpy(&buff[0x1B0], &wxSavePath, sizeof(wxSavePath));
- memcpy(&buff[0x29C], &temp, sizeof(temp));
-
- __asm {
- pushad;
- pushfd;
- call dlCall4;
- push 0x1;
- push 0x0;
- lea ecx, buff;
- push ecx;
- mov ecx, eax;
- call dlCall5;
- mov status, eax;
- lea ecx, buff;
- push 0x0;
- call dlCall6;
- popfd;
- popad;
- }
+ QWORD mgr = GetPreDownLoadMgr();
+ status = (int)PushAttachTask(mgr, pChatMsg, 0, 1);
+ FreeChatMsg(pChatMsg);
return status;
}
-int RevokeMsg(uint64_t id)
-{
- int status = -1;
- uint64_t localId;
- uint32_t dbIdx;
- if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
- LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
- return status;
- }
-
- char chat_msg[0x2D8] = { 0 };
-
- DWORD rmCall1 = g_WeChatWinDllAddr + g_WxCalls.rm.call1;
- DWORD rmCall2 = g_WeChatWinDllAddr + g_WxCalls.rm.call2;
- DWORD rmCall3 = g_WeChatWinDllAddr + g_WxCalls.rm.call3;
- DWORD rmCall4 = g_WeChatWinDllAddr + g_WxCalls.rm.call4;
- DWORD rmCall5 = g_WeChatWinDllAddr + g_WxCalls.rm.call5;
-
- __asm {
- pushad;
- pushfd;
- lea ecx, chat_msg;
- call rmCall1;
- call rmCall2;
- push dword ptr [dbIdx];
- lea ecx, chat_msg;
- push dword ptr [localId];
- call rmCall3;
- add esp, 0x8;
- call rmCall2;
- lea ecx, chat_msg;
- push ecx;
- mov ecx, eax;
- call rmCall4;
- mov status, eax;
- lea ecx, chat_msg;
- push 0x0;
- call rmCall5;
- popfd;
- popad;
- }
-
- return status;
-}
-
-string GetAudio(uint64_t id, string dir)
+string GetAudio(QWORD id, string dir)
{
string mp3path = (dir.back() == '\\' || dir.back() == '/') ? dir : (dir + "/");
mp3path += to_string(id) + ".mp3";
@@ -346,104 +294,66 @@ string GetAudio(uint64_t id, string dir)
OcrResult_t GetOcrResult(string path)
{
OcrResult_t ret = { -1, "" };
-
+#if 0 // 参数没调好,会抛异常,看看有没有好心人来修复
if (!fs::exists(path)) {
LOG_ERROR("Can not find: {}", path);
return ret;
}
+ GetOCRManager_t GetOCRManager = (GetOCRManager_t)(g_WeChatWinDllAddr + 0x1D6C3C0);
+ DoOCRTask_t DoOCRTask = (DoOCRTask_t)(g_WeChatWinDllAddr + 0x2D10BC0);
+
+ QWORD unk1 = 0, unk2 = 0, unused = 0;
+ QWORD *pUnk1 = &unk1;
+ QWORD *pUnk2 = &unk2;
// 路径分隔符有要求,必须为 `\`
wstring wsPath = String2Wstring(fs::path(path).make_preferred().string());
-
WxString wxPath(wsPath);
- WxString nullObj;
- WxString ocrBuffer;
+ vector *pv = (vector *)HeapAlloc(GetProcessHeap(), 0, 0x20);
+ RawVector_t *pRv = (RawVector_t *)pv;
+ pRv->finish = pRv->start;
+ char buff[0x98] = { 0 };
+ memcpy(buff, &pRv->start, sizeof(QWORD));
- DWORD ocrCall1 = g_WeChatWinDllAddr + g_WxCalls.ocr.call1;
- DWORD ocrCall2 = g_WeChatWinDllAddr + g_WxCalls.ocr.call2;
- DWORD ocrCall3 = g_WeChatWinDllAddr + g_WxCalls.ocr.call3;
+ QWORD mgr = GetOCRManager();
+ ret.status = (int)DoOCRTask(mgr, (QWORD)&wxPath, unused, (QWORD)buff, (QWORD)&pUnk1, (QWORD)&pUnk2);
- DWORD tmp = 0;
- int status = -1;
- __asm {
- pushad;
- pushfd;
- lea ecx, ocrBuffer;
- call ocrCall1;
- call ocrCall2;
- lea ecx, nullObj;
- push ecx;
- lea ecx, tmp;
- push ecx;
- lea ecx, ocrBuffer;
- push ecx;
- push 0x0;
- lea ecx, wxPath;
- push ecx;
- mov ecx, eax;
- call ocrCall3;
- mov status, eax;
- popfd;
- popad;
+ QWORD count = GET_QWORD(buff + 0x8);
+ if (count > 0) {
+ QWORD header = GET_QWORD(buff);
+ for (QWORD i = 0; i < count; i++) {
+ QWORD content = GET_QWORD(header);
+ ret.result += Wstring2String(GET_WSTRING(content + 0x28));
+ ret.result += "\n";
+ header = content;
+ }
}
-
- if (status != 0)
- {
- LOG_ERROR("OCR status: {}", to_string(status));
- return ret; // 识别出错
- }
-
- ret.status = status;
-
- DWORD addr = (DWORD)&ocrBuffer;
- DWORD header = GET_DWORD(addr);
- DWORD num = GET_DWORD(addr + 0x4);
- if (num <= 0) {
- return ret; // 识别内容为空
- }
-
- for (uint32_t i = 0; i < num; i++) {
- DWORD content = GET_DWORD(header);
- ret.result += Wstring2String(GET_WSTRING(content + 0x14));
- ret.result += "\n";
- header = content;
- }
-
+#endif
return ret;
}
+int RevokeMsg(QWORD id)
+{
+ int status = -1;
+#if 0 // 这个挺鸡肋的,因为自己发的消息没法直接获得 msgid,就这样吧
+ QWORD localId;
+ uint32_t dbIdx;
+ if (GetLocalIdandDbidx(id, &localId, &dbIdx) != 0) {
+ LOG_ERROR("Failed to get localId, Please check id: {}", to_string(id));
+ return status;
+ }
+#endif
+ return status;
+}
+
string GetLoginUrl()
{
- if (GET_DWORD(g_WeChatWinDllAddr + g_WxCalls.login) == 1) {
- LOG_DEBUG("Already logined.");
- return ""; // 已登录直接返回空字符
- }
-
- DWORD refreshLoginQrcodeCall1 = g_WeChatWinDllAddr + g_WxCalls.rlq.call1;
- DWORD refreshLoginQrcodeCall2 = g_WeChatWinDllAddr + g_WxCalls.rlq.call2;
-
- // 刷新二维码
- __asm {
- pushad;
- pushfd;
- call refreshLoginQrcodeCall1;
- mov ecx, eax;
- call refreshLoginQrcodeCall2;
- popfd;
- popad;
- }
-
- // 获取二维码链接
- char *url = GET_STRING(g_WeChatWinDllAddr + g_WxCalls.rlq.url);
- uint8_t cnt = 0;
- while (url[0] == 0) { // 刷新需要时间,太快了会获取不到
- if (cnt > 5) {
- LOG_ERROR("Refresh QR Code timeout.");
- return "";
- }
- Sleep(1000);
- cnt++;
- }
+ char url[] = "方法还没实现";
return "http://weixin.qq.com/x/" + string(url);
}
-#endif
+
+int ReceiveTransfer(string wxid, string transferid, string transactionid)
+{
+ // 别想了,这个不实现了
+ return -1;
+}
diff --git a/WeChatFerry/spy/funcs.h b/WeChatFerry/spy/funcs.h
index 2b33509..a41a77b 100644
--- a/WeChatFerry/spy/funcs.h
+++ b/WeChatFerry/spy/funcs.h
@@ -11,3 +11,4 @@ int DownloadAttach(uint64_t id, std::string thumb, std::string extra);
int RevokeMsg(uint64_t id);
OcrResult_t GetOcrResult(std::string path);
string GetLoginUrl();
+int ReceiveTransfer(std::string wxid, std::string transferid, std::string transactionid);
diff --git a/WeChatFerry/spy/load_calls.cpp b/WeChatFerry/spy/load_calls.cpp
deleted file mode 100644
index 0bffe4f..0000000
--- a/WeChatFerry/spy/load_calls.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#include
-#include