当二进制被剥离并且_start不推送main的绝对地址值时,如何找到main函数的起始地址?

逆向工程 二元分析 x86 小精灵 静态分析 二进制
2021-06-21 15:17:03

因此,假设 ELF 二进制文件被剥离了 - 意味着没有符号表 - 并且该_start函数在调用之前不会推送 main 的地址__libc_start_main

这发生在为 32 位和 64 位架构编译的二进制文件中,不知道为什么有时_start不推送绝对值而是推送寄存器值(如果有人知道,请告诉我)。

在这种情况下,有什么方法可以生成从 main 开始的二进制文件的调用图(没有名称)?有什么办法可以找到我的地址main吗?因为现在它在堆栈上压入一个寄存器值并且绝对地址main没有出现在任何指令中!

我已经添加了一个简单 C 程序的 32 位版本的启动例程示例,用 gcc 编译-m32 (本节中的 64 位版本有点相同,没有绝对地址)。

我不确定为什么我的一些 32 位程序在调用之前推送 main 的绝对地址,__libc_main_start而有些则没有。如果你知道答案,请告诉我。

00001070 <_start>:
    1070:       31 ed                   xor    ebp,ebp
    1072:       5e                      pop    esi
    1073:       89 e1                   mov    ecx,esp
    1075:       83 e4 f0                and    esp,0xfffffff0
    1078:       50                      push   eax
    1079:       54                      push   esp
    107a:       52                      push   edx
    107b:       e8 22 00 00 00          call   10a2 <_start+0x32>
    1080:       81 c3 80 2f 00 00       add    ebx,0x2f80
    1086:       8d 83 50 d4 ff ff       lea    eax,[ebx-0x2bb0]
    108c:       50                      push   eax
    108d:       8d 83 f0 d3 ff ff       lea    eax,[ebx-0x2c10]
    1093:       50                      push   eax
    1094:       51                      push   ecx
    1095:       56                      push   esi
    1096:       ff b3 f8 ff ff ff       push   DWORD PTR [ebx-0x8]
    109c:       e8 9f ff ff ff          call   1040 <__libc_start_main@plt>
    10a1:       f4                      hlt
    10a2:       8b 1c 24                mov    ebx,DWORD PTR [esp]
    10a5:       c3                      ret
    10a6:       66 90                   xchg   ax,ax
    10a8:       66 90                   xchg   ax,ax
    10aa:       66 90                   xchg   ax,ax
    10ac:       66 90                   xchg   ax,ax
    10ae:       66 90                   xchg   ax,ax
1个回答

该二进制文件似乎是一个与位置无关的可执行文件(PIE)。

这意味着不能将绝对地址压入堆栈:位置无关代码的目的是在创建进程时通过地址空间布局随机化(ASLR)(由内核实现)将代码加载到虚拟内存中的随机位置,因此没有在运行之前可以知道绝对地址。相反,使用程序计数器相对寻址,其中重定位是参考 CPU 指令指针计算的。1

笔记:

  1. 的绝对地址main可以在运行时找到
  2. main每次执行程序时的绝对地址都会不同
  3. 如果二进制文件未被剥离,则可以静态生成调用图,并且无论二进制文件是否被剥离,都可以生成 CFG。

当我使用-piefPIE选项用 GCC 编译 C 程序时,我能够生成类似的代码完整命令:

gcc -m32 -pie -fPIE [source_file] -o [output_file_name]


提供的汇编代码段中指令的偏移量是这里发生的事情的重要线索。

在一个普通的 x86 ELF32 二进制文件中,程序初始化代码的偏移量(其第一条指令由e_entryELF 标头中的字段中的值指向,也就是程序入口点)将离规范入口点不远0x8048000. 这是一个示例,我们可以将其用作比较的基线:

080482f0 <_start>:
 80482f0:       31 ed                   xor    ebp,ebp
 80482f2:       5e                      pop    esi
 80482f3:       89 e1                   mov    ecx,esp
 80482f5:       83 e4 f0                and    esp,0xfffffff0
 80482f8:       50                      push   eax
 80482f9:       54                      push   esp
 80482fa:       52                      push   edx
 80482fb:       68 70 84 04 08          push   0x8048470
 8048300:       68 00 84 04 08          push   0x8048400
 8048305:       51                      push   ecx
 8048306:       56                      push   esi
 8048307:       68 ed 83 04 08          push   0x80483ed
 804830c:       e8 cf ff ff ff          call   80482e0 <__libc_start_main@plt>
 8048311:       f4                      hlt    
 8048312:       66 90                   xchg   ax,ax
 8048314:       66 90                   xchg   ax,ax

现在让我们将这些指令偏移量与位置无关代码中的偏移量进行比较:

00000530 <_start>:
 530:   31 ed                   xor    ebp,ebp
 532:   5e                      pop    esi
 533:   89 e1                   mov    ecx,esp
 535:   83 e4 f0                and    esp,0xfffffff0
 538:   50                      push   eax
 539:   54                      push   esp
 53a:   52                      push   edx
 53b:   e8 22 00 00 00          call   562 <_start+0x32>       <------------
 540:   81 c3 c0 1a 00 00       add    ebx,0x1ac0
 546:   8d 83 80 e7 ff ff       lea    eax,[ebx-0x1880]        <------------
 54c:   50                      push   eax
 54d:   8d 83 10 e7 ff ff       lea    eax,[ebx-0x18f0]        <------------
 553:   50                      push   eax
 554:   51                      push   ecx
 555:   56                      push   esi
 556:   ff b3 f4 ff ff ff       push   DWORD PTR [ebx-0xc]     <------------
 55c:   e8 bf ff ff ff          call   520 <__libc_start_main@plt>
 561:   f4                      hlt    
 562:   8b 1c 24                mov    ebx,DWORD PTR [esp]     <------------
 565:   c3                      ret    
 566:   66 90                   xchg   ax,ax
 568:   66 90                   xchg   ax,ax

这里我们注意到这里的偏移量与非 PIE ELF 二进制文件中的偏移量完全不同。此外,箭头突出显示与问题中的代码片段的相似之处,在此上下文中特别相关。就像问题中提供的代码示例一样,这里的代码完全没有绝对地址。


参考:

  1. gcc 和 ld 中与位置无关的可执行文件的 -fPIE 选项是什么?