彙編-識別條件分歧

簡介:

本文主要內容是,反彙編過程中識別條件分歧。進行分析之前必須明白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語言中的條件是相反的,所以整體上是等價的(這不是廢話嗎,不等價還叫編譯嗎)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章