Frida拦截方法中如何读取C# List<int>等复杂对象的值?
读取Hook到的C# List内容(Frida)
C#的List<int>是托管对象,直接读取指针内存需要结合它的内部结构,或者借助Frida针对不同运行时的API来简化操作。下面分两种最常见的场景说明:
场景1:Mono运行时(Unity/Xamarin等)
如果你的目标应用基于Mono运行时(比如Unity游戏、Xamarin安卓应用),Frida提供了专门的Mono命名空间来直接操作托管对象,这是最可靠的方式:
- 先在脚本开头初始化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");
- 在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>的内存结构:
逆向确定字段偏移(以arm64架构为例):
- 通常
List<int>的结构包含:对象头(8字节)、方法表指针(8字节)、_items(数组指针,8字节)、_size(元素数量,4字节)。 - 对应的偏移:
_items在0x10,_size在0x18。
- 通常
直接读取内存解析:
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
相关产品推荐
相关产品推荐

