You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Frida拦截方法中如何读取C# List<int>等复杂对象的值?

读取Hook到的C# List内容(Frida)

C#的List<int>是托管对象,直接读取指针内存需要结合它的内部结构,或者借助Frida针对不同运行时的API来简化操作。下面分两种最常见的场景说明:

场景1:Mono运行时(Unity/Xamarin等)

如果你的目标应用基于Mono运行时(比如Unity游戏、Xamarin安卓应用),Frida提供了专门的Mono命名空间来直接操作托管对象,这是最可靠的方式:

  1. 先在脚本开头初始化Mono相关的类和字段信息:
// 初始化Mono API(确保Frida已加载Mono支持)
const mono = Mono.Runtime.getApi();

// 获取泛型List<T>的类定义
const listGenericClass = Mono.Class.fromName("System.Collections.Generic", "List`1");
// 实例化为List<int>的具体类
const intListClass = listGenericClass.instantiate(Mono.Class.fromName("System", "Int32"));

// 提前获取List<int>内部的_size(元素数量)和_items(存储数组)字段
const sizeField = intListClass.field("_size");
const itemsField = intListClass.field("_items");
  1. 在Hook的onEnter方法中解析List内容:
Interceptor.attach(Module.findBaseAddress("library.so").add(0x123456), {
    onEnter(args) {
        // 将第三个参数(args[2])转换为Mono托管对象
        const listObj = new Mono.Object(args[2]);

        // 获取List当前的元素数量
        const listSize = listObj.field(sizeField).value;
        console.log(`List<int> 元素数量: ${listSize}`);

        // 获取内部存储元素的数组指针
        const itemsArrayPtr = listObj.field(itemsField).value;
        console.log("List<int> 元素内容:");
        
        // 遍历数组读取每个int元素(int占4字节)
        for (let i = 0; i < listSize; i++) {
            const elementValue = itemsArrayPtr.add(i * 4).readS32();
            console.log(`  索引${i}: ${elementValue}`);
        }
    }
});

小提示:

如果遇到压缩指针(64位进程中启用了指针压缩),需要先把args[2]转换为真实地址:

const realPtr = ptr(args[2].toInt64() << 3); // 压缩指针是右移3位存储,左移恢复真实地址
const listObj = new Mono.Object(realPtr);

场景2:Native AOT编译的程序

如果目标程序是Native AOT编译(直接编译为机器码,无Mono运行时),需要先逆向确认List<int>的内存结构:

  1. 逆向确定字段偏移(以arm64架构为例):

    • 通常List<int>的结构包含:对象头(8字节)、方法表指针(8字节)、_items(数组指针,8字节)、_size(元素数量,4字节)。
    • 对应的偏移:_items在0x10,_size在0x18。
  2. 直接读取内存解析:

Interceptor.attach(Module.findBaseAddress("library.so").add(0x123456), {
    onEnter(args) {
        const listPtr = args[2];
        
        // 读取元素数量(arm64下偏移0x18,x86架构可能是0xC,需逆向确认)
        const listSize = listPtr.add(0x18).readS32();
        console.log(`List<int> 元素数量: ${listSize}`);

        // 读取内部数组的指针
        const itemsArrayPtr = listPtr.add(0x10).readPointer();
        console.log("List<int> 元素内容:");
        
        // 遍历数组读取每个int元素
        for (let i = 0; i < listSize; i++) {
            const elementValue = itemsArrayPtr.add(i * 4).readS32();
            console.log(`  索引${i}: ${elementValue}`);
        }
    }
});

注意:

不同架构(x86/arm/arm64)和.NET版本的List<T>结构偏移可能有差异,建议用IDA/Ghidra逆向目标程序的List<int>结构来确认准确偏移。


内容的提问来源于stack exchange,提问作者najm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:43:16