C&C++软件逆向技术

5、循环结构识别分析

1、课程目标

  1. 掌握for/while/do-while的汇编特征
  2. 学会识别循环的边界条件
  3. 理解循环优化技术
  4. 掌握嵌套循环的分析方法

2、名词解释

术语英文说明
循环体Loop Body循环执行的主体代码
循环条件Loop Condition继续循环的判断条件
循环展开Loop Unrolling复制循环体减少跳转次数
循环不变量Loop Invariant在循环中不变的值

3、使用工具

工具用途
IDA Pro控制流图识别循环
Ghidra反编译还原循环

4、技术原理

4.1、循环类型特征

类型判断位置特征
while循环前可能一次不执行
do-while循环后至少执行一次
for循环前有明确的初始/条件/增量

4.2、汇编模式

while循环:
    jmp check
  loop_body:
    ...
  check:
    cmp/test
    jcc loop_body

do-while循环:
  loop_body:
    ...
    cmp/test
    jcc loop_body

for循环与while类似,但有明确的计数器操作

5、代码实现

5.1、示例1:while循环

int SumWhile(int n) {
    int sum = 0;
    int i = 1;
    while (i <= n) {
        sum += i;
        i++;
    }
    return sum;
}

// 反汇编:
// xor eax, eax           ; sum = 0
// mov ecx, 1             ; i = 1
// jmp check
// loop_body:
// add eax, ecx           ; sum += i
// inc ecx                ; i++
// check:
// cmp ecx, [ebp+8]       ; i <= n?
// jle loop_body
// ret                    ; return sum

5.2、示例2:do-while循环

int SumDoWhile(int n) {
    int sum = 0;
    int i = 1;
    do {
        sum += i;
        i++;
    } while (i <= n);
    return sum;
}

// 反汇编:
// xor eax, eax           ; sum = 0
// mov ecx, 1             ; i = 1
// loop_body:
// add eax, ecx           ; sum += i
// inc ecx                ; i++
// cmp ecx, [ebp+8]       ; i <= n?
// jle loop_body          ; 条件在末尾
// ret

5.3、示例3:for循环

int SumFor(int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += i;
    }
    return sum;
}

// 反汇编:
// xor eax, eax           ; sum = 0
// xor ecx, ecx           ; i = 0
// jmp check
// loop_body:
// add eax, ecx           ; sum += i
// inc ecx                ; i++
// check:
// cmp ecx, [ebp+8]       ; i < n?
// jl loop_body
// ret

5.4、示例4:嵌套循环

void MatrixFill(int matrix[10][10]) {
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 10; j++) {
            matrix[i][j] = i * 10 + j;
        }
    }
}

// 反汇编特征:
// - 两层跳转结构
// - 外层计数器在内层循环前保存
// - 数组寻址: base + i*40 + j*4
//
// outer_loop:
//   xor edi, edi         ; j = 0
// inner_loop:
//   ; 计算地址并赋值
//   inc edi
//   cmp edi, 10
//   jl inner_loop
//   inc esi               ; i++
//   cmp esi, 10
//   jl outer_loop

5.5、示例5:循环展开优化

void ArrayCopy(int* dst, int* src, int count) {
    for (int i = 0; i < count; i++) {
        dst[i] = src[i];
    }
}

// 优化后可能展开为:
// loop:
//   mov eax, [esi]
//   mov [edi], eax
//   mov eax, [esi+4]      ; 展开第2次
//   mov [edi+4], eax
//   mov eax, [esi+8]      ; 展开第3次
//   mov [edi+8], eax
//   mov eax, [esi+12]     ; 展开第4次
//   mov [edi+12], eax
//   add esi, 16
//   add edi, 16
//   sub ecx, 4
//   jnz loop

5.6、示例6:字符串处理循环

int StringLength(const char* s) {
    int len = 0;
    while (*s != '\0') {
        len++;
        s++;
    }
    return len;
}

// 反汇编:
// mov esi, [ebp+8]       ; s
// xor eax, eax           ; len = 0
// loop:
// movzx ecx, byte [esi]  ; *s
// test cl, cl            ; == 0?
// jz done
// inc eax                ; len++
// inc esi                ; s++
// jmp loop
// done:
// ret

5.7、示例7:识别循环类型

/*
识别技巧:

1. while循环:
   - 循环开始有jmp跳到条件判断
   - 判断在循环体后面

2. do-while循环:
   - 没有初始跳转
   - 判断在循环体末尾
   - 条件跳转回循环开始

3. for循环:
   - 有明显的初始化(xor reg, reg 或 mov reg, imm)
   - 有明显的增量操作(inc/add)
   - 通常有比较和条件跳转

4. 无限循环:
   - 无条件判断
   - jmp跳回循环开始
*/

// IDA控制流图中的循环:
// - 显示为回边(back edge)
// - 有从下向上的箭头

6、课后作业

  1. 基础练习:编写三种循环,观察编译结果差异
  2. 嵌套循环:分析一个双层循环的反汇编
  3. 优化对比:对比Debug和Release版本的循环
  4. 实战还原:从反汇编还原一个复杂循环结构