linux - 如何在没有任何符号信息的elf可执行文件中找到main函数的入口点?

标签 linux reverse elf

我在Ubuntu-Linux 11.10平台上开发了一个cpp小程序。 现在我想对其进行逆向工程。我是初学者。 我使用这样的工具:GDB 7.0、hte editor、hexeditor

我第一次让它变得如此简单。在符号信息的帮助下,我找到了 main 函数的地址,并制作了我需要的一切。 然后我 strip 化了 (--strip-all) 可执行的 elf 文件,但我遇到了一些问题。 我知道 main 函数在这个程序中是从 0x8960 开始的。 但是我不知道如果没有这些知识我应该如何找到这一点。 我尝试使用 gdb 逐步调试我的程序,但它进入了 __libc_start_main 然后进入 ld-linux.so.3(因此,它会找到并加载程序所需的共享库)。我调试了大约10分钟。当然,我可能会在 20 分钟内到达主函数的入口点,但似乎必须存在更简单的方法。

我应该怎么做才能在没有任何符号信息的情况下找到 main 函数的入口点? 您能否在 gdb 的帮助下通过 elf 文件的逆向工程向我推荐一些好书/网站/其他资源? 任何帮助将不胜感激。

最佳答案

在剥离的 Linux ELF 二进制文件中定位 main() 很简单。不需要符号信息。

__libc_start_main的原型(prototype)是

int __libc_start_main(int (*main) (int, char**, char**), 
                      int argc, 
                      char *__unbounded *__unbounded ubp_av, 
                      void (*init) (void), 
                      void (*fini) (void), 
                      void (*rtld_fini) (void), 
                      void (*__unbounded stack_end));

main()的运行时内存地址是第一个参数对应的参数,int (*main) (int, char**, char**) .这意味着在调用 __libc_start_main 之前保存在运行时堆栈上的最后一个内存地址是 main() 的内存地址,因为参数被反向插入运行时堆栈它们对应的参数在函数定义中的顺序。

可以分4步在gdb中输入main():

  1. 找到程序入口点
  2. 找到调用__libc_start_main的地方
  3. 在调用 _libc_start_main 之前将断点设置到最后保存在堆栈中的地址
  4. 让程序执行继续,直到main() 的断点被击中

32 位和 64 位 ELF 二进制文件的过程相同。

在名为“test_32”的剥离 32 位 ELF 二进制文件示例中输入 main():

$ gdb -q -nh test_32
Reading symbols from test_32...(no debugging symbols found)...done.
(gdb) info file                                  #step 1
Symbols from "/home/c/test_32".
Local exec file:
    `/home/c/test_32', file type elf32-i386.
    Entry point: 0x8048310
    < output snipped >
(gdb) break *0x8048310
Breakpoint 1 at 0x8048310
(gdb) run
Starting program: /home/c/test_32 

Breakpoint 1, 0x08048310 in ?? ()
(gdb) x/13i $eip                                 #step 2
=> 0x8048310:   xor    %ebp,%ebp
   0x8048312:   pop    %esi
   0x8048313:   mov    %esp,%ecx
   0x8048315:   and    $0xfffffff0,%esp
   0x8048318:   push   %eax
   0x8048319:   push   %esp
   0x804831a:   push   %edx
   0x804831b:   push   $0x80484a0
   0x8048320:   push   $0x8048440
   0x8048325:   push   %ecx
   0x8048326:   push   %esi
   0x8048327:   push   $0x804840b                # address of main()
   0x804832c:   call   0x80482f0 <__libc_start_main@plt>
(gdb) break *0x804840b                           # step 3
Breakpoint 2 at 0x804840b
(gdb) continue                                   # step 4 
Continuing.

Breakpoint 2, 0x0804840b in ?? ()                # now in main()
(gdb) x/x $esp+4
0xffffd110: 0x00000001                           # argc = 1
(gdb) x/s **(char ***) ($esp+8)
0xffffd35c: "/home/c/test_32"                    # argv[0]
(gdb)

在一个名为“test_64”的剥离 64 位 ELF 二进制文件示例中输入 main():

$ gdb -q -nh test_64
Reading symbols from test_64...(no debugging symbols found)...done.
(gdb) info file                                  # step 1
Symbols from "/home/c/test_64".
Local exec file:
    `/home/c/test_64', file type elf64-x86-64.
    Entry point: 0x400430
    < output snipped >
(gdb) break *0x400430
Breakpoint 1 at 0x400430
(gdb) run 
Starting program: /home/c/test_64 

Breakpoint 1, 0x0000000000400430 in ?? ()
(gdb) x/11i $rip                                 # step 2
=> 0x400430:    xor    %ebp,%ebp
   0x400432:    mov    %rdx,%r9
   0x400435:    pop    %rsi
   0x400436:    mov    %rsp,%rdx
   0x400439:    and    $0xfffffffffffffff0,%rsp
   0x40043d:    push   %rax
   0x40043e:    push   %rsp
   0x40043f:    mov    $0x4005c0,%r8
   0x400446:    mov    $0x400550,%rcx
   0x40044d:    mov    $0x400526,%rdi            # address of main()
   0x400454:    callq  0x400410 <__libc_start_main@plt>
(gdb) break *0x400526                            # step 3
Breakpoint 2 at 0x400526
(gdb) continue                                   # step 4
Continuing.

Breakpoint 2, 0x0000000000400526 in ?? ()        # now in main()
(gdb) print $rdi                                    
$3 = 1                                           # argc = 1
(gdb) x/s **(char ***) ($rsp+16)
0x7fffffffe35c: "/home/c/test_64"                # argv[0]
(gdb) 

可以在 Patrick Horgan 的教程 "Linux x86 Program Start Up or - How the heck do we get to main()?" 中找到有关程序初始化的详细处理和调用 main() 之前发生的事情以及如何到达 main()

关于linux - 如何在没有任何符号信息的elf可执行文件中找到main函数的入口点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9885545/

相关文章:

shared-libraries - Linux ELF 文件 : How to get the shared object belonging to an imported function

ruby-on-rails - 无法安装 nokogiri gem

c - x86-64 上的 long double 是什么?

c# - 如何在不更改相同列表的情况下反转通用列表?

Python 从另一个列表创建特定列表

java - 从 eclipse 包中提取后损坏的 ELF header /部分

linux - 如何在shell脚本中捕获信号?

python - 如何解决错误 "no module found named pyside2"?

java - 在反向java中打印行

c++ - 编辑 ELF 文件中的变量值?