汇编-识别条件分歧

简介:

本文主要内容是,反汇编过程中识别条件分歧。进行分析之前必须明白J系列指令,主要是JMP无条件跳转和 JE(JNE)、JZ(JNZ)、JA(JNA)、JB(JNB)、JG(JNG)、GL(GNL)等指令。

上面指令中 E - equal - 等于;Z - zero - 零 ; A - above - 无符号大于; B - below - 无符号小于; G - great - 有符号大于; L - low - 有符号小于; N - no - 不。


If条件分歧: 

if语句的特征是【 cmp+J指令+代码块+jmp】,J指令跳至else语句块,JMP指令跳转至 if - else 语句结束的地方。


测试代码—01:if else 语句

int v1 = 0;
scanf("%d", &v1);
if (v1==10)
{
    printf("%d\r\n", v1);
}
else if (v1==11)
{
    printf("%d\r\n", v1);
}

测试代码—02:if else 语句的汇编表示

    if (v1==10)                            //C语言源码,if (v1==10)
00811756 83 7D F8 0A          cmp         dword ptr [v1],0Ah        //v1和10比较
0081175A 75 13                jne         Sub_1+4Fh (081176Fh)      //如果不等于则跳转,这里挑战到 0x081176F 处,即 else 语句块处。
    {
        printf("%d\r\n", v1);
0081175C 8B 45 F8             mov         eax,dword ptr [v1]        //如果不满足 jne 条件,则不跳转继续执行 if 语句块中的代码
0081175F 50                   push        eax
00811760 68 34 7B 81 00       push        offset string "%d\r\n" (0817B34h)
00811765 E8 CA FB FF FF       call        _printf (0811334h)
0081176A 83 C4 08             add         esp,8
0081176D EB 5C                jmp         Sub_1+0ABh (08117CBh)     //跳转到最后
    }
    else if (v1==11)                      //C语言源码,else if (v1==11)
0081176F 83 7D F8 0B          cmp         dword ptr [v1],0Bh
00811773 75 13                jne         Sub_1+68h (0811788h)      //不等于则跳转
    {
        printf("%d\r\n", v1);
00811775 8B 45 F8             mov         eax,dword ptr [v1]
00811778 50                   push        eax
00811779 68 34 7B 81 00       push        offset string "%d\r\n" (0817B34h)
0081177E E8 B1 FB FF FF       call        _printf (0811334h)
00811783 83 C4 08             add         esp,8
00811786 EB 43                jmp         Sub_1+0ABh (08117CBh)     //跳转到最后
    }

Switch条件分歧:

C语言测试源代码:源码后面写有汇编详解

#include<iostream>
using namespace std;
void Sub_if();
void Sub_switch_0();
void Sub_switch_1();
void Sub_switch_4();
int v1;
int main()
{
    Sub_if();
    Sub_switch_0();
    Sub_switch_1();
    Sub_switch_4();
    return 0;
}
void Sub_if()
{
    Sv1 = 2;
    if (v1 == 1) cout << "Hello Word!\r\n";
    else if (v1 == 2) cout << "Hello Word!\r\n";
    else if (v1 == 11) cout << "Hello Word!\r\n";
    else if (v1 == 21) cout << "Hello Word!\r\n";
    else if (v1 == 22) cout << "Hello Word!\r\n";
    else cout << "Hello Word!\r\n";
}
void Sub_switch_0()    //测试case最小值等于0的情况
{
    v1 = 2;
    switch (v1)
    {
    case 0:cout << "Hello Word!\r\n";
    case 1:cout << "Hello Word!\r\n";
    case 2:cout << "Hello Word!\r\n";
    case 3:cout << "Hello Word!\r\n";
    case 4:cout << "Hello Word!\r\n";
    default:cout << "Hello Word!\r\n";
        break;
    }
}
void Sub_switch_1()    //测试case分支小于等于3的情况
{
    v1 = 2;
    switch (v1)
    {
    case 0:cout << "Hello Word!\r\n";
    case 1:cout << "Hello Word!\r\n";
    case 2:cout << "Hello Word!\r\n";
    default:cout << "Hello Word!\r\n";
        break;
    }
}
void Sub_switch_4()    //测试case最小值不为0的情况
{
    v1 = 21;
    switch (v1)
    {
    case 11:cout << "Hello Word!\r\n";
    case 21:cout << "Hello Word!\r\n";
    case 31:cout << "Hello Word!\r\n";
    case 32:cout << "Hello Word!\r\n";
    case 45:cout << "Hello Word!\r\n";
    default:cout << "Hello Word!\r\n";
        break;
    }
}


Switch语句通过索引表(跳转表)实现,当switch判断的时候会有一句会sub或add最小值,使其成为0(仅当最小值不等于0时);还会有一句cmp和ja指令,判断v1和表长,如果v1大于表长就会跳转到switch结束的地方。


测试代码—01:3个case块以内,是连续的【je + cmp】,和if语句一样。

//Sub_switch_1
//和if样式一样(3 case 以内)
    switch (v1)
01215095 8B 45 F8             mov         eax,dword ptr [v1]
01215098 89 85 30 FF FF FF    mov         dword ptr [ebp-0D0h],eax
0121509E 83 BD 30 FF FF FF 0A cmp         dword ptr [ebp-0D0h],0Ah
012150A5 74 14                je          Sub_2+4Bh (012150BBh)
012150A7 83 BD 30 FF FF FF 0B cmp         dword ptr [ebp-0D0h],0Bh
012150AE 74 1E                je          Sub_2+5Eh (012150CEh)
012150B0 83 BD 30 FF FF FF 14 cmp         dword ptr [ebp-0D0h],14h
012150B7 74 28                je          Sub_2+71h (012150E1h)
012150B9 EB 37                jmp         Sub_2+82h (012150F2h)

测试代码—02:3个case块以上,通过算法寻址,跳转到应该执行的 case 分支。cmp和ja指令,判断v1和表长,如果v1大于表长就会跳转到 default 语句块。

//Sub_switch_0
//跳转表(3 case 以上),当最小case为0时
    switch (v1)
01101C6E A1 38 B1 10 01       mov         eax,dword ptr [v1]  
01101C73 89 85 3C FF FF FF    mov         dword ptr [ebp-0C4h],eax  
01101C79 83 BD 3C FF FF FF 04 cmp         dword ptr [ebp-0C4h],4           //表长比较,这个表长是我自己起的名字,表长等于 [case中的最大值减最小值] 。
01101C80 77 6C                ja          $LN8+13h (01101CEEh)             //如果 [v1-case最小值](因为这里[case最小值为0],所以没有加减指令,下一个代码会很清晰地表现出来)大于表长,那么case中就不可能有数字等于v1,所以跳转至default语句块中。0x01101CEE是 default 的代码地址。
01101C82 8B 8D 3C FF FF FF    mov         ecx,dword ptr [ebp-0C4h]         //此时ecx中的值就是case在索检表中的位置,类似于数组下标。至于ecx中的值是怎么出来的,无需了解
01101C88 FF 24 8D 18 1D 10 01 jmp         dword ptr [ecx*4+1101D18h]       //跳转至case语句块中,0x1101D18是索检表头地址,该地址+ecx*4 就是case语句的地址

测试代码—03:3个case块以上,case 的最小值不是0时,在上面代码的基础上加了 sub(add) 指令,。

//Sub_switch_4
//跳转表(3 case 以上),当最小case不为0时
    switch (v1)
00845C5E A1 38 B1 84 00       mov         eax,dword ptr [v1]  
00845C63 89 85 3C FF FF FF    mov         dword ptr [ebp-0C4h],eax        
00845C69 8B 8D 3C FF FF FF    mov         ecx,dword ptr [ebp-0C4h]  
00845C6F 83 E9 0B             sub         ecx,0Bh                         //注意sub指令的作用
00845C72 89 8D 3C FF FF FF    mov         dword ptr [ebp-0C4h],ecx  
00845C78 83 BD 3C FF FF FF 22 cmp         dword ptr [ebp-0C4h],22h        //下面的指令同测试 代码——02后面的那几行
00845C7F 77 73                ja          $LN8+13h (0845CF4h)  
00845C81 8B 95 3C FF FF FF    mov         edx,dword ptr [ebp-0C4h]  
00845C87 0F B6 82 34 5D 84 00 movzx       eax,byte ptr [edx+845D34h]      //eax中的值就是case在索检表中的位置,和上面的代码中的ecx相同
00845C8E FF 24 85 1C 5D 84 00 jmp         dword ptr [eax*4+845D1Ch]

结束语:

If语句的条件判断在在汇编中是和C语言中相反的,但跳转逻辑是一样的,只是编译器用另一种方式翻译了一下,以便安排结构。C语言中满足判断条件才执行 if 语块下的内容否则执行 else 语句块下的内容; 经编译器编译后从汇编语言的角度看,满足跳转条件后跳转到C语言中的 else 语块对应的地方,不满足则不跳转继续向下执行 if 语块对应的代码。因为汇编中的条件和C语言中的条件是相反的,所以整体上是等价的(这不是废话吗,不等价还叫编译吗)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值