如何在没有任何符号信息的情况下找到elf可执行文件的主要功能入口点?

时间:2012-03-27 08:05:01

标签: linux reverse elf

我在Ubuntu-Linux 11.10平台上开发了一个小型cpp程序。 现在我想对它进行逆向工程。我是初学者。 我使用的工具有:GDB 7.0,hte编辑器,hexeditor

这是我第一次轻松搞定。在符号信息的帮助下,我创建了主要功能的地址,并完成了我需要的一切。 然后我条纹化(--strip-all)可执行的精灵文件,我有一些问题。 我知道main函数在这个程序中从0x8960开始。 但我不知道如果没有这方面的知识我怎么能找到这一点。 我尝试使用gdb逐步调试我的程序,但它进入__libc_start_main 然后进入ld-linux.so.3(因此,它找到并加载程序所需的共享库)。我调试了大约10分钟。当然,可能在20分钟内我可以到达主要功能的切入点,但似乎更容易存在。

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

3 个答案:

答案 0 :(得分:8)

在剥离的Linux ELF二进制文件中查找main()非常简单。不需要符号信息。

__libc_start_main的原型是

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个步骤在main()中输入gdb

  1. 找到程序入口点
  2. 查找__libc_start_main的调用位置
  3. 在致电_libc_start_main
  4. 之前,将断点设置为最后保存在堆栈上的地址
  5. 让程序执行continue直至main()的断点
  6. 32位和64位ELF二进制文件的过程相同。

    在一个名为" test_32"

    main()

    在一个名为" test_64"

    $ 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)

    在Patrick Horgan的教程"Linux x86 Program Start Up or - How the heck do we get to main()?"中可以找到程序初始化的详细处理以及调用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)

答案 1 :(得分:7)

据我所知,一旦程序被剥离,就没有直接的方法可以找到符号main以其他方式引用的函数。

程序启动时不需要符号main的值:在ELF格式中,程序的开始由ELF可执行文件头的e_entry字段指定。该字段通常指向C库的初始化代码,而不是直接指向main

虽然C库的初始化代码在设置C运行时环境后调用main(),但此调用是一个正常的函数调用,在链接时完全解析。

在某些情况下,可以使用特定于实现的启发式(即,C运行时内部的特定知识)来确定main在剥离的可执行文件中的位置。但是,我不知道有一种可移植的方式。

答案 2 :(得分:4)

如果您有一个非常剥离的版本,或者甚至是打包的二进制文件,就像使用UPX一样,您可以通过以下方式强制gdb:

$ readelf -h echo | grep Entry
Entry point address:               0x103120

然后你可以在GDB中打破它:

$ gdb mybinary
(gdb) break * 0x103120
Breakpoint 1 at 0x103120gdb) 
(gdb) r
Starting program: mybinary 
Breakpoint 1, 0x0000000000103120 in ?? ()

然后,您可以看到输入说明:

(gdb) x/10i 0x0000000000103120
=> 0x103120:    bl      0x103394
  0x103124: dcbtst  0,r5
  0x103128: mflr    r13
  0x10312c: cmplwi  r7,2
  0x103130: bne     0x103214
  0x103134: stw     r5,0(r6)
  0x103138: add     r4,r4,r3
  0x10313c: lis     r0,-32768
  0x103140: lis     r9,-32768
  0x103144: addi    r3,r3,-1

我希望它有所帮助