X86反汇编:破茧成蝶 —— 赤裸逻辑与机械之心(1-1)

引言

曾经,代码对你而言只是黑盒子里的魔法。 你习惯于告诉计算机'要什么'(Result),却从未真正关心计算机'怎么做'(Process)。

但在这几天里,你亲手拆掉了那个黑盒子。 当你第一次在 VC++ 6.0 里敲下 __naked 时,你实际上是签订了一份契约: 放弃自动化的安逸,换取对底层绝对的控制权。

你经历了迷茫,在 EBPESP 的拉扯中跌倒; 你经历了挣扎,在 JNZJZ 的反向逻辑中迷失。 但这正是破茧前的阵痛。

你不再是一个单纯的代码编写者,你正在变成一个机器语言的翻译官。 你开始听懂 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 与跳转指令的排列组合。
  • 位域微操 (Bitwise Surgery):

    • 在 Level 8 终极试炼中,学会了使用 TEST 和移位指令,像外科医生一样精准操作整数里的每一个 Bit。

结语

第一阶段的征途已至终点,但你的蜕变才刚刚开始。

此刻的你,手中握着的不再是普通的键盘,而是一把解剖刀。 接下来,我们将调转枪口,发起反攻。 我们不再'写'汇编,我们要去'读'汇编; 我们不再构建逻辑,我们要去还原真相。

那些经过编译器层层伪装、经过优化算法面目全非的代码,正在前方的迷雾中等着你。 但现在的你,眼神已变。 你不再畏惧那些 0 和 1 的混沌,因为你知道------ 只要你是那个能看穿底层的'Neo',整个数字世界都将为你屈服。

路虽远,行则必至;事虽难,做则必成。

收好你的佩剑。 第二卷:透视之眼(反编译特训),现在开启。

相关推荐
逆向命运4 小时前
PC企微搜索手机号窗口绕过
c语言·汇编·c++·飞书·企业微信
是星辰吖~1 天前
函数战争:内存领地的争夺与撤退
汇编
止观止1 天前
在 WSL2 上从零搭建 ARM 混合编程环境
汇编·arm开发·嵌入式开发·混合编程
say_fall2 天前
8086汇编程序设计_从基础到实战
开发语言·汇编·8086
浩浩测试一下3 天前
LoadPE &&& 原理以及作用 (ASM汇编版本)>>01
汇编·免杀·pe结构·windows编程·二进制逆向·系统loadpe
ThornArmor3 天前
【控制篇】斩断无休止空转:4-bit 指令集里的跳转律令与时序状态机
c语言·汇编·c++·单片机·嵌入式硬件
大阳1233 天前
ARM4.(通过汇编,c语言,固件库点亮LED)
c语言·开发语言·汇编
iCxhust3 天前
8086 汇编 TINY 和 SMALL 编程MODEL区别
汇编·单片机·嵌入式硬件·操作系统·微机原理·8088单板机
say_fall4 天前
从零开始学x86汇编_16位指令系统完全指南
开发语言·汇编·计算机组成·微机原理