简介:
本文主要内容是,反汇编过程中识别条件分歧。进行分析之前必须明白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语言中的条件是相反的,所以整体上是等价的(这不是废话吗,不等价还叫编译吗)。