2025年4月8日 星期二 乙巳(蛇)年 正月初九 夜 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 软件应用 > 反汇编工具

分支结构在反汇编中特征

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

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高。

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