C 如何分解剥离应用程序的主要功能?

C 如何分解剥离应用程序的主要功能?,c,linux,gdb,strip,disassembly,C,Linux,Gdb,Strip,Disassembly,假设我编译了下面的应用程序并去掉了它的符号 #include <stdio.h> int main() { printf("Hello\n"); } 如果应用程序没有剥离,那么分解主功能就很容易了。但是,我不知道如何分解剥离应用程序的主要功能 (gdb) disas main No symbol table is loaded. Use the "file" command. (gdb) info line main Function "main" not defin

假设我编译了下面的应用程序并去掉了它的符号

#include <stdio.h>

int main()
{
    printf("Hello\n");
}
如果应用程序没有剥离,那么分解主功能就很容易了。但是,我不知道如何分解剥离应用程序的主要功能

(gdb) disas main
No symbol table is loaded.  Use the "file" command.

(gdb) info line main
Function "main" not defined.
我怎么做呢?有可能吗

注意事项:这只能通过GDB完成。忘记objdump。假设我没有访问代码的权限


一个循序渐进的例子将不胜感激。

IIRC,
x/i
是你的朋友。当然,你必须弄清楚自己要反汇编到哪个位置。

如何使用
信息文件
获取节列表(带地址),然后从那里开始

例如:

gdb) info files

Symbols from "/home/bob/tmp/t".
Local exec file:
`/home/bob/tmp/t', file type elf64-x86-64.
Entry point: 0x400490
0x0000000000400270 - 0x000000000040028c is .interp
0x000000000040028c - 0x00000000004002ac is .note.ABI-tag
    ....

0x0000000000400448 - 0x0000000000400460 is .init
    ....
反汇编
.init

(gdb) disas 0x0000000000400448,0x0000000000400460
Dump of assembler code from 0x400448 to 0x400460:
   0x0000000000400448:  sub    $0x8,%rsp
   0x000000000040044c:  callq  0x4004bc
   0x0000000000400451:  callq  0x400550
   0x0000000000400456:  callq  0x400650
   0x000000000040045b:  add    $0x8,%rsp
   0x000000000040045f:  retq   
然后继续拆卸其余部分

如果我是你,并且我有与你的可执行文件相同的GCC版本,我会检查在一个虚拟的非剥离可执行文件上调用的函数序列。调用的顺序在大多数常见情况下可能是相似的,因此通过比较,这可能有助于您从启动顺序一直到
main
。不过,优化可能会带来麻烦


如果二进制文件被剥离和优化,
main
可能不作为二进制文件中的“实体”存在;很可能你不会比这类手术做得更好。

好的,这里是我之前答案的大版本。我想我现在找到办法了

您(仍然:)有以下具体问题:

(gdb) disas main
No symbol table is loaded.  Use the "file" command.
现在,如果您编译代码(我在末尾添加了一个
返回0
),您将得到
gcc-S

    pushq   %rbp
    movq    %rsp, %rbp
    movl    $.LC0, %edi
    call    puts
    movl    $0, %eax
    leave
    ret
现在,您可以看到二进制文件提供了一些信息:

条纹:

(gdb) info files
Symbols from "/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip".
Local exec file:
    `/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip', file type elf64-x86-64.
    Entry point: 0x400440
    0x0000000000400238 - 0x0000000000400254 is .interp
    ...
    0x00000000004003a8 - 0x00000000004003c0 is .rela.dyn
    0x00000000004003c0 - 0x00000000004003f0 is .rela.plt
    0x00000000004003f0 - 0x0000000000400408 is .init
    0x0000000000400408 - 0x0000000000400438 is .plt
    0x0000000000400440 - 0x0000000000400618 is .text
    ...
    0x0000000000601010 - 0x0000000000601020 is .data
    0x0000000000601020 - 0x0000000000601030 is .bss
这里最重要的条目是
.text
。它是代码的汇编开始部分的通用名称,从我们对main bellow的解释,从它的大小,您可以看到它包括main。如果您反汇编它,您将看到对_libc_start_main的调用。最重要的是,您正在反汇编一个好的入口点,它是真正的代码(将数据更改为代码不会产生误导)

因此,您需要不断尝试,直到找到自己的方法,在以下位置设置断点:

0x400440
0x40046c
0x400490
0x4004f4
0x40051e
0x400524

从另一个答案来看,我们应该保留以下信息:

在文件的非条带化版本中,我们看到:

(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400524 <+0>: push   %rbp
   0x0000000000400525 <+1>: mov    %rsp,%rbp
   0x0000000000400528 <+4>: mov    $0x40062c,%edi
   0x000000000040052d <+9>: callq  0x400418 <puts@plt>
   0x0000000000400532 <+14>:    mov    $0x0,%eax
   0x0000000000400537 <+19>:    leaveq 
   0x0000000000400538 <+20>:    retq   
End of assembler dump.
因此,除非您能从main的起始位置获得一些提示(比如使用另一个带有符号的代码),否则另一种方法是,如果您能获得有关firsts汇编指令的一些信息,那么您可以在特定位置进行反汇编,并查看它是否匹配。如果您根本无法访问代码,您仍然可以阅读以了解代码中应显示多少节,然后尝试计算地址。尽管如此,您仍然需要有关代码中各部分的信息

这是一项艰苦的工作,我的朋友!祝你好运


因为在paradyn项目中有一个很棒的新的免费工具叫做unstrip(完全公开:我在这个项目中工作),它将重写你的程序二进制文件,向其中添加符号信息,并在剥离的Elf二进制文件中为你恢复所有(或几乎所有)函数,精确度极高。它不会将主函数标识为“main”,但会找到它,您可以应用上面提到的启发式方法来确定哪个函数是主函数


很抱歉,这不是gdb唯一的解决方案

我已经知道了这一点,但是分解main函数对我没有任何帮助,因为问题是首先要定位它。所以你的问题是关于main的本地化。从二进制流中获取指令是次要的。我误解了这个问题。假设我没有应用程序的源代码,也没有访问GDB以外的其他工具的权限。使用IDA;)。。。对不起,我无法抗拒。我知道你只想把它用于GDB。无论谁否决了我的问题,请解释原因。@STATUS\u ACCESS\u DENIED我这个周末标记了几十个答案,我怀疑有人不喜欢删除他们的答案。在3分钟内,我几乎所有的问题和最近的答案都被否决了。这就是为什么我问投票被否决的原因。显然,这个人决定撤销他们的投票。啊,好的。但是,当你被严重否决并重新计算你的代表时,启发法不起作用吗?我想我在什么地方读到了一些关于这方面的东西。@karlphillip:这是你能到的最远的地方了。拆解的艺术就是找出那些东西,即使你没有符号名称。所有平台上的文件结构都允许您查看从何处开始,但接下来完全由您来挖掘CRT代码并找到
main()
。例如,IDA使用签名在很大程度上实现了自动化,其方法与Mat建议的手动方法类似。如果二进制文件是动态链接的,您仍然可以使用ltrace来查找_libc_start_main(调用main(),加上一些设置),这将使您接近。事实上,如果我能够访问代码,这将相当容易。我所要做的就是编译一个带有符号的版本,或者加载那个二进制文件,或者告诉gdb从哪里加载符号,如果我真的要调试这个版本的话。问题是我真的没有源代码,也没有我感兴趣调试的应用程序的非剥离版本。谢谢你的努力+1在给定ELF的情况下,您所寻找的是一种计算起点的方法。您需要深入了解ELF定义,并了解每个部分可以在偏移量中向下移动干管的程度。但是你仍然需要知道分区的数量和大小。那
信息文件
可以帮你一点忙。如果我在接下来的几天里发现一些更新,我会在这里发表评论。祝你好运。另一个有用的信息是如何在不知道在哪里设置断点的情况下一步一步地尝试。您可以使用
catch syscall write
或只是
catch syscall
,然后尝试运行。它并不总是有效的,因为如果断点太早,则缺少上下文。我只是测试
   0x000000000040045d:  mov    $0x400524,%rdi
   0x0000000000400464:  callq  0x400428 <__libc_start_main@plt>
(gdb) break *0x400524
Breakpoint 1 at 0x400524
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste/stackoverflow/disassembly/d2 

Breakpoint 1, 0x0000000000400524 in main ()
(gdb) n
Single stepping until exit from function main, 
which has no line number information.
hello 1
__libc_start_main (main=<value optimized out>, argc=<value optimized out>, ubp_av=<value optimized out>, 
    init=<value optimized out>, fini=<value optimized out>, rtld_fini=<value optimized out>, 
    stack_end=0x7fffffffdc38) at libc-start.c:258
258 libc-start.c: No such file or directory.
    in libc-start.c
(gdb) n

Program exited normally.
(gdb) 
(gdb) disas 0x0000000000400524,0x0000000000400600
Dump of assembler code from 0x400524 to 0x400600:
   0x0000000000400524:  push   %rbp
   0x0000000000400525:  mov    %rsp,%rbp
   0x0000000000400528:  sub    $0x10,%rsp
   0x000000000040052c:  movl   $0x1,-0x4(%rbp)
   0x0000000000400533:  mov    $0x40064c,%eax
   0x0000000000400538:  mov    -0x4(%rbp),%edx
   0x000000000040053b:  mov    %edx,%esi
   0x000000000040053d:  mov    %rax,%rdi
   0x0000000000400540:  mov    $0x0,%eax
   0x0000000000400545:  callq  0x400418 <printf@plt>
   0x000000000040054a:  mov    $0x0,%eax
   0x000000000040054f:  leaveq 
   0x0000000000400550:  retq   
   0x0000000000400551:  nop
   0x0000000000400552:  nop
   0x0000000000400553:  nop
   0x0000000000400554:  nop
   0x0000000000400555:  nop
   ...
#include <stdio.h>

int main(void)
{
    int i=1;
    printf("hello %d\n", i);
    return 0;
}
(gdb) break *0x0000000000400440
Breakpoint 2 at 0x400440
(gdb) run
Starting program: /home/beco/Documents/fontes/cpp/teste/stackoverflow/disassembly/d2 

Breakpoint 2, 0x0000000000400440 in _start ()
(gdb) n
Single stepping until exit from function _start, 
which has no line number information.
0x0000000000400428 in __libc_start_main@plt ()
(gdb) n
Single stepping until exit from function __libc_start_main@plt, 
which has no line number information.
0x0000000000400408 in ?? ()
(gdb) n
Cannot find bounds of current function
0x400440
0x40046c
0x400490
0x4004f4
0x40051e
0x400524
(gdb) disas main
Dump of assembler code for function main:
   0x0000000000400524 <+0>: push   %rbp
   0x0000000000400525 <+1>: mov    %rsp,%rbp
   0x0000000000400528 <+4>: mov    $0x40062c,%edi
   0x000000000040052d <+9>: callq  0x400418 <puts@plt>
   0x0000000000400532 <+14>:    mov    $0x0,%eax
   0x0000000000400537 <+19>:    leaveq 
   0x0000000000400538 <+20>:    retq   
End of assembler dump.
(gdb) disas 0x0000000000400524,0x0000000000400539
Dump of assembler code from 0x400524 to 0x400539:
   0x0000000000400524:  push   %rbp
   0x0000000000400525:  mov    %rsp,%rbp
   0x0000000000400528:  mov    $0x40062c,%edi
   0x000000000040052d:  callq  0x400418 <puts@plt>
   0x0000000000400532:  mov    $0x0,%eax
   0x0000000000400537:  leaveq 
   0x0000000000400538:  retq   
End of assembler dump.