For循环逆向特征

文章目录

    • [1. 先把与 for 循环直接相关的指令抽出来](#1. 先把与 for 循环直接相关的指令抽出来)
    • [2. 这一类 for 循环在逆向中的典型控制流特征](#2. 这一类 for 循环在逆向中的典型控制流特征)
      • [2.1 前测试循环(pre-test loop)的模式](#2.1 前测试循环(pre-test loop)的模式)
    • [3. 循环变量的存储特征(Debug 版)](#3. 循环变量的存储特征(Debug 版))
    • [4. 与编译器 / 调试模式相关的「环境特征」](#4. 与编译器 / 调试模式相关的「环境特征」)
    • [5. 结合本例,总结 for 循环的逆向特征](#5. 结合本例,总结 for 循环的逆向特征)
    • [6. 如果用一句话概括本例 for 循环的逆向特征](#6. 如果用一句话概括本例 for 循环的逆向特征)
cpp 复制代码
void func()
{
	for (int index = 0; index < 10; index++)
	{
		printf("%d", index);
	}
	return ;
}

1. 先把与 for 循环直接相关的指令抽出来

for (int index = 0; index < 10; index++) 对应的关键汇编是:

asm 复制代码
; 初始化部分:index = 0
00F51B86  mov         dword ptr [ebp-8],0  

; 跳到"条件判断"位置
00F51B8D  jmp         00F51B98h  

; 递增部分:index++
00F51B8F  mov         eax,dword ptr [ebp-8]  
00F51B92  add         eax,1  
00F51B95  mov         dword ptr [ebp-8],eax  

; 条件判断:index < 10 ?
00F51B98  cmp         dword ptr [ebp-8],0Ah  
00F51B9C  jge         00F51BB1h      ; index >= 10 则跳出循环

; 循环体:printf("%d", index);
00F51B9E  mov         eax,dword ptr [ebp-8]  
00F51BA1  push        eax  
00F51BA2  push        offset string "%d"  
00F51BA7  call        _printf  
00F51BAC  add         esp,8  

; 回到"递增"位置
00F51BAF  jmp         00F51B8Fh

其对应的 C 代码就是:

cpp 复制代码
for (int index = 0; index < 10; index++) {
    printf("%d", index);
}

2. 这一类 for 循环在逆向中的典型控制流特征

2.1 前测试循环(pre-test loop)的模式

调试版 MSVC 生成的这种 for 循环,控制流大致是:

  1. 初始化 :把循环变量写入栈上局部变量位置
    mov [ebp-8], 0

  2. 无条件跳到"判断"位置
    jmp 条件判断标签

  3. 循环体之后是"递增"代码块
    index++ 在体之后单独一块:

    asm 复制代码
    mov eax, [ebp-8]
    add eax, 1
    mov [ebp-8], eax
  4. 条件判断块

    asm 复制代码
    cmp [ebp-8], 0Ah
    jge 退出标签           ; index >= 10 则退出
  5. 如果条件不满足退出,则顺序往下执行循环体 ,体结尾再 jmp 回递增块。

用伪控制流写出来就是:

text 复制代码
init:
    index = 0
    jmp cond           ; 先不执行递增,直接去做第一次条件判断

inc:
    index = index + 1

cond:
    if (index >= 10) goto end

body:
    printf("%d", index);
    goto inc

end:
    ...

这个结构是"前测试循环 ":每次执行 body 前都先走条件判断(第一次迭代也通过那条 jmp 跳到判断位置)。

逆向特征总结:

  • 有一个 init 区块 :给某个 [ebp-偏移] 赋初值。
  • init 后面立即是一个 无条件 jmp,跳到后面某处。
  • 在 jmp 跳到的位置之前,有一段代码(inc),负责对同一个 [ebp-偏移] 做自增/自减。
  • 在 jmp 的目标位置,有:
    • cmp [ebp-偏移], 常数
    • 接着一个 条件跳转(如 jge、jl、jle、jg、jb、ja 等) 跳到循环外。
  • 条件跳转"失败"(即不跳出)时,顺序执行到循环体,循环体尾部有一个 jmp 回到递增位置。

只要你在逆向时看到:

asm 复制代码
mov [ebp-x], 初值
jmp cond

inc:
  ... 修改 [ebp-x] ...

cond:
  cmp [ebp-x], 常量
  jcc out      ; 某种条件跳出

body:
  ...
  jmp inc

就可以高度怀疑这是一个 forwhile 循环,其中:

  • init + inc + cond 更像 for
  • init + cond + body + inc 典型 for 展开结构

3. 循环变量的存储特征(Debug 版)

在这段汇编中,循环变量 index 存在这里:

asm 复制代码
[ebp-8]  ; 一个 4 字节的局部变量

典型特征:

  • 使用 MSVC Debug 编译时,局部变量几乎都放在栈上,通过 [ebp-偏移] 访问,而不是一直放在寄存器中。
  • 对同一个 [ebp-8]
    • 先有 mov [ebp-8], 0 ------ 初始化
    • 后面反复有读改写:mov eax,[ebp-8] / add eax,1 / mov [ebp-8],eax
    • 在比较和函数参数中也一直从 [ebp-8] 取值。

所以在逆向时,可以通过:

  • 查找某个固定的栈偏移 [ebp-8] 被重复使用:
    • 初始化一次
    • 比较一次(或多次)
    • 递增/递减一次
    • 多次作为函数参数或计算参与者

来锁定"这是一个循环变量"。


4. 与编译器 / 调试模式相关的「环境特征」

你贴出的完整函数还有一些明显的 MSVC Debug 模式 印记,这些在逆向时常见,用来判断编译设置,而不是循环本身,但对理解整体有帮助:

  • 函数头部:

    asm 复制代码
    push ebp
    mov  ebp, esp
    sub  esp, 0CCh
    push ebx
    push esi
    push edi
    lea  edi, [ebp-0Ch]
    mov  ecx, 3
    mov  eax, 0CCCCCCCCh
    rep  stos dword ptr es:[edi]
    call @__CheckForDebuggerJustMyCode@4

    特征:

    • sub esp, 0CCh:申请一大块栈空间,常见于 Debug。
    • mov eax,0CCCCCCCCh + rep stos dword ptr:用 0xCC 填充局部变量区域,这就是 VS 的"调试毒值",帮助发现未初始化使用。
    • @__CheckForDebuggerJustMyCode@4:VS 调试器用的检查函数。
  • 函数结尾:

    asm 复制代码
    add         esp,0CCh  
    cmp         ebp,esp  
    call        __RTC_CheckEsp  
    mov         esp,ebp  
    pop         ebp  
    ret

    特征:

    • __RTC_CheckEsp:运行时检查(Runtime Check),验证栈平衡。

这些说明:这是 MSVC + Debug 配置 + 开启运行时检查 下生成的未优化代码,因此:

  • 循环不会被优化成更紧凑的寄存器型实现;
  • 控制流非常"直白":看起来比 Release 版本"啰嗦",反而更有利于教学和肉眼逆向。

5. 结合本例,总结 for 循环的逆向特征

针对你这段代码中的 for (int index = 0; index < 10; index++),可以归纳出以下 逆向特征

  1. 控制流形态(前测试 for 循环)

    • 初始化块(对某个局部变量赋初值)
    • 紧跟一个无条件 jmp 跳到条件判断处
    • 在条件处:
      • cmp [局部变量], 上界常量
      • 满足某条件(大于等于、等于等)则 jcc 跳出到循环外
    • 若未跳出,则顺序执行循环体
    • 循环体末尾 jmp 回到递增块
    • 递增块对同一个局部变量进行自增/自减,然后 jmp 或落回条件判断处
  2. 变量使用模式

    • 一个固定的 [ebp-偏移]
      • 初始化一次;
      • 在比较指令里和常量比较;
      • 通过 add/sub/dec/inc 做线性更新;
      • 在循环体中被多次读取并参与计算或当作参数。
  3. 条件比较与跳转

    • 常见比较形式:cmp [ebp-8], 0Ah
    • 常见跳转指令:jl/jle/jg/jge/jb/ja 等,用来控制"继续/退出"。
    • 判断逻辑可译回:while (index < 10) / for (index=0; index<10; index++) 等。
  4. 在本例中的具体映射

    • mov [ebp-8], 0int index = 0;
    • cmp [ebp-8], 0Ah + jgeindex < 10
    • mov/add/mov(对 [ebp-8])→ index++
    • 中间的 _printf 调用部分就是循环体。

6. 如果用一句话概括本例 for 循环的逆向特征

在 MSVC Debug 版中,这个 for (int index = 0; index < 10; index++) 的逆向特征是:

以栈上局部变量 [ebp-8] 为循环计数器,通过「mov [ebp-8],0 初始化 → jmp 到条件块 → 在条件块 cmp [ebp-8], 0Ah / jge 控制退出 → 循环体后 jmp 回到一个对 [ebp-8]+1 的递增块」这样一个 init → jmp → cond(cmp+jcc) → body → jmp(inc) 的典型前测试 for 循环控制流结构,配合 Debug 模式的栈上局部变量和 0xCC 填充环境,很容易从汇编恢复出原始的 for 循环逻辑。

相关推荐
Darken031 个月前
基于C语言的学习---循环
学习·for循环·while循环·do-while循环·循环的嵌套
一雨方知深秋1 个月前
程序流程控制
java·for循环·while循环·if分支·switch分支·dowhile循环·嵌套循环
奔跑吧邓邓子3 个月前
【C语言实战(6)】解锁C语言循环密码:for循环实战探秘
c语言·实战·for循环
xiecoding.cn8 个月前
C语言求1到n的和(附带源码和解析)
c语言·for循环·求1到n的和·c语言求1到n的和·c语言for循环
howard200510 个月前
1.5.3 掌握Scala内建控制结构 - for循环
scala·for循环
liangbm31 年前
MATLAB系列04:循环结构
开发语言·数据结构·matlab·for循环·循环结构·工程基础·程序流程
二进制的声音1 年前
【三种循环结构】for循环、while循环和do-while循环
循环·for循环·while循环·do-while循环·三种循环区别
Jack_hrx2 年前
在 Java 中从 for 循环中移出元素的原理及解决方案
java·循环·for循环·数组下标越界
跟着飞哥学编程2 年前
【C++ 程序设计入门基础】- 第3节-循环结构01
开发语言·c++·for循环