如何利用Frida读取Cgo编译DLL中DoRequest参数结构体的属性?
读取Go编译C DLL中DoRequest参数结构体的Frida实现
核心思路
- 基于C#结构体定义,在Frida中映射对应的64位内存布局,考虑指针大小(8字节)和字段对齐规则
- 区分字符串类型:默认C#
string对应UTF-16(LPWSTR),标注[MarshalAs(UnmanagedType.LPUTF8Str)]的字段对应UTF-8(char*) - 可通过Frida的
NativeStruct自动处理布局,或手动计算字段偏移读取数据
完整Frida Hook代码
方法1:使用NativeStruct自动处理布局
这种方式无需手动计算偏移,Frida会根据目标平台自动处理结构体对齐:
var doRequest = Module.getExportByName("client64.dll", "DoRequest"); // 映射C#的Struct0结构体 const RequestStruct = new NativeStruct({ string_0: 'pointer', string_1: 'pointer', string_2: 'pointer', string_3: 'pointer', weirdString: 'pointer', // 对应特殊命名字段}{__>_ string_4: 'pointer', string_5: 'pointer', string_6: 'pointer', string_7: 'pointer', string_8: 'pointer', string_9: 'pointer', int_0: 'int', int_1: 'int', string_10: 'pointer', bool_0: 'uint8', // UnmanagedType.I1对应1字节无符号整数 bool_1: 'uint8', string_11: 'pointer', int_2: 'int', uint_0: 'uint' }); // 读取UTF-16格式字符串(C#默认string类型) function readUnicodeString(ptr) { return ptr.isNull() ? 'null' : ptr.readUtf16String(); } // 读取UTF-8格式字符串(对应LPUTF8Str标注的字段) function readUtf8String(ptr) { return ptr.isNull() ? 'null' : ptr.readUtf8String(); } Interceptor.attach(doRequest, { onEnter: function(args) { console.log("[+] Hooked DoRequest"); var structPtr = args[0]; console.log("[+] Struct pointer: " + structPtr); // 解析结构体实例 var requestStruct = new RequestStruct(structPtr); // 打印所有字段内容 console.log("\n[+] Struct fields:"); console.log(`string_0: ${readUnicodeString(requestStruct.string_0)}`); console.log(`string_1: ${readUnicodeString(requestStruct.string_1)}`); console.log(`string_2: ${readUnicodeString(requestStruct.string_2)}`); console.log(`string_3: ${readUnicodeString(requestStruct.string_3)}`); console.log(`}{__>_: ${readUnicodeString(requestStruct.weirdString)}`); console.log(`string_4 (UTF8): ${readUtf8String(requestStruct.string_4)}`); console.log(`string_5 (UTF8): ${readUtf8String(requestStruct.string_5)}`); console.log(`string_6: ${readUnicodeString(requestStruct.string_6)}`); console.log(`string_7: ${readUnicodeString(requestStruct.string_7)}`); console.log(`string_8: ${readUnicodeString(requestStruct.string_8)}`); console.log(`string_9: ${readUnicodeString(requestStruct.string_9)}`); console.log(`int_0: ${requestStruct.int_0}`); console.log(`int_1: ${requestStruct.int_1}`); console.log(`string_10: ${readUnicodeString(requestStruct.string_10)}`); console.log(`bool_0: ${requestStruct.bool_0 !== 0}`); console.log(`bool_1: ${requestStruct.bool_1 !== 0}`); console.log(`string_11: ${readUnicodeString(requestStruct.string_11)}`); console.log(`int_2: ${requestStruct.int_2}`); console.log(`uint_0: ${requestStruct.uint_0}`); } });
方法2:手动计算偏移读取(适用于自动布局异常场景)
如果NativeStruct自动处理的布局不符合实际内存结构,可手动计算64位下的字段偏移:
var doRequest = Module.getExportByName("client64.dll", "DoRequest"); // 64位系统下的字段偏移(按C# Sequential布局规则计算) const offsets = { string_0: 0x00, string_1: 0x08, string_2: 0x10, string_3: 0x18, weirdString: 0x20, string_4: 0x28, string_5: 0x30, string_6: 0x38, string_7: 0x40, string_8: 0x48, string_9: 0x50, int_0: 0x58, int_1: 0x5C, string_10: 0x60, bool_0: 0x68, bool_1: 0x69, string_11: 0x70, int_2: 0x78, uint_0: 0x7C }; function readUnicodeString(ptr) { return ptr.isNull() ? 'null' : ptr.readUtf16String(); } function readUtf8String(ptr) { return ptr.isNull() ? 'null' : ptr.readUtf8String(); } Interceptor.attach(doRequest, { onEnter: function(args) { console.log("[+] Hooked DoRequest"); var structPtr = args[0]; console.log("[+] Struct pointer: " + structPtr); console.log("\n[+] Struct fields:"); console.log(`string_0: ${readUnicodeString(structPtr.add(offsets.string_0).readPointer())}`); console.log(`string_1: ${readUnicodeString(structPtr.add(offsets.string_1).readPointer())}`); console.log(`string_2: ${readUnicodeString(structPtr.add(offsets.string_2).readPointer())}`); console.log(`string_3: ${readUnicodeString(structPtr.add(offsets.string_3).readPointer())}`); console.log(`}{__>_: ${readUnicodeString(structPtr.add(offsets.weirdString).readPointer())}`); console.log(`string_4 (UTF8): ${readUtf8String(structPtr.add(offsets.string_4).readPointer())}`); console.log(`string_5 (UTF8): ${readUtf8String(structPtr.add(offsets.string_5).readPointer())}`); console.log(`string_6: ${readUnicodeString(structPtr.add(offsets.string_6).readPointer())}`); console.log(`string_7: ${readUnicodeString(structPtr.add(offsets.string_7).readPointer())}`); console.log(`string_8: ${readUnicodeString(structPtr.add(offsets.string_8).readPointer())}`); console.log(`string_9: ${readUnicodeString(structPtr.add(offsets.string_9).readPointer())}`); console.log(`int_0: ${structPtr.add(offsets.int_0).readInt()}`); console.log(`int_1: ${structPtr.add(offsets.int_1).readInt()}`); console.log(`string_10: ${readUnicodeString(structPtr.add(offsets.string_10).readPointer())}`); console.log(`bool_0: ${structPtr.add(offsets.bool_0).readU8() !== 0}`); console.log(`bool_1: ${structPtr.add(offsets.bool_1).readU8() !== 0}`); console.log(`string_11: ${readUnicodeString(structPtr.add(offsets.string_11).readPointer())}`); console.log(`int_2: ${structPtr.add(offsets.int_2).readInt()}`); console.log(`uint_0: ${structPtr.add(offsets.uint_0).readUInt()}`); } });
注意事项
- 若读取到乱码,检查字符串类型是否匹配:默认C#
string用readUtf16String,标注LPUTF8Str的字段用readUtf8String - 若字段值异常,可通过C#的
Marshal.SizeOf(typeof(Struct0))计算结构体总大小,验证Frida读取的内存范围是否一致 - Go编译的C DLL使用C调用约定,Frida默认兼容,无需额外配置
内容的提问来源于stack exchange,提问作者Sam
相关产品推荐
相关产品推荐

