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