逆向基础:循环结构分析

程序语言的控制结构不外乎分支与循环,学习完分支结构后自然要对循环结构的反汇编代码有个了解。C语言的循环结构有for循环、while循环、do循环和goto循环。这里介绍前3种循环方式。

1.for循环结构

for循环也可以称为步进循环,它的特点是用于已经明确循环范围。看下面一个简单的C语言代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int main()
{
int nNum = 0,nSum = 0;

for (nNum = 1; nNum <= 100; nNum ++)
{
nSum +=nNum;
}
printf("nSum = %d \r\n",nSum);

return 0;
}

这是一个经典的求1至100的累加求和的程序。通过这个程序来认识for循环结构的反汇编代码。

点击显/隐内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.text:00401028                 mov     [ebp+var_4], 0
.text:0040102F mov [ebp+var_8], 0
.text:00401036 mov [ebp+var_4], 1
.text:0040103D jmp short loc_401048
.text:0040103F ; ---------------------------------------------------------------------------
.text:0040103F
.text:0040103F loc_40103F: ; CODE XREF: _main+47j
.text:0040103F mov eax, [ebp+var_4]
.text:00401042 add eax, 1
.text:00401045 mov [ebp+var_4], eax
.text:00401048
.text:00401048 loc_401048: ; CODE XREF: _main+2Dj
.text:00401048 cmp [ebp+var_4], 64h
.text:0040104C jg short loc_401059
.text:0040104E mov ecx, [ebp+var_8]
.text:00401051 add ecx, [ebp+var_4]
.text:00401054 mov [ebp+var_8], ecx
.text:00401057 jmp short loc_40103F
.text:00401059 ; ---------------------------------------------------------------------------
.text:00401059
.text:00401059 loc_401059: ; CODE XREF: _main+3Cj
.text:00401059 mov edx, [ebp+var_8]
.text:0040105C push edx
.text:0040105D push offset Format ; "nSum = %d \r\n"
.text:00401062 call _printf
.text:00401067 add esp, 8
.text:0040106A xor eax, eax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
.text:00401028                 mov     [ebp+nNum], 0
.text:0040102F mov [ebp+nSum], 0
.text:00401036 mov [ebp+nNum], 1
.text:0040103D jmp short LOC_CMP
.text:0040103F ; ---------------------------------------------------------------------------
.text:0040103F
.text:0040103F LOC_STEP: ; CODE XREF: _main+47j
.text:0040103F mov eax, [ebp+nNum]
.text:00401042 add eax, 1
.text:00401045 mov [ebp+nNum], eax
.text:00401048
.text:00401048 LOC_CMP: ; CODE XREF: _main+2Dj
.text:00401048 cmp [ebp+nNum], 64h
.text:0040104C jg short LOC_ENDFOR
.text:0040104E mov ecx, [ebp+nSum]
.text:00401051 add ecx, [ebp+nNum]
.text:00401054 mov [ebp+nSum], ecx
.text:00401057 jmp short LOC_STEP
.text:00401059 ; ---------------------------------------------------------------------------
.text:00401059
.text:00401059 LOC_ENDFOR: ; CODE XREF: _main+3Cj
.text:00401059 mov edx, [ebp+nSum]
.text:0040105C push edx
.text:0040105D push offset Format ; "nSum = %d \r\n"
.text:00401062 call _printf
.text:00401067 add esp, 8
.text:0040106A xor eax, eax

这次的反汇编代码,我修改了其中的变量、标号,看起来更加直观。for结构可以分为3个部分,在LOC_STEP上面的部分是初始化部分,在LOC_STEP下面的部分是修改循环变量的部分,在LOC_CMP下面和LOC_ENDFOR上面部分是比较循环条件和循环体的部分。
for循环的反汇编结构如下:

1
2
3
4
5
6
7
8
9
10
	   ;初始化循环变量
jmp LOC_CMP
LOC_STEP:
;修改循环变量
LOC_CMP:
;循环变量的判断
jxx LOC_ENDFOR
;循环体
jmp LOC_STEP
LOC_ENDFOR:

再用IDA来看下生成的流程结构图,如下图所示:

2.do…while循环结构

do循环体总是会被执行一次,这是do循环和while循环的区别。这里还是1至100的累加和代码,来看一下它的反汇编形式。先看C语言代码,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main()
{
int nNum = 1,nSum = 0;

do
{
nSum += nNum;
nNum ++;
}while (nNum <= 100);

printf("nSum = %d \r\n",nSum);

return 0;
}

do循环的结构要比for循环简单很多,反汇编代码也少很多。先看一下IDA生成的流程图,如下图所示:

反汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.text:00401028                 mov     [ebp+var_4], 1
.text:0040102F mov [ebp+var_8], 0
.text:00401036
.text:00401036 LOC_DO: ; CODE XREF: _main+3Cj
.text:00401036 mov eax, [ebp+var_8]
.text:00401039 add eax, [ebp+var_4]
.text:0040103C mov [ebp+var_8], eax
.text:0040103F mov ecx, [ebp+var_4]
.text:00401042 add ecx, 1
.text:00401045 mov [ebp+var_4], ecx
.text:00401048 cmp [ebp+var_4], 64h
.text:0040104C jle short LOC_DO
.text:0040104E mov edx, [ebp+var_8]
.text:00401051 push edx
.text:00401052 push offset Format ; "nSum = %d \r\n"
.text:00401057 call _printf
.text:0040105C add esp, 8
.text:0040105F xor eax, eax

do循环的主体就在LOC_DO和0040104C的jie之间。其结构调整如下:

1
2
3
4
5
6
	;初始化循环变量
LOC_DO:
;执行循环体
;修改循环体
;循环体变量的比较
Jxx LOC_DO

3.while循环结构

while循环与do循环的区别在于,在进入循环体之前需要先进行一次条件判断,循环体有可能因为循环条件的不成立而一次也不执行。1至100累加求和的while循环代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

int main()
{
int nNum = 1,nSum = 0;

while (nNum <= 100 )
{
nSum +=nNum;
nNum ++;
}

printf("nSum = %d \r\n",nSum);

return 0;
}

再来看下它的反汇编代码,while循环比do循环多了一个条件的判断,因此会多一条分支。反汇编代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
.text:00401028                 mov     [ebp+nNum], 1
.text:0040102F mov [ebp+nSum], 0
.text:00401036
.text:00401036 LOC_WHILE: ; CODE XREF: _main+3Ej
.text:00401036 cmp [ebp+nNum], 64h
.text:0040103A jg short LOC_WHILEEND
.text:0040103C mov eax, [ebp+nSum]
.text:0040103F add eax, [ebp+nNum]
.text:00401042 mov [ebp+nSum], eax
.text:00401045 mov ecx, [ebp+nNum]
.text:00401048 add ecx, 1
.text:0040104B mov [ebp+nNum], ecx
.text:0040104E jmp short LOC_WHILE
.text:00401050 ; ---------------------------------------------------------------------------
.text:00401050
.text:00401050 LOC_WHILEEND: ; CODE XREF: _main+2Aj
.text:00401050 mov edx, [ebp+nSum]
.text:00401053 push edx
.text:00401054 push offset Format ; "nSum = %d \r\n"
.text:00401059 call _printf
.text:0040105E add esp, 8
.text:00401061 xor eax, eax

while循环的主要部分全部在LOC_WHILE和LOC_WHILEEND之间。在LOC_WHILE下面的两句cmp和jxx指令,在LOC_WHILEEND上面是jmp指令。这两部分是固定的格式;其结构整理如下:

1
2
3
4
5
6
7
	;初始化循环变量等
LOC_WHILE:
cmp xxx, xxx
jxx LOC_WHILEEND
;循环体
jmp LOC_WHILE
LOC_WHILEEND:

再来看下IDA生成的流程图,如下图所示:

对于for循环、do循环和while循环这3种循环而言,do循环的效率显然更高,而while循环相对来说比for循环效率更高些。

本文标题:逆向基础:循环结构分析

文章作者:water

发布时间:2018年07月24日 - 11:24:43

最后更新:2018年07月24日 - 13:07:39

原始链接:http://9cat.top/2018/07/24/逆向基础:循环结构分析/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

------ 本文结束------
分享
分享