前言好久不见甚是思念2026年的第一篇文章希望看到这篇博客的大家都能发财。新的一年我们将开启一个新的专栏也就是之前就略有涉及的安全你可以叫他信息安全或者网络空间安全今天我们来进行一次简单二进制爆破的逆向工程虽然名字听着很唬人但其实也就那样哈哈话不多说直接开始。一、visual studio 编写程序#includestdio.h// 一个简单的加法函数方便我们观察参数是如何通过寄存器传递的intsecret_add(inta,intb){intresultab;returnresult;}intmain(){intx5;inty5;intsumsecret_add(x,y);if(sum25){printf(Success! Sum is %d\n,sum);}else{printf(Failed!\n);}return0;}这是一段很简单的程序代码首先定义了一个函数secret_add它将两个整数作为输入将它们的和作为输出主程序中将两个5作为参数输入再判断输出的结果是否大于25大于则输出success不大于也就是小于或等于则输出failed。很明显我们输入的5510,1025,所以结果应该是failed。下面我们将上面这段程序在visual studio中运行结果如下可以看到这里输出的结果为failed二、visual studio分析汇编代码1.调试在 int x5这个地方我们打一个断点然后我们进行调试查看汇编代码和c语言代码之间的对应关系并且分析这里面的逻辑·。2.分析汇编第一部分变量赋值与函数调用这部分对应 C 语言的 int x 5; int y 5; int sum secret_add(x, y);。mov dword ptr [x], 5将常数 5 放入内存中变量 x 的位置。mov dword ptr [y], 5将常数 5 放入内存中变量 y 的位置。mov edx, dword ptr [y]将 y 的值5加载到 edx 寄存器。mov ecx, dword ptr [x]将 x 的值5加载到 ecx 寄存器。call secret_add (…)跳转到 secret_add 函数执行。mov dword ptr [sum], eax函数返回值通常存在 eax 中这行将其存入 sum。第二部分核心逻辑判断 (If-Else)1.比较与跳转cmp dword ptr [sum], 19h指令 cmp (Compare) 比较指令。意义 拿 sum 的值和 19h十六进制的 19 等于十进制的 25做比较。底层 它其实是在做减法运算 sum - 25但不保存结果只根据结果修改 CPU 的状态标志位比如是否为零、是否为负。jle main50h (07FF7…)指令 jle (Jump if Less or Equal) 小于或等于则跳转。逻辑 如果刚才比较的结果是 sum 25CPU 就直接“跳”到偏移地址为 50h 的地方即 else 分支。2.True 分支 (If 成立)如果没跳走就会执行打印 Success 的代码mov edx, dword ptr [sum]把 10 传给 edx对应 printf 中的 %d。lea rcx, [string “Success!..”]把字符串的地址传给 rcx。call printf调用打印函数。jmp main5Dh执行完后必须用一个 无条件跳转指令 jmp 跨过 else 部分直接去执行 return 0。3. False 分支 (Else 逻辑)如果 sum 25程序会跳到这里lea rcx, [string “Failed!\n”]加载 “Failed!” 字符串地址。call printf执行打印。第三部分函数收尾对应 return 0; }。xor eax, eax技巧 在汇编中xor eax, eax自己异或自己是将寄存器清零最快的方式。意义 设置返回值为 0。ret意义 返回调用者。三、x86dbg 进行爆破1.编译原理知识补充我们先来看一下visual studio中上面这个项目相关的一些文件截图了解一下它与我们后面的逆向之间的关系。以上是图1以上是图2以上是图3图1源代码与工程核心 (Root 目录)list2.cpp定义你的 C 源文件。意义这是你的命根子。所有的逻辑secret_add、if-else都写在这里。它是人类能读懂的文本文件。Project1.sln定义解决方案文件 (Solution)。意义VS 的入口。它并不包含代码而是记录了一个或多个项目Project的组织关系。双击它就能打开整个开发环境。Project1.vcxproj定义项目文件。意义这是给 MSBuild微软编译引擎看的说明书。里面记录了包含哪些 .cpp 文件、使用了什么编译选项是 x64 还是 x86、引用了哪些库。.vs (隐藏文件夹)定义VS 本地缓存。意义记录了你上次打开了哪些文件、光标在哪里、断点设在哪里。删掉它不影响代码但你下次打开项目时VS 会像失忆了一样重新加载。图2中间编译产物 (Debug 目录)list1.obj / list2.obj定义目标文件 (Object File)。意义极其重要 编译器把 .cpp 翻译成机器语言后先存成 .obj。它是二进制格式但还不能运行。它就像是拼图的一块。vc145.pdb / Project1.pdb定义程序数据库文件 (Program Database)。意义逆向的神器。它存储了“调试符号”。它记录了内存地址 0x00401000 对应源码的第 10 行以及变量 sum 存在哪个寄存器里。没有它你在 x64dbg 里看到的就全是冷冰冰的地址没有函数名。Project1.ilk定义增量链接文件。意义为了让你下次修改代码后编译更快链接器会记录上一次链接的信息。Project1.log / debug.log定义编译日志。意义记录了编译过程中有没有报错、报了什么警告。图3最终产物 (x64\Debug 目录)Project1.exe程序的“执行体”定义这是最终的可执行文件二进制机器码。本质链接器Linker将之前生成的 .obj 文件与系统标准库如 printf 的实现缝合在一起的产物。逆向视角它是 x64dbg 唯一关心的东西。在没有 PDB 的情况下它就是一堆无意义的 0 和 1。Project1.pdb程序的“说明书”定义程序数据库文件Program Database。本质它是一个调试符号文件。它不包含可执行代码但记录了二进制地址与你写的源代码list2.cpp之间的映射关系。重要性在 Visual Studio 中有了它你才能在调试时看到变量名、函数名和行号。在逆向对抗中正因为 PDB 暴露了代码的所有商业秘密所以你在分析商业软件如深信服 EDR时永远看不到它。知识分享商业软件为什么难以破解很多人以为写完代码按运行.cpp 就直接变 .exe 了。其实中间经过了编译器 (Compiler) 生成了 .obj然后由链接器 (Linker) 把它们缝合在一起。而那个 .pdb 文件则是连接‘代码世界’和‘二进制世界’的桥梁。如果你在逆向时没有 PDB 文件你就是在‘盲操’这也就是为什么破解商业软件比破解自己写的代码难得多的原因。商业软件比如 Office、游戏、甚至是深信服的防护软件之所以不提供 .pdb 文件最根本的原因就是为了增加逆向工程的难度保护核心代码不被轻易看穿。1.什么是 PDB 泄露信息差的消失如果一个商业软件带了 .pdb 文件那么在逆向者眼里它就变成了“半透明”的函数名全显原本在 x64dbg 里只是一串冷冰冰的地址如 sub_7FF71234有了 PDB 就会变成 CheckUserLicense检查用户授权或 EncryptPassword加密密码。变量名泄露原本是 [rbp-0x10]有了 PDB 会显示为 IsTrialExpired试用期是否结束。源码行对齐逆向者可以直接看到汇编指令对应哪一行 C 源码。结论提供 PDB 就相当于把保险柜的内部构造图直接贴在了保险柜门上。2.商业对抗符号剥离 (Symbol Stripping)为了防止被逆向商业软件发布时会进行“符号剥离”只保留运行必须的代码CPU 运行程序只需要地址不需要名字。所以发布版Release 版会删掉所有的调试符号。代码混淆 (Obfuscation)有些公司甚至会故意把函数名改成无意义的乱码如 a1, b2让逆向者即使猜也猜不出来逻辑。3.为什么微软会公开部分系统的 PDB你可能会发现在调试系统问题时VS 可以从微软服务器下载 ntdll.dll 等系统文件的 PDB。为了兼容性和开发微软希望全球的开发者能更方便地调试与系统底层相关的 Bug。核心逻辑依然隐藏虽然公开了 PDB但那些 PDB 通常是“精简版”只包含函数名不包含具体的源码路径和局部变量名。2.x86dbg精准爆破1导入文件我们在x86dbg打开链接生成的exe文件导入后界面如下我们先来看下各个部分的含义a.反汇编窗口 (Disassembly Window) —— 核心“手术台”位于界面左上角这是你观察和修改代码的地方。地址栏最左侧的十六进制数字如 00007FF7…代表指令在内存中的具体位置。机器码紧随其后的十六进制数如 7E 12是 CPU 真正执行的数据。反汇编指令人类可读的助记符如 jle、cmp、nop。注释与引用最右侧x64dbg 会自动标注出字符串如 “Success!”或对应的源码行号。b.寄存器窗口 (Registers Window) —— CPU 的“演算纸”位于右侧。它实时显示 CPU 内部的状态。RAX/RBX/RCX 等这些是临时存放数据的“小抽屉”。比如函数返回值通常存在 RAX 中。RIP (Instruction Pointer)最重要的寄存器指向 CPU 即将执行 的下一条指令地址。EFLAGS (标志位)在执行 cmp 指令后ZF零标志、SF符号标志等会变色决定了接下来的 jle 是否跳转。c.内存布局/十六进制转储 (Hex Dump) —— 程序的“原始肉体”位于底部。它以最原始的十六进制形式展示内存中的数据。你可以这里看到程序加载的所有 DLL、全局变量或未加密的硬编码字符串。d.堆栈窗口 (Stack Window) —— 函数的“备忘录”位于右下角。它记录了函数调用时的局部变量、返回地址和参数传递情况。(2) nop的艺术第一部分操作步骤在汇编代码区域单击右键点击搜索-所有模块-字符串我们会进入下面的界面在这里我们找到“success”这一行双击打开找到success上面的“jle 0x00007FF7B72E18F0”将它改为nop随后我们按住ctrlp调出补丁将文件修复后可以选择保存到桌面命名为“baopo_success.exe”这时候我们的终端防护系统会发出警报为什么会发出警报我们后面再说。最后我们打开桌面的cmd运行这个exe程序得到的结果是success这是为什么呢第二部分底层原理1程序的“正常轨道”与“分叉路口”在汇编层面程序的执行顺序默认是从上往下一行行走的。但是 if-else 逻辑制造了一个“分叉路口”。正常逻辑未修改时CPU 执行 cmp 指令算出551055105510发现10≤2510 \le 2510≤25。CPU 执行 jle 指令。jle 的意思是“如果小于等于就跳走”。发生跳转CPU 的执行头RIP 寄存器直接瞬移到了 Failed 打印的代码块地址。结果它跳过了中间那段 Success 的代码。2为什么是 NOPNOP 的全称是 No Operation机器码为 0x90。对 CPU 来说它是一条合法的指令但它的作用只有一个让 CPU 浪费一个时钟周期然后什么都不做直接执行下一行。3为什么改为 NOP 就能成功核心原理当你把 jle 这一行指令改写成 NOP 时你实际上是拆掉了路标铺平了道路。修改后的逻辑爆破后CPU 执行 cmp依然算出结果小于 25。CPU 走到原本 jle 的位置发现现在这里变成了 NOP。关键点来了因为没有了“跳走”的指令CPU 就失去了“瞬移”的能力。它只能呆头呆脑地继续往下走。而紧接着 NOP 后面的下一行代码恰恰就是打印 Success 的指令。结果CPU “被迫”撞进了 Success 分支。4 深度类比、铁轨上的道岔你可以把程序想象成一辆在铁轨上行驶的火车原本的 jle是一个道岔。如果条件满足道岔就会把火车引导向“失败”的侧线。你的 NOP 操作相当于你把这个道岔给拆了换成了两段笔直的铁轨。结果无论火车的条件是什么它现在都只能沿着主线Success 分支一直开下去。第三部分为什么深信服终端防护系统会发出警报a.静态特征检测 (File Integrity Signature)这是最基础的一道防线。哈希校验 (Hashing)当你第一次编译生成 Project1.exe 时它有一个唯一的“指纹”Hash值。当你用 x64dbg 修改了其中一个字节并保存为 Project1_OK.exe 时这个文件的指纹就彻底变了。签名缺失正规软件都有数字签名。你的修改导致原有的如果有的话签名失效或者干脆是一个未知的“三无”程序在尝试执行敏感操作。补丁特征 (Patching Patterns)深信服的病毒库里记录了大量“指令修改”的特征。你把 jle 改成 nop机器码从 7E 变成 90这种在代码段Code Section直接改写指令的行为是典型的木马/破解补丁特征。b.行为监测 (Behavioral Analysis)即使你成功保存了文件运行时的行为也会被监控。可疑修改EDR 会监控系统中所有文件的修改句柄。当它发现 x64dbg.exe 正在频繁读写另一个可疑的 .exe 内存或磁盘空间时它会判定这是一种**“代码注入”或“非法修补”**行为。未知风险深信服 EDR 有一套云端查询机制。当它发现一个本地新生成的、从未在白名单中出现过的程序尝试启动它会默认采取“先拦截、后询问”的高压策略。c.沙箱与 AI 启发式引擎现在的安全系统如你截图中的深信服 EDR通常内置了 AI 引擎启发式扫描它不只是看你改了什么而是分析这种改动后的程序“长得像不像病毒”。虚拟执行在你的程序真正跑起来之前EDR 可能已经在后台的一个微型沙箱里模拟运行了它。如果发现程序逻辑异常比如跳过了关键校验它就会触发预警。总结今天我们用visual studio和x86dbg进行了一次简单的二进制爆破主要是通过修改汇编指令修改了CPU的寻址顺序最终实现了对if语句逻辑的更改。虽然过程简单但是从中可以学到一些编译原理和底层的逻辑。下次我们玩些更有意思的东西有兴趣的朋友可以点个关注哟。