使用frida发送微信消息给好友
# 前言之前说过怎么用(https://www.52pojie.cn/thread-1646860-1-1.html),原理大概就是构造内存机器码。其实frida也可以做类似操作,构造数据和机器码,然后调用,基本是一样的。我就不重复操作了,这里说下另一种方法。
想使用frida来发送消息这个想法很早之前就有了,只是搜了很久,一直没有frida在Windows端的操作,教程基本都是针对安卓的。
最早是在吾爱看到一篇使用frida来hook接收消息的函数,[帖子看这里](https://www.52pojie.cn/thread-1346100-4-1.html)
我还在下面评论了下怎么用frida来发送消息,可惜没有回应,直到最近我看到一个教程(赵庆明老师的`Frida快速入门/OD/CE/IDA/Python/TS/JS`),链接就不放了,避免做广告的嫌疑,有兴趣的自己百度
![在这里插入图片描述](https://img-blog.csdnimg.cn/673733541da14cedae7234466f8bbaae.png)
## 准备工作
我假设你已经熟悉了frida的开发环境和使用,只讲一些关键思路
调用安卓的so函数,可以使用`NativeFunction`调用地址。但是 ,Windows相对于安卓而言,x86函数有多种调用约定,无法自动处理。我没有找到frida如何处理调用约定,再加上微信逆出来的函数,有些还不是标准的调用约定,也就是说很难在外部直接调用call,只能用汇编的形式来调用
那只能换一种思路来搜了,如何使用frida来写汇编,在x86平台用的是 (https://frida.re/docs/javascript-api/#x86writer)
#### 写汇编
比如用frida翻译一下发消息的函数
![在这里插入图片描述](https://img-blog.csdnimg.cn/7f799109d16f4f83888050f03bec91aa.png)
先分配一块内存地址,用来写汇编:
```javascript
let m2 = Memory.alloc(Process.pageSize);
```
开始往内存地址里写入内容:
```javascript
Memory.patchCode(m2, Process.pageSize, (code) => {
console.log("code", code);
let asm = new X86Writer(code);
});
```
Memory.patchCode就是往地址里写入汇编代码的,第一个参数是内存地址,第二个参数是内存大小,第三个则是写入的函数 参数code就是m2
翻译上面的汇编:
1. `push edi`
```javascript
// putPushReg是push一个寄存器
asm.putPushReg('edi')
```
2.`lea eax, dword ptr ds:`
因为frida内置的lea只能将一个寄存器赋值给另一个寄存器,而这里是对地址操作,所以我这里用mov来赋值。按我的理解来看,这里应该需要先构造个指针,然后在mov给eax。但是测试发现这样操作居然报错了,直接将wxNull赋值给eax就没问题。
```
let wxNull = Memory.alloc(0x100);
asm.putMovRegAddress("eax", wxNull );
// let wxNull = Memory.alloc(0x100);
// let wxNullPointer = Memory.alloc(Process.pointerSize);
// wxNullPointer.writePointer(wxNull);
// asm.putMovRegAddress("eax", wxNullPointer);
```
3. `push 0x1`
```javascript
asm.putPushU32(0x1);
```
4. `mov edi, dword ptr ds:`
pWxmsg是个结构体,后面再看怎么赋值
```javascript
asm.putMovRegAddress("edi", pWxmsg);
```
5. `push edi`
```javascript
asm.putPushReg('edi');
```
6.`lea edx, dword ptr ds:`
正常也是应该赋值给一个指针再mov,也是报错,直接mov就没问题。可能是我理解的有问题,算了,能跑就行了
```javascript
asm.putMovRegAddress('edx', wxWxid);
```
7.`lea ecx, dword ptr ds:`
```javascript
let buffer = Memory.alloc(0x3B0);
asm.putMovRegAddress('ecx', buffer);
```
8.`call address`
版本还是用的上一篇python发消息里的版本。其他版本的偏移自己找下吧
```javascript
let ModAddress = Process.getModuleByName('wechatwin.dll');
let callAddress = ModAddress.base.add('0x521D30');
asm.putCallAddress(callAddress);
```
9. `最后三个`
```javascript
asm.putAddRegImm("esp", 0xC);
asm.putPopReg("edi");
asm.putRet();
```
10. 写入内存
```javascript
asm.flush();
```
#### 构造结构体
wxWxid和pWxmsg都需要是结构体的形式,所以得构造出一个结构体,这个和python是一样操作的。分配内存,如何写地址进去
```javascript
function build_struct(content) {
let struct_address = Memory.alloc(20);
let length = content.readUtf16String().length;
struct_address.writePointer(content);
struct_address.add(0x4).writeU32(length);
struct_address.add(0x8).writeU32(length * 2);
struct_address.add(0xC).writeU32(0);
struct_address.add(0x10).writeU32(0);
// console.log(hexdump(struct_address));
return struct_address;
}
let wxid = "filehelper";
let U16Wxid = Memory.allocUtf16String(wxid);
let wxWxid = build_struct(U16Wxid);
```
#### 完整代码:
效果图:
![在这里插入图片描述](https://img-blog.csdnimg.cn/eca81c55465843bb83c997cb14bdf745.png)
```javascript
// 打印汇编代码, 直接抄的课程代码
function show_asm(start, length = 50) {
for (let index = 0; index < length; index++) {
let inst = Instruction.parse(start);
// console.log(JSON.stringify(inst));
let byteArray = start.readByteArray(inst.size);
let byteCode = Array.prototype.slice.call(new Uint8Array(byteArray));
let mCode = byteCode.map(x => x.toString(16).padStart(2, "0")).join(" ").toUpperCase();
console.log(inst.address.toString().toUpperCase().replace("0X", "0x"), mCode.padEnd(14, " "), "\t", inst.toString().toUpperCase().replace("0X", "0x"));
start = inst.next;
if (start.readU32() == 0)
break;
}
}
// 构造微信通用结构体
function build_struct(content, length) {
let struct_address = Memory.alloc(20);
struct_address.writePointer(content);
struct_address.add(0x4).writeU32(length);
struct_address.add(0x8).writeU32(length * 2);
struct_address.add(0xC).writeU32(0);
struct_address.add(0x10).writeU32(0);
// console.log(hexdump(struct_address));
return struct_address;
}
function build_function_sendmsg() {
let wxNull = Memory.alloc(0x100);
let buffer = Memory.alloc(0x3B0);
// 构建wxid结构体
let wxid = "filehelper";
let U16Wxid = Memory.allocUtf16String(wxid);
let wxid_struct_address = build_struct(U16Wxid, wxid.length);
// 构造消息结构体
let msg = (new Date()).toJSON();
console.log("当前发送消息内容: ", msg);
let U16Msg = Memory.allocUtf16String(msg);
let msg_struct_address = build_struct(U16Msg, msg.length);
// 计算函数地址
let ModAddress = Process.getModuleByName('wechatwin.dll');
let callAddress = ModAddress.base.add('0x521D30');
// 写入汇编
let m2 = Memory.alloc(Process.pageSize);
Memory.patchCode(m2, Process.pageSize, (code) => {
// console.log("code", code);
let asm = new X86Writer(code);
asm.putPushReg('edi');
asm.putMovRegAddress("eax", wxNull);
asm.putPushU32(0x1);
asm.putPushReg('eax');
asm.putMovRegAddress("edi", msg_struct_address);
asm.putPushReg('edi');
asm.putMovRegAddress('edx', wxid_struct_address);
asm.putMovRegAddress('ecx', buffer);
asm.putCallAddress(callAddress);
asm.putAddRegImm("esp", 0xC);
asm.putPopReg("edi");
asm.putRet();
asm.flush();
});
show_asm(m2);
// 调用函数, 第一个是地址,第二个是返回值类型,第三个是参数列表
let sendmsg_call = new NativeFunction(m2, "void", []);
sendmsg_call();
}
// build_function_sendmsg();
```
如果想更新最新版本,一般情况下只需要更改callAddress 后面的偏移,发文本消息一般不会怎么变
如果想再python端调用,只需要用rpc.exports导出build_function_sendmsg给python。这些都和安卓是一样的,就不重复说了 学习了楼主辛苦了 好的,不错不错 又一种新的方式,不错,mark下 牛蛙牛蛙 太厉害了 大佬厉害,试试 拿走了 太厉害了大佬 学习了,谢谢大佬分享:lol 谢谢大佬分享的思路,学习