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

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

#包括<标准h>
int main()
{
printf(“Hello\n”);
}

构建过程:

gcc-o hello.c
脱衣舞——脱衣舞不需要的你好

如果应用程序没有剥离,那么分解主功能就很容易了。但是,我不知道如何分解剥离应用程序的main函数

(gdb)disas main
未加载任何符号表。使用“文件”命令。
(gdb)主信息线
未定义函数“main”。

我怎么做呢?有可能吗

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

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

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

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

(gdb)disas main
未加载任何符号表。使用“文件”命令。

现在,如果您编译代码(我在末尾添加了一个返回0),您将得到gcc-S

pushq%rbp
movq%rsp,%rbp
movl$.LC0,%edi
看涨期权
movl$0,%eax
离开
ret

现在,您可以看到二进制文件提供了一些信息:

条纹:

(gdb)信息文件
来自“/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip”的符号。
本地执行文件:
`/home/beco/Documents/fontes/cpp/teste/stackoverflow/distrip',文件类型为elf64-x86-64。
入口点:0x400440
0x0000000000400238-0x0000000000400254为.interp
...
0x0000000000403A8-0x0000000000403C0为.rela.dyn
0x00000000004003c0-0x00000000004003f0为.rela.plt
0x00000000004003f0-0x0000000000400408是.init
0x000000000040408-0x00000000004308为.plt
0x000000000040440-0x0000000000400618为.text
...
0x00000000000601010-0x00000000000020为.数据
0x00000000000601020-0x00000000000601030为.bss

这里最重要的条目是.text。它是代码的汇编开始部分的通用名称,从我们对main bellow的解释,从它的大小,您可以看到它包括main。如果您反汇编它,您将看到对_libc_start_main的调用。最重要的是,您正在反汇编一个好的入口点,它是真正的代码(将数据更改为代码不会产生误导)

disas 0x000000000040440,0x0000000000400618
将汇编程序代码从0x400440转储到0x400618:
0x000000000040440:xor%ebp,%ebp
0x000000000040442:mov%rdx,%r9
0x000000000040445:pop%rsi
0x000000000040446:mov%rsp,%rdx
0x000000000040449:和$0xFFFFFFFFFFF0,%rsp
0x00000000004044D:推送%rax
0x00000000004044E:推送%rsp
0x00000000004044F:mov$0x400540,%r8
0x000000000040456:mov$0x400550,%rcx
0x00000000004045D:mov$0x400524,%rdi
0x000000000040464:callq 0x400428&lt__libc_启动[email protected]&燃气轮机;
0x000000000040469:hlt
...
0x00000000004046C:sub$0x8,%rsp
...
0x000000000040482:retq
0x000000000040483:否
...
0x000000000040490:推送%rbp
..
0x00000000004004f2:LEVEQ
0x00000000004004f3:retq
0x0000000000404F4:data32 data32 nopw%cs:0x0(%rax,%rax,1)
...
0x000000000040051d:LEVEQ
0x000000000040051e:jmpq*%rax
...
0x000000000040520:LEVEQ
0x0000000000400521:retq
0x0000000000400522:否
0x0000000000400523:否
0x0000000000400524:推送%rbp
0x00000000000400525:mov%rsp,%rbp
0x0000000000400528:mov$0x40062c,%edi
0x000000000040052d:callq 0x400418<[email protected]&燃气轮机;
0x0000000000400532:mov$0x0,%eax
0x0000000000400537:LEVEQ
0x0000000000400538:retq

对_libc_start_main的调用的第一个参数是指向main()的指针。因此,堆栈中调用前的最后一个参数是main()地址

0x00000000004045D:mov$0x400524,%rdi
0x000000000040464:callq 0x400428&lt__libc_启动[email protected]&燃气轮机;

这里是0x400524(我们已经知道)。现在设置断点并尝试以下操作:

(gdb)中断*0x400524
断点1位于0x400524
(gdb)运行
启动程序:/home/beco/Documents/fontes/cpp/teste/stackoverflow/discomposition/d2
主()中的断点1,0x0000000000400524
(gdb)n
单步执行,直到退出主功能,
它没有行号信息。
你好1
__libc_start_main(main=<值优化输出>,argc=<值优化输出>,ubp_av=<值优化输出>,
init=<值优化输出>,fini=<值优化输出>,rtld_fini=<值优化输出>,
堆栈_end=0x7fffffffdc38)在libc开始处。c:258
258 libc start.c:没有这样的文件或目录。
在libc start.c中
(gdb)n
程序正常退出。
(gdb)

现在,您可以使用以下方法对其进行拆解:

(gdb)disas 0x0000000000400524,0x000000000040060
将汇编程序代码从0x400524转储到0x400600:
0x0000000000400524:推送%rbp
0x00000000000400525:mov%rsp,%rbp
0x0000000000400528:子$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<[email protected]&燃气轮机;
0x000000000040054a:mov$0x0,%eax
0x000000000040054f:LEVEQ
0x000000000040550:retq
0x0000000000400551:否
0x0000000000400552:否
0x0000000000400553:否
0x0000000000400554:否
0x0000000000400555:否
...

这主要是解决方案

顺便说一句,这是一个不同的代码,看看它是否工作。这就是为什么上面的组件有点不同。以上代码来自此c文件:

#包括<标准h>
内部主(空)
{
int i=1;
printf(“你好%d\n”,i);
返回0;
}

但是


如果这不起作用,那么您仍然有一些提示:

从现在开始,您应该在所有函数的开头设置断点。它们就在ret离开之前。第一个入口点是.text本身。这是程序集的开始,但不是主程序集

问题是,断点并不总是让程序运行。就像文本中的这个:

(gdb)中断*0x000000000040440
0x400440处的断点2
(gdb)运行
启动程序:/home/beco/Documents/fontes/cpp/teste/stackoverflow/discomposition/d2
断点2,0x000000000040440在_start()中
(gdb)n
单步执行直到退出功能_start,
它没有行号信息。
0x000000000040428英寸libc_启动[email protected] ()
(gdb)n
单步执行直到退出函数_libc_启动[email protected], 
它没有行号信息。
0x0000000000400408英寸??()
(gdb)n
找不到当前函数的边界

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

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

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

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

(gdb)disas main
主功能的汇编程序代码转储:
0x0000000000400524&lt+0>:推送%rbp
0x0000000000400525&lt+1>:mov%rsp,%rbp
0x0000000000400528&lt+4>:mov$0x40062c,%edi
0x000000000040052d&lt+9>:callq 0x400418<[email protected]&燃气轮机;
0x0000000000400532&lt+14>:mov$0x0,%eax
0x0000000000400537&lt+19>:利沃克
0x0000000000400538&lt+20>:retq
汇编程序转储结束。

现在我们知道main位于0x0000000000400524,0x0000000000400539。如果我们使用相同的偏移量来查看条带化二进制文件,我们会得到相同的结果:

(gdb)disas 0x0000000000400524,0x0000000000400539
将汇编程序代码从0x400524转储到0x400539:
0x0000000000400524:推送%rbp
0x00000000000400525:mov%rsp,%rbp
0x0000000000400528:mov$0x40062c,%edi
0x000000000040052d:callq 0x400418<[email protected]&燃气轮机;
0x0000000000400532:mov$0x0,%eax
0x0000000000400537:LEVEQ
0x0000000000400538:retq
汇编程序转储结束。

因此,除非您能从main的起始位置获得一些提示(比如使用另一个带有符号的代码),否则另一种方法是,如果您能获得有关firsts汇编指令的一些信息,那么您可以在特定位置进行反汇编,并查看它是否匹配。如果您根本无法访问代码,您仍然可以阅读ELF定义,了解代码中应该出现多少节,然后尝试计算地址。尽管如此,您仍然需要有关代码中各部分的信息

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

贝科

发表评论