第1部分
PE 导入 thunk 的工作方式与 ELF PLT 不同。第一次调用时没有调用动态解析器,但所有导入指针都会在进程启动时提前解析(类似于LD_BIND_NOW
)。指针被分组在一个类似 GOT 的导入地址表 (IAT) 中,包含有关 DLL 和导入符号的详细信息的元数据存储在由 PE 标头引用的导入目录中。
要恢复符号,您需要解析导入目录。详细信息可以在官方PE 格式规范中找到。
第2部分
编辑后,您似乎正在处理本机到托管代码 thunk。
我已经完成了以下实验以生成混合可执行文件:
m.cpp
(托管代码):
using namespace System;
void hello()
{
String^ str = "Hello World";
Console::WriteLine(str);
}
n.cpp
(本机代码):
void hello();
void main()
{
hello();
}
编译并链接:
cl /c /clr /Zi m.cpp
cl /c /Zi n.cpp
link /debug /out:mixed.exe m.obj n.obj
拆卸本地部分(并通过 PDB 获得符号)后,我可以观察到以下内容:
.text:00007FF798E81090 main proc near
.text:00007FF798E81090 sub rsp, 28h
.text:00007FF798E81094 call ?hello@@YAXXZ ; hello(void)
.text:00007FF798E81099 xor eax, eax
.text:00007FF798E8109B add rsp, 28h
.text:00007FF798E8109F retn
.text:00007FF798E8109F main endp
通话后:
.nep:00007FF798ECC000 ?hello@@YAXXZ proc near
.nep:00007FF798ECC000 jmp short loc_7FF798ECC00A
.nep:00007FF798ECC002 ud2
.nep:00007FF798ECC004 jmp cs:__m2mep@?hello@@$$FYAXXZ
.nep:00007FF798ECC00A loc_7FF798ECC00A:
.nep:00007FF798ECC00A jmp cs:__mep@?hello@@$$FYAXXZ
.nep:00007FF798ECC00A ?hello@@YAXXZ endp
最后,遵循jmp
:
.data:00007FF798EE7000 __m2mep@?hello@@$$FYAXXZ dq 6000001h
.data:00007FF798EE7008 __mep@?hello@@$$FYAXXZ dq 6000001h
值 6000001 是一个CLR 令牌。高字节字节是令牌种类,或元数据表索引,在本例中为 0x6 表示Method。在 .NET 查看器(如 ILDASM 或 dnSpy)中查找它,我们可以看到它引用了 RVA 的托管方法“hello” 000010a0
。转到那个地址,我们看到:
.text:00007FF798E810A0 ?hello@@$$FYAXXZ:
.text:00007FF798E810A0 add esi, [rax]
.text:00007FF798E810A2 add [rax], eax
.text:00007FF798E810A4 sldt word ptr [rax]
.text:00007FF798E810A7 add [rdx], al
.text:00007FF798E810A9 db 2 dup(0), 11h
.text:00007FF798E810AC hello:
.text:00007FF798E810AC adc al, 0Ah
.text:00007FF798E810AE jb short loc_7FF798E810B1
.text:00007FF798E810B0 db 0
.text:00007FF798E810B1 loc_7FF798E810B1:
.text:00007FF798E810B1 add [rax+0Ah], dh
.text:00007FF798E810B4 dd 22806h
.text:00007FF798E810B8 db 0, 0Ah, 2Ah, 0CCh
它作为 x64 代码没有任何意义,因此这显然是 CLI 字节码,应该使用 .net 反编译器进行检查。奇怪的是,我尝试的那些似乎都没有显示该功能,但我设法从 ILDASM 获得了 IL 反汇编:
.method /*06000001*/ assembly static void modopt([mscorlib/*23000001*/]System.Runtime.CompilerServices.CallConvCdecl/*01000001*/)
hello() cil managed
{
.vtentry 1 : 1
// Code size 15 (0xf)
.maxstack 1
.locals /*11000002*/ ([0] string str)
IL_0000: ldnull
IL_0001: stloc.0
IL_0002: ldstr "Hello World" /* 70000001 */
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: call void [mscorlib/*23000001*/]System.Console/*01000003*/::WriteLine(string) /* 0A000002 */
IL_000e: ret
} // end of global method hello
您可能可以遵循类似的方法并在元数据表或 IL 反汇编中查找令牌 0x06000094 以找出托管代码中跳转的目的地。
随机观察:
该段.nep
似乎意味着“本机入口点”
__mep
令牌名称中的前缀可能意味着“托管入口点”。