《coredump问题原理探究》Linux x86版4.2节函数的逆向之顺序结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xuzhina/article/details/8564337

先看一下例子:

#include <stdio.h>

int main()
{
     int a = 0, b = 0, c = 0, d = 0;
     scanf( "%d,%d", &a,&b );
     a += b;
     scanf( "%d", &c );
     a += c;
     scanf( "%d", &d );
     a += d;
 
     return a;
}

看一下main函数的汇编:

(gdb) disassemble main
Dump of assembler code for function main:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp
   0x08048579 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048581 <+17>:    movl   $0x0,0x18(%esp)
   0x08048589 <+25>:    movl   $0x0,0x14(%esp)
   0x08048591 <+33>:    movl   $0x0,0x10(%esp)
   0x08048599 <+41>:    lea    0x18(%esp),%eax
   0x0804859d <+45>:    mov    %eax,0x8(%esp)
   0x080485a1 <+49>:    lea    0x1c(%esp),%eax
   0x080485a5 <+53>:    mov    %eax,0x4(%esp)
   0x080485a9 <+57>:    movl   $0x80486b4,(%esp)
   0x080485b0 <+64>:    call   0x8048440 <scanf@plt>
   0x080485b5 <+69>:    mov    0x1c(%esp),%edx
   0x080485b9 <+73>:    mov    0x18(%esp),%eax
   0x080485bd <+77>:    add    %edx,%eax
   0x080485bf <+79>:    mov    %eax,0x1c(%esp)
   0x080485c3 <+83>:    lea    0x14(%esp),%eax
   0x080485c7 <+87>:    mov    %eax,0x4(%esp)
   0x080485cb <+91>:    movl   $0x80486ba,(%esp)
   0x080485d2 <+98>:    call   0x8048440 <scanf@plt>
   0x080485d7 <+103>:   mov    0x1c(%esp),%edx
   0x080485db <+107>:   mov    0x14(%esp),%eax
   0x080485df <+111>:   add    %edx,%eax
   0x080485e1 <+113>:   mov    %eax,0x1c(%esp)
   0x080485e5 <+117>:   lea    0x10(%esp),%eax
   0x080485e9 <+121>:   mov    %eax,0x4(%esp)
   0x080485ed <+125>:   movl   $0x80486ba,(%esp)
   0x080485f4 <+132>:   call   0x8048440 <scanf@plt>
   0x080485f9 <+137>:   mov    0x1c(%esp),%edx
   0x080485fd <+141>:   mov    0x10(%esp),%eax
   0x08048601 <+145>:   add    %edx,%eax
   0x08048603 <+147>:   mov    %eax,0x1c(%esp)
   0x08048607 <+151>:   mov    0x1c(%esp),%eax
   0x0804860b <+155>:   jmp    0x8048615 <main+165>
   0x0804860d <+157>:   mov    %eax,(%esp)
   0x08048610 <+160>:   call   0x8048460 <_Unwind_Resume@plt>
   0x08048615 <+165>:   leave  
   0x08048616 <+166>:   ret    
End of assembler dump.

区区十来行代码,就变成了非常多的汇编语句,非常令人害怕。实际上,不需要那么害怕。

先看一下call指令的地方,由于call指令是调用函数的,所以,用它可以大致定一下这样的范围。

先看一下三个call指令的地址:

   0x080485b0 <+64>:    call   0x8048440 <scanf@plt>
   0x080485d2 <+98>:    call   0x8048440 <scanf@plt>
   0x080485f4 <+132>:   call   0x8048440 <scanf@plt>

可以知道,

   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp
   0x08048579 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048581 <+17>:    movl   $0x0,0x18(%esp)
   0x08048589 <+25>:    movl   $0x0,0x14(%esp)
   0x08048591 <+33>:    movl   $0x0,0x10(%esp)
   0x08048599 <+41>:    lea    0x18(%esp),%eax
   0x0804859d <+45>:    mov    %eax,0x8(%esp)
   0x080485a1 <+49>:    lea    0x1c(%esp),%eax
   0x080485a5 <+53>:    mov    %eax,0x4(%esp)
   0x080485a9 <+57>:    movl   $0x80486b4,(%esp)

这一段汇编的地址少于第一个call指令地址0x080485b0,所以,它们大概对应于代码:

int a = 0, b = 0, c = 0, d = 0;

   0x080485b5 <+69>:    mov    0x1c(%esp),%edx
   0x080485b9 <+73>:    mov    0x18(%esp),%eax
   0x080485bd <+77>:    add    %edx,%eax
   0x080485bf <+79>:    mov    %eax,0x1c(%esp)
   0x080485c3 <+83>:    lea    0x14(%esp),%eax
   0x080485c7 <+87>:    mov    %eax,0x4(%esp)
   0x080485cb <+91>:    movl   $0x80486ba,(%esp)

这一段汇编由于地址大于0x080485b0,小于0x080485d2,所以,它们大致对应于代码:

a += b;

   0x080485d7 <+103>:   mov    0x1c(%esp),%edx
   0x080485db <+107>:   mov    0x14(%esp),%eax
   0x080485df <+111>:   add    %edx,%eax
   0x080485e1 <+113>:   mov    %eax,0x1c(%esp)
   0x080485e5 <+117>:   lea    0x10(%esp),%eax
   0x080485e9 <+121>:   mov    %eax,0x4(%esp)
   0x080485ed <+125>:   movl   $0x80486ba,(%esp)

这一段汇编地址大于0x080485d2,小于0x080485f4,所以,它们大致对应于代码:

a += c;

   0x080485f9 <+137>:   mov    0x1c(%esp),%edx
   0x080485fd <+141>:   mov    0x10(%esp),%eax
   0x08048601 <+145>:   add    %edx,%eax
   0x08048603 <+147>:   mov    %eax,0x1c(%esp)
   0x08048607 <+151>:   mov    0x1c(%esp),%eax
   0x0804860b <+155>:   jmp    0x8048615 <main+165>
   0x0804860d <+157>:   mov    %eax,(%esp)
   0x08048610 <+160>:   call   0x8048460 <_Unwind_Resume@plt>
   0x08048615 <+165>:   leave  
   0x08048616 <+166>:   ret    

这一段汇编地址大于0x080485f4,所以,它们对应于代码:

a += d;

return a;

看,分析汇编,并不是那么让人恐怖。但上面由于有一些指令是编译器生成的,有一些是函数调用时把参数入栈的指令,所以,要筛选出这些指令,仅以第一段汇编为例(即第一个scanf调用前的汇编):

   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp
   0x08048579 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048581 <+17>:    movl   $0x0,0x18(%esp)
   0x08048589 <+25>:    movl   $0x0,0x14(%esp)
   0x08048591 <+33>:    movl   $0x0,0x10(%esp)
   0x08048599 <+41>:    lea    0x18(%esp),%eax
   0x0804859d <+45>:    mov    %eax,0x8(%esp)
   0x080485a1 <+49>:    lea    0x1c(%esp),%eax
   0x080485a5 <+53>:    mov    %eax,0x4(%esp)
   0x080485a9 <+57>:    movl   $0x80486b4,(%esp)

由第三章可知,

   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp

是属于函数开头的特征指令,所以,这是由编译器自动生成的。

   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x20,%esp

则是用来调整esp和分配局部变量空间的,也是编译器自动生成的。所以,第一段汇编只剩下

   0x08048579 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048581 <+17>:    movl   $0x0,0x18(%esp)
   0x08048589 <+25>:    movl   $0x0,0x14(%esp)
   0x08048591 <+33>:    movl   $0x0,0x10(%esp)
   0x08048599 <+41>:    lea    0x18(%esp),%eax
   0x0804859d <+45>:    mov    %eax,0x8(%esp)
   0x080485a1 <+49>:    lea    0x1c(%esp),%eax
   0x080485a5 <+53>:    mov    %eax,0x4(%esp)
   0x080485a9 <+57>:    movl   $0x80486b4,(%esp)

是和

 int a = 0, b = 0, c = 0, d = 0;

对应。由于

scanf( "%d,%d", &a,&b );

是有三个参数,根据第三章内容,

   0x08048599 <+41>:    lea    0x18(%esp),%eax
   0x0804859d <+45>:    mov    %eax,0x8(%esp)
   0x080485a1 <+49>:    lea    0x1c(%esp),%eax
   0x080485a5 <+53>:    mov    %eax,0x4(%esp)
   0x080485a9 <+57>:    movl   $0x80486b4,(%esp)

刚好是把scanf的三个参数入栈的操作,可以通过运行时得到0x80486b4指向的内容:

gdb) tbreak *0x080485b0
Temporary breakpoint 1 at 0x80485b0
(gdb) r
Starting program: /home/buckxu/work/4/1/xuzhina_dump_c4_s1 

Temporary breakpoint 1, 0x080485b0 in main ()
(gdb) x /s 0x80486b4
0x80486b4 <__dso_handle+4>:      "%d,%d"

也就是说,只有

   0x08048579 <+9>:     movl   $0x0,0x1c(%esp)
   0x08048581 <+17>:    movl   $0x0,0x18(%esp)
   0x08048589 <+25>:    movl   $0x0,0x14(%esp)
   0x08048591 <+33>:    movl   $0x0,0x10(%esp)

才是和

int a = 0, b = 0, c = 0, d = 0;

对应的。

小结:

由于顺序结构的逆向非常考验汇编基础,但如果是有函数调用的话,先找call指令,根据call指令来划分范围,筛选出编译器自动生成的指令。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IMWeb前端团队

Node中没搞明白require和import,你会被坑的很惨

ES6标准发布后,module成为标准,标准的使用是以export指令导出接口,以import引入模块,但是在我们一贯的node模块中,我们采用的是Common...

33680
来自专栏名山丶深处

Hello——Java10新特性,请了解一下

15150
来自专栏逆向技术

C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原

      C++反汇编第六讲,认识C++中的Try catch语法,以及在反汇编中还原 我们以前讲SEH异常处理的时候已经说过了,C++中的Try catch...

254100
来自专栏王亚昌的专栏

lua中设置只读table

C++里有const用来定义常量,保护参数或函数意外地修改,提高程序的健壮性。在lua里虽然没有没有类似的关键字,我们可以用...

12710
来自专栏微信公众号:Java团长

Java 类加载机制详解

Java 虚拟机一般使用 Java 类的流程为:首先将开发者编写的 Java 源代码(.java文件)编译成 Java 字节码(.class文件),然后类加载器...

12840
来自专栏Java 源码分析

CountDownLatch 源码分析

CountDownLatch 源码分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE...

37060
来自专栏JavaQ

Java研发方向如何准备BAT技术面试答案(上)

最近因为忙于工作,没时间整理,本篇是下班后晚上抽空整理的,文中部分答案本来是想自己好好整理一份的,但是时间真的很紧,所以就整理了一下网络上的文章链接,挑了写的不...

38450
来自专栏琯琯博客

PHP 优化技巧

36050
来自专栏安恒网络空间安全讲武堂

typecho漏洞分析与HCTF实战

typecho漏洞分析与HCTF实战 0x00前记 通过最近的比赛,决定沉淀下来,从复现cms开始慢慢锻炼自己的审计能力,毕竟这个年头的CTF,不会审计只能活在...

27780
来自专栏Scott_Mr 个人专栏

初识 Runtime

346100

扫码关注云+社区

领取腾讯云代金券