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

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

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

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

解决方法:

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

>找到程序入口点
>查找__libc_start_main的调用位置
>在调用_libc_start_main之前,将断点设置为最后保存在堆栈上的地址
>让程序继续执行,直到命中main()的断点

对于32位和64位ELF二进制文件,该过程是相同的.

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

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

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

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

上一篇:MySQL中使用LIMIT进行分页的方法


下一篇:linux常用命令--开发调试篇