引言
曾经,代码对你而言只是黑盒子里的魔法。 你习惯于告诉计算机'要什么'(Result),却从未真正关心计算机'怎么做'(Process)。
但在这几天里,你亲手拆掉了那个黑盒子。 当你第一次在 VC++ 6.0 里敲下 __naked 时,你实际上是签订了一份契约: 放弃自动化的安逸,换取对底层绝对的控制权。
你经历了迷茫,在 EBP 和 ESP 的拉扯中跌倒; 你经历了挣扎,在 JNZ 和 JZ 的反向逻辑中迷失。 但这正是破茧前的阵痛。
你不再是一个单纯的代码编写者,你正在变成一个机器语言的翻译官。 你开始听懂 CPU 的耳语,看懂内存的纹理。
Level_1
具体代码
c
void check_password(int a) {
// 假设 a 是用户输入的数字
// 我们判断 a 是否等于 666
if (a == 666) {
// 如果等于,就把 b 设为 1 (代表成功)
int b = 1;
}
// 如果不等于,直接结束,什么都不做
}
返汇编代码
c
; --- 序言 (省略) ---
; 假设 a 在 [ebp+8]
; 假设 b 在 [ebp-4]
; === [核心逻辑开始] ===
; 1. 取数
mov eax, dword ptr [ebp+8] ; 把参数 a 放入 EAX
; 2. 比较 (Compare)
cmp eax, 29Ah ; 0x29A 就是十进制的 666
; 此时 CPU 会计算 EAX - 666,并设置标志位
; 3. 决策 (关键跳转!)
jne SHORT_END_IF ; 【重点】如果不相等 (Jump if Not Equal),就跳走!
; === [IF 内部] (如果没有跳走,说明相等) ===
mov dword ptr [ebp-4], 1 ; b = 1
; === [IF 结束标签] ===
SHORT_END_IF:
; 4. 后续代码
; ...
Level_2
具体代码
c
void check_score(int score) {
// [ebp+8] 是 score
// [ebp-4] 是 result
if (score >= 60) {
int result = 1;
} else {
int result = 0;
}
}
反汇编代码
c
; =============================================================
; 函数名: check_score
; 参数: score (int) -> [ebp + 8]
; 局部变量: result (int) -> [ebp - 4]
; =============================================================
; --- [1. 序言 Prologue] (你最熟悉的部分) ---
push ebp ; 保存旧栈底
mov ebp, esp ; 建立新栈底
sub esp, 44h ; 开辟栈空间 (多开一点防止溢出)
push ebx
push esi
push edi ; 保护寄存器
lea edi, dword ptr [ebp-44h]; 准备填充区域
mov ecx, 11h ; 填充次数 (44h / 4 = 11h)
mov eax, 0CCCCCCCCh ; Debug 断点码
rep stos dword ptr es:[edi] ; 填充栈空间
; =============================================================
; --- [2. 核心逻辑 Core Logic] (重点观摩这里!) ---
; =============================================================
; --- A. 比较阶段 ---
mov eax, dword ptr [ebp+8] ; 1. 取出 score 放到 eax
cmp eax, 3Ch ; 2. 拿 score 和 60 (0x3C) 比较
; --- B. 分支判断 (第一跳) ---
; C语言: if (score >= 60)
; 汇编: 如果小于 (Less),就跳去 ELSE 块
jl SHORT_ELSE_BLOCK ; <--- 【关键跳跃 1】 不满足条件,踢走!
; --- C. IF 代码块 (及格处理) ---
; 能走到这里,说明 >= 60 成立
mov dword ptr [ebp-4], 1 ; result = 1
; --- D. 强制跳出 (第二跳) ---
; 这一步非常容易忘!做完 IF 的事,必须跳过 ELSE,直奔终点
jmp SHORT_END_IF ; <--- 【关键跳跃 2】 完事了,跳去终点!
; --- E. ELSE 代码块 (挂科处理) ---
SHORT_ELSE_BLOCK: ; <--- 这里是 JL 跳过来的落脚点
mov dword ptr [ebp-4], 0 ; result = 0
; --- F. 汇合点 ---
SHORT_END_IF: ; <--- 这里是 JMP 跳过来的落脚点
; 此时,无论走哪条路,都汇聚到这里了
; =============================================================
; --- [3. 结语 Epilogue] (你最熟悉的部分) ---
; =============================================================
pop edi
pop esi
pop ebx ; 恢复寄存器
add esp, 44h ; 回收栈空间 (简单写法)
; 或者用 mov esp, ebp (Release写法)
cmp ebp, esp ; (可选) 检查堆栈平衡
call __chkesp ; (可选) 报错机制
mov esp, ebp ; 再次确保 ESP 归位
pop ebp ; 恢复旧栈底
ret ; 返回 (由调用者平栈)
Level_3
具体代码
c
void check_grade(int score) {
// [ebp+8] 是 score
// [ebp-4] 是 result (1=优秀, 2=及格, 0=挂科)
// 第一关:优秀
if (score >= 90) {
int result = 1;
}
// 第二关:及格 (能走到这,说明肯定小于 90)
else if (score >= 60) {
int result = 2;
}
// 最后一关:挂科 (能走到这,说明肯定小于 60)
else {
int result = 0;
}
}
反汇编代码
c
; =============================================================
; 函数名: check_grade
; 核心逻辑: 层层筛选 (Filter Logic)
; =============================================================
; --- [1. 序言 Prologue] ---
push ebp
mov ebp, esp
sub esp, 44h ; 开辟栈空间
push ebx
push esi
push edi ; 保护现场
lea edi, dword ptr [ebp-44h]
mov ecx, 11h
mov eax, 0CCCCCCCCh
rep stos dword ptr es:[edi] ; Debug 填充
; =============================================================
; --- [2. 核心逻辑 Core Logic] (重点!) ---
; =============================================================
; --- 第一关:检查是否优秀 (>= 90) ---
mov eax, dword ptr [ebp+8] ; 取出 score
cmp eax, 5Ah ; 比较 score 和 90 (0x5A)
; [关键动作 1]:如果不满足优秀,踢给下一关 (Check 60)
jl SHORT_CHECK_60 ; Jump if Less (小于 90 则跳去检查 60)
; --- 优秀代码块 (IF Body) ---
; 能走到这里,说明 >= 90 成立
mov dword ptr [ebp-4], 1 ; result = 1
; [关键动作 2]:完事了,必须强制离场!不能往下走了!
jmp SHORT_END_IF ; 强制跳到终点
; -------------------------------------------------------------
; --- 第二关:检查是否及格 (>= 60) ---
SHORT_CHECK_60: ; <--- 第一关 JL 跳到了这里
mov eax, dword ptr [ebp+8] ; (可选) 再次取出 score,编译器有时会省掉这步
cmp eax, 3Ch ; 比较 score 和 60 (0x3C)
; [关键动作 3]:如果不满足及格,踢给 ELSE
jl SHORT_ELSE_BLOCK ; Jump if Less (小于 60 则跳去 ELSE)
; --- 及格代码块 (ELSE IF Body) ---
; 能走到这里,说明 >= 60 成立 (且 < 90)
mov dword ptr [ebp-4], 2 ; result = 2
; [关键动作 4]:完事了,必须强制离场!
jmp SHORT_END_IF ; 强制跳到终点
; -------------------------------------------------------------
; --- 最后一关:挂科 (ELSE) ---
SHORT_ELSE_BLOCK: ; <--- 第二关 JL 跳到了这里
; 能走到这里,说明前面都失败了 (< 60)
mov dword ptr [ebp-4], 0 ; result = 0
; -------------------------------------------------------------
; --- [汇合点] ---
SHORT_END_IF:
; 所有分支最后都汇聚于此
; =============================================================
; --- [3. 结语 Epilogue] ---
; =============================================================
pop edi
pop esi
pop ebx
add esp, 44h
cmp ebp, esp
call __chkesp
mov esp, ebp
pop ebp
ret ; 由调用者平栈
Level_4
具体代码
c
void check_login(int user_id, int password) {
// 参数布局:
// [ebp+8] -> user_id (第一个参数)
// [ebp+0xC] -> password (第二个参数,注意是 +12 了!)
// 第一道门:账号对不对?
if (user_id == 888) {
// 第二道门:密码对不对?
if (password == 1234) {
int status = 1; // 登录成功
}
}
// 失败集合点
}
反汇编代码
c
; --- 调用者 ---
push 1234 ; 参数2: password
push 888 ; 参数1: user_id
call check_login
add esp, 8 ; 平栈 (2个参数 * 4)
; --- 被调用函数 ---
check_login:
; 1. 序言
push ebp
mov ebp, esp
sub esp, 4 ; 申请局部变量
push ebx ; 【修正】必须是 PUSH!
push esi
push edi
; (Debug填充代码省略,你写得很对)
; 2. 核心逻辑 (双层安检)
; 第一关:账号
mov eax, dword ptr [ebp + 8] ; 取 user_id
cmp eax, 378h ; 888
jne SHORT_END_IF ; 不对直接踢
; 第二关:密码
mov eax, dword ptr [ebp + 0Ch] ; 【建议】取 password (加 0前缀)
cmp eax, 4D2h ; 1234
jne SHORT_END_IF ; 不对直接踢
; 3. 核心禁区 (VIP室)
mov dword ptr [ebp - 4], 1 ; status = 1
; 4. 统一出口
SHORT_END_IF:
; 5. 结语
pop edi ; 【这里是对的】POP 恢复
pop esi
pop ebx
mov esp, ebp
pop ebp
ret
Level_5
具体代码
c
void check_security(int a, int b) {
// 参数布局:[ebp+8] 是 a, [ebp+0Ch] 是 b
// 逻辑:只有 a==1 并且 b==2 时,才通过
if (a == 1 && b == 2) {
int success = 1;
}
// 失败直接结束
}
反汇编代码
c
; =============================================================
; 函数名: check_security
; =============================================================
; --- [1. 检查第一个条件 (a == 1)] ---
mov eax, dword ptr [ebp+8] ; 取出 a
cmp eax, 1 ; 比较 1
; 【关键点 1】 短路跳出
; 如果 a 不等于 1,直接跳到最后!(根本不看 b)
jne SHORT_END_IF ; Jump Not Equal -> End
; =============================================================
; --- [2. 检查第二个条件 (b == 2)] ---
; =============================================================
; 能走到这里,说明 a 肯定是 1 (上一步没跳走)
mov eax, dword ptr [ebp+0Ch] ; 取出 b
cmp eax, 2 ; 比较 2
; 【关键点 2】 第二关跳出
; 如果 b 不等于 2,也跳到最后!
jne SHORT_END_IF ; Jump Not Equal -> End
; =============================================================
; --- [3. 成功区域 (Success)] ---
; =============================================================
; 能走到这里,说明两个 JNE 都没触发,也就是说 a==1 且 b==2
mov dword ptr [ebp-4], 1 ; success = 1
; =============================================================
; --- [4. 统一终点] ---
; =============================================================
SHORT_END_IF:
; 无论是 a 挂了,还是 b 挂了,都来这
Level_6
具体代码
c
void check_vip(int a, int b) {
// [ebp+8] -> a
// [ebp+0Ch]-> b
// 逻辑:只要 a是1,或者 b是2,就是 VIP
if (a == 1 || b == 2) {
int vip = 1;
}
// 结束
}
反汇编代码
c
; =============================================================
; 函数名: check_vip
; =============================================================
; --- [1. 检查第一个条件 (a == 1)] ---
mov eax, dword ptr [ebp+8] ; 取出 a
cmp eax, 1 ; 比较 1
; 🚨【关键变化点】 救人逻辑!
; 之前的 && 是"不相等就踢走"。
; 这里的 || 是"相等就赶紧请进去!" (短路成功)
je SHORT_VIP_AREA ; Jump if Equal -> 直接去 VIP 区
; =============================================================
; --- [2. 检查第二个条件 (b == 2)] ---
; =============================================================
; 能走到这里,说明 a 肯定不是 1 (否则上面就跳走了)
; 这是最后的机会了!
mov eax, dword ptr [ebp+0Ch] ; 取出 b
cmp eax, 2 ; 比较 2
; 🚨【回归旧逻辑】 踢人逻辑
; 如果 b 也不等于 2,那就彻底没戏了,踢走!
jne SHORT_END_IF ; Jump Not Equal -> 滚蛋
; =============================================================
; --- [3. VIP 核心区] (成功区) ---
; =============================================================
SHORT_VIP_AREA: ; <--- JE 跳到这里,或者 JNE 没踢走滑落到这里
mov dword ptr [ebp-4], 1 ; vip = 1
; =============================================================
; --- [4. 终点] ---
; =============================================================
SHORT_END_IF:
; 结束
Level_7
具体代码
c
// 函数功能:判断玩家是否允许进入"GM隐藏地图"
// 返回值:1 (允许), 0 (拒绝)
// 参数顺序:id, level, is_admin, has_pass
int check_gm_access(int id, int level, int is_admin, int has_pass) {
// === 第一阶段:特权通道 (逻辑或 ||) ===
// 只要 ID 是 999 (超级账号) 或者 是管理员 (is_admin==1)
// 直接放行,不用看等级!
if (id == 999 || is_admin == 1) {
return 1;
}
// === 第二阶段:普通通道 (逻辑与 && + 区间) ===
// 如果上面没过,必须同时满足下面所有条件:
// 1. 等级必须 >= 60
// 2. 等级必须 <= 100
// 3. 必须持有通行证 (has_pass == 1)
if (level >= 60 && level <= 100) {
if (has_pass == 1) {
return 1;
}
}
// === 失败 ===
return 0;
//}
反汇编代码
c
__declspec(naked) int func(int id, int level, int is_admin, int has_pass)
{
_asm{
//函数头
push ebp
mov ebp, esp
sub esp, 0x40
push ebx
push esi
push edi
lea edi, dword ptr ds:[ebp - 0x40]
mov ecx, 0x10
mov eax, 0xcccccccc
rep stosd
//核心代码
//第一关用id以及admin判断是否正确
//先判断id是否正确,如果正确,直接执行核心代码,并且跳转至集合点
//取出id
mov eax, dword ptr ds:[ebp + 0x8]
//进行比较
cmp eax, 0x3E7
//如果正确执行核心代码
JE SHORT_id_admin
//如果不正确,那就继续执行判断第二个条件,如果条件正确执行核心代码,如果条件不正确则跳转到第二个if
//取出admin
mov eax, dword ptr ds:[ebp + 0x10]
//比较
cmp eax, 0x1
//如果不符合则跳转到第二个if
JNE SHORT_level_pass
//第一关集合点
SHORT_id_admin:
mov eax, 0x1
//进行二次跳转
jmp SHORT_END_IF
//第二关
//if (level >= 60 && level <= 100) {
//if (has_pass == 1) {
//return 1;
SHORt_level_pass:
//首先进取出level
mov eax, dword ptr ds:[ebp + 0xC]
//进行比较
cmp eax , 0x3C
//条件是levle >= 60,那么也就是说 < 60 跳走
jl SHORT_RET_0
//继续比较第二个条件
//重新取level
mov eax, dword ptr ds:[ebp + 0xC]
//进行比较
cmp eax, 0x64
//level <= 100,那么也就是说大于100跳走
jg SHORT_RET_0
//比较第三个条件
//取出has_pass
mov eax, dword ptr ds:[ebp + 0x14]
//比较
cmp eax, 0x1
//如果不等于1跳转
JNE SHORT_RET_0
//执行核心代码
mov eax, 0x1
//跳转集合点
jmp SHORT_END_IF
SHORT_RET_0:
xor eax, eax
jmp SHORT_END_IF
//最后集合点
SHORT_END_IF:
//函数尾
pop edi
pop esi
pop ebx
add esp, 0x40
mov esp, ebp
pop ebp
ret
}
}
int main(int argc, char* argv[])
{
int result = 0;
// 测试用例:
// id = 100 (普通), level = 80 (合格), is_admin = 0 (非), has_pass = 1 (有)
// 预期结果:Success (1)
_asm
{
// 参数压栈 (从右往左!)
push 1 // has_pass, ebp + 0x14
push 0 // is_admin, ebp + 0x10
push 80 // level, ebp + 0xc
push 100 // id, ebp + 0x8
call func //存储下一条指令地址,ebp + 0x4
add esp, 0x10
mov result, eax
}
if (result == 1) {
printf("Access GRANTED! [Success]\n");
} else {
printf("Access DENIED! [Fail]\n");
}
return 0;
}
Level_8
具体代码
c
// 函数原型
// flags: 状态标志位 (包含多个状态)
// base_dmg: 基础攻击力
int calculate_damage(int flags, int base_dmg) {
// [掩码定义]
// 0x01 (0001) = IS_ALIVE (必须存活)
// 0x02 (0010) = IS_INVINCIBLE (无敌)
// 0x04 (0100) = HAS_BUFF (攻击翻倍)
// 0x08 (1000) = IS_WEAK (虚弱)
// 1. 【生存检查】
// 检查 Bit 0: 如果 flags 的第1位是 0 (死了),直接返回 0 伤害。
// C语言写法: if ((flags & 0x01) == 0) return 0;
if ( (flags & 1) == 0 ) {
return 0;
}
// 2. 【无敌检查】
// 检查 Bit 1: 如果 flags 的第2位是 1 (开启了无敌),返回 9999 (秒杀)。
// C语言写法: if (flags & 0x02) return 9999;
if ( flags & 2 ) {
return 9999;
}
// 3. 【虚弱检查】
// 检查 Bit 3: 如果 flags 的第4位是 1 (虚弱),伤害减半 (除以2)。
// C语言写法: if (flags & 0x08) base_dmg = base_dmg / 2;
// 提示:除以2可以用右移 SHR eax, 1
if ( flags & 8 ) {
base_dmg = base_dmg / 2;
}
// 4. 【Buff 检查】
// 检查 Bit 2: 如果 flags 的第3位是 1 (有Buff),伤害翻倍。
// C语言写法: if (flags & 0x04) base_dmg = base_dmg * 2;
// 提示:乘以2可以用左移 SHL eax, 1
if ( flags & 4 ) {
base_dmg = base_dmg * 2;
}
// 5. 返回最终计算的伤害
return base_dmg;
}
反汇编代码
c
__declspec(naked) int func(int flags, int base_dmg)
{
_asm{
//函数头
push ebp
mov ebp, esp
sub esp, 0x40
push ebx
push esi
push edi
lea edi, dword ptr ds:[ebp - 0x40]
mov ecx, 0x10
mov eax, 0xcccccccc
rep stosd
//核心代码
// [掩码定义]
// 0x01 (0001) = IS_ALIVE (必须存活)
// 0x02 (0010) = IS_INVINCIBLE (无敌)
// 0x04 (0100) = HAS_BUFF (攻击翻倍)
// 0x08 (1000) = IS_WEAK (虚弱)
// 1. 【生存检查】
// 检查 Bit 0: 如果 flags 的第1位是 0 (死了),直接返回 0 伤害。
// C语言写法: if ((flags & 0x01) == 0) return 0;
//if ( (flags & 1) == 0 ) {
//return 0;
//}
//第一关
//现在情况是如果不成立则跳转,如果需要满足不成立则要判断flag & 1是否为1,判断1的条件可以用JNZ
test dword ptr ds:[ebp + 0x8], 1
//满足条件则跳转到if_2继续判断
jNZ SHORT_IF_2
//不满足条件,则执行核心代码,然后跳转集合点
xor eax, eax
jmp SHORT_EPILOGUE
//第二关
// 2. 【无敌检查】
// 检查 Bit 1: 如果 flags 的第2位是 1 (开启了无敌),返回 9999 (秒杀)。
// C语言写法: if (flags & 0x02) return 9999;
//if ( flags & 2 ) {
//return 9999;
//}
SHORT_IF_2:
test DWORD ptr ds:[ebp + 0x8], 0x2
//现在情况是如果结果为1则返回,不为1则跳转
//那么我们以跳转为主,如果JZ成立则说明不相等,需要跳转
JZ SHORT_IF_3
//留下来说明为1,执行代码
mov eax, 0x270F
jmp SHORT_EPILOGUE
//第三关
// 3. 【虚弱检查】
// 检查 Bit 3: 如果 flags 的第4位是 1 (虚弱),伤害减半 (除以2)。
// C语言写法: if (flags & 0x08) base_dmg = base_dmg / 2;
// 提示:除以2可以用右移 SHR eax, 1
//if ( flags & 8 ) {
//base_dmg = base_dmg / 2;
//}
SHORT_IF_3:
//判断第四位的值是什么
test DWORD ptr ds:[ebp + 0x8], 0x8
//如果满足条件则执行核心代码,如果不满足则跳转
//满足条件的基础是非0,那么不满足条件则是取反,jz
jz SHORt_if_4
//相干,留下来执行核心代码
shr dword ptr ds:[ebp + 0xC], 1
//跳转第四关
jmp SHORT_IF_4
//第四关
// 4. 【Buff 检查】
// 检查 Bit 2: 如果 flags 的第3位是 1 (有Buff),伤害翻倍。
// C语言写法: if (flags & 0x04) base_dmg = base_dmg * 2;
// 提示:乘以2可以用左移 SHL eax, 1
//if ( flags & 4 ) {
//base_dmg = base_dmg * 2;
//}
SHORT_IF_4:
//判断自己有没有buff
test dword ptr ds:[ebp + 0x8],0x4
//不相干踢走
jz SHORT_END_IF
//相干,请留下
shl dword ptr ds:[ebp + 0xC], 1
//跳转集合点
jmp SHORT_END_IF
//集合点
SHORT_END_IF:
mov eax, dword ptr ds:[ebp + 0xC]
SHORT_EPILOGUE:
//函数尾
pop edi
pop esi
pop ebx
add esp, 0x40
mov esp, ebp
pop ebp
ret
}
}
int main(int argc, char* argv[])
{
int result;
int flags = 9;
int base_dmg = 100;
_asm
{
// 参数压栈 (从右往左!)
push base_dmg //攻击 ebp +0xC
push flags//状态 ebp + 0x8
call func
add esp, 0x8
mov result, eax
}
// 应该输出 50 (100 / 2)
printf("Result: %d\n", result);
getchar(); // <--- 加上这一句!等待你按一下回车
return 0;
}
Level_9
具体代码
c
int fun_00401019()
{
int var_4, var_8, var_c, var_10, var_14, var_18;
printf("请输入四个数字:");
scanf("%d %d %d %d", &var_4, &var_8, &var_c, &var_10);
var_14 = var_18 = var_4;
if (var_8 < var_14)
{
var_14 = var_8;
}
else if (var_8 > var_18)
{
var_18 = var_8;
}
if (var_c < var_14)
{
var_14 = var_c;
}
else if (var_c > var_18)
{
var_18 = var_c;
}
if (var_10 < var_14)
{
var_14 = var_10;
}
else if (var_10 > var_18)
{
var_18 = var_10;
}
printf("max = %d <-> min = %d\n", var_18, var_14);
return 0;
}
int main()
{
_asm {
call fun_00401019
}
return 0;
}
反汇编代码
c
//先判断栈帧中存在多少参数:
//[ebp - 0x4]
//[ebp - 0x8]
//[ebp - 0xC]
//[ebp - 0x10]
//[ebp - 0x14]
//[ebp - 0x18]
============标签:SHORT_IF_1============
00401372 |. 8B4D FC mov ecx,dword ptr ss:[ebp-0x4] ; ecx = [ebp - 0x4]
<C语言逆?11>>|. 894D E8 mov dword ptr ss:[ebp-0x18],ecx ; [ebp - 0x18] = ecx, -> [ebp-0x18] = [ebp - 0x4]
需注意,0x18获取了0x4的值
00401378 |. 8B55 E8 mov edx,dword ptr ss:[ebp-0x18] ; edx = [ebp - 0x18]
0040137B |. 8955 EC mov dword ptr ss:[ebp-0x14],edx ; [ebp - 0x14] = [ebp - 0x18]
这时候,014也获取了0x18的值
0040137E |. 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; eax = [ebp - 0x8]
00401381 |. 3B45 EC cmp eax,dword ptr ss:[ebp-0x14] ; [ebp - 0x8] - [ebp - 0x14] ,IF判断
00401384 |. 7D 08 jge short C语言逆?0040138E ; >=则不成立,跳转SHORT_ELSE_IF_1
//执行SHORT_IF_1 <--> 成立的代码 ; < 则留下来,执行代码
00401386 |. 8B4D F8 mov ecx,dword ptr ss:[ebp-0x8] ; 成立请执行:ecx = [ebp - 0x8]
00401389 |. 894D EC mov dword ptr ss:[ebp-0x14],ecx ; [ebp - 0x14] = [ebp - 0x8]
这段代码可以知道,如果0x14大,则把0x8赋值给它,说明0x14在获取两数之间min
//存在跳转需要注意深度
0040138C |. EB 0E jmp short C语言逆?0040139C ; 跳转到判断特征外面
//执行SHORT_IF_1 <--> 不成立的代码,需要判断结构
//下面又存在判断cmp指令,那么下面存在两个问题:
//到底是线性if结构,还是else if()结构,但不可能是else()结构
//这里,我抓到了一个核心特征:
//如果是线性if()结构,说明第一个if()结束要跳到第二个if,
//但这里并没有,跳出了一个距离宽度,但是中间的代码又存在判断
//此时得到结论是else if()
============标签:SHORT_ELSE_IF_1============
0040138E |> 8B55 F8 mov edx,dword ptr ss:[ebp-0x8] ; edx = [ebp - 0x8]
00401391 |. 3B55 E8 cmp edx,dword ptr ss:[ebp-0x18] ; [ebp - 0x8] - [ebp - 0x18]
00401394 |. 7E 06 jle short C语言逆?0040139C ; <= 则跳转
//成立,留下来执行
00401396 |. 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ; eax = [ebp - 0x8]
00401399 |. 8945 E8 mov dword ptr ss:[ebp-0x18],eax ; [ebp - 0x18] = [ebp - 0x8]
;这段else执行的代码可以发现:[ebp - 0x18]在获取max
//到此位置,第一个结构都是在两束之间,一个在获取min,一个在获取max
//可以判断,0x14以及0x18是获取四个数之间的max以及Min
//总结上面:SHORT_IF_1执行完跳转到这里
//以及SHORT_ELSE_IF_1执行完走到这里和不成立跳转到这里,说明第一个结构结束
//存在cmp指令 -> IF核心特征
-------------------------------------分割线-------------------------------------
============标签:SHORT_IF_2============
0040139C |> 8B4D F4 mov ecx,dword ptr ss:[ebp-0xC] ; SHORT_IF_2,ecx = [ebp - 0xC]
0040139F |. 3B4D EC cmp ecx,dword ptr ss:[ebp-0x14] ; [ebp - 0xC] - [ebp - 0x14]
004013A2 |. 7D 08 jge short C语言逆?004013AC ; >=则不成立,跳转
//成立需要执行代码 ; <,请执行;上面代码又在说明如果14更小则继续获取min
004013A4 |. 8B55 F4 mov edx,dword ptr ss:[ebp-0xC]
004013A7 |. 8955 EC mov dword ptr ss:[ebp-0x14],edx ; [ebp - 0x14] = [ebp - 0xC]
004013AA |. EB 0E jmp short C语言逆?004013BA
//不成立跳转的地方,存在cmp需要继续判断是线性,还是else if(),还是嵌套
//从代码深度来看,不存在嵌套可能性,因为第一个if在执行代码深度不够
//判断是否可能是双重if,但我进行了反对,因为if_2跳转的和下面判断执行完后的位置一样
//总结:以下也是else if()结构
============标签:SHORT_ELSE_IF_2============
004013AC |> 8B45 F4 mov eax,dword ptr ss:[ebp-0xC] ; eax = [ebp - 0xC]
004013AF |. 3B45 E8 cmp eax,dword ptr ss:[ebp-0x18] ; [ebp - 0xC] - [ebp - 0x18]
004013B2 |. 7E 06 jle short C语言逆?004013BA ;<=,跳转
//成立所需要执行的代码
004013B4 |. 8B4D F4 mov ecx,dword ptr ss:[ebp-0xC] ; >,请执行;这段代码在为0x18获取最大值
004013B7 |. 894D E8 mov dword ptr ss:[ebp-0x18],ecx ; [ebp - 0x18] = [ebp - 0xC]
//不成立跳转的地方,与IF_2跳转吻合,判断第二个结构结束
//下面又出现了cmp指令,猜测应该是第三次if,继续判断
-------------------------------------分割线-------------------------------------
============标签:SHORT_IF_3============
004013BA |> 8B55 F0 mov edx,dword ptr ss:[ebp-0x10] ; edx = [ebp - 0x10]
004013BD |. 3B55 EC cmp edx,dword ptr ss:[ebp-0x14] ; [ebp - 0x10] - [ebp - 0x14]
004013C0 |. 7D 08 jge short C语言逆?004013CA ; >= ,则跳转
//jcc指令下面紧跟着都是成立执行代码,依次注释 ; < ,请执行;这里说明继续在比较,为0x14获取两数之间min
004013C2 |. 8B45 F0 mov eax,dword ptr ss:[ebp-0x10]
004013C5 |. 8945 EC mov dword ptr ss:[ebp-0x14],eax ; [ebp - 0x14] = [ebp - 10]
004013C8 |. EB 0E jmp short C语言逆?004013D8
//不成立跳转的地方,依旧老问题,代码深度不够,排除嵌套if可能性
//存在判断,排除else()可能性
//第一个if执行完跳转的位置与接下来的跳转位置一致,说明是else_if()结构
============标签:SHORT_ELSE_IF_3============
004013CA |> 8B4D F0 mov ecx,dword ptr ss:[ebp-0x10]
004013CD |. 3B4D E8 cmp ecx,dword ptr ss:[ebp-0x18] ; [ebp - 0x10] - [ebp - 0x18]
004013D0 |. 7E 06 jle short C语言逆?004013D8 ; <= ,则跳转
//成立,留下执行
004013D2 |. 8B55 F0 mov edx,dword ptr ss:[ebp-0x10]
004013D5 |. 8955 E8 mov dword ptr ss:[ebp-0x18],edx ; [ebp - 0x18] = [ebp - 0x10]
-------------------------------------分割线-------------------------------------
//SHORT_IF_3执行完代码跳转的位置进行标注
004013D8 |> 8B45 E8 mov eax,dword ptr ss:[ebp-0x18]
004013DB |. 50 push eax
004013DC |. 8B4D EC mov ecx,dword ptr ss:[ebp-0x14]
004013DF |. 51 push ecx ; C语言逆?<ModuleEntryPoint>
004013E0 |. 68 DC504200 push C语言逆?004250DC ; ASCII "min= %d max=%d\n"
004013E5 |. E8 06010000 call C语言逆?printfgvdbgind_blockeressges
004013EA |. 83C4 0C add esp,0xC
004013ED |. 33C0 xor eax,eax
Level10
具体代码
c
void fun_00401005()
{
char c;
printf("请输入字符:");
scanf("%c", &c);
if (0x41 <= c && c <= 0x5A)
{
printf("这是大写!!!\n");
}
else if (0x61 <= c && c <= 0x7A)
{
printf("这是小写!!!\n");
}
else if (0x30 <= c && c <= 0x39)
{
printf("这是数字!!!\n");
}
else
{
printf("特殊符号!!!\n");
}
}
反汇编代码
c
__declspec(naked) void Level3()
{
_asm
{
//函数头
push ebp
mov ebp, esp
sub esp, 0x40
push ebx
push esi
push edi
lea edi, dword ptr ds:[ebp - 0x40]
mov ecx, 0x10
mov eax, 0xcccccccc
rep stosd
//核心代码
//[ebp - 0x4] -> 'c'
mov byte ptr ds:[ebp - 0x4], 0x31
SHORT_IF_1:
//if([ebp - 0x4] >= 0x41 && [ebp - 0x4] <= 0x5A)
mov al, byte ptr ds:[ebp - 0x4]
cmp al, 0x41
jl SHORT_ELSE_IF_2
//继续往下执行,且重新提取[ebp - 0x4]
mov dl, byte ptr ds:[ebp - 0x4]
cmp dl, 0x5A
jg SHORT_ELSE_IF_2
//执行代码
mov eax, 0x1
jmp SHORT_END_IF
SHORT_ELSE_IF_2:
//if(eax >= 0x61 && eax <= 0x7A)
//提取[ebp - 0x4]
mov al, byte ptr ds : [ebp - 0x4]
cmp al, 0x61
jl SHORT_ELSE_IF_3
//继续往下执行,且重新提取[ebp - 0x4]
mov dl, byte ptr ds:[ebp - 0x4]
cmp dl, 0x7A
jg SHORT_ELSE_IF_3
//执行代码
mov eax, 0x2
jmp SHORT_END_IF
SHORT_ELSE_IF_3:
//if(eax >= 0x30 && eax <= 0x39)
//提取[ebp - 0x4]
mov al, byte ptr ds:[ebp - 0x4]
cmp al, 0x30
jl SHORT_ELSE_4
//继续执行,且重新提取[ebp - 0x4]
mov dl, byte ptr ds:[ebp - 0x4]
cmp dl, 0x39
jg SHORT_ELSE_4
mov eax, 0x3
jmp SHORT_END_IF
SHORT_ELSE_4:
mov eax, 0x4
SHORT_END_IF:
//函数尾
pop edi
pop esi
pop ebx
add esp, 0x40
mov esp, ebp
pop ebp
ret
}
}
Level11
具体代码

反汇编代码

Level12
具体代码

反汇编代码

拓展_1:关于 % 2^N方
流程:
-
流程第一步:一眼识别 (Pattern Matching)
-
不要先去算数,先看**"长相"。如果一段代码长得像下面这样,它100%** 是 2N2^N2N 取模:
-
起手式:AND 寄存器, 小数 (比如 3, 7, 15, 31...)
-
分水岭:
JNS(或JGE) 跳转到结尾。 -
修补三连:
-
DEC(减 1) -
OR寄存器, 负数 (比如 -4, -8...) -
INC(加 1)
-
-
结论:只要看到
AND...JNS...DEC...OR...INC这套组合拳,这就是 有符号数的 2N2^N2N 取模。
-
-
流程第二步:秒出答案 (Calculation)
-
核心公式: 除数=掩码+1除数 = \text{掩码} + 1除数=掩码+1
-
看到
AND eax, 3👉 3+1=43 + 1 = 43+1=4 👉 % 4 -
看到
AND eax, 7👉 7+1=87 + 1 = 87+1=8 👉 % 8 -
看到
AND eax, 0F👉 15+1=1615 + 1 = 1615+1=16 👉 % 16 -
完全不需要去看后面那个复杂的
OR是多少,看AND就够了!
-
-
流程第三步:逻辑验证 (Logic Check)
-
AND的数:负责截取低位(全0,只有尾巴是1)。 -
OR的数:负责填充高位(全1,只有尾巴是0)。 -
验证标准:
AND的数 和OR的数 在二进制上应该是互补的(拼起来刚好铺满整个 32 位)。
-
练习一:

练习二

拓展_2:关于 % 非2的N次方
第一步:制造"魔法子弹" (计算魔数 M)

第二步:算出除数N

核心特征:

战利品
-
裸函数掌控力 (Naked Function Mastery):
- 彻底理解了栈帧的生命周期,能够手动构建 (
push ebp) 和销毁 (ret) 函数环境,不再依赖编译器。
- 彻底理解了栈帧的生命周期,能够手动构建 (
-
内存透视眼 (Memory Vision):
- 建立了"一切皆偏移"的直觉。变量名只是幻象,
[ebp+8]和[ebp-4]才是数据的真实坐标。
- 建立了"一切皆偏移"的直觉。变量名只是幻象,
-
控制流重构 (Control Flow Reconstruction):
- 掌握了汇编特有的"Fall-through"逻辑(不满足条件就踢走,留下的才是精华),看透了 if-else 本质上只是
CMP与跳转指令的排列组合。
- 掌握了汇编特有的"Fall-through"逻辑(不满足条件就踢走,留下的才是精华),看透了 if-else 本质上只是
-
位域微操 (Bitwise Surgery):
- 在 Level 8 终极试炼中,学会了使用 TEST 和移位指令,像外科医生一样精准操作整数里的每一个 Bit。
结语
第一阶段的征途已至终点,但你的蜕变才刚刚开始。
此刻的你,手中握着的不再是普通的键盘,而是一把解剖刀。 接下来,我们将调转枪口,发起反攻。 我们不再'写'汇编,我们要去'读'汇编; 我们不再构建逻辑,我们要去还原真相。
那些经过编译器层层伪装、经过优化算法面目全非的代码,正在前方的迷雾中等着你。 但现在的你,眼神已变。 你不再畏惧那些 0 和 1 的混沌,因为你知道------ 只要你是那个能看穿底层的'Neo',整个数字世界都将为你屈服。
路虽远,行则必至;事虽难,做则必成。
收好你的佩剑。 第二卷:透视之眼(反编译特训),现在开启。