您当前的位置:首页 > 计算机 > 软件应用 > 反汇编工具

分支结构在反汇编中特征

时间:10-05来源:作者:点击数:

IDA分析 if…else分支结构

Debug版本

.text:00411E45                 mov     [ebp+var_8], 1  ; int nTest = 1; 函数参数赋值
.text:00411E4C                 cmp     [ebp+var_8], 0  ; 比较大小
.text:00411E50                 jle     short loc_411E61 ; 前一个值小于或等于后一个值,则跳转;否则,指令继续按顺序执行。
.text:00411E52                 push    offset aHelloWorld ; "Hello World!\r\n"
.text:00411E57                 call    sub_4113C5      ; 函数调用结束后,就只剩下函数参数还未平栈
.text:00411E5C                 add     esp, 4          ; 平栈
.text:00411E5F                 jmp     short loc_411E6E ; 跳转回return 0执行
.text:00411E61 ; ---------------------------------------------------------------------------
.text:00411E61
.text:00411E61 loc_411E61:                             ; CODE XREF: _main_0+30↑j
.text:00411E61                 push    offset aHelloEverybody ; "Hello everybody!\r\n"
.text:00411E66                 call    sub_4113C5
.text:00411E6B                 add     esp, 4
.text:00411E6E
.text:00411E6E loc_411E6E:                             ; CODE XREF: _main_0+3F↑j
.text:00411E6E                 xor     eax, eax
.text:00411E70                 pop     edi
.text:00411E71                 pop     esi
.text:00411E72                 pop     ebx
.text:00411E73                 add     esp, 0CCh

jle的跳转条件是 前一个数小于等于后一个数

当执行到 "jle" 指令时,会检查处理器标志寄存器中的小于等于标志位(LE)。如果 LE 为 1(被置位),表示前面的比较操作结果小于或等于零,此时程序将跳转到指定的目标地址继续执行。如果 LE 为 0(未被置位),则跳转不会发生,程序将顺序执行下一条指令

Release发布版

和Debug区别在于只剩下一个分支,经过了编译优化;编译器是被cmp比较后是一个常量,就减掉了那个不会被执行的分支

if……else反汇编结构

cmp xxxx
jcc 地址
_if
    XXXX
    jmp ends
_else
    XXXX
_end
    xor eax,eax
    xxx
    ret

IDA分析 if…else 多分支结构

Debug版本

程序流程图

jnz跳转条件:jnz指令用于判断前一个操作的结果是否为零,如果结果不为零,则执行跳转操作。

当执行到 "jnz" 指令时,会检查处理器标志寄存器中的零标志位(ZF)。如果 ZF 为 0(未被置位),表示前面的操作结果不为零,此时程序将跳转到指定的目标地址继续执行。如果 ZF 为 1(被置位),则跳转不会发生,程序将顺序执行下一条指令。

汇编执行逻辑

  1. jle判断小于等于0跳转右边,否则左边
  2. 跳转到右边后,判断是否为0,为0左边,不为0右边
  3. 三条分支走向return 0
.text:00411E4C                 cmp     [ebp+var_8], 0
.text:00411E50                 jle     short loc_411E61
.text:00411E52                 push    offset aHelloWorld ; "Hello World!\r\n"
.text:00411E57                 call    sub_4113C5
.text:00411E5C                 add     esp, 4
.text:00411E5F                 jmp     short loc_411E83 ; 若结果小于等于,就跳转
.text:00411E61 ; ---------------------------------------------------------------------------
.text:00411E61
.text:00411E61 loc_411E61:                             ; CODE XREF: _main_0+30↑j
.text:00411E61                 cmp     [ebp+var_8], 0
.text:00411E65                 jnz     short loc_411E76 ; 若结果不等于0则跳转
.text:00411E67                 push    offset aHelloEverybody ; "Hello everybody!\r\n"
.text:00411E6C                 call    sub_4113C5
.text:00411E71                 add     esp, 4
.text:00411E74                 jmp     short loc_411E83
.text:00411E76 ; ---------------------------------------------------------------------------
.text:00411E76
.text:00411E76 loc_411E76:                             ; CODE XREF: _main_0+45↑j
.text:00411E76                 push    offset aHello   ; "Hello "
.text:00411E7B                 call    sub_4113C5
.text:00411E80                 add     esp, 4
.text:00411E83
.text:00411E83 loc_411E83:                             ; CODE XREF: _main_0+3F↑j
.text:00411E83                                         ; _main_0+54↑j
.text:00411E83                 xor     eax, eax

其发布版也是进行了优化

if……else if……else反汇编结构

cmp XXXX
jcc _elseif else

_if
    XXX
    jmp _end

_elseif:
    cmp XXX
    jcc _else
    XXXX
    jmp _end

else:
    XXXX

_end:
    xor eax,eax
    ret

nTest输入分析多分支结构

Debug版

Release版

使用其他汇编指令来优化

IDA分析switch

分支小于四

测试代码

#include <stdio.h>

int main()
{
	int n = 0;
	scanf_s("%d", &n);
	switch (n)
	{
	case 1:
		printf("n==1");
		break;
	case 2:
		printf("n==2");
		break;
	case 3:
		printf("n==3");
		break;
	}
	return 0;
}

当执行到 "jz" 指令时,会检查处理器标志寄存器中的零标志位(ZF)。如果 ZF 为 1(被置位),表示前面的操作结果为零,此时程序将跳转到指定的目标地址继续执行。如果 ZF 为 0(未被置位),则跳转不会发生,程序将顺序执行下一条指令。

反汇编分析

.text:004150FF                 mov     dword ptr [ebp+n], 0 ; int n = 0;
.text:00415106                 lea     eax, [ebp+n]
.text:00415109                 push    eax             ; char
.text:0041510A                 push    offset aD       ; 这是个全局变量
.text:0041510F                 call    sub_4113E3      ; 调用scanf函数
.text:00415114                 add     esp, 8          ; 平栈
.text:00415117                 mov     eax, dword ptr [ebp+n] ; switch (n)
.text:0041511A                 mov     [ebp+var_D4], eax ; switch (n),局部变量之间赋值也是借助寄存器
.text:00415120                 cmp     [ebp+var_D4], 1 ; case 1
.text:00415127                 jz      short loc_41513D ; ZF=1,跳转,说明两个比较值相等;ZF=0,反之
.text:00415129                 cmp     [ebp+var_D4], 2 ; case 2
.text:00415130                 jz      short loc_41514C
.text:00415132                 cmp     [ebp+var_D4], 3 ; case 3
.text:00415139                 jz      short loc_41515B
.text:0041513B                 jmp     short loc_415168 ; 如果三个都没匹配到,就会自导跳转到为末尾
.text:0041513D ; ---------------------------------------------------------------------------
.text:0041513D
.text:0041513D loc_41513D:                             ; CODE XREF: _main_0+57↑j
.text:0041513D                 push    offset aN1      ; "n==1"
.text:00415142                 call    sub_4113C5      ; 调用printf
.text:00415147                 add     esp, 4
.text:0041514A                 jmp     short loc_415168
.text:0041514C ; ---------------------------------------------------------------------------
.text:0041514C
.text:0041514C loc_41514C:                             ; CODE XREF: _main_0+60↑j
.text:0041514C                 push    offset aN2      ; "n==2"
.text:00415151                 call    sub_4113C5      ; 调用printf
.text:00415156                 add     esp, 4
.text:00415159                 jmp     short loc_415168
.text:0041515B ; ---------------------------------------------------------------------------
.text:0041515B
.text:0041515B loc_41515B:                             ; CODE XREF: _main_0+69↑j
.text:0041515B                 push    offset aN3      ; "n==3"
.text:00415160                 call    sub_4113C5      ; 调用printf
.text:00415165                 add     esp, 4
.text:00415168
.text:00415168 loc_415168:                             ; CODE XREF: _main_0+6B↑j
.text:00415168                                         ; _main_0+7A↑j ...
.text:00415168                 xor     eax, eax
.text:0041516A                 push    edx
.text:0041516B                 mov     ecx, ebp        ; Esp
.text:0041516D                 push    eax
.text:0041516E                 lea     edx, Fd         ; Fd
.text:00415174                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)

当case语句块小于4时,switch语句所反汇编的代码有区别于if else,if else是在判断语句后紧跟代码 块,而switch语句是将所有的代码块放置在一起,上半部分是判断语句,下半块是代码块。效率和if else相同

分支大于4有序线性结构

测试代码

#include <stdio.h>
int main()
{
    int n = 0;
    scanf_s("%d", &n);
    switch (n)
    {
    case 1:
        printf("n==1");
        break;
    case 2:
        printf("n==2");
        break;
    case 3:
        printf("n==3");
        break;
    case 5:
        printf("n==5");
        break;
    }
    return 0;
}

输入参数N传入switch的n

.text:00415117                 mov     eax, dword ptr [ebp+n] ; switch (n)
.text:0041511A                 mov     [ebp+switch_n], eax ; switch (n)
.text:00415120                 mov     ecx, [ebp+switch_n] ; switch (n)

把输入的n减1

.text:00415126                 sub     ecx, 1         

把减去的值赋值给ecx,并且与case最大值减一比较(5-1)

.text:00415129                 mov     [ebp+switch_n], ecx
.text:0041512F                 cmp     [ebp+switch_n], 4

倘若前者大于后者,就跳转,说明超过了case的界限

.text:00415136                 ja      short def_41513E ; switch_n大于4就跳转

对于case数大于4,且线性结构,编译器就会使用比例因子寻址

.text:00415138                 mov     edx, [ebp+switch_n]
.text:0041513E                 jmp     ds:jpt_41513E[edx*4] ; switch jump

地址jpt_41513E这里存储了一个跳转表,会根据edx*4进行偏移

4个case语句一共5个地址,其中第四个地址是为了补上case 4的缺失,指向的是switch语句的结束位 置。让整个表构成有序的线性结构,从而提高执行效率。

这个缺失的地址必须补上,如果没有补上跳转,则无法用数组寻址的方式跳转到对应的case语句,整个 跳转表从未补齐的那一个地址开始全部都是错误的。

找到对应的地址进行跳转

倘如下标不从1开始

测试代码

#include <stdio.h>
int main()
{
int n = 0;
scanf_s("%d", &n);
switch (n)
{
case 5:
printf("n==1");
break;
case 6:
printf("n==2");
break;
case 7:
printf("n==3");
break;
case 10:
printf("n==5");
break;
}
return 0;
}

查看核心反汇编代码

把传入的switch_n - case(max);之后和最大的case(5)比较,前者大于后者,则跳转;然后跳转至跳转表;

分支大于4 无序线性结构

测试代码

#include <stdio.h>
int main()
{
int n = 0;
scanf_s("%d", &n);
switch (n)
{
case 5:
printf("n==1");
break;
case 7:
printf("n==2");
break;
case 9:
printf("n==3");
break;
case 11:
printf("n==5");
break;
case 6:
printf("n==6");
break;
case 255:
printf("n==255");
break;
}
return 0;
}

查看反汇编核心代码

传入的switch_n减去case(5),之后和case(max)比较,大于则跳转;

把这个值放到edx中,把它作为下标,在byte_41520C这个数组中取值

这里也有一张表,这张表叫索引表。索引表中保存的是跳转表的数组下标。

case 5->0
case 6->1
case 7->2
case 8->6 因为没有case8 所以这里被置成了6
case 9->3
.....

因为这种无序的switch case会有一个索引不方便的问题,所以说他设计了一张索引表,把所有的case 后面跟的数字都制作成下标统一放在这个索引表里面。

这样的话就有一个好处,不管你的case语句后面跟着的数值是多少,我都能直接拿到我要的索引。

比如说

case 9 减去5=6

那么我就找这张表下标为6的数值,取出来进行寻址

这样就可以把4取出来了。

索引表就解决了case语句块无序的问题,这样就可以再用跳转表进行寻址了

.text:00415148                 jmp     ds:jpt_415148[eax*4] ; switch jump

然后用比例因子寻址的方式跳转到对应的地址

case的值没有明显的线性关系时,就会出现两张表,一张索引表,保存下标,一张线性表,保

存地址

但是还有个效率的问题:无序线性结构会有两次查表的过程,因此效率会有所下降

if elseswitch哪个效率高?

当switch分支小于4时,其反汇编采用的结构和if else相同,两者效率一样。

当switch分支大于4时,switch采用跳转表的方式来执行代码块,此时效率比if else高。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门