逆向工程初探之循环结构的判别

逆向工程初探之循环结构的判别

逆向了几个样本之后,现在对于循环结构的判别有所了解
循环结构分为三种


while 先比较判断,后执行循环体
do while 先执行循环体,后比较判断
for 先初始化,再比较判断,最后执行循环体。

While

while 在执行循环语句块之前,必须要进行条件判断,根据比较结果再选择是否执行循环语句块。

#include<iostream>
#include<windows.h>
#include<tchar.h>
using namespace std;

int main()
{
	int nSum = 0;
	int nIndex = 0;
	int nCount = 10;
	while (nIndex <= nCount)
	{
		nSum += nIndex;
		nIndex++;
	} 
}

这里使用了vs2015进行反汇编 与正常的汇编语句可能有出入
while语句就是先用一个jg指令,如果不符合条件就向下跳转,越过循环,然后每执行一次循环语句块,jmp跳转至while开头,然后再进行比较

int main()
{
00BF16D0 55                   push        ebp  
00BF16D1 8B EC                mov         ebp,esp  
00BF16D3 81 EC E4 00 00 00    sub         esp,0E4h  
00BF16D9 53                   push        ebx  
00BF16DA 56                   push        esi  
00BF16DB 57                   push        edi  
00BF16DC 8D BD 1C FF FF FF    lea         edi,[ebp-0E4h]  
00BF16E2 B9 39 00 00 00       mov         ecx,39h  
00BF16E7 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
00BF16EC F3 AB                rep stos    dword ptr es:[edi]  
	int nSum = 0;
00BF16EE C7 45 F8 00 00 00 00 mov         dword ptr [nSum],0  
	int nIndex = 0;
00BF16F5 C7 45 EC 00 00 00 00 mov         dword ptr [nIndex],0  
	int nCount = 10;
00BF16FC C7 45 E0 0A 00 00 00 mov         dword ptr [nCount],0Ah  
	while (nIndex <= nCount)
00BF1703 8B 45 EC             mov         eax,dword ptr [nIndex]  
00BF1706 3B 45 E0             cmp         eax,dword ptr [nCount]  
00BF1709 7F 14                jg          main+4Fh (0BF171Fh)  
	{
		nSum += nIndex;
00BF170B 8B 45 F8             mov         eax,dword ptr [nSum]  
00BF170E 03 45 EC             add         eax,dword ptr [nIndex]  
00BF1711 89 45 F8             mov         dword ptr [nSum],eax  
		nIndex++;
00BF1714 8B 45 EC             mov         eax,dword ptr [nIndex]  
00BF1717 83 C0 01             add         eax,1  
00BF171A 89 45 EC             mov         dword ptr [nIndex],eax  
	}
00BF171D EB E4                jmp         main+33h (0BF1703h)  
}
00BF171F 33 C0                xor         eax,eax  
00BF1721 5F                   pop         edi  
00BF1722 5E                   pop         esi  
00BF1723 5B                   pop         ebx  
00BF1724 8B E5                mov         esp,ebp  
00BF1726 5D                   pop         ebp  
00BF1727 C3                   ret  

总结

WHILE_BEGIN:
jxx WHILE_END; 条件成立跳转到循环语句块结尾处
······ ;循环语句块
jmp WHILE_BEGIN;跳转到取出条件比较数据处
WHILE_END:

do while

do while 的工作流程清晰,识别起来相对简单。先执行语句块,再进行比较。当条件成立时,会继续执行语句块。
我们可以用goto语句来模拟do循环,这样会加深我们对与do while的理解

#include<iostream>
#include<windows.h>
#include<tchar.h>
using namespace std;

int main()
{
	int nSum = 0;
	int nIndex = 0;
	int nCount = 10;
GOTO:
	nSum += nIndex;
	nIndex++;
	if (nIndex <= nCount)
	{
		goto GOTO;
	}
}

汇编

002116D0  push        ebp  
002116D1  mov         ebp,esp  
002116D3  sub         esp,0E4h  
002116D9  push        ebx  
002116DA  push        esi  
002116DB  push        edi  
002116DC  lea         edi,[ebp-0E4h]  
002116E2  mov         ecx,39h  
002116E7  mov         eax,0CCCCCCCCh  
002116EC  rep stos    dword ptr es:[edi]  
	int nSum = 0;
002116EE  mov         dword ptr [nSum],0  
	int nIndex = 0;
002116F5  mov         dword ptr [nIndex],0  
	int nCount = 10;
002116FC  mov         dword ptr [nCount],0Ah  
GOTO:
	nSum += nIndex;
00211703  mov         eax,dword ptr [nSum]  
00211706  add         eax,dword ptr [nIndex]  
00211709  mov         dword ptr [nSum],eax  
	nIndex++;
0021170C  mov         eax,dword ptr [nIndex]  
0021170F  add         eax,1  
00211712  mov         dword ptr [nIndex],eax  
	if (nIndex <= nCount)
00211715  mov         eax,dword ptr [nIndex]  
00211718  cmp         eax,dword ptr [nCount]  
0021171B  jg          GOTO+1Ch (021171Fh)  
	{
		goto GOTO;
0021171D  jmp         GOTO (0211703h)  

等效do while

#include<iostream>
#include<windows.h>
#include<tchar.h>
using namespace std;

int main()
{
	int nSum = 0;
	int nIndex = 0;
	int nCount = 10;
	do
	{
		nSum += nIndex;
		nIndex++;
	} while (nIndex <= nCount);
}
006D16D0  push        ebp  
006D16D1  mov         ebp,esp  
006D16D3  sub         esp,0E4h  
006D16D9  push        ebx  
006D16DA  push        esi  
006D16DB  push        edi  
006D16DC  lea         edi,[ebp-0E4h]  
006D16E2  mov         ecx,39h  
006D16E7  mov         eax,0CCCCCCCCh  
006D16EC  rep stos    dword ptr es:[edi]  
	int nSum = 0;
006D16EE  mov         dword ptr [nSum],0  
	int nIndex = 0;
006D16F5  mov         dword ptr [nIndex],0  
	int nCount = 10;
006D16FC  mov         dword ptr [nCount],0Ah  
	do
	{
		nSum += nIndex;
006D1703  mov         eax,dword ptr [nSum]  
006D1706  add         eax,dword ptr [nIndex]  
006D1709  mov         dword ptr [nSum],eax  
		nIndex++;
006D170C  mov         eax,dword ptr [nIndex]  
006D170F  add         eax,1  
006D1712  mov         dword ptr [nIndex],eax  
	} while (nIndex <= nCount);
006D1715  mov         eax,dword ptr [nIndex]  
006D1718  cmp         eax,dword ptr [nCount]  
006D171B  jle         main+33h (06D1703h)  

在这两种等效的方法中,我们要区别的是
if语句和do while 都会有一个比较的过程,但是区别在于 if语句跳转条件是与判断语句相反的,而且是一个向下跳转的过程。
而do while 语句跳转语句与判断语句一致,是一个向上跳转的过程。
这样if与do while就很好区分了

总结

DO_BEGIN:
······ ;循环语句块
;影响标记位的指令
jxx DO_BEGIN ;向上跳转
如果遇到以上代码块,即可判断此循环为do while ,且do while 只需要一次比较语句。

for

for 循环是三种循环结构中最复杂的一种。for循环由赋初值、设置循环条件、设置循环步长这三条语句组成。而由于for循环更加符合人类的思维方式,在循环中使用频率最高,所以掌握for循环在逆向中比较重要。

#include<iostream>
#include<windows.h>
#include<tchar.h>
using namespace std;

int main()
{
	int nSum = 0;
	int nIndex = 0;
	int nCount = 10;
	for (nIndex=0;nIndex<=nCount;nIndex++)
	{
		nSum += nIndex;
	}
}

004D16D0  push        ebp  
004D16D1  mov         ebp,esp  
004D16D3  sub         esp,0E4h  
004D16D9  push        ebx  
004D16DA  push        esi  
004D16DB  push        edi  
004D16DC  lea         edi,[ebp-0E4h]  
004D16E2  mov         ecx,39h  
004D16E7  mov         eax,0CCCCCCCCh  
004D16EC  rep stos    dword ptr es:[edi]  
	int nSum = 0;
004D16EE  mov         dword ptr [nSum],0  
	int nIndex = 0;
004D16F5  mov         dword ptr [nIndex],0  
	int nCount = 10;
004D16FC  mov         dword ptr [nCount],0Ah  
	for (nIndex=0;nIndex<=nCount;nIndex++)
004D1703  mov         dword ptr [nIndex],0  //初始化计数器变量
004D170A  jmp         main+45h (04D1715h)  //跳转至04D1715,跳过步长
004D170C  mov         eax,dword ptr [nIndex]  
004D170F  add         eax,1  //步长加一
004D1712  mov         dword ptr [nIndex],eax  
004D1715  mov         eax,dword ptr [nIndex]  
004D1718  cmp         eax,dword ptr [nCount]  //条件比较
004D171B  jg          main+58h (04D1728h)  
	{
		nSum += nIndex;
004D171D  mov         eax,dword ptr [nSum]  
004D1720  add         eax,dword ptr [nIndex]  
004D1723  mov         dword ptr [nSum],eax  
	}
004D1726  jmp         main+3Ch (04D170Ch)  //向上跳转
}

for循环在debug下的调试有三个跳转指令,为什么要设计这么复杂,因为要满足在调试时的单步调试,所以进行了一一对应。

总结:

mov i,xxx
jmp FOR_CMP ;跳到循环条件判定
FOR_STEP:
修改循环变量
MOV
ADD
MOV
FOR_CMP: ;条件判定
mov
cmp
jxx FOR_END:

JMP FOR_STEP ;向上跳转,修改流程回到步长计算部分
FOR_END;
遇到以上代码块,就可以判定它为一个for循环结构。这种结构被赋初值后,利用jmp跳过第一次步长计算。然后,可以通过是for循环独有的,在计数器变量被赋初值后,利用jmp跳过第一次步长计算。然后,可以通过三个跳转指令还原for循环的各个组成部分;第一个jmp指令之前的代码为初始化部分;从第一个jmp指令到循环条件比较处之间的代码为步长计算部分;在条件跳转指令jxx之后寻找一个jmp指令,这个jmp指令必须是向上跳转的,且其目标是到步长计算的位置,在jxx和这个jmp之间的代码为循环语句块。

参考书籍

《C反汇编与逆向分析技术揭秘》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值