文章目录
-
- [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 循环,控制流大致是:
-
初始化 :把循环变量写入栈上局部变量位置
mov [ebp-8], 0 -
无条件跳到"判断"位置 :
jmp 条件判断标签 -
循环体之后是"递增"代码块 :
index++在体之后单独一块:asmmov eax, [ebp-8] add eax, 1 mov [ebp-8], eax -
条件判断块:
asmcmp [ebp-8], 0Ah jge 退出标签 ; index >= 10 则退出 -
如果条件不满足退出,则顺序往下执行循环体 ,体结尾再
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
就可以高度怀疑这是一个 for 或 while 循环,其中:
- 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 模式 印记,这些在逆向时常见,用来判断编译设置,而不是循环本身,但对理解整体有帮助:
-
函数头部:
asmpush 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 调试器用的检查函数。
-
函数结尾:
asmadd 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++),可以归纳出以下 逆向特征:
-
控制流形态(前测试 for 循环)
- 初始化块(对某个局部变量赋初值)
- 紧跟一个无条件
jmp跳到条件判断处 - 在条件处:
cmp [局部变量], 上界常量- 满足某条件(大于等于、等于等)则
jcc跳出到循环外
- 若未跳出,则顺序执行循环体
- 循环体末尾
jmp回到递增块 - 递增块对同一个局部变量进行自增/自减,然后
jmp或落回条件判断处
-
变量使用模式
- 一个固定的
[ebp-偏移]:- 初始化一次;
- 在比较指令里和常量比较;
- 通过 add/sub/dec/inc 做线性更新;
- 在循环体中被多次读取并参与计算或当作参数。
- 一个固定的
-
条件比较与跳转
- 常见比较形式:
cmp [ebp-8], 0Ah - 常见跳转指令:
jl/jle/jg/jge/jb/ja等,用来控制"继续/退出"。 - 判断逻辑可译回:
while (index < 10)/for (index=0; index<10; index++)等。
- 常见比较形式:
-
在本例中的具体映射
mov [ebp-8], 0→int index = 0;cmp [ebp-8], 0Ah+jge→index < 10mov/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 循环逻辑。