[系统安全14]动态调试技巧补充-循环
循环
vc++使用三种语法来完成循环结构,分别为do..while,while,for。虽然完成的功能都是循环,但是每种语法有着不同的执行流程。
do..while循环:先执行循环体,后比较判断。
while循环:先比较判断,后执行循环体。
for循环:先初始化,再比较判断,最后执行循环体。
逆向前的知识
- 局部变量
在OD中局部变量一般是EBP-8,EBP-C....
函数开始有push ebp、mov ebp,esp,或者也可能有ESP+xxx,但是函数开头没有push ebp之类的指令。
- 函数传参
在OD中函数传参一般是EBP+8,EBP+c...
函数开头有push ebp、mov ebp,esp。
- 传递参数
传递参数是【push 十六进制的值】,然后执行call指令,这里相当于调用了一个函数。
003F197E 6A 01 push 0x1 ;这是被传参的参数
003F1980 E8 46F9FFFF call 循环.003F12CB
003F1985 83C4 04 add esp,0x4
003F1988 6A 14 push 0x14 ;这是被传参的参数
003F198A E8 7AF7FFFF call 循环.003F1109
003F198F 83C4 04 add esp,0x4
003F1992 6A 03 push 0x3 ;这是被传参的参数
003F1994 E8 7AF7FFFF call 循环.003F1113
反汇编C语言源码
#include "stdafx.h"
//for循环函数
int for_fun(int nCount)
{
int nSum = 0;
for (int nIndex = 0 ; nIndex<nCount;nIndex++)
{
nSum += nIndex;
}
printf(" 111111111111111 %d\n ", nSum);
return nSum;
};
//do..while循环函数
int do_while_fun(int nCount)
{
int nSum = 0;
int nIndex = 0;
do {
nSum += nIndex;
nIndex++;
//循环判断,是否结束循环体
} while (nIndex<=nCount);
printf(" 22222222222222222 %d\n ", nSum);
return nSum;
}
//while循环函数
int while_fun(int nCount)
{
int nSum = 0;
int nIndex = 0;
while (nIndex<nCount)
{
nSum += nIndex;
nIndex++;
}
printf("333333333333333333 %d\n ", nSum);
return nSum;
}
int main()
{
for_fun(2);
do_while_fun(20);
while_fun(3);
return 0;
}
do...while循环
do..while的工作流程相对比较简单,先执行加的运算,然后在cmp判断条件是否满足,然后jle到mov nSum的值给eax的地方。
003F16F0 > 55 push ebp
003F16F1 8BEC mov ebp,esp
003F16F3 81EC D8000000 sub esp,0xD8
003F16F9 53 push ebx
003F16FA 56 push esi ; 循环.<ModuleEntryPoint>
003F16FB 57 push edi ; 循环.<ModuleEntryPoint>
003F16FC 8DBD 28FFFFFF lea edi,dword ptr ss:[ebp-0xD8]
003F1702 B9 36000000 mov ecx,0x36
003F1707 B8 CCCCCCCC mov eax,0xCCCCCCCC
003F170C F3:AB rep stos dword ptr es:[edi]
003F170E C745 F8 0000000>mov dword ptr ss:[ebp-0x8],0x0 ; int nSum = 0
003F1715 C745 EC 0000000>mov dword ptr ss:[ebp-0x14],0x0 ; int nIndex = 0
003F171C 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; mov nSum的值给eax
003F171F 0345 EC add eax,dword ptr ss:[ebp-0x14] ; nIndex+nSum=eax
003F1722 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; 把nIdex+nSum的结果给eax,等价于:nSum += i;
003F1725 8B45 EC mov eax,dword ptr ss:[ebp-0x14]
003F1728 83C0 01 add eax,0x1 ; nIndex++
003F172B 8945 EC mov dword ptr ss:[ebp-0x14],eax
003F172E 8B45 EC mov eax,dword ptr ss:[ebp-0x14] ; nIndex要赋值给eax
003F1731 3B45 08 cmp eax,dword ptr ss:[ebp+0x8] ; +号函数传参的参数,我传参进来的是20,hex为14
003F1734 ^ 7E E6 jle short 循环.003F171C ; Do..while循环没有jmp,jxx只要满足条件则跳转,等价于} while (nIndex<=nCount);
003F1736 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; 把nSum赋值给eax
003F1739 50 push eax
003F173A 68 4C6B3F00 push 循环.003F6B4C ; 22222222222222222 %d\n
003F173F E8 E1FBFFFF call 循环.003F1325 ; printf
003F1744 83C4 08 add esp,0x8
003F1747 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
003F174A 5F pop edi ; 循环.<ModuleEntryPoint>
003F174B 5E pop esi ; 循环.<ModuleEntryPoint>
003F174C 5B pop ebx
003F174D 81C4 D8000000 add esp,0xD8
003F1753 3BEC cmp ebp,esp
003F1755 E8 BEF9FFFF call 循环.003F1118
003F175A 8BE5 mov esp,ebp
003F175C 5D pop ebp
003F175D C3 retn
do..while循环特征
如果遇到以下代码块,就可以判断是do...while循环,do..while在执行前无需检查就执行代码块内的语句,然后判断条件是否满足,根据条件跳转指令决定是否跳转到循环语句的首地址。
do_while 循环
mov [xxx],0
loop:
xxxxx
cmp eax,xxx
jxx loop_;
while循环
while循环和do循环正好相反,在执行循环语句块之前,必须要进行条件判断,判断比较结果后再选择是否执行循环语句块。执行完毕后再跳回到循环语句块的首地址,再进行比较,条件满足跳出循环。
011E182E C745 F8 0000000>mov dword ptr ss:[ebp-0x8],0x0 ; int nSum = 0;
011E1835 C745 EC 0000000>mov dword ptr ss:[ebp-0x14],0x0 ; int nIndex = 0;
011E183C 8B45 EC mov eax,dword ptr ss:[ebp-0x14]
011E183F 3B45 08 cmp eax,dword ptr ss:[ebp+0x8] ; while(nIndex<nCount),这里是与传参进来的参数进行比对
011E1842 7D 14 jge short 循环.011E1858 ; 满足条件jge执行,然后跳转到011E1858
011E1844 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; 局部变量nSum 赋值给 eax
011E1847 0345 EC add eax,dword ptr ss:[ebp-0x14] ; eax跟nIndex相加,结果保存在eax。
011E184A 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; nSum = nIndex+nSum
011E184D 8B45 EC mov eax,dword ptr ss:[ebp-0x14]
011E1850 83C0 01 add eax,0x1 ; nIndex+1,等价于nIndex++
011E1853 8945 EC mov dword ptr ss:[ebp-0x14],eax
011E1856 ^ EB E4 jmp short 循环.011E183C ; 跳转回去,while条件对比的地方,把nIndex执行后的结果赋值到eax寄存器中
011E1858 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
011E185B 50 push eax
while循环特征
while循环的特征是先执行对比,在执行语句。
mov [xxx],0
loop_
cmp eax,xxx
jxx loop_break;
xxxx
jmp loop_
for循环
for循环是三种循环结构中最复杂的一种。for循环里由赋初值,设置循环条件、设置循环步长这三条语句组成。
在反汇编指令中,一共有3条跳转指令。jmp,jge,jmp。第一条jmp指令跳转到判断循环条件是否满足的地方,执行代码块内的数据,等价于执行nSum = nSum+nIndex;第二条cmp指令对比如果满足条件修改标志位执行【jge short 循环.001617CA】跳出循环,否则不实现跳转继续向下执行。第三条jmp指令跳转到【mov eax,dword ptr ss:[ebp-0x14]】 、【add eax,0x1】,等价于执行nIndex++,执行add操作后, 再次对比条件是否满足。
0016179E C745 F8 0000000>mov dword ptr ss:[ebp-0x8],0x0 ; int nSum = 0;
001617A5 C745 EC 0000000>mov dword ptr ss:[ebp-0x14],0x0 ; int nIndex = 0;
001617AC EB 09 jmp short 循环.001617B7
001617AE 8B45 EC mov eax,dword ptr ss:[ebp-0x14] ; 等价于执行nIndex++
001617B1 83C0 01 add eax,0x1
001617B4 8945 EC mov dword ptr ss:[ebp-0x14],eax
001617B7 8B45 EC mov eax,dword ptr ss:[ebp-0x14] ; nIndex赋值给eax
001617BA 3B45 08 cmp eax,dword ptr ss:[ebp+0x8] ; 判断循环条件是否满足,是不是需要继续循环,否则跳转到nIndex++的地址处,等价于nIndex<nCount
001617BD 7D 0B jge short 循环.001617CA ; 满足条件跳转
001617BF 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
001617C2 0345 EC add eax,dword ptr ss:[ebp-0x14] ; 等价于nSum = nSum+nIndex
001617C5 8945 F8 mov dword ptr ss:[ebp-0x8],eax
001617C8 ^ EB E4 jmp short 循环.001617AE
001617CA 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
001617CD 50 push eax
for循环特征
for循环的简单特征如下,比while和do..while循环要复杂一些的地方是多了一层跳转,要执行条件值++的操作
mov [xxx],0
jmp jmp_cmp
loop_
i++;
jmp_cmp
cmp eax,xxx
jxx loop_break;
xxxxx
jmp loop_