# 循环结构

## 14.1 简单的例子

### 14.1.1 x86

C/C++循环操作是由for()、while()、do/while()命令发起的。

``````for(初始化; 条件; 每次迭代时执行的语句)
{
循环体;
}
``````

``````#include <stdio.h>
void f(int i)
{
printf ("f(%d)
", i);
};
int main()
{
int i;
for (i=2; i<10; i++)
f(i);
return 0;
};
``````

``````_i\$ = -4
_main     PROC
push    ebp
mov     ebp, esp
push    ecx
mov     DWORD PTR _i\$[ebp], 2       ; loop initialization
jmp     SHORT \$LN3@main
\$LN2@main:
mov     eax, DWORD PTR _i\$[ebp]     ; here is what we do after each iteration:
add     eax, 1                      ; add 1 to i value
mov     DWORD PTR _i\$[ebp], eax
\$LN3@main:
cmp     DWORD PTR _i\$[ebp], 10      ; this condition is checked *before* each iteration
jge     SHORT \$LN1@main             ; if i is biggest or equals to 10, let’s finish loop
mov     ecx, DWORD PTR _i\$[ebp]     ; loop body: call f(i)
push    ecx
call    _f
jmp     SHORT \$LN2@main             ; jump to loop begin
\$LN1@main:                                  ; loop end
xor     eax, eax
mov     esp, ebp
pop     ebp
ret     0
_main ENDP
``````

GCC 4.4.1生成的代码也基本相同，只有一些微妙的区别。

``````main        proc near           ; DATA XREF: _start+17
var_20      = dword ptr -20h
var_4       = dword ptr -4
push    ebp
mov     ebp, esp
and     esp, 0FFFFFFF0h
sub     esp, 20h
mov     [esp+20h+var_4], 2 ; i initializing
jmp     short loc_8048476
loc_8048465:
mov     eax, [esp+20h+var_4]
mov     [esp+20h+var_20], eax
call    f
add     [esp+20h+var_4], 1 ; i increment
loc_8048476:
cmp     [esp+20h+var_4], 9
jle     short loc_8048465 ; if i<=9, continue loop
mov     eax, 0
leave
retn
main        endp
``````

``````_main PROC
push esi
mov esi, 2
\$LL3@main:
push esi
call _f
inc esi
cmp esi, 10 ; 0000000aH
jl SHORT \$LL3@main
xor eax, eax
pop esi
ret 0
_main ENDP
``````

``````main    proc near
var_10  = dword ptr -10h
push    ebp
mov     ebp, esp
and     esp, 0FFFFFFF0h
sub     esp, 10h
mov     [esp+10h+var_10], 2
call    f
mov     [esp+10h+var_10], 3
call    f
mov     [esp+10h+var_10], 4
call    f
mov     [esp+10h+var_10], 5
call    f
mov     [esp+10h+var_10], 6
call    f
mov     [esp+10h+var_10], 7
call    f
mov     [esp+10h+var_10], 8
call    f
mov     [esp+10h+var_10], 9
call    f
xor     eax, eax
leave
retn
main endp
``````

GCC直接把我们的循环给分解成顺序结构了。

``````        public main
main    proc near
var_20  = dword ptr -20h
push    ebp
mov     ebp, esp
and     esp, 0FFFFFFF0h
push    ebx
mov     ebx, 2 ; i=2
sub     esp, 1Ch
nop     ; aligning label loc_80484D0 (loop body begin) by 16-byte border
loc_80484D0:
mov     [esp+20h+var_20], ebx ; pass i as first argument to f()
add     ebx, 1 ; i++
call    f
cmp     ebx, 64h ; i==100?
jnz     short loc_80484D0 ; if not, continue
xor     eax, eax ; return 0
pop     ebx
mov     esp, ebp
pop     ebp
retn
main endp
``````

### 14.1.2 OllyDbg

9是i的最后一个循环制，这也就是为什么JL在递增的最后不会触发，之后函数结束，如图14.3。

### 14.1.2 x86:跟踪

`tracer.exe -l:loops_2.exe bpx=loops_2.exe!0x00401026`

BPX的作用只是在对应地址上设置断点然后输出寄存器状态。

``````PID=12884|New process loops_2.exe
(0) loops_2.exe!0x401026
EAX=0x00a328c8 EBX=0x00000000 ECX=0x6f0f4714 EDX=0x00000000
ESI=0x00000002 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=PF ZF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000003 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF PF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000004 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF PF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000005 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000006 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF PF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000007 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000008 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF AF SF IF
(0) loops_2.exe!0x401026
EAX=0x00000005 EBX=0x00000000 ECX=0x6f0a5617 EDX=0x000ee188
ESI=0x00000009 EDI=0x00333378 EBP=0x0024fbfc ESP=0x0024fbb8
EIP=0x00331026
FLAGS=CF PF AF SF IF
PID=12884|Process loops_2.exe exited. ExitCode=0 (0x0)
``````

`tracer.exe -l:loops_2.exe bpf=loops_2.exe!0x00401020,trace:cc`

bpf的意思是在函数上设置断点。

``````0x401020 (.text+0x20), e= 1 [PUSH ESI] ESI=1
0x401021 (.text+0x21), e= 1 [MOV ESI, 2]
0x401026 (.text+0x26), e= 8 [PUSH ESI] ESI=2..9
0x401027 (.text+0x27), e= 8 [CALL 8D1000h] tracing nested maximum level (1) reached,
skipping this CALL 8D1000h=0x8d1000
0x40102c (.text+0x2c), e= 8 [INC ESI] ESI=2..9
0x40102d (.text+0x2d), e= 8 [ADD ESP, 4] ESP=0x38fcbc
0x401030 (.text+0x30), e= 8 [CMP ESI, 0Ah] ESI=3..0xa
0x401033 (.text+0x33), e= 8 [JL 8D1026h] SF=false,true OF=false
0x401035 (.text+0x35), e= 1 [XOR EAX, EAX]
0x401037 (.text+0x37), e= 1 [POP ESI]
0x401038 (.text+0x38), e= 1 [RETN] EAX=0
``````

### 14.1.4 ARM

#### 无优化 Keil + ARM模式

``````main
STMFD   SP!, {R4,LR}
MOV     R4, #2
B       loc_368
; ---------------------------------------------------------------------------

loc_35C                 ; CODE XREF: main+1C
MOV     R0, R4
BL      f
ADD     R4, R4, #1
loc_368                 ; CODE XREF: main+8
CMP     R4, #0xA
BLT     loc_35C
MOV     R0, #0
LDMFD   SP!, {R4,PC}
``````

``````MOV R4,#2初始化i。
MOV R0,R4 和 BL f 指令组成循环体，第一个指令为f（）准备参数，第二个用来调用它。
ADD R4,R4, #1 指令在每次迭代中为i加一。
CMP R4,#0xA 将i和0xA（10）比较，下一个指令BLT（Branch Less Than，分支小于）将在i<10时跳转。

``````

#### 优化后的 Keil + ARM模式

``````_main
PUSH    {R4,LR}
MOVS    R4, #2

loc_132             ; CODE XREF: _main+E
MOVS    R0, R4
BL      example7_f
ADDS    R4, R4, #1
CMP     R4, #0xA
BLT     loc_132
MOVS    R0, #0
POP     {R4,PC}
``````

#### 优化后的 Xcode（LLVM） + thumb-2 模式

``````_main
PUSH    {R4,R7,LR}
MOVW    R4, #0x1124 ; "%d
"
MOVS    R1, #2
MOVT.W  R4, #0
ADD     R7, SP, #4
MOV     R0, R4
BLX     _printf
MOV     R0, R4
MOVS    R1, #3
BLX     _printf
MOV     R0, R4
MOVS    R1, #4
BLX     _printf
MOV     R0, R4
MOVS    R1, #5
BLX     _printf
MOV     R0, R4
MOVS    R1, #6
BLX     _printf
MOV     R0, R4
MOVS    R1, #7
BLX     _printf
MOV     R0, R4
MOVS    R1, #8
BLX     _printf
MOV     R0, R4
MOVS    R1, #9
BLX     _printf
MOVS    R0, #0
POP     {R4,R7,PC}
``````

``````void f(int i)
{
// do something here
printf ("%d", i);
};
``````

## 14.1.6 更多的一些事情

``````for (i=0; i<total_entries_to_process; i++)
loop_body;
``````