又是我。我正在开发一个工具可以反汇编/重新组装剥离的二进制文件,现在我陷入了(外部)符号重用问题。
测试在 32 位 Linux x86 平台上进行。
假设我正在处理一个 C++ 程序,在GCC编译器生成的汇编代码中,存在一些这样的指令:
call _ZNSt8ios_baseC2Ev
movl _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+4, %ebx
movb $0, 312(%esp)
movl _ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+8, %ecx
....
请特别注意符号_ZTTSt14basic_ifstreamIcSt11char_traitsIcEE。
编译后,假设我得到一个unstripped二进制文件,我像这样检查了这个符号:
readelf -s a.out | grep "_ZTTSt14basic"
69: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS@GLIBCXX_3.4 (3)
72: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS@GLIBCXX_3.4 (3)
705: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS
1033: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS
看,这是我的第一个问题,为什么符号的名称_ZTTSt14basic_ifstreamIcSt11char_traitsIcEE修改为_ZTTSt14basic_ifstreamIcS和_ZTTSt14basic_ifstreamIcS@GLIBCXX_3.4 (3)?
什么是_ZTTSt14basic_ifstreamIcS@GLIBCXX_3.4 (3)?
然后我像这样剥离了二进制文件:
strip a.out
readelf -s a.out | grep "_ZTTSt14basic"
69: 080a7390 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ifstreamIcS@GLIBCXX_3.4 (3)
72: 080a7220 16 OBJECT WEAK DEFAULT 27 _ZTTSt14basic_ofstreamIcS@GLIBCXX_3.4 (3)
然后我反汇编二进制后,对应的反汇编指令是:
8063ee7: e8 84 54 fe ff call 8049370 <_ZNSt8ios_baseC2Ev@plt>
8063eec: 8b 1d 94 73 0a 08 mov 0x80a7394,%ebx
8063ef2: c6 84 24 38 01 00 00 movb $0x0,0x138(%esp)
8063ef9: 00
8063efa: 8b 0d 98 73 0a 08 mov 0x80a7398,%ecx
此时我们可以计算出 0x80a7394 等于_ZTTSt14basic_ifstreamIcSt11char_traitsIcEE+4。
为了重用这些指令,我修改了代码:
call _ZNSt8ios_baseC2Ev
mov _ZTTSt14basic_ifstreamIcS+4,%ebx
movb $0x0,0x138(%esp)
mov _ZTTSt14basic_ifstreamIcS+8,%ecx
并做了一些这样的更新(请参阅此问题以供参考):
echo ""_ZTTSt14basic_ifstreamIcS@GLIBCXX_3.4 (3)" = 0x080a7390;" > symbolfile
g++ -Wl,--just-symbols=symbolfile final.s
readelf -s a.out | grep "_ZTTSt14basic"
3001: 080a7390 0 NOTYPE LOCAL DEFAULT 27 _ZTTSt14basic_ifstreamIcS
8412: 080a7390 0 NOTYPE GLOBAL DEFAULT ABS _ZTTSt14basic_ifstreamIcS
我调试了新生成的二进制文件,令我惊讶的是,在新生成的二进制文件中,symbol_ZTTSt14basic_ifstreamIcS在函数调用后没有得到任何值_ZNSt8ios_baseC2Ev,而在原始二进制文件中,在函数调用后,_ZTTSt14basic_ifstreamIcS确实得到了一些引用库部分的内存地址. 意思是:
call _ZNSt8ios_baseC2Ev
mov _ZTTSt14basic_ifstreamIcS+4,%ebx <--- %ebx gets zero!
movb $0x0,0x138(%esp)
mov _ZTTSt14basic_ifstreamIcS+8,%ecx <--- %ecx gets zero!
我必须声明,在原始二进制文件的这些行中,寄存器 %ebx 和 %ecx 都获得了一些引用 libc 部分的地址。
这是我的第二个问题,为什么_ZTTSt14basic_ifstreamIcS在函数调用后符号没有得到任何值_ZNSt8ios_baseC2Ev?我也尝试过使用符号名称_ZTTSt14basic_ifstreamIcSt11char_traitsIcEE。但这也行不通。
我够清楚了吗?谁能救救我的屁股?谢谢!