资讯动态

二进制逆向入门:用x64dbg在crackmes.one挖出硬编码密码

发布时间:2026/9/17 17:32:22 来源:尧图企业网站定制
记得第一次打开 crackmes.one 的时候我对着满屏的英文标题和一堆陌生术语发了好长时间呆。作为一个平时写 C/C 和 Python 的程序员二进制x64dbg这些词对我来说完全是另一个世界的东西。但真正上手做完第一道题我才发现逆向并没有想象中那么高不可攀关键是找到合适的练习入口再配上一套顺手的工具。这篇笔记是二进制破冰录的第一篇我想从一个最基础也最典型的场景说起在 crackmes.one 上下载一个练习程序用 x64dbg 把它藏在里面的硬编码密码挖出来。如果你是第一次接触二进制逆向这篇文章可以当你的第一张地图。我会先解释 crackmes.one 到底是什么、怎么挑题再带你熟悉 x64dbg 的基本操作然后完整走一遍找密码的实战链路最后把新手最容易踩的坑提前摆出来。内容不深但每一步都是可以照着做的。1. crackmes.one为什么它是逆向新手最靠谱的练功房1.1 平台上的 crackme 到底是什么规则如何crackmes.one 是一个专门收集逆向练习程序的网站这类程序通常被大家叫做 crackme。简单说就是作者故意写出来让别人拆解的小程序程序内部藏着一个或多个关卡可能是一个密码、一个序列号也可能是一套完整的注册验证逻辑。你的任务就是通过静态分析看代码或动态调试跑起来观察把藏在里面的秘密找出来。这个平台和恶意破解软件有本质区别crackme 的作者在发布时就已经给了你逆向它的许可程序存在的唯一目的就是被人拆解。很多做漏洞研究、恶意代码分析、游戏保护对抗的人早期都是靠大量 crackme 刷手感练出来的。平台上的题目会标注运行平台Windows、Linux、DOS 等、难度等级、作者说明还有社区打分和评论方便你判断题目大概是什么水平。我在首页翻的时候最直观的感受是终于有一个地方可以大胆地拆程序了。平时在公司里连自己项目的二进制都不能随便折腾更别说碰第三方软件。但在 crackmes.one 上你可以随便跑、随便断、随便改内存拆坏了重新下载一份就行。这种允许失败的环境对新手建立调试直觉非常重要。1.2 第一道题怎么挑从难度到平台一个都不能少新手挑题有几个关键点我把它们按重要程度排了个序。第一看难度。平台会把难度分成多个等级刚开始一定选最低的那一档。低难度题目的逻辑通常很简单往往就是一个密码字符串明文写在程序里。选简单题不丢人恰恰相反这是建立调试手感和信心的必经之路。我见过太多人上来就想搞无壳 反调试 花指令的题结果卡在入口处一天直接劝退。记住你是来练基本功的不是来挑战极限的。第二看平台和位数。你平时用什么系统、打算用什么调试器就选什么平台的题。我的建议是从 Windows 平台的 32 位程序开始因为 Windows 下调试器生态最顺32 位程序的汇编相对简单网上能搜到的参考资料也最丰富。平台详情页一般会标明 Architecture 是 x86 还是 x64下载前多看一眼免得拿错工具。第三看社区反馈。评分高、下载次数多的题目说明大家玩过之后觉得质量不错难度标注也相对可信。评论里经常会有提示先看字符串这题用字符串搜索就能过之类的线索但如果你不想被剧透可以先不看评论自己动手试一遍再说。第四下载后不要直接双击运行。这是我比较坚持的一个安全习惯任何从网上下载的可执行文件先丢到 VirusTotal 上扫一遍。crackme 虽然绝大多数是安全的但互联网下载渠道鱼龙混杂没法保证每个文件都干净。我自己的做法是在 VMware 虚拟机里专门开一个 Windows 测试环境把下载好的 crackme 放进去运行和调试。虚拟机的好处是随便折腾快照一拍坏了就回滚和日常系统互不污染。提示第一次做逆向练习强烈建议全程在虚拟机里进行。这不只是防病毒也是防止自己手滑把系统环境弄乱——调试器修改内存、注入指令这类操作一旦挂错进程后果很难收拾。2. x64dbg 调试器上手前要搞清楚的四个关键点2.1 为什么选 x64dbg而不是 OllyDbg 或 IDA市面上常见的逆向工具挺多OllyDbg、x64dbg、IDA Pro、Ghidra、WinDbg、radare2 各有各的强项。对纯新手来说x64dbg 是最合适的动态调试起点原因有三个。第一它开源、免费、还在持续更新。OllyDbg 算是老前辈但主要支持 32 位很多年没有大更新对新系统、新指令集的支持越来越吃力。x64dbg 则同时提供 x32dbg 和 x64dbg 两个可执行文件分别调试 32 位和 64 位程序插件生态也比较活跃。第二它的界面设计对动态调试非常友好。CPU 窗口同时显示反汇编、寄存器、数据、堆栈四个子窗口打开就能看全局。断点窗口、内存布局、模块列表这些常用面板也都能快速调出来。对新手来说一眼能看到自己在哪是最重要的。第三学习资源多。网上关于 x64dbg 的教程、插件、讨论数量比其他调试器多得多遇到问题容易搜到答案不至于卡在一个小细节上耽误半天。需要说明的是x64dbg 和 IDA/Ghidra 不是替代关系。x64dbg 是动态调试器重点在程序跑起来之后再观察看实时寄存器、内存内容和每条指令的执行效果IDA 和 Ghidra 偏静态分析适合做整体代码脉络梳理。两者经常搭配使用但最开始先用好动态调试器就足够解决大量简单题了。2.2 四个核心窗口的分工与常用快捷键x64dbg 启动后最显眼的是 CPU 窗口它由四个子窗口组成很多人第一次看到会觉得眼花其实只要记住每个窗口的职责就清楚了。左上角是反汇编窗口显示当前指令的地址、机器码、汇编助记符和注释这是你主要盯的地方。右上角是寄存器窗口显示通用寄存器、标志位、指令指针等当前值相当于 CPU 实时状态的仪表盘。左下角是数据窗口显示内存内容可以切换成 ASCII、Unicode、十六进制等不同显示方式。右下角是堆栈窗口显示当前函数调用栈包括返回地址、局部变量等信息。四个窗口是联动的。你在反汇编窗口选中某个地址数据窗口会跟着显示这个地址的内容程序每执行一步寄存器和堆栈都会实时变化。动态调试的乐趣就在这里你像外科医生一样一刀一刀解剖程序的运行过程。常用快捷键不用背太多先记熟这几个就够了按键功能备注F9运行一直跑到下一个断点或程序结束F2设置/取消断点在反汇编窗口当前行切换断点F7单步进入进入汇编指令内部如 callF8单步步过执行当前指令但不进入子函数F12暂停程序程序跑飞时用来强制中断CtrlG跳转到地址/表达式输入地址或符号名CtrlF在当前窗口搜索可用在反汇编、数据等窗口CtrlB二进制搜索在内存区搜索二进制内容从我的经验来看F9、F2、F7、F8 这四个键覆盖了八成以上的日常操作。遇到复杂逻辑再用 F12 暂停、用 CtrlG 跳转。不要一上来就背快捷键边用边记效率最高等题做多了手指自然会记住。2.3 加载程序后先停在入口点别在系统断点里迷路x64dbg 加载一个 exe 文件时默认会停在系统断点System Breakpoint处。所谓系统断点是程序被系统加载器刚拉起、还没执行任何程序自身代码时的位置此时你看到的是 ntdll.dll 里的系统代码。如果在这里直接按 F9 运行程序会一口气跑完所有逻辑可能直接弹出控制台窗口等待输入如果在这里反复按 F8 单步又会一头扎进系统 API 内部越走越深很快迷路。我的建议是在加载对话框里勾选入口断点Entry Breakpoint。这样 x64dbg 会在程序入口点自动暂停。程序入口点是操作系统把程序加载好之后开始执行开发者代码的第一条指令从这里开始调试视野最干净整个控制流都在你眼前展开。如果你已经把程序加载了但没勾入口断点可以这样补救在命令栏输入bp EntryPoint设置一个临时断点按 F9 运行程序会在入口停下。或者在符号面板里找到当前主模块展开后找到 EntryPoint 标号右键设置断点并跳转过去。提示一个干净的入口点是新手调试的前提。最常见的迷路场景就是在系统断点处反复按 F8结果一头扎进 ntdll 的初始化逻辑里完全找不到自己在哪。记住先把程序拉到入口点再开始干活。3. 实战从观察程序行为到挖出硬编码密码的完整链路3.1 先当普通用户跑一遍记录行为下面用一个典型的第一题来讲完整流程。假设我从 crackmes.one 下载了一个 Windows 32 位程序文件名按平台习惯暂时叫 ch01.exe。这题难度最低作者描述就一句话find the password。拿到程序我不会上来就开调试器而是先把它当普通软件跑一遍。在虚拟机里打开命令行窗口执行 ch01.exe屏幕上出现一行提示Enter the password:我随手输入一个错误密码123456回车程序立刻回了一句Wrong! Try again.然后退出。这就是关键的第一步记录行为。通过一次正常运行我已经知道程序做了三件事显示提示、读取输入、输出判断结果。那么它内部一定有一个比较输入和正确答案的逻辑而正确答案要么是硬编码字符串要么是从某段数据里推导出来的。对新手而言大部分低难度题目的密码就是硬编码字符串所以下一步直接用字符串搜索来定位。可能有读者会问为什么不直接在入口点单步调试一条一条指令跟过去理论上可以但一个哪怕只有几 KB 的程序从入口到输入判断点之间也会经过大量初始化逻辑、库函数调用纯单步跟下去效率太低而且对汇编不熟的人很容易跟丢。字符串搜索相当于先给地图标了宝藏地点再直接跑过去远比全图扫描高效。3.2 用字符串引用定位关键代码区域在 x64dbg 中加载 ch01.exe勾选入口断点按 F9 停在入口位置。此时不要急着开始单步先在反汇编窗口点击右键找到菜单里的搜索相关入口然后依次选择当前模块→字符串引用String references。x64dbg 会打开一个新的引用面板列出当前模块里所有可读的 ASCII 和 Unicode 字符串。在我这道题里能看到这些内容Enter the password:Wrong! Try again.Congratulations! You found it.%s前三个字符串都在程序运行过程中出现过尤其Wrong! Try again.和Congratulations! You found it.它们就是两条分支的结果提示。双击Wrong! Try again.这一行x64dbg 会自动跳到反汇编窗口中引用这个字符串的指令位置。这里有个小技巧双击引用列表里的字符串跳转到的不是字符串本身所在的内存地址而是代码中引用该字符串的那条指令。然后再往上翻几行就能把整个判断区域的上下文看得明明白白。在我这个例子里跳转后看到的是这样一段逻辑地址和寄存器和实际题目不同但结构相似00401150 lea eax, [string Enter the password:] 00401156 push eax 00401157 call printf 0040115C lea ecx, [input_buffer] 0040115F push ecx 00401160 call scanf 00401165 lea edx, [string s3cr3t_p4ss] 0040116B push edx 0040116C push ecx 0040116D call strcmp 00401172 test eax, eax 00401174 jne loc_wrong 00401176 lea esi, [string Congratulations! You found it.] 0040117C call printf 00401181 ret最亮眼的就是lea edx, [string s3cr3t_p4ss]这一行。x64dbg 的反汇编注释直接显示出了引用的字符串内容也就是说密码s3cr3t_p4ss已经清清楚楚地写在这里了。3.3 在比较指令处挖出密码并验证既然已经看到了s3cr3t_p4ss我直接把它记下来回到命令行重新运行 ch01.exe输入这个密码Enter the password: s3cr3t_p4ss Congratulations! You found it.控制台输出成功提示第一题完成。整个过程不到十分钟甚至可能更短。但只挖出密码还不够过瘾我会回到调试器把这段逻辑彻底弄明白。程序调用scanf读取输入把内容存进input_buffer然后调用strcmp把输入缓冲区与硬编码字符串s3cr3t_p4ss比较。strcmp的返回值存在eax里如果两个字符串相等eax为 0后面的test eax, eax和jne loc_wrong组合在一起就实现了相等则落入成功分支不相等则跳到 Wrong 分支的判断。这里还有一个特别直观的动态调试操作在jne loc_wrong那一行按 F2 设置断点重新运行程序输入任意错误密码。程序执行到这个跳转指令时会自动暂停。这时候你可以在寄存器窗口里直接双击 ZF 标志位把它从 0 改成 1让跳转条件不成立然后按 F9 继续运行程序就会走进成功分支打出Congratulations!。也可以在反汇编窗口按空格把jne loc_wrong改成jmp强制跳转。这是动态调试最直观的魅力你不只是看懂了代码还能亲手篡改程序的执行路径。提示用 F2 下断点后记得通过重启程序CtrlF2避免旧断点影响新的调试流程。如果想清空所有断点可以在断点窗口里一键移除。新手经常忘了这一点导致改完代码后程序异常还以为是环境问题。4. 找到的不只是密码更是二进制的数据形态4.1 字符串常量在 PE 文件里如何存在刚做完第一题的时候我其实有个疑问为什么字符串搜索这么好用x64dbg 凭什么能在一堆二进制数据里精准地列出我关心的字符串要回答这个问题得从编译过程说起。用 C 语言写一个最简单的判断程序#include stdio.h #include string.h int main() { char input[64]; const char *password s3cr3t_p4ss; printf(Enter the password: ); scanf(%63s, input); if (strcmp(input, password) 0) { printf(Congratulations! You found it.\n); } else { printf(Wrong! Try again.\n); } return 0; }编译成 Windows 常见的 PE 格式可执行文件后代码和数据会分门别类放进不同的节Section。指令代码一般在.text节只读常量数据包括字符串在.rdata节可读写的全局变量在.data节。这些字符串常量在文件里就是一段连续的字节序列比如s3cr3t_p4ss对应的十六进制是73 33 63 72 33 74 5F 70 34 73 73 00最后的00是 C 字符串的结束符\0。那么程序怎么知道字符串在哪呢编译器在生成指令时会把字符串所在的内存地址直接编码到指令里。上面例子里的lea edx, [string s3cr3t_p4ss]就是让edx寄存器指向这个字符串在内存中的地址。理解了这一点你就能明白为什么 x64dbg 在反汇编注释里能直接显示出字符串内容——因为它读出了该地址处的字节序列发现是一段可打印字符。4.2 为什么明文密码这么容易暴露明文密码容易暴露的根本原因是程序要在运行时和用户交互它必须把提示语、错误信息、成功信息这些常量打包进二进制这些常量一旦以连续可读字节的形式存在任何扫描器都能轻松发现。x64dbg 的字符串搜索功能本质上是把模块内存中所有满足连续可打印字符且长度达到一定阈值的字节序列列出来。这也解释了为什么很多 crackme 题目会刻意对字符串做处理用 XOR 加密字符串、把密码拆成几个部分再拼接、运行时动态生成字符串目的就是避开这种直接的字符串搜索。这是后面更进阶的内容。对新手来说理解程序为了和用户交互必然留下字符串足迹这一点是建立二进制直觉的第一步。从防御角度反过来看这也给写程序的人提了个醒如果你在开发一个需要校验密码的应用就不该把密码明文写死在代码里更不该用strcmp把输入和密码直接比较。正确的做法是存哈希值、加盐哈希校验时比较的是哈希结果而不是明文。即便攻击者拿到了哈希也不能像拿到明文那样直接输入通关。crackme 是故意留漏洞的练习程序和真实产品的安全设计是两个方向但这个对比能帮你理解为什么很多看起来很简单的判断在实际系统里会做得绕来绕去。4.3 拿到密码之后下一层该看什么当你能轻松找出第一个硬编码密码之后就不要再满足于拿到 flag、截图、下一题了。我自己的习惯是每做完一题强制自己回来做两件事写 writeup换一种思路重新求解。写 writeup 不是写给别人看的而是把我从哪里入手、看到了什么、为什么在这里下断点、程序整体逻辑是什么完整记录下来。这个习惯帮我建立了非常清晰的排查框架之后再遇到更复杂的题我能很快回忆起以前处理过的模式。很多新手做了几十题还是水平原地踏步缺的就是这个复盘动作。换思路求解怎么理解第一题我用字符串搜索找到了密码那我再试试别的动态调试路径比如不在入口处搜索字符串而是在scanf返回后紧跟的指令处下断点单步跟踪strcmp的两个参数观察寄存器和栈上的数据变化在硬编码字符串的地址上设置内存断点看它什么时候被访问从访问点反向定位逻辑尝试修改jne跳转指令注入新的指令序列让程序始终走成功分支。几种做法里尤其推荐理解内存断点的思路。你先在数据窗口里跳转到s3cr3t_p4ss的地址右键设置一个内存断点然后重新运行程序并输入错误密码当代码读取这个字符串、准备和输入比较时调试器会瞬间拦住你把你带到访问该字符串的那条指令。这种从数据反查代码的思维在以后追查复杂数据流、脱加密壳时会非常有用建议多练几次。5. 新手最容易踩的坑以及我的避坑心得5.1 在系统 DLL 里打转入口点的坑第一个最常见的坑我在第二章提过就是加载程序后没有停在入口点结果在 ntdll 或系统 DLL 里一顿步进完全找不到自己的程序代码在哪儿。出现这种情况别慌最快的解决办法是在符号面板里找到主模块比如 ch01展开后找到入口点地址右键选择转到入口点或者直接在命令栏输入bp EntryPoint下断点再按 F9 运行让程序停在自己家里。另外注意x64dbg 文件夹里有两个可执行文件x32dbg.exe 用来调试 32 位程序x64dbg.exe 用来调试 64 位程序。虽然它们的界面几乎一样但用错版本会导致加载失败或者寄存器显示异常。我见过不少新手拿 64 位调试器和 32 位程序强行配对折腾半天发现是版本选错了。5.2 断点位置的坑下得太早或太晚断点下得太早比如程序还没读取输入你就把断点下在strcmp调用处那程序根本不会触发断点下得太晚比如已经输出了判断结果你想看的比较逻辑早就执行完了。新手最常遇到的疑问就是我明明在关键位置下了断点怎么程序一直没有反应解决办法通常有两个方向。一是先用字符串搜索把目标代码区域缩小再决定断点下在哪条指令上二是用靠行为定位的方法比如在scanf调用返回后的那一行下断点输入完密码后程序必然停在那里然后你就可以用 F8 一步步跟上程序后面的判断逻辑亲眼看到寄存器里发生了什么。调试经验说白了就是猜一个位置验证一个位置多试几次断点的手感自然就有了。还有一个容易被忽略的点有些 crackme 会在开头调用IsDebuggerPresent、NtQueryInformationProcess这类反调试 API。如果你发现程序在调试器里的行为和正常运行明显不同比如直接退出、输出乱码先别怀疑自己操作错了很可能是题目故意设置的保护。这时候可以通过字符串区里的输出提示缩小判断区域或者翻一下题目评论看看作者是不是留了一层小机关。5.3 环境与安全的坑虚拟机、壳、责任边界环境上的第一个坑是直接在真实物理机上双击陌生 crackme。这个我在第二章强调过再做一次提醒强烈建议在虚拟机里跑快照是你最好的后悔药。第二个坑是没有检查程序有没有加壳。如果一个 crackme 加了 UPX、ASProtect 或 VMProtect那么入口点看到的已经不是正常逻辑代码而是脱壳和解压逻辑。对新手来说第一题一定不要选带壳的题目等熟悉了 PE 结构和常见保护机制之后再专门去学脱壳也不迟。判断有没有壳可以用 Detect It Easy 这类工具扫描一下几秒就能出结果。责任边界这一点虽然听起来像说教但我还是必须认真讲。crackme 是作者明确许可别人逆向的练习程序对它的所有操作都限定在练习范畴。同一个调试器、同一套技术如果拿去分析收费软件的注册算法、绕过商业授权、破解在线服务那就超出了合法边界也违背了逆向学习的基本伦理。我给自己定的规矩是只逆向自己拥有源码或得到明确授权的程序商业软件、闭源服务的破解一概不碰。技术本身是中性的关键看人怎么用。在实际操作中还有一个安全习惯值得养成把用于逆向练习的 Windows 虚拟机和日常工作的系统分开快照命名写清楚不要混用账号和数据。这样就算某个 crackme 带恶意行为损失也控制在虚拟机里随时可以回滚到干净状态。最后再聊两句个人的体会。做完第一道题之后的那几分钟我确实有点兴奋——不是觉得破解了什么而是第一次清楚地看见程序在底层原来是这个样子。一个简单的strcmp、一串明文字符串在我眼前从朦胧的机器码变成了可以理解的语言。这种看见底层的感觉会推动你去学更多东西寄存器是干嘛的、栈是怎么增长的、PE 文件头里藏了什么、函数调用约定为什么这么设计。如果你也想入门我的建议是别一上来就啃厚厚的逆向教材太容易劝退。先去 crackmes.one 挑一道最简单的题用 x64dbg 把第一个硬编码字符串挖出来。这个小小的正反馈比任何课程都能让你留下来。下一篇我打算写更常见的场景——当密码不是明文而是通过 XOR 之类的算法隐藏起来时怎么用动态调试一层层把它剥开。中间会用到更多寄存器分析和内存操作希望到时候你已经拿到了自己的第一个找到密码的记录。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价