Merge branch 'main' of github.com:f1ynng8/wechat_chatter

This commit is contained in:
f1ynng8
2026-06-20 22:27:34 +08:00
19 changed files with 1252 additions and 321 deletions
+2
View File
@@ -1,4 +1,6 @@
# WeChat 4.0 Message hook
4.1.9之后的不更新了,感谢各位大佬的支持
本代码库中的所有代码、示例、文档及相关内容(以下简称“本项目”)仅供学习、研究和技术交流之目的使用。使用本项目所产生的任何风险(包括但不限于数据丢失、系统崩溃、安全问题、法律风险等)均由使用者自行承担。
我hook的是微信三端最底层的发消息能力,这块代码是开源的,感兴趣google tencent/mars
+3 -3
View File
@@ -2,9 +2,9 @@
图片hook: startUploadMedia ida搜), OnUploadCompleted ce查)
startUploadMedia 上传图片,OnUploadCompleted 上传图片完成后的回调
加密后的图片在 _OnRecvFileData
download系列都是_OnRecvFileData,在不同的文件下面
var downloadImagAddr // image_download
var downloadFileAddr // c2c_download
var downloadFileAddr // c2c_download 文件和缩略图
var downloadImagAddr // image_download 高清图
var downloadVideoAddr // hdvideo_streaming OnRecvedData
WeChatExt中会有导致idapro出问题的代码
+12 -12
View File
@@ -23,10 +23,7 @@ function scanPatterns() {
console.log(`"${name}": "${offset}", 绝对地址: ${address}`);
break;
case "downloadFileAddr":
console.log(`"${name}": "${offset.sub(0x3c)}", 绝对地址: ${address.sub(0x3c)}`);
break;
case "downloadImageAddr":
console.log(`"${name}": "${offset.sub(0x18)}", 绝对地址: ${address.sub(0x18)}`);
console.log(`"${name}": "${offset.sub(0x08)}", 绝对地址: ${address.sub(0x08)}`);
break;
case "uploadImageAddr":
console.log(`"${name}": "${offset.sub(0x30)}", 绝对地址: ${address.sub(0x30)}`);
@@ -35,7 +32,7 @@ function scanPatterns() {
console.log(`"${name}": "${offset.sub(0x44)}", 绝对地址: ${address.sub(0x44)}`);
break;
case "buf2RespAddr":
console.log(`"${name}": "${offset.sub(0x10)}", 绝对地址: ${address.sub(0x10)}`);
console.log(`"${name}": "${offset.sub(0x14)}", 绝对地址: ${address.sub(0x14)}`);
break;
case "startDownloadMedia":
console.log(`"${name}": "${offset.sub(0x28)}", 绝对地址: ${address.sub(0x28)}`);
@@ -43,6 +40,9 @@ function scanPatterns() {
case "downloadVideoAddr":
console.log(`"${name}": "${offset.sub(0x2c)}", 绝对地址: ${address.sub(0x2c)}`);
break;
case "downloadImageAddr":
console.log(`"${name}": "${offset.sub(0x18)}", 绝对地址: ${address.sub(0x18)}`);
break;
}
},
onError: function(reason) {
@@ -67,13 +67,13 @@ const myPatterns = [
name: "req2bufEnterAddr",
pattern: "09 0F 46 F8 C9 01 00 B4 E8 03 18 AA 2A 21 40 B9"
},
{
name: "downloadImageAddr",
pattern: "68 52 41 F9 19 75 41 F9 1A 00 14 8B 5F 03 19 EB"
},
{
name: "downloadFileAddr",
pattern: "F5 03 00 AA 68 4A 41 F9 08 41 46 39 60 22 01 91 69 52 41 F9"
},
{
name: "downloadImageAddr",
pattern: "68 52 41 F9 19 8D 41 F9 1A 00 14 8B 5F 03 19 EB"
pattern: "FF 07 00 B9 68 52 41 F9 15 71 41 F9 E0 03 14 AA"
},
{
name: "uploadImageAddr",
@@ -85,7 +85,7 @@ const myPatterns = [
},
{
name: "buf2RespAddr",
pattern: "3C 00 80 52 E0 C3 00 91"
pattern: "E8 DF C1 39 28 E6 FF 36 E0 33 40 F9 E8 3B 40 F9"
},
{
name: "startDownloadMedia",
@@ -93,7 +93,7 @@ const myPatterns = [
},
{
name: "downloadVideoAddr",
pattern: "E0 03 07 91 01 00 80 52 45 C1 80 52 06 00 80 52 07 00 80 D2"
pattern: "E0 03 01 91 01 00 80 52 A5 5B 80 52 06 00 80 52"
}
];
+218 -195
View File
@@ -1,6 +1,9 @@
// 1. 获取微信主模块的基地址
var moduleName = "wechat.dylib";
var baseAddr = Process.findModuleByName(moduleName).base;
var targetPath = "/Applications/WeChat.app/Contents/Resources/wechat.dylib";
var module = Process.enumerateModules().find(function(m) {
return m.path === targetPath;
});
const baseAddr = module.base;
if (!baseAddr) {
console.error("[!] 找不到 WeChat 模块基址,请检查进程名。");
}
@@ -103,14 +106,14 @@ function generateBytes(n) {
// 双方公共使用的地址
var triggerX1Payload;
var triggerX0;
var req2bufEnterAddr = baseAddr.add(0x388b400);
var req2bufExitAddr = baseAddr.add(0x388C514);
var sendFuncAddr = baseAddr.add(0x4a5e108);
var req2bufEnterAddr = baseAddr.add(0x39d51d8);
var req2bufExitAddr = baseAddr.add(0x39D62EC);
var sendFuncAddr = baseAddr.add(0x4bd0e1c);
var insertMsgAddr = ptr(0);
var sendMsgType = "";
// 图片消息全局变量
var imageCallbackFuncAddr = baseAddr.add(0x25A3944);
var imageCallbackFuncAddr = baseAddr.add(0x266E3A0);
var imgProtobufAddr = imageCallbackFuncAddr.add(0x50);
var patchImgProtobufFunc1 = imageCallbackFuncAddr.add(0x10);
var patchImgProtobufFunc1Byte;
@@ -120,7 +123,7 @@ var imgProtobufDeleteAddr = imageCallbackFuncAddr.add(0x6c);
var imgProtobufDeleteAddrByte;
// 视频消息全局变量
var videoCallbackFuncAddr = baseAddr.add(0x25EC2D4);
var videoCallbackFuncAddr = baseAddr.add(0x26DD8C8);
var videoProtobufAddr = videoCallbackFuncAddr.add(0x50);
var patchVideoProtobufFunc1 = videoCallbackFuncAddr.add(0x10);
var patchVideoProtobufFunc1Byte;
@@ -129,22 +132,22 @@ var patchVideoProtobufFunc2Byte;
var videoProtobufDeleteAddr = videoCallbackFuncAddr.add(0x6c);
var videoProtobufDeleteAddrByte;
var uploadImageAddr = baseAddr.add(0x4ba56cc);
var cndOnCompleteAddr = baseAddr.add(0x38487b8);
var imgMessageCallbackFunc1 = baseAddr.add(0x8A05298);
var videoMessageCallbackFunc1 = baseAddr.add(0x8A07018);
var uploadImageAddr = baseAddr.add(0x4d4bbd0);
var cndOnCompleteAddr = baseAddr.add(0x399180c);
var imgMessageCallbackFunc1 = baseAddr.add(0x8C2C348);
var videoMessageCallbackFunc1 = baseAddr.add(0x8C2F0F8);
var uploadGetCallbackWrapperAddr = baseAddr.add(0x4b73190);
var uploadGetCallbackWrapperFuncAddr = baseAddr.add(0x3848004);
var uploadOnCompleteAddr = baseAddr.add(0x4B7378C);
var uploadOnCompleteFuncAddr = baseAddr.add(0x38491E8);
var uploadGetCallbackWrapperAddr = baseAddr.add(0x4d0a928);
var uploadGetCallbackWrapperFuncAddr = baseAddr.add(0x3991058);
var uploadOnCompleteAddr = baseAddr.add(0x4D0B0E0);
var uploadOnCompleteFuncAddr = baseAddr.add(0x399223C);
var uploadImageX1 = ptr(0);
var imgCgiAddr = ptr(0);
var sendImgMessageAddr = ptr(0);
var imgMessageAddr = ptr(0);
var imgProtoX1PayloadAddr = ptr(0);
var uploadGlobalX0 = ptr(0)
var uploadGlobalX0;
var uploadFunc1Addr = ptr(0)
var uploadFunc2Addr = ptr(0)
var imageIdAddr = ptr(0)
@@ -913,100 +916,101 @@ setImmediate(attachVideoProto());
function triggerUploadImg(receiver, md5, imagePath) {
const payload = [
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 10802b8b0 的指针
0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 107fd5908 的指针
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40
0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68
0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37,
0x69, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0x13, // 发送人id长度
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x98
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xa0
0xA0, 0xBE, 0x2D, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0xa8
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xb8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xe0 图片地址 高清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_hd.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xf0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x108
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x110 图片地址 普清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x118
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x120
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x140 图片地址 缩略图 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_thumb.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x148
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x150
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x168
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x170
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x188
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x190
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x1a0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1a8
0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, // 0x1b0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1b8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c8
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d0
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1f0
0xD0, 0x78, 0x46, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x1f8 某个key ecd57e9cf85f2e2087aee8c0fd1e445e
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x208
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x220
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x228
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x230
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x238
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x240
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x248
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x250
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x258
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x270
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x278
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x280
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x2e8
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x00 函数指针1 (writePointer覆盖)
0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x08 函数指针2 (writePointer覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40
0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x48 videoId (writePointer覆盖)
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x58
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 发送人wxid (writeUtf8String覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, // 0x78 [0x7F]=长度0x13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90
0x01, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x98
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 md5 (writePointer覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xB8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE0
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xE8 视频路径 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xF8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x108
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x110
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x118 视频路径2 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x120
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x128
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x130
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x138
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x140
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x148 视频路径3 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x150
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x168
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, // 0x170 [0x174]=0x0B
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x188
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x190
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1B0
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0x1B8 [0x1BC]=0xAAAAAAAA
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D8
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200 aes key (writePointer覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x208
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x220
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x228
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x230
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x238
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x240
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x248
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x250
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x258
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x270
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x278
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x280
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C8
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x2F0
]
patchString(imageIdAddr, receiver + "_" + String(Math.floor(Date.now() / 1000)) + "_" + Math.floor(Math.random() * 1001) + "_1");
@@ -1020,10 +1024,10 @@ function triggerUploadImg(receiver, md5, imagePath) {
uploadImageX1.add(0x48).writePointer(imageIdAddr);
uploadImageX1.add(0x68).writeUtf8String(receiver);
uploadImageX1.add(0xa8).writePointer(md5Addr);
uploadImageX1.add(0xe0).writePointer(ImagePathAddr1);
uploadImageX1.add(0x110).writePointer(ImagePathAddr1);
uploadImageX1.add(0x140).writePointer(ImagePathAddr1);
uploadImageX1.add(0x1f8).writePointer(uploadAesKeyAddr);
uploadImageX1.add(0xe8).writePointer(ImagePathAddr1);
uploadImageX1.add(0x118).writePointer(ImagePathAddr1);
uploadImageX1.add(0x148).writePointer(ImagePathAddr1);
uploadImageX1.add(0x200).writePointer(uploadAesKeyAddr);
const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']);
@@ -1039,100 +1043,101 @@ function triggerUploadImg(receiver, md5, imagePath) {
function triggerUploadVideo(receiver, md5, videoPath) {
const payload = [
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 10802b8b0 的指针
0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 函数 107fd5908 的指针
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, // 0x40
0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 图片id // 0x48
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x00 函数指针1 (writePointer覆盖)
0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x08 函数指针2 (writePointer覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x10
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x18
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x20
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40
0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x48 videoId (writePointer覆盖)
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 发送人 0x68
0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37,
0x69, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0x13, // 发送人id长度
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x58
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 发送人wxid (writeUtf8String覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, // 0x78 [0x7F]=长度0x13
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90
0x01, 0xAA, 0xAA, 0xAA, 0x04, 0x00, 0x00, 0x00, // 0x98
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xa0
0xA0, 0xBE, 0x2D, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0xa8
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xb0
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xb8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xe0 图片地址 高清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_hd.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xe8
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xf0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xf8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 md5 (writePointer覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xB8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xE8 视频路径 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xF8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x108
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x110 图片地址 普清 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x118
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x120
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x140 图片地址 缩略图 /Users/yincong/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/wxid_ldftuhe36izg19_5e7d/temp/04ebaab7e3ea6050e26ff31d89cc121e/2026-01/Img/166_1768214492_thumb.jpg
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x148
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x150
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x110
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x118 视频路径2 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x120
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x128
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x130
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x138
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x140
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x148 视频路径3 (writePointer覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x150
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160
0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xE0, 0x03, // 0x168
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x170
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x168
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, // 0x170 [0x174]=0x0B
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x188
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x190
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x1a0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1a8
0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, // 0x1b0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1b8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1c8
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d0
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1d8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1e8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1f0
0xD0, 0x78, 0x46, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x1f8 某个key ecd57e9cf85f2e2087aee8c0fd1e445e
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x208
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x188
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x190
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1B0
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x1B8 [0x1BC]=0xAAAAAAAA
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D8
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200 aes key (writePointer覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x208
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x220
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x228
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x230
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x238
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x240
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x248
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x250
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x258
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x250
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x258
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x270
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x278
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,// 0x280
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x278
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x280
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2a8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2b8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2c8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2d8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2e0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x2e8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C8
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // 0x2F0
]
patchString(videoIdAddr, receiver + "_" + String(Math.floor(Date.now() / 1000)) + "_" + Math.floor(Math.random() * 1001) + "_1");
@@ -1146,10 +1151,17 @@ function triggerUploadVideo(receiver, md5, videoPath) {
uploadVideoX1.add(0x48).writePointer(videoIdAddr);
uploadVideoX1.add(0x68).writeUtf8String(receiver);
uploadVideoX1.add(0xa8).writePointer(md5Addr);
uploadVideoX1.add(0xe0).writePointer(videoPathAddr1);
uploadVideoX1.add(0x110).writePointer(videoPathAddr1);
uploadVideoX1.add(0x140).writePointer(videoPathAddr1);
uploadVideoX1.add(0x1f8).writePointer(uploadAesKeyAddr);
uploadVideoX1.add(0xe8).writePointer(videoPathAddr1);
uploadVideoX1.add(0x118).writePointer(videoPathAddr1);
uploadVideoX1.add(0x148).writePointer(videoPathAddr1);
uploadVideoX1.add(0x200).writePointer(uploadAesKeyAddr);
console.log(hexdump(uploadVideoX1, {
offset: 0,
length: 0x2e8,
header: true,
ansi: true
}))
const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']);
@@ -1160,9 +1172,20 @@ function triggerUploadVideo(receiver, md5, videoPath) {
function attachUploadMedia() {
Interceptor.attach(uploadImageAddr.add(0x10), {
onEnter: function (args) {
if (uploadGlobalX0) {
return
}
console.log(hexdump(this.context.x1, {
offset: 0,
length: 0x2e8,
header: true,
ansi: true
}))
uploadGlobalX0 = this.context.x0;
const selfId = this.context.x1.add(0x68).readUtf8String();
const filePath = this.context.x1.add(0xe0).readPointer().readUtf8String();
const filePath = this.context.x1.add(0xe8).readPointer().readUtf8String();
send({
type: "upload",
self_id: selfId,
+10 -10
View File
@@ -9,10 +9,10 @@ if (!baseAddr) {
console.log("[+] WeChat base address: " + baseAddr);
var buf2RespAddr = baseAddr.add(0x39FAC00)
var downloadImagAddr = baseAddr.add(0x4CFFF3C) // image_download
var downloadFileAddr = baseAddr.add(0x4C9EF48) // c2c_download
var downloadVideoAddr = baseAddr.add(0x4CB8070) // hdvideo_streaming
var startDownloadMedia = baseAddr.add(0x4bdd618)
var downloadImagAddr = baseAddr.add(0x4E6F32C) // image_download
var downloadFileAddr = baseAddr.add(0x4E0E264) // c2c_download
var downloadVideoAddr = baseAddr.add(0x4E28044) // hdvideo_streaming
var startDownloadMedia = baseAddr.add(0x4d4c838)
var downloadGlobalX0;
var downloadFileX1 = ptr(0)
@@ -153,8 +153,8 @@ function setReceiver() {
Interceptor.attach(downloadFileAddr, {
onEnter: function (args) {
var dataPtr = this.context.x22;
var dataLen = this.context.x0.toInt32();
var fileId = this.context.sp.add(0x30).readPointer().readUtf8String();
var dataLen = this.context.x2.toInt32();
var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String();
var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String();
if (dataLen > 0) {
@@ -184,10 +184,10 @@ function setReceiver() {
Interceptor.attach(downloadVideoAddr, {
onEnter: function (args) {
var dataPtr = this.context.x1;
var dataLen = this.context.x24.toInt32();
var fileId = this.context.x22.add(0x40).readPointer().readUtf8String();
var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String();
var dataPtr = this.context.x20.add(0x178).readPointer();
var dataLen = this.context.x23.toInt32();
var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String();
var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String();
if (dataLen > 0) {
var buffer = dataPtr.readByteArray(dataLen);
+3 -2
View File
@@ -18,7 +18,7 @@ var triggerX1Payload;
var insertMsgAddr;
// 回复消息回调函数地址 (基于用户提供: 0x24BDE40)
var replyCallbackFuncAddr = baseAddr.add(0x24BDE40);
var replyCallbackFuncAddr = baseAddr.add(0x260C444);
var replyProtobufAddr = replyCallbackFuncAddr.add(0x50);
var patchReplyProtobufFunc1 = replyCallbackFuncAddr.add(0x10);
var patchReplyProtobufFunc1Byte;
@@ -26,13 +26,14 @@ var patchReplyProtobufFunc2 = replyCallbackFuncAddr.add(0x30);
var patchReplyProtobufFunc2Byte;
var replyProtobufDeleteAddr = replyCallbackFuncAddr.add(0x6c);
var replyProtobufDeleteAddrByte;
var replyMessageCallbackFunc1 = baseAddr.add(0x8C29A08); // 复用图片的callback函数
// Reply消息相关地址 - 需要导出为全局变量供file.js使用
var sendReplyMessageAddr = ptr(0);
var replyMessageAddr = ptr(0);
var replyCgiAddr = ptr(0);
var replyProtobufBufAddr = ptr(0); // 用于存储protobuf数据的缓冲区
var replyMessageCallbackFunc1 = baseAddr.add(0x89170D0); // 复用图片的callback函数
// Reply消息内容相关
var replyContent = ""; // 回复的文本内容
+10 -7
View File
@@ -1,5 +1,8 @@
var moduleName = "wechat.dylib";
var baseAddr = Process.findModuleByName(moduleName).base;
var targetPath = "/Applications/WeChat.app/Contents/Resources/wechat.dylib";
var module = Process.enumerateModules().find(function(m) {
return m.path === targetPath;
});
const baseAddr = module.base;
if (!baseAddr) {
console.error("[!] 找不到 WeChat 模块基址,请检查进程名。");
}
@@ -78,7 +81,7 @@ function patchString(addr, plainStr) {
// -------------------------全局变量分区-------------------------
// 文本消息全局变量
var textCallbackFuncAddr = baseAddr.add(0x2587018);
var textCallbackFuncAddr = baseAddr.add(0x26779D4);
var protobufAddr = textCallbackFuncAddr.add(0x40);
var patchTextProtobufAddr = textCallbackFuncAddr.add(0x20);
var patchTextProtobufByte
@@ -90,15 +93,15 @@ var sendTextMessageAddr = ptr(0);
var textMessageAddr = ptr(0);
var textProtoX1PayloadAddr = ptr(0);
var sendMessageCallbackFunc = baseAddr.add(0x8A047B0);
var sendMessageCallbackFunc = baseAddr.add(0x8C2C880);
// 双方公共使用的地址
var triggerX1Payload;
var triggerX0;
var req2bufEnterAddr = baseAddr.add(0x388b400);
var req2bufExitAddr = baseAddr.add(0x388C514);
var sendFuncAddr = baseAddr.add(0x4a5e108);
var req2bufEnterAddr = baseAddr.add(0x39d51d8);
var req2bufExitAddr = baseAddr.add(0x39D62EC);
var sendFuncAddr = baseAddr.add(0x4bd0e1c);
var insertMsgAddr = ptr(0);
var sendMsgType = "";
+59
View File
@@ -55,6 +55,52 @@ func sendHandler(w http.ResponseWriter, r *http.Request) {
Content: v.Data.File,
Type: v.Type,
}
} else if v.Type == "reply" {
if v.Data.ReplyMessage == nil {
Error("reply_message为空")
continue
}
rm := v.Data.ReplyMessage
// 提取被回复消息的内容
referContent := ""
referMsgType := 1 // 默认text
if len(rm.Message) > 0 {
switch rm.Message[0].Type {
case "text":
referContent = rm.Message[0].Data.Text
referMsgType = 1
case "image":
referMsgType = 3
case "video":
referMsgType = 43
case "file":
referMsgType = 49
}
}
// 提取发送者昵称
displayName := ""
if rm.Sender != nil {
displayName = rm.Sender.Nickname
}
// msgsource需要JSON unescape(双重编码: \\u003c → \u003c → <
msgsource := jsonUnescapeString(rm.MsgResource)
msgChan <- &SendMsg{
UserId: req.UserID,
GroupID: req.GroupID,
Content: v.Data.Text,
Type: "reply",
ReferMsgId: rm.MessageId,
ReferMsgSender: rm.UserID,
ReferMsgType: referMsgType,
ReferCreateTime: rm.Time,
ReferMsgsource: msgsource,
ReferDisplayName: displayName,
ReferContent: referContent,
}
}
}
@@ -123,3 +169,16 @@ func SendHttpReq(jsonData []byte) {
Info("返回内容", "status", resp.StatusCode, "body", string(body))
}
// jsonUnescapeString 对双重JSON编码的字符串做unescape
// 例如: \\u003c → \u003c (第一次json.Unmarshal) → < (本函数)
func jsonUnescapeString(s string) string {
if s == "" {
return s
}
var result string
if err := json.Unmarshal([]byte(`"`+s+`"`), &result); err != nil {
return s
}
return result
}
+1 -1
View File
@@ -60,7 +60,7 @@ func initFlag() {
flag.StringVar(&config.FridaGadgetAddr, "gadget_addr", "127.0.0.1:27042", "Gadget 地址: 127.0.0.1:27042 仅当 type 为 gadget 时有效")
flag.StringVar(&config.OnebotToken, "token", "MuseBot", "OneBot Token: MuseBot")
flag.StringVar(&config.ImagePath, "image_path", "", "图片路径: /Users/xxx/Library/Containers/com.tencent.xinWeChat/Data/Documents/xwechat_files/xxx/temp/xxx/2026-01/Img/")
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_8_107_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
flag.StringVar(&config.WechatConf, "wechat_conf", "../wechat_version/4_1_9_52_mac.json", "微信配置文件路径: ../wechat_version/4_1_6_12_mac.json")
flag.StringVar(&config.ConnType, "conn_type", "http", "连接类型: http | websocket")
flag.IntVar(&config.SendInterval, "send_interval", 1000, "发送间隔: ms")
flag.IntVar(&config.WechatPid, "wechat_pid", 0, "微信进程 PID,不设置则自动查找")
+15 -6
View File
@@ -63,6 +63,14 @@ type SendMsg struct {
VideoId string
Duration int32
VideoSize int32
ReferMsgId string
ReferMsgSender string
ReferMsgType int
ReferCreateTime int64
ReferMsgsource string
ReferDisplayName string
ReferContent string
}
type VideoInfo struct {
@@ -83,12 +91,13 @@ type Message struct {
}
type SendRequestData struct {
Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
File string `json:"file,omitempty"`
URL string `json:"url,omitempty"`
QQ string `json:"qq,omitempty"`
Media []byte `json:"media,omitempty"`
Id string `json:"id,omitempty"`
Text string `json:"text,omitempty"`
File string `json:"file,omitempty"`
URL string `json:"url,omitempty"`
QQ string `json:"qq,omitempty"`
Media []byte `json:"media,omitempty"`
ReplyMessage *WechatMessage `json:"reply_message,omitempty"`
}
type Config struct {
+404
View File
@@ -0,0 +1,404 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.11
// protoc v3.5.1
// source: proto/wxproto/reply_msg.proto
package wxproto
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 发送回复(引用)消息的protobuf结构
// CG路径: /cgi-bin/micromsg-bin/sendappmsg
// 消息类型: appmsg type=57
type WxSendReplyMsg struct {
state protoimpl.MessageState `protogen:"open.v1"`
Header *ReplyMsgHeader `protobuf:"bytes,1,opt,name=header" json:"header,omitempty"`
Body *ReplyMsgBody `protobuf:"bytes,2,opt,name=body" json:"body,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *WxSendReplyMsg) Reset() {
*x = WxSendReplyMsg{}
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *WxSendReplyMsg) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*WxSendReplyMsg) ProtoMessage() {}
func (x *WxSendReplyMsg) ProtoReflect() protoreflect.Message {
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use WxSendReplyMsg.ProtoReflect.Descriptor instead.
func (*WxSendReplyMsg) Descriptor() ([]byte, []int) {
return file_proto_wxproto_reply_msg_proto_rawDescGZIP(), []int{0}
}
func (x *WxSendReplyMsg) GetHeader() *ReplyMsgHeader {
if x != nil {
return x.Header
}
return nil
}
func (x *WxSendReplyMsg) GetBody() *ReplyMsgBody {
if x != nil {
return x.Body
}
return nil
}
// Header: 固定64字节
type ReplyMsgHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
Flag []byte `protobuf:"bytes,1,opt,name=flag" json:"flag,omitempty"` // 固定 [0x00]
Timestamp *uint32 `protobuf:"varint,2,opt,name=timestamp" json:"timestamp,omitempty"` // 请求时间戳(秒)
ClientProof []byte `protobuf:"bytes,3,opt,name=client_proof,json=clientProof" json:"client_proof,omitempty"` // 16字节随机数据/md5
DeviceId *uint64 `protobuf:"varint,4,opt,name=device_id,json=deviceId" json:"device_id,omitempty"` // 设备标识(大整数)
Platform *string `protobuf:"bytes,5,opt,name=platform" json:"platform,omitempty"` // 平台信息 "UnifiedPCMac 26 arm64"
Version *uint32 `protobuf:"varint,6,opt,name=version" json:"version,omitempty"` // 版本/标记 = 163
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReplyMsgHeader) Reset() {
*x = ReplyMsgHeader{}
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReplyMsgHeader) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReplyMsgHeader) ProtoMessage() {}
func (x *ReplyMsgHeader) ProtoReflect() protoreflect.Message {
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReplyMsgHeader.ProtoReflect.Descriptor instead.
func (*ReplyMsgHeader) Descriptor() ([]byte, []int) {
return file_proto_wxproto_reply_msg_proto_rawDescGZIP(), []int{1}
}
func (x *ReplyMsgHeader) GetFlag() []byte {
if x != nil {
return x.Flag
}
return nil
}
func (x *ReplyMsgHeader) GetTimestamp() uint32 {
if x != nil && x.Timestamp != nil {
return *x.Timestamp
}
return 0
}
func (x *ReplyMsgHeader) GetClientProof() []byte {
if x != nil {
return x.ClientProof
}
return nil
}
func (x *ReplyMsgHeader) GetDeviceId() uint64 {
if x != nil && x.DeviceId != nil {
return *x.DeviceId
}
return 0
}
func (x *ReplyMsgHeader) GetPlatform() string {
if x != nil && x.Platform != nil {
return *x.Platform
}
return ""
}
func (x *ReplyMsgHeader) GetVersion() uint32 {
if x != nil && x.Version != nil {
return *x.Version
}
return 0
}
// Body: 消息体
type ReplyMsgBody struct {
state protoimpl.MessageState `protogen:"open.v1"`
Sender *string `protobuf:"bytes,1,opt,name=sender" json:"sender,omitempty"` // 接收方wxid (field1是target)
Unknown2 []byte `protobuf:"bytes,2,opt,name=unknown2" json:"unknown2,omitempty"` // 空 (必须显式存在)
Unknown3 *int32 `protobuf:"varint,3,opt,name=unknown3" json:"unknown3,omitempty"` // 0 (必须显式存在)
Receiver *string `protobuf:"bytes,4,opt,name=receiver" json:"receiver,omitempty"` // 发送方wxid
MsgType *int32 `protobuf:"varint,5,opt,name=msg_type,json=msgType" json:"msg_type,omitempty"` // 消息类型 = 57
Content []byte `protobuf:"bytes,6,opt,name=content" json:"content,omitempty"` // XML内容 (appmsg XML)
SendTimestamp *int64 `protobuf:"varint,7,opt,name=send_timestamp,json=sendTimestamp" json:"send_timestamp,omitempty"` // 发送时间戳
ClientMsgId *string `protobuf:"bytes,8,opt,name=client_msg_id,json=clientMsgId" json:"client_msg_id,omitempty"` // 消息ID
Unknown9 *int32 `protobuf:"varint,9,opt,name=unknown9" json:"unknown9,omitempty"` // 0
Flag *int32 `protobuf:"varint,10,opt,name=flag" json:"flag,omitempty"` // 1
Unknown11 *int32 `protobuf:"varint,11,opt,name=unknown11" json:"unknown11,omitempty"` // 0
Msgsource []byte `protobuf:"bytes,12,opt,name=msgsource" json:"msgsource,omitempty"` // msgsource XML
Unknown13 []byte `protobuf:"bytes,13,opt,name=unknown13" json:"unknown13,omitempty"` // 空 (必须显式存在)
Unknown14 []byte `protobuf:"bytes,14,opt,name=unknown14" json:"unknown14,omitempty"` // 空 (必须显式存在)
Unknown15 []byte `protobuf:"bytes,15,opt,name=unknown15" json:"unknown15,omitempty"` // 空 (必须显式存在)
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ReplyMsgBody) Reset() {
*x = ReplyMsgBody{}
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ReplyMsgBody) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ReplyMsgBody) ProtoMessage() {}
func (x *ReplyMsgBody) ProtoReflect() protoreflect.Message {
mi := &file_proto_wxproto_reply_msg_proto_msgTypes[2]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ReplyMsgBody.ProtoReflect.Descriptor instead.
func (*ReplyMsgBody) Descriptor() ([]byte, []int) {
return file_proto_wxproto_reply_msg_proto_rawDescGZIP(), []int{2}
}
func (x *ReplyMsgBody) GetSender() string {
if x != nil && x.Sender != nil {
return *x.Sender
}
return ""
}
func (x *ReplyMsgBody) GetUnknown2() []byte {
if x != nil {
return x.Unknown2
}
return nil
}
func (x *ReplyMsgBody) GetUnknown3() int32 {
if x != nil && x.Unknown3 != nil {
return *x.Unknown3
}
return 0
}
func (x *ReplyMsgBody) GetReceiver() string {
if x != nil && x.Receiver != nil {
return *x.Receiver
}
return ""
}
func (x *ReplyMsgBody) GetMsgType() int32 {
if x != nil && x.MsgType != nil {
return *x.MsgType
}
return 0
}
func (x *ReplyMsgBody) GetContent() []byte {
if x != nil {
return x.Content
}
return nil
}
func (x *ReplyMsgBody) GetSendTimestamp() int64 {
if x != nil && x.SendTimestamp != nil {
return *x.SendTimestamp
}
return 0
}
func (x *ReplyMsgBody) GetClientMsgId() string {
if x != nil && x.ClientMsgId != nil {
return *x.ClientMsgId
}
return ""
}
func (x *ReplyMsgBody) GetUnknown9() int32 {
if x != nil && x.Unknown9 != nil {
return *x.Unknown9
}
return 0
}
func (x *ReplyMsgBody) GetFlag() int32 {
if x != nil && x.Flag != nil {
return *x.Flag
}
return 0
}
func (x *ReplyMsgBody) GetUnknown11() int32 {
if x != nil && x.Unknown11 != nil {
return *x.Unknown11
}
return 0
}
func (x *ReplyMsgBody) GetMsgsource() []byte {
if x != nil {
return x.Msgsource
}
return nil
}
func (x *ReplyMsgBody) GetUnknown13() []byte {
if x != nil {
return x.Unknown13
}
return nil
}
func (x *ReplyMsgBody) GetUnknown14() []byte {
if x != nil {
return x.Unknown14
}
return nil
}
func (x *ReplyMsgBody) GetUnknown15() []byte {
if x != nil {
return x.Unknown15
}
return nil
}
var File_proto_wxproto_reply_msg_proto protoreflect.FileDescriptor
const file_proto_wxproto_reply_msg_proto_rawDesc = "" +
"\n" +
"\x1dproto/wxproto/reply_msg.proto\x12\awxproto\"l\n" +
"\x0eWxSendReplyMsg\x12/\n" +
"\x06header\x18\x01 \x01(\v2\x17.wxproto.ReplyMsgHeaderR\x06header\x12)\n" +
"\x04body\x18\x02 \x01(\v2\x15.wxproto.ReplyMsgBodyR\x04body\"\xb8\x01\n" +
"\x0eReplyMsgHeader\x12\x12\n" +
"\x04flag\x18\x01 \x01(\fR\x04flag\x12\x1c\n" +
"\ttimestamp\x18\x02 \x01(\rR\ttimestamp\x12!\n" +
"\fclient_proof\x18\x03 \x01(\fR\vclientProof\x12\x1b\n" +
"\tdevice_id\x18\x04 \x01(\x04R\bdeviceId\x12\x1a\n" +
"\bplatform\x18\x05 \x01(\tR\bplatform\x12\x18\n" +
"\aversion\x18\x06 \x01(\rR\aversion\"\xc0\x03\n" +
"\fReplyMsgBody\x12\x16\n" +
"\x06sender\x18\x01 \x01(\tR\x06sender\x12\x1a\n" +
"\bunknown2\x18\x02 \x01(\fR\bunknown2\x12\x1a\n" +
"\bunknown3\x18\x03 \x01(\x05R\bunknown3\x12\x1a\n" +
"\breceiver\x18\x04 \x01(\tR\breceiver\x12\x19\n" +
"\bmsg_type\x18\x05 \x01(\x05R\amsgType\x12\x18\n" +
"\acontent\x18\x06 \x01(\fR\acontent\x12%\n" +
"\x0esend_timestamp\x18\a \x01(\x03R\rsendTimestamp\x12\"\n" +
"\rclient_msg_id\x18\b \x01(\tR\vclientMsgId\x12\x1a\n" +
"\bunknown9\x18\t \x01(\x05R\bunknown9\x12\x12\n" +
"\x04flag\x18\n" +
" \x01(\x05R\x04flag\x12\x1c\n" +
"\tunknown11\x18\v \x01(\x05R\tunknown11\x12\x1c\n" +
"\tmsgsource\x18\f \x01(\fR\tmsgsource\x12\x1c\n" +
"\tunknown13\x18\r \x01(\fR\tunknown13\x12\x1c\n" +
"\tunknown14\x18\x0e \x01(\fR\tunknown14\x12\x1c\n" +
"\tunknown15\x18\x0f \x01(\fR\tunknown15B>Z<github.com/yincongcyincong/weixin-macos/onebot/proto/wxproto"
var (
file_proto_wxproto_reply_msg_proto_rawDescOnce sync.Once
file_proto_wxproto_reply_msg_proto_rawDescData []byte
)
func file_proto_wxproto_reply_msg_proto_rawDescGZIP() []byte {
file_proto_wxproto_reply_msg_proto_rawDescOnce.Do(func() {
file_proto_wxproto_reply_msg_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_proto_wxproto_reply_msg_proto_rawDesc), len(file_proto_wxproto_reply_msg_proto_rawDesc)))
})
return file_proto_wxproto_reply_msg_proto_rawDescData
}
var file_proto_wxproto_reply_msg_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_proto_wxproto_reply_msg_proto_goTypes = []any{
(*WxSendReplyMsg)(nil), // 0: wxproto.WxSendReplyMsg
(*ReplyMsgHeader)(nil), // 1: wxproto.ReplyMsgHeader
(*ReplyMsgBody)(nil), // 2: wxproto.ReplyMsgBody
}
var file_proto_wxproto_reply_msg_proto_depIdxs = []int32{
1, // 0: wxproto.WxSendReplyMsg.header:type_name -> wxproto.ReplyMsgHeader
2, // 1: wxproto.WxSendReplyMsg.body:type_name -> wxproto.ReplyMsgBody
2, // [2:2] is the sub-list for method output_type
2, // [2:2] is the sub-list for method input_type
2, // [2:2] is the sub-list for extension type_name
2, // [2:2] is the sub-list for extension extendee
0, // [0:2] is the sub-list for field type_name
}
func init() { file_proto_wxproto_reply_msg_proto_init() }
func file_proto_wxproto_reply_msg_proto_init() {
if File_proto_wxproto_reply_msg_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_proto_wxproto_reply_msg_proto_rawDesc), len(file_proto_wxproto_reply_msg_proto_rawDesc)),
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proto_wxproto_reply_msg_proto_goTypes,
DependencyIndexes: file_proto_wxproto_reply_msg_proto_depIdxs,
MessageInfos: file_proto_wxproto_reply_msg_proto_msgTypes,
}.Build()
File_proto_wxproto_reply_msg_proto = out.File
file_proto_wxproto_reply_msg_proto_goTypes = nil
file_proto_wxproto_reply_msg_proto_depIdxs = nil
}
+42
View File
@@ -0,0 +1,42 @@
syntax = "proto2";
package wxproto;
option go_package = "github.com/yincongcyincong/weixin-macos/onebot/proto/wxproto";
// 发送回复(引用)消息的protobuf结构
// CG路径: /cgi-bin/micromsg-bin/sendappmsg
// 消息类型: appmsg type=57
message WxSendReplyMsg {
optional ReplyMsgHeader header = 1;
optional ReplyMsgBody body = 2;
}
// Header: 固定64字节
message ReplyMsgHeader {
optional bytes flag = 1; // 固定 [0x00]
optional uint32 timestamp = 2; // 请求时间戳(秒)
optional bytes client_proof = 3; // 16字节随机数据/md5
optional uint64 device_id = 4; // 设备标识(大整数)
optional string platform = 5; // 平台信息 "UnifiedPCMac 26 arm64"
optional uint32 version = 6; // 版本/标记 = 163
}
// Body: 消息体
message ReplyMsgBody {
optional string sender = 1; // 接收方wxid (field1是target)
optional bytes unknown2 = 2; // 空 (必须显式存在)
optional int32 unknown3 = 3; // 0 (必须显式存在)
optional string receiver = 4; // 发送方wxid
optional int32 msg_type = 5; // 消息类型 = 57
optional bytes content = 6; // XML内容 (appmsg XML)
optional int64 send_timestamp = 7; // 发送时间戳
optional string client_msg_id = 8; // 消息ID
optional int32 unknown9 = 9; // 0
optional int32 flag = 10; // 1
optional int32 unknown11 = 11; // 0
optional bytes msgsource = 12; // msgsource XML
optional bytes unknown13 = 13; // 空 (必须显式存在)
optional bytes unknown14 = 14; // 空 (必须显式存在)
optional bytes unknown15 = 15; // 空 (必须显式存在)
}
+177
View File
@@ -0,0 +1,177 @@
package main
import (
"encoding/hex"
"fmt"
"math/rand"
"time"
"google.golang.org/protobuf/proto"
"github.com/yincongcyincong/weixin-macos/onebot/proto/wxproto"
)
// BuildReplyMsgProto 构建发送回复消息的protobuf并返回hex编码的字符串
func BuildReplyMsgProto(sender, receiver string, replyInfo *ReplyInfo) (string, error) {
now := time.Now().Unix()
// 构建appmsg XML
appmsgXml := buildReplyAppmsgXml(replyInfo)
// 构建客户端消息ID
clientMsgId := fmt.Sprintf("%s_%d_%d_xwechat_1", sender, now, rand.Intn(100))
// msgsource
msgsource := "<msgsource><alnode><fr>1</fr></alnode></msgsource>"
// proto2 需要使用指针
var (
unknown2 = []byte{}
unknown3 = int32(0)
msgType = int32(57)
unknown9 = int32(0)
flag = int32(1)
unknown11 = int32(0)
unknown13 = []byte{}
unknown14 = []byte{}
unknown15 = []byte{}
timestamp = uint32(now)
deviceId = generateDeviceId()
version = uint32(163)
)
msg := &wxproto.WxSendReplyMsg{
Header: &wxproto.ReplyMsgHeader{
Flag: []byte{0x00},
Timestamp: &timestamp,
ClientProof: generateRandomBytes(16),
DeviceId: &deviceId,
Platform: proto.String("UnifiedPCMac 26 arm64"),
Version: &version,
},
Body: &wxproto.ReplyMsgBody{
Sender: &receiver,
Unknown2: unknown2,
Unknown3: &unknown3,
Receiver: &sender,
MsgType: &msgType,
Content: []byte(appmsgXml),
SendTimestamp: proto.Int64(now),
ClientMsgId: &clientMsgId,
Unknown9: &unknown9,
Flag: &flag,
Unknown11: &unknown11,
Msgsource: []byte(msgsource),
Unknown13: unknown13,
Unknown14: unknown14,
Unknown15: unknown15,
},
}
data, err := proto.Marshal(msg)
if err != nil {
return "", fmt.Errorf("marshal reply proto failed: %w", err)
}
fmt.Println(fmt.Printf("0x% x\n", data))
return hex.EncodeToString(data), nil
}
// ReplyInfo 回复消息的全部信息
type ReplyInfo struct {
Content string // 回复的文本内容
MsgId string // 被回复消息的svrid
MsgSender string // 被回复消息的发送者wxid
MsgType int // 被回复消息的类型 (1=text, 3=image, 43=video, 49=appmsg)
CreateTime int64 // 被回复消息的时间戳(毫秒)
Msgsource string // 被回复消息的msgsource
DisplayName string // 被回复消息发送者的昵称
MsgContent string // 被回复消息的内容
}
// buildReplyAppmsgXml 构建回复消息的appmsg XML,字段顺序匹配微信真实protobuf
func buildReplyAppmsgXml(info *ReplyInfo) string {
// 时间戳:毫秒转秒
createTime := info.CreateTime / 1000
xml := `<appmsg appid="" sdkver="0">`
xml += `<title>` + escapeXmlStr(info.Content) + `</title>`
xml += `<des></des>`
xml += `<action></action>`
xml += `<type>57</type>`
xml += `<showtype>0</showtype>`
xml += `<soundtype>0</soundtype>`
xml += `<mediatagname></mediatagname>`
xml += `<messageext></messageext>`
xml += `<messageaction></messageaction>`
xml += `<content></content>`
xml += `<contentattr>0</contentattr>`
xml += `<url></url>`
xml += `<lowurl></lowurl>`
xml += `<dataurl></dataurl>`
xml += `<lowdataurl></lowdataurl>`
xml += `<songalbumurl></songalbumurl>`
xml += `<songlyric></songlyric>`
xml += `<template_id></template_id>`
xml += `<appattach><totallen>0</totallen><attachid></attachid><emoticonmd5></emoticonmd5><fileext></fileext><aeskey></aeskey></appattach>`
xml += `<extinfo></extinfo>`
xml += `<sourceusername></sourceusername>`
xml += `<sourcedisplayname></sourcedisplayname>`
xml += `<thumburl></thumburl>`
xml += `<md5></md5>`
xml += `<statextstr></statextstr>`
// refermsg - 字段顺序与微信一致: chatusr → type → createtime → msgsource → displayname → svrid → fromusr → content
xml += `<refermsg>`
xml += `<chatusr>` + escapeXmlStr(info.MsgSender) + `</chatusr>`
xml += `<type>` + fmt.Sprintf("%d", info.MsgType) + `</type>`
xml += `<createtime>` + fmt.Sprintf("%d", createTime) + `</createtime>`
xml += `<msgsource>` + escapeXmlStr(info.Msgsource) + `</msgsource>`
xml += `<displayname>` + escapeXmlStr(info.DisplayName) + `</displayname>`
xml += `<svrid>` + escapeXmlStr(info.MsgId) + `</svrid>`
xml += `<fromusr>` + escapeXmlStr(info.MsgSender) + `</fromusr>`
xml += `<content>` + escapeXmlStr(info.MsgContent) + `</content>`
xml += `</refermsg>`
xml += `</appmsg>`
xml += `<fromusername>` + escapeXmlStr(info.MsgSender) + `</fromusername>`
return xml
}
// escapeXmlStr 简单的XML转义
func escapeXmlStr(s string) string {
result := ""
for _, c := range s {
switch c {
case '&':
result += "&amp;"
case '<':
result += "&lt;"
case '>':
result += "&gt;"
case '"':
result += "&quot;"
case '\'':
result += "&apos;"
default:
result += string(c)
}
}
return result
}
// generateRandomBytes 生成随机字节
func generateRandomBytes(n int) []byte {
b := make([]byte, n)
for i := range b {
b[i] = byte(rand.Intn(256))
}
return b
}
// generateDeviceId 动态生成设备ID (大整数,高位置1模拟真实设备)
func generateDeviceId() uint64 {
return rand.Uint64() | (0xFFFFFFFF << 32)
}
+182 -18
View File
@@ -42,7 +42,7 @@ function generateAESKey() {
// 文本消息全局变量
var textCallbackFuncAddr = baseAddr.add({{.textCallbackFuncAddr}});
var protobufAddr = textCallbackFuncAddr.add(0x44);
var protobufAddr = textCallbackFuncAddr.add(0x40);
var patchTextProtobufAddr = textCallbackFuncAddr.add(0x20);
var patchTextProtobufByte
var patchTextProtobufDeleteAddr = textCallbackFuncAddr.add(0x5C);
@@ -137,6 +137,23 @@ var textProtoHexGlobal = "";
var imgProtoHexGlobal = "";
// 视频消息protobuf全局变量 (从Go直接传入hex编码)
var videoProtoHexGlobal = "";
// 回复消息protobuf全局变量 (从Go直接传入hex编码)
var replyProtoHexGlobal = "";
// 回复消息全局变量
var replyCallbackFuncAddr = baseAddr.add({{.replyCallbackFuncAddr}});
var replyProtobufAddr = replyCallbackFuncAddr.add(0x50);
var patchReplyProtobufFunc1 = replyCallbackFuncAddr.add(0x10);
var patchReplyProtobufFunc1Byte;
var patchReplyProtobufFunc2 = replyCallbackFuncAddr.add(0x30);
var patchReplyProtobufFunc2Byte;
var replyProtobufDeleteAddr = replyCallbackFuncAddr.add(0x6c);
var replyProtobufDeleteAddrByte;
var replyMessageCallbackFunc = baseAddr.add({{.replyMessageCallbackFunc}});
var replyCgiAddr = ptr(0);
var sendReplyMessageAddr = ptr(0);
var replyMessageAddr = ptr(0);
var replyProtoX1PayloadAddr = ptr(0);
// -------------------------全局变量分区-------------------------
@@ -344,6 +361,10 @@ function attachReq2buf() {
insertMsgAddr.writePointer(sendVideoMessageAddr);
console.log("[+] 发送视频消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendVideoMessageAddr +
"[+] Req2Buf 写入后内存预览: " + insertMsgAddr);
} else if (sendMsgType === "reply") {
insertMsgAddr.writePointer(sendReplyMessageAddr);
console.log("[+] 发送回复消息成功! Req2Buf 已将 X24+0x60 指向新地址: " + sendReplyMessageAddr +
"[+] Req2Buf 写入后内存预览: " + insertMsgAddr);
}
}
});
@@ -688,10 +709,10 @@ function triggerUploadImg(receiver, md5, imagePath, payloadHex) {
uploadImageX1.add(0x48).writePointer(imageIdAddr);
uploadImageX1.add(0x68).writeUtf8String(receiver);
uploadImageX1.add(0xa8).writePointer(md5Addr);
uploadImageX1.add(0xe0).writePointer(ImagePathAddr1);
uploadImageX1.add(0x110).writePointer(ImagePathAddr1);
uploadImageX1.add(0x140).writePointer(ImagePathAddr1);
uploadImageX1.add(0x1f8).writePointer(uploadAesKeyAddr);
uploadImageX1.add(0xe8).writePointer(ImagePathAddr1);
uploadImageX1.add(0x118).writePointer(ImagePathAddr1);
uploadImageX1.add(0x148).writePointer(ImagePathAddr1);
uploadImageX1.add(0x200).writePointer(uploadAesKeyAddr);
const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']);
@@ -718,10 +739,10 @@ function triggerUploadVideo(receiver, md5, videoPath, payloadHex) {
uploadVideoX1.add(0x48).writePointer(videoIdAddr);
uploadVideoX1.add(0x68).writeUtf8String(receiver);
uploadVideoX1.add(0xa8).writePointer(md5Addr);
uploadVideoX1.add(0xe0).writePointer(videoPathAddr1);
uploadVideoX1.add(0x110).writePointer(videoPathAddr1);
uploadVideoX1.add(0x140).writePointer(videoPathAddr1);
uploadVideoX1.add(0x1f8).writePointer(uploadAesKeyAddr);
uploadVideoX1.add(0xe8).writePointer(videoPathAddr1);
uploadVideoX1.add(0x118).writePointer(videoPathAddr1);
uploadVideoX1.add(0x148).writePointer(videoPathAddr1);
uploadVideoX1.add(0x200).writePointer(uploadAesKeyAddr);
const startUploadMedia = new NativeFunction(uploadImageAddr, 'int64', ['pointer', 'pointer']);
@@ -734,7 +755,7 @@ function attachUploadMedia() {
try {
uploadGlobalX0 = this.context.x0;
const selfId = this.context.x1.add(0x68).readUtf8String();
const filePath = this.context.x1.add(0xe0).readPointer().readUtf8String();
const filePath = this.context.x1.add(0xe8).readPointer().readUtf8String();
send({
type: "upload",
self_id: selfId,
@@ -853,6 +874,148 @@ function attachGetCallbackFromWrapper() {
setImmediate(attachGetCallbackFromWrapper);
// -------------------------发送回复消息分区-------------------------
function setupSendReplyMessageDynamic() {
replyCgiAddr = Memory.alloc(128);
sendReplyMessageAddr = Memory.alloc(256);
replyMessageAddr = Memory.alloc(256);
replyProtoX1PayloadAddr = Memory.alloc(4096);
patchString(replyCgiAddr, "/cgi-bin/micromsg-bin/sendappmsg");
sendReplyMessageAddr.add(0x00).writeU64(0);
sendReplyMessageAddr.add(0x08).writeU64(0);
sendReplyMessageAddr.add(0x10).writeU64(0);
sendReplyMessageAddr.add(0x18).writeU64(1);
sendReplyMessageAddr.add(0x20).writeU32(taskIdGlobal);
sendReplyMessageAddr.add(0x28).writePointer(replyMessageAddr);
replyMessageAddr.add(0x00).writePointer(replyMessageCallbackFunc);
replyMessageAddr.add(0x08).writeU32(taskIdGlobal);
replyMessageAddr.add(0x0c).writeU32(0x6e);
replyMessageAddr.add(0x10).writeU64(0x3);
replyMessageAddr.add(0x18).writePointer(replyCgiAddr);
replyMessageAddr.add(0x20).writeU64(0x22);
replyMessageAddr.add(0x28).writeU64(uint64("0x8000000000000030"));
replyMessageAddr.add(0x30).writeU64(uint64("0x0000000001010100"));
patchReplyProtobufFunc1Byte = patchReplyProtobufFunc1.readByteArray(4);
patchReplyProtobufFunc2Byte = patchReplyProtobufFunc2.readByteArray(4);
replyProtobufDeleteAddrByte = replyProtobufDeleteAddr.readByteArray(4);
console.log("[+] Reply message setup complete. CgiAddr: " + replyCgiAddr + " SendAddr: " + sendReplyMessageAddr);
}
setImmediate(setupSendReplyMessageDynamic);
function patchReplyProtoBuf() {
Interceptor.attach(replyCallbackFuncAddr, {
onEnter: function (args) {
var firstValue = this.context.sp.add(0x10).readU32();
if (firstValue === taskIdGlobal) {
if (patchReplyProtobufFunc1.readU32() !== 3573751839) {
Memory.patchCode(patchReplyProtobufFunc1, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc1});
cw.putNop();
cw.flush();
});
Memory.patchCode(patchReplyProtobufFunc2, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc2});
cw.putNop();
cw.flush();
});
Memory.patchCode(replyProtobufDeleteAddr, 4, code => {
const cw = new Arm64Writer(code, {pc: replyProtobufDeleteAddr});
cw.putNop();
cw.flush();
});
}
} else {
if (patchReplyProtobufFunc1.readU32() === 3573751839) {
Memory.patchCode(patchReplyProtobufFunc1, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc1});
cw.putBytes(new Uint8Array(patchReplyProtobufFunc1Byte));
cw.flush();
});
Memory.patchCode(patchReplyProtobufFunc2, 4, code => {
const cw = new Arm64Writer(code, {pc: patchReplyProtobufFunc2});
cw.putBytes(new Uint8Array(patchReplyProtobufFunc2Byte));
cw.flush();
});
Memory.patchCode(replyProtobufDeleteAddr, 4, code => {
const cw = new Arm64Writer(code, {pc: replyProtobufDeleteAddr});
cw.putBytes(new Uint8Array(replyProtobufDeleteAddrByte));
cw.flush();
});
}
}
}
})
}
setImmediate(patchReplyProtoBuf);
function attachReplyProto() {
Interceptor.attach(replyProtobufAddr, {
onEnter: function (args) {
var currTaskId = this.context.sp.add(0x30).readU32();
if (currTaskId !== taskIdGlobal) {
return;
}
if (!replyProtoHexGlobal || replyProtoHexGlobal.length === 0) {
console.error("[!] replyProtoHexGlobal 为空");
return;
}
const finalPayload = hexToByteArray(replyProtoHexGlobal);
replyProtoX1PayloadAddr.writeByteArray(finalPayload);
this.context.x1 = replyProtoX1PayloadAddr;
this.context.x2 = ptr(finalPayload.length);
console.log("[+] Reply protobuf注入完成, length=" + finalPayload.length);
},
});
}
setImmediate(attachReplyProto);
function triggerSendReplyMessage(taskId, sender, receiver, protoHex, payloadHex) {
if (!taskId || !receiver || !sender) {
console.error("[!] reply: taskId or receiver or sender is empty!");
return "fail";
}
if (!triggerX0 || !triggerX1Payload) {
console.error("[!] triggerX0 或 triggerX1Payload 尚未初始化,请等待 hook 捕获");
return "fail";
}
replyProtoHexGlobal = protoHex;
taskIdGlobal = taskId;
replyMessageAddr.add(0x08).writeU32(taskIdGlobal);
sendReplyMessageAddr.add(0x20).writeU32(taskIdGlobal);
const payloadData = hexToByteArray(payloadHex);
triggerX1Payload.writeByteArray(payloadData);
triggerX1Payload.add(0x18).writePointer(replyCgiAddr);
triggerX1Payload.add(0xb8).writePointer(triggerX1Payload.add(0xc0));
triggerX1Payload.add(0x190).writePointer(triggerX1Payload.add(0x198));
sendMsgType = "reply"
const MMStartTask = new NativeFunction(sendFuncAddr, 'int64', ['pointer', 'pointer']);
try {
MMStartTask(triggerX0, triggerX1Payload);
return "1";
} catch (e) {
console.error("[!] Error trigger reply MMStartTask: " + e);
return "fail";
}
}
// -------------------------发送回复消息分区-------------------------
rpc.exports = {
triggerSendImgMessage: triggerSendImgMessage,
triggerUploadImg: triggerUploadImg,
@@ -860,6 +1023,7 @@ rpc.exports = {
triggerDownload: triggerDownload,
triggerUploadVideo: triggerUploadVideo,
triggerSendVideoMessage: triggerSendVideoMessage,
triggerSendReplyMessage: triggerSendReplyMessage,
};
// -------------------------发送图片消息分区-------------------------
@@ -930,10 +1094,10 @@ function setReceiver() {
Interceptor.attach(downloadFileAddr, {
onEnter: function (args) {
var dataPtr = this.context.x22;
var dataLen = this.context.x20.toInt32();
var fileId = this.context.sp.add(0x30).readPointer().readUtf8String();
var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String();
var dataPtr = this.context.x22;
var dataLen = this.context.x2.toInt32();
var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String();
var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String();
if (dataLen > 0) {
var buffer = dataPtr.readByteArray(dataLen);
@@ -972,10 +1136,10 @@ function setReceiver() {
Interceptor.attach(downloadVideoAddr, {
onEnter: function (args) {
var dataPtr = this.context.x1;
var dataLen = this.context.x24.toInt32();
var fileId = this.context.x22.add(0x40).readPointer().readUtf8String();
var cdnUrl = this.context.x22.add(0x58).readPointer().readUtf8String();
var dataPtr = this.context.x20.add(0x178).readPointer();
var dataLen = this.context.x23.toInt32();
var fileId = this.context.x19.add(0x2E0).readPointer().readUtf8String();
var cdnUrl = this.context.x19.add(0x2F8).readPointer().readUtf8String();
if (dataLen > 0) {
var buffer = dataPtr.readByteArray(dataLen);
+5
View File
@@ -124,6 +124,11 @@ func BuildSendPayload(taskId int64, msgType string) string {
payloadData[16] = 0x10
payloadData[28] = 0x21
payloadData[92] = 0x6E
case "reply":
payloadData[0] = 0x6E
payloadData[16] = 0x10
payloadData[28] = 0x22
payloadData[92] = 0x6E
}
// 构建完整buffer: taskId(4字节) + payloadData(412字节) = 0x1A0字节
+54 -43
View File
@@ -4,11 +4,10 @@ import "encoding/hex"
// BuildUploadPayload 构建上传图片/视频的payload模板,返回hex编码字符串
// uploadType: "img", "video"
// JS侧会在固定偏移写入运行时指针: 0x00, 0x08, 0x48, 0x68(receiver), 0xa8, 0xe0, 0x110, 0x140, 0x1f8
// JS侧会在固定偏移写入运行时指针: 0x00, 0x08, 0x48, 0x68(receiver), 0xa8, 0xe8, 0x118, 0x148, 0x200
func BuildUploadPayload(uploadType string) string {
// 公共部分 (656字节 = 0x290),以img为基准
// 差异字节: [0x44] img=0x01/video=0x0B, [0x9C] img=0x01/video=0x04
// [0x16C-0x16F] img=01000000/video=0400E003
// 公共部分 (760字节 = 0x2F8),以img为基准
// 差异字节: [0x9C] img=0x01/video=0x04, [0x1BC-0x1BF] img=AAAAAAAA/video=01000000
payload := []byte{
0x20, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x00 (被uploadFunc1Addr覆盖)
0x00, 0x05, 0x33, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x08 (被uploadFunc2Addr覆盖)
@@ -18,66 +17,66 @@ func BuildUploadPayload(uploadType string) string {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x28
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x30
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x38
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40 ([0x44]由uploadType决定)
0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x40
0xD0, 0x72, 0x20, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x48 (被imageIdAddr/videoIdAddr覆盖)
0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x50
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x58
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x60
0x77, 0x78, 0x69, 0x64, 0x5F, 0x37, 0x77, 0x64, // 0x68 (被receiver覆盖)
0x31, 0x65, 0x63, 0x65, 0x39, 0x39, 0x66, 0x37, // 0x70
0x69, 0x32, 0x31, 0x00, 0x00, 0x00, 0x00, 0x13, // 0x78
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x68 (被receiver覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x70
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, // 0x78
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x80
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x88
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x90
0x01, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x98 ([0x9C]由uploadType决定)
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0xA0
0xA0, 0xBE, 0x2D, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0xA8 (被md5Addr覆盖)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xA8 (被md5Addr覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xB0
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xB8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xC8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xD8
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xE0 (被ImagePath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xE8
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xF0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF8
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0xE0 文件大小(-1=未知)
0x00, 0x55, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0xE8 (被ImagePath/videoPath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0xF0
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0xF8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x100
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x108
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x110 (被ImagePath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x118
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x120
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x128
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x110
0x40, 0x54, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x118 (被ImagePath/videoPath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x120
0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x128
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x130
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x138
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x140 (被ImagePath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x148
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x150
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x140
0x40, 0x5D, 0xDB, 0x89, 0x0B, 0x00, 0x00, 0x00, // 0x148 (被ImagePath/videoPath覆盖)
0xB2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x150
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x158
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x160
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x168 ([0x16C-0x16F]由uploadType决定)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x170
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x168
0x00, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, // 0x170 [0x174]=0x0B
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x178
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x180
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x188
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x190
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x190
0x00, 0xAA, 0xAA, 0xAA, 0x01, 0x00, 0x00, 0x00, // 0x198
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1A8
0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x0A, 0x0A, // 0x1B0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1B8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1B0
0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // 0x1B8 ([0x1BC-0x1BF]由uploadType决定)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1C8
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D0
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1D8
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F0
0xD0, 0x78, 0x46, 0x8C, 0x0B, 0x00, 0x00, 0x00, // 0x1F8 (被uploadAesKeyAddr覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, // 0x208
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x1F8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x200 (被uploadAesKeyAddr覆盖)
0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x208
0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x210
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x218
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x220
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x228
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x230
@@ -85,23 +84,35 @@ func BuildUploadPayload(uploadType string) string {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x240
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x248
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x250
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x258
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x258
0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // 0x260
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x268
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x270
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x278
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x280
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x288
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x290
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x298
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2A8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2B8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2C8
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2D8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2E8
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x2F0
}
switch uploadType {
case "video":
payload[0x44] = 0x0B
payload[0x9C] = 0x04
payload[0x16C] = 0x04
payload[0x16D] = 0x00
payload[0x16E] = 0xE0
payload[0x16F] = 0x03
payload[0x1BC] = 0x01
payload[0x1BD] = 0x00
payload[0x1BE] = 0x00
payload[0x1BF] = 0x00
}
return hex.EncodeToString(payload)
+31 -2
View File
@@ -142,6 +142,29 @@ func SendWechatMsg(m *SendMsg) {
case "download":
result := fridaScript.ExportsCall("triggerDownload", targetId, m.FIleCdnUrl, m.AesKey, m.FilePath, m.FileType)
Info("📩 下载任务执行结果", "result", result, "task_id", currTaskId, "wechat_id", myWechatId, "target_id", targetId)
case "reply":
replyInfo := &ReplyInfo{
Content: m.Content,
MsgId: m.ReferMsgId,
MsgSender: m.ReferMsgSender,
MsgType: m.ReferMsgType,
CreateTime: m.ReferCreateTime,
Msgsource: m.ReferMsgsource,
DisplayName: m.ReferDisplayName,
MsgContent: m.ReferContent,
}
protoHex, err := BuildReplyMsgProto(myWechatId, targetId, replyInfo)
if err != nil {
Error("构建回复protobuf失败", "err", err)
return
}
payloadHex := BuildSendPayload(currTaskId, "reply")
result := fridaScript.ExportsCall("triggerSendReplyMessage", currTaskId, myWechatId, targetId, protoHex, payloadHex)
Info("📩 发送回复任务执行结果", "result", result, "task_id", currTaskId, "wechat_id", myWechatId, "target_id", targetId)
if result != "1" {
Error("发送回复失败", "task_id", currTaskId, "target_id", targetId, "result", result)
return
}
}
select {
@@ -225,8 +248,14 @@ func HandleMsg(jsonData []byte) ([]byte, error) {
Error("XML解析失败", "err", err)
return nil, err
}
data, err := DownloadFile(fileMsg.Emoji.ThumbUrl)
// 优先thumburl,为空则用externurl
emojiUrl := fileMsg.Emoji.ThumbUrl
if emojiUrl == "" {
emojiUrl = fileMsg.Emoji.ExternUrl
}
data, err := DownloadFile(emojiUrl)
if err != nil {
Error("下载表情失败", "err", err)
return nil, err
-22
View File
@@ -1,22 +0,0 @@
{
"textCallbackFuncAddr": "0x26779D4",
"sendMessageCallbackFunc": "0x8C2C880",
"sendFuncAddr": "0x4A61D3C",
"req2bufEnterAddr": "0x38cba28",
"req2bufExitAddr": "0x38CC97C",
"imageCallbackFuncAddr": "0x266E3A0",
"imgMessageCallbackFunc": "0x8C2C348",
"uploadImageAddr": "0x4bdc9b0",
"cndOnCompleteAddr": "0x388883c",
"buf2RespAddr": "0x38f1520",
"uploadGetCallbackWrapperAddr": "0x4b9b754",
"uploadGetCallbackWrapperFuncAddr": "",
"uploadOnCompleteAddr": "",
"uploadOnCompleteFuncAddr": "",
"downloadImagAddr": "0x4CFFF3C",
"startDownloadMedia": "0x4bdd618",
"downloadFileAddr": "0x4C9EF50",
"downloadVideoAddr": "0x4CB8070",
"videoCallbackFuncAddr": "0x26DD8C8",
"videoMessageCallbackFunc": "0x8C2F0F8"
}
+24
View File
@@ -0,0 +1,24 @@
{
"textCallbackFuncAddr": "0x26779D4",
"sendMessageCallbackFunc": "0x8C2C880",
"sendFuncAddr": "0x4bd0e1c",
"req2bufEnterAddr": "0x39d51d8",
"req2bufExitAddr": "0x39D62EC",
"imageCallbackFuncAddr": "0x266E3A0",
"imgMessageCallbackFunc": "0x8C2C348",
"uploadImageAddr": "0x4d4bbd0",
"cndOnCompleteAddr": "0x399180c",
"buf2RespAddr": "0x39FAC00",
"uploadGetCallbackWrapperAddr": "0x4d0a928",
"uploadGetCallbackWrapperFuncAddr": "0x3991058",
"uploadOnCompleteAddr": "0x4D0B0E0",
"uploadOnCompleteFuncAddr": "0x399223C",
"startDownloadMedia": "0x4d4c838",
"downloadImagAddr": "0x4E6F32C",
"downloadFileAddr": "0x4E0E264",
"downloadVideoAddr": "0x4E28044",
"videoCallbackFuncAddr": "0x26DD8C8",
"videoMessageCallbackFunc": "0x8C2F0F8",
"replyCallbackFuncAddr": "0x260C444",
"replyMessageCallbackFunc": "0x8C29A08"
}