在反汇编/重组 C++ 程序中重用符号

逆向工程 拆卸 部件 小精灵 符号 重新组装
2021-06-30 17:54:21

又是我。我正在开发一个工具可以反汇编/重新组装剥离的二进制文件,现在我陷入了(外部)符号重用问题。

测试在 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但这也行不通。

我够清楚了吗?谁能救救我的屁股?谢谢!

1个回答

老问题,但我想我可以回答一部分

为什么符号名称_ZTTSt14basic_ifstreamIcSt11char_traitsIcEE修改为_ZTTSt14basic_ifstreamIcS

我认为您刚刚遇到了终端宽度限制。默认情况下readelf将输出行限制为 80 个字符,您需要传递-W以禁用它:

-W --wide              Allow output width to exceed 80 characters