• 使用frida发送微信消息给好友


    前言

    之前说过怎么用python来发送微信文本消息,原理大概就是构造内存机器码。其实frida也可以做类似操作,构造数据和机器码,然后调用。我就不重复操作了,这里说下另一种方法。

    想使用frida来发送消息这个想法很早之前就有了,只是搜了很久,一直没有frida在Windows端的操作,教程基本都是针对安卓的。

    最早是在吾爱看到一篇使用frida来hook接收消息的函数,帖子看这里

    我还在下面评论了下怎么用frida来发送消息,可惜没有回应,直到最近我看到一个教程(赵庆明老师的Frida快速入门/OD/CE/IDA/Python/TS/JS),链接就不放了,避免做广告的嫌疑,有兴趣的自己百度
    在这里插入图片描述

    准备工作

    我假设你已经熟悉了frida的开发环境和使用,只讲一些关键思路

    调用安卓的so函数,可以使用NativeFunction调用地址。但是 ,Windows相对于安卓而言,x86函数有多种调用约定,无法自动处理。我没有找到frida如何处理调用约定,再加上微信逆出来的函数,有些还不是标准的调用约定,也就是说很难在外部直接调用call,只能用汇编的形式来调用

    那只能换一种思路来搜了,如何使用frida来写汇编,在x86平台用的是 x86writer

    写汇编

    比如用frida翻译一下发消息的函数
    在这里插入图片描述
    先分配一块内存地址,用来写汇编:

    let m2 = Memory.alloc(Process.pageSize);
    
    • 1

    开始往内存地址里写入内容:

    Memory.patchCode(m2, Process.pageSize, (code) => {
        console.log("code", code); 
        let asm = new X86Writer(code);
    });
    
    • 1
    • 2
    • 3
    • 4

    Memory.patchCode就是往地址里写入汇编代码的,第一个参数是内存地址,第二个参数是内存大小,第三个则是写入的函数 参数code就是m2

    翻译上面的汇编:

    1. push edi
    // putPushReg是push一个寄存器
    asm.putPushReg('edi')
    
    • 1
    • 2
    1. lea eax, dword ptr ds:[wxNull]
      因为frida内置的lea只能将一个寄存器赋值给另一个寄存器,而这里是对地址操作,所以我这里用mov来赋值。按我的理解来看,这里应该需要先构造个指针,然后在mov给eax。但是测试发现这样操作居然报错了,直接将wxNull赋值给eax就没问题。
    // let wxNull = Memory.alloc(0x100);
    // let wxNullPointer = Memory.alloc(Process.pointerSize);
    // wxNullPointer.writePointer(wxNull);
    // asm.putMovRegAddress("eax", wxNullPointer);
    
    let wxNull = Memory.alloc(0x100);
    asm.putMovRegAddress("eax", wxNull );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. push 0x1
    asm.putPushU32(0x1);
    
    • 1
    1. mov edi, dword ptr ds:[pWxmsg]
      pWxmsg是个结构体,后面再看怎么赋值
    asm.putMovRegAddress("edi", pWxmsg);
    
    • 1
    1. push edi
    asm.putPushReg('edi');
    
    • 1
    1. lea edx, dword ptr ds:[wxWxid]
      正常也是应该赋值给一个指针再mov,也是报错,直接mov就没问题。可能是我理解的有问题,算了,能跑就行了
    asm.putMovRegAddress('edx', wxWxid);
    
    • 1
    1. lea ecx, dword ptr ds:[buffer]
    let buffer = Memory.alloc(0x3B0);
    asm.putMovRegAddress('ecx', buffer);
    
    • 1
    • 2
    1. call address
      版本还是用的上一篇python发消息里的版本。其他版本的偏移自己找下吧
    let ModAddress = Process.getModuleByName('wechatwin.dll');
    let callAddress = ModAddress.base.add('0x521D30');
    asm.putCallAddress(callAddress);
    
    • 1
    • 2
    • 3
    1. 最后三个
    asm.putAddRegImm("esp", 0xC);
    asm.putPopReg("edi");
    asm.putRet();
    
    • 1
    • 2
    • 3
    1. 写入内存
    asm.flush();
    
    • 1
    构造结构体

    wxWxid和pWxmsg都需要是结构体的形式,所以得构造出一个结构体,这个和python是一样操作的。分配内存,如何写地址进去

    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 U16Wxid = Memory.allocUtf16String(wxid);
    let wxWxid = build_struct(U16Wxid);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    完整代码:

    效果图:
    在这里插入图片描述

    // 打印汇编代码, 直接抄的课程代码
    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();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70

    如果想更新最新版本,一般情况下只需要更改callAddress 后面的偏移,发文本消息一般不会怎么变

    如果想再python端调用,只需要用rpc.exports导出build_function_sendmsg给python。这些都和安卓是一样的,就不重复说了

  • 相关阅读:
    Mi小爱音箱pro接入GPT
    c#基础入门
    Postman之接口测试
    2022-08-24 第二小组 张明旭 前端学习记录
    虚拟机和linux
    Docker 快速安装Jenkins完美教程 (亲测采坑后详细步骤)(转)
    爬虫神器|这是我过Debugger检测最简单的方法,没有之一
    Flowable 的使用
    C#WPF通过串口(232协议)调用基恩士打标机进行打标
    cpp primer plus笔记06-函数
  • 原文地址:https://blog.csdn.net/Qwertyuiop2016/article/details/127959834