《coredump问题原理探究》Linux x86版4.3节函数的逆向之条件结构

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

在x86里,条件跳转的指令有:

JMP:无条件跳转
JAE/JNB:大于或等于跳转,用于无符号整数运算
JA/JNBE:不小于或不等于跳转,用于无符号整数运算
JB/JNAE:小于跳转,用于无符号整数运算
JBE/JNA:小于或等于跳转,用于无符号整数运算
JG/JNLE:大于跳转,用于有符号整数运算
JGE/JNL:大于或等于跳转,用于有符号整数运算
JL/JNGE :小于跳转,用于有符号整数运算
JLE/JNG:小于或等于跳转,用于有符号整数运算
JE/JZ:等于跳转
JNE/JNZ:不等于跳转
JC:有进位跳转
JNC:无进位跳转
JNO:不溢出跳转
JNP/JPO:奇偶性为奇数跳转
JNS:符号位为 "0"跳转
JO:溢出跳转
JP/JPE:奇偶性为偶数跳转
JS:符号位为 "1" 跳转

上面这些指令,大多会检测EFLAGS寄存器相应的标志位来再决定是否跳转。而这些指令之前,往往会有一些设置这些标志位的指令。最常见的是cmp, test指令。那么,就可以根据上面指令快速构建条件结构的骨架。

下面通过例子来验证一下,由于在C/C++里,条件语句有if…elseif…else和switch两种,所以在例子会对两者都有探究。

先看一下例子:

 #include <stdio.h>

 int cond_if( int a, int b, int c )
 {
     if ( a == 0 )
     {
         return b + c;
     }
     else if ( a > 0 )
     {
         return b - c;
     }
     else
     {
         return b*c;
     }
 }
 
 int cond_switch( int a, int b, int c )
 {
     switch( a )
     {
         case 0:
             return b + c;
         case 1:
             return b - c;
         default:
             return b*c;
     }
 }
 
 int main()
 {
     int a = 0, b = 0, c = 0;

     scanf( "%d,%d,%d", &a, &b,&c );
	 
     return cond_if( a,b,c ) + cond_switch( a, b, c );
}

先看一下cond_if的汇编:

(gdb) disassemble cond_if
Dump of assembler code for function _Z7cond_ifiii:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp

   0x08048573 <+3>:     cmpl   $0x0,0x8(%ebp)
   0x08048577 <+7>:     jne    0x8048583 <_Z7cond_ifiii+19>

   0x08048579 <+9>:     mov    0x10(%ebp),%eax
   0x0804857c <+12>:    mov    0xc(%ebp),%edx
   0x0804857f <+15>:    add    %edx,%eax

   0x08048581 <+17>:    jmp    0x804859e <_Z7cond_ifiii+46>
   0x08048583 <+19>:    cmpl   $0x0,0x8(%ebp)
   0x08048587 <+23>:    jle    0x8048597 <_Z7cond_ifiii+39>

   0x08048589 <+25>:    mov    0x10(%ebp),%eax
   0x0804858c <+28>:    mov    0xc(%ebp),%edx
   0x0804858f <+31>:    mov    %edx,%ecx
   0x08048591 <+33>:    sub    %eax,%ecx
   0x08048593 <+35>:    mov    %ecx,%eax

   0x08048595 <+37>:    jmp    0x804859e <_Z7cond_ifiii+46>

   0x08048597 <+39>:    mov    0xc(%ebp),%eax
   0x0804859a <+42>:    imul   0x10(%ebp),%eax
   0x0804859e <+46>:    pop    %ebp
   0x0804859f <+47>:    ret    
End of assembler dump.

   0x08048573 <+3>:     cmpl   $0x0,0x8(%ebp)
   0x08048577 <+7>:     jne    0x8048583 <_Z7cond_ifiii+19>

可知,这是判断cond_if的第一个参数a,是否等于0。如果不等于则跳转。那么,

   0x08048579 <+9>:     mov    0x10(%ebp),%eax
   0x0804857c <+12>:    mov    0xc(%ebp),%edx
   0x0804857f <+15>:    add    %edx,%eax

应该对应a等于0时要执行的语句,即

7	         return b + c;

   0x08048583 <+19>:    cmpl   $0x0,0x8(%ebp)
   0x08048587 <+23>:    jle    0x8048597 <_Z7cond_ifiii+39>

可知,这是判断a是否小于等于0,如果是则跳转。那么,

   0x08048589 <+25>:    mov    0x10(%ebp),%eax
   0x0804858c <+28>:    mov    0xc(%ebp),%edx
   0x0804858f <+31>:    mov    %edx,%ecx
   0x08048591 <+33>:    sub    %eax,%ecx
   0x08048593 <+35>:    mov    %ecx,%eax

应该对于a大于0的语句,即

11	         return b - c;

同时由

   0x08048583 <+19>:    cmpl   $0x0,0x8(%ebp)
   0x08048587 <+23>:    jle    0x8048597 <_Z7cond_ifiii+39>

可知, 0x08048597开始的指令是对应a小于0的情况,即

   0x08048597 <+39>:    mov    0xc(%ebp),%eax
   0x0804859a <+42>:    imul   0x10(%ebp),%eax

对应于

15	         return b*c;

再看一下cond_switch的汇编:

(gdb) disassemble cond_switch                
Dump of assembler code for function _Z11cond_switchiii:
   0x080485a0 <+0>:     push   %ebp
   0x080485a1 <+1>:     mov    %esp,%ebp
   0x080485a3 <+3>:     mov    0x8(%ebp),%eax

   0x080485a6 <+6>:     test   %eax,%eax
   0x080485a8 <+8>:     je     0x80485b1 <_Z11cond_switchiii+17>
   0x080485aa <+10>:    cmp    $0x1,%eax
   0x080485ad <+13>:    je     0x80485bb <_Z11cond_switchiii+27>
   0x080485af <+15>:    jmp    0x80485c9 <_Z11cond_switchiii+41>

   0x080485b1 <+17>:    mov    0x10(%ebp),%eax
   0x080485b4 <+20>:    mov    0xc(%ebp),%edx
   0x080485b7 <+23>:    add    %edx,%eax

   0x080485b9 <+25>:    jmp    0x80485d0 <_Z11cond_switchiii+48>

   0x080485bb <+27>:    mov    0x10(%ebp),%eax
   0x080485be <+30>:    mov    0xc(%ebp),%edx
   0x080485c1 <+33>:    mov    %edx,%ecx
   0x080485c3 <+35>:    sub    %eax,%ecx
   0x080485c5 <+37>:    mov    %ecx,%eax

   0x080485c7 <+39>:    jmp    0x80485d0 <_Z11cond_switchiii+48>

   0x080485c9 <+41>:    mov    0xc(%ebp),%eax
   0x080485cc <+44>:    imul   0x10(%ebp),%eax
   0x080485d0 <+48>:    pop    %ebp
   0x080485d1 <+49>:    ret    
End of assembler dump.

   0x080485a6 <+6>:     test   %eax,%eax
   0x080485a8 <+8>:     je     0x80485b1 <_Z11cond_switchiii+17>

可知,0x080485b1到0x080485b9这一段代码是属于参数a为0的情况(eax的值是从8(%ebp)即a得来的),所以,

   0x080485b1 <+17>:    mov    0x10(%ebp),%eax
   0x080485b4 <+20>:    mov    0xc(%ebp),%edx
   0x080485b7 <+23>:    add    %edx,%eax

对应于

24	             return b + c;

   0x080485aa <+10>:    cmp   $0x1,%eax
   0x080485ad <+13>:    je    0x80485bb <_Z11cond_switchiii+27>

可知,0x080485bb到0x080485c7是对应于a为1的情况,所以,

   0x080485bb <+27>:    mov    0x10(%ebp),%eax
   0x080485be <+30>:    mov    0xc(%ebp),%edx
   0x080485c1 <+33>:    mov    %edx,%ecx
   0x080485c3 <+35>:    sub    %eax,%ecx
   0x080485c5 <+37>:    mov    %ecx,%eax

对应

26	             return b - c;

   0x080485c9 <+41>:    mov    0xc(%ebp),%eax
   0x080485cc <+44>:    imul   0x10(%ebp),%eax

是由

   0x080485af <+15>:    jmp    0x80485c9 <_Z11cond_switchiii+41>

跳转的,0x080485af是判断完a不为0,1之后才会执行的,所以这段汇编对应于

28	             return b*c;

从上面来看,通过对跳转语句进行分析,能够很快还原代码原先的逻辑,也很容易定位到哪一行代码。也可以看到,if…else if…else和switch实际上在汇编里是没什么区别。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 源码分析

Java 虚拟机运行时数据区

运行时数据区: Java 虚拟机的运行时数据区按照大的可以分为线程独立使用的数据区,和所有线程共享的数据区。 一.线程独立使用数据区 1.程序计数器 程序计数器...

3424
来自专栏技术小站

编程填空:第i位替换 编程填空:第i位取反 编程填空:左边i位取反

写出函数中缺失的部分,使得函数返回值为一个整数,该整数的第i位和m的第i位相同,其他位和n相同。

2271
来自专栏转载gongluck的CSDN博客

Lua学习笔记

--Lua笔记-- --0.Lua开篇-- --http://www.cnblogs.com/stephen-liu74/archive/2012/06/11/...

6206
来自专栏mukekeheart的iOS之旅

Java基础——异常体系

在Java中,异常对象都是派生于Throwable类的一个实例,Java的异常体系如下图所示: ?    所有的异常都是由Throwable继承而来,在下一层立...

2827
来自专栏Python研发

Memcached·Redis缓存的基本操作

Memcached 是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载。它通过在内存中缓存数据和对象来减少读取数据库的次数,从而提高动态、...

1664
来自专栏java学习

Java每日一练(2017/7/7)

1 (单选题)有以下程序片段,下列哪个选项不能插入到行 1 。()。 1. 2.public class A{ 3.//do sth 4. } A publ...

38011
来自专栏Android开发指南

7:多线程

3058
来自专栏nnngu

015 反射中的 Class.forName() 与 ClassLoader.loadClass() 的区别

Class.forName() 与 ClassLoader.loadClass() 大家都知道是反射用来构造类的方法,但是他们的用法还是有一定区别的。 在讲区别...

2643
来自专栏数据结构与算法

3185 队列练习 1

3185 队列练习 1 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 黄金 Gold 题目描述 Description 给定一...

3126
来自专栏yl 成长笔记

深刻理解反射(Reflection)

最近公司在搞自动化测试,由于版权问题,无法用 ’录制脚本‘ 进行,也就没法用 VS 自带的 UITest 框架(蛋疼), 所以只能开源的 FlaUI 框架来搞了...

1484

扫码关注云+社区

领取腾讯云代金券