mirror of
https://github.com/yincongcyincong/wechat_chatter.git
synced 2026-07-15 10:26:52 +08:00
Merge branch 'main' of github.com:f1ynng8/wechat_chatter
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
# WeChat 4.0 Message hook
|
||||
4.1.9之后的不更新了,感谢各位大佬的支持
|
||||
|
||||
本代码库中的所有代码、示例、文档及相关内容(以下简称“本项目”)仅供学习、研究和技术交流之目的使用。使用本项目所产生的任何风险(包括但不限于数据丢失、系统崩溃、安全问题、法律风险等)均由使用者自行承担。
|
||||
|
||||
我hook的是微信三端最底层的发消息能力,这块代码是开源的,感兴趣google tencent/mars
|
||||
|
||||
+3
-3
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 = "";
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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; // 空 (必须显式存在)
|
||||
}
|
||||
@@ -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: ×tamp,
|
||||
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 += "&"
|
||||
case '<':
|
||||
result += "<"
|
||||
case '>':
|
||||
result += ">"
|
||||
case '"':
|
||||
result += """
|
||||
case '\'':
|
||||
result += "'"
|
||||
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
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
Reference in New Issue
Block a user