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. 实战还原:从反汇编还原一个复杂循环结构