1. BombLab实验概述BombLab是《深入理解计算机系统》CSAPP课程中的经典实验通过逆向工程拆解一个二进制炸弹程序。这个程序包含6个常规关卡和1个隐藏关卡每个阶段都需要输入特定密码才能通过否则会触发爆炸。实验目的是锻炼学生阅读汇编代码、使用调试工具和理解程序底层机制的能力。实验环境需要Ubuntu系统和以下工具GDBGNU调试器用于动态分析程序objdump反汇编工具查看机器码对应的汇编指令strings提取二进制文件中的字符串2. 实验工具准备2.1 GDB调试基础在开始拆弹前需要掌握几个关键GDB命令# 启动调试 gdb bomb # 设置断点 (gdb) break phase_1 # 单步执行 (gdb) stepi (gdb) nexti # 查看寄存器 (gdb) info registers # 查看内存内容 (gdb) x/20xw $rsp # 查看栈内容 (gdb) x/s 0x402400 # 查看字符串 # 反汇编当前函数 (gdb) disas2.2 objdump反汇编使用objdump获取完整的汇编代码objdump -d bomb bomb.s这会生成包含所有函数汇编代码的bomb.s文件方便全局分析。3. Phase 1字符串比对第一个关卡是最简单的热身核心逻辑在phase_1函数400ee0: 48 83 ec 08 sub $0x8,%rsp 400ee4: be 00 24 40 00 mov $0x402400,%esi 400ee9: e8 4a 04 00 00 callq 401338 strings_not_equal 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 phase_10x17 400ef2: e8 43 05 00 00 callq 40143a explode_bomb关键点分析将地址0x402400存入%esi作为比较字符串调用strings_not_equal函数比较输入字符串与该地址内容测试返回值非零则引爆使用GDB查看目标字符串(gdb) x/s 0x402400 0x402400: Border relations with Canada have never been better.因此第一关密码就是这个字符串。这个阶段主要考察识别内存中的字符串常量理解函数调用约定参数通过%rdi和%rsi传递掌握基础的GDB内存查看命令4. Phase 2数列验证第二关开始涉及循环和栈操作400f05: e8 52 05 00 00 callq 40145c read_six_numbers 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) 400f0e: 74 20 je 400f30 phase_20x34 400f10: e8 25 05 00 00 callq 40143a explode_bomb ... 400f17: 8b 43 fc mov -0x4(%rbx),%eax 400f1a: 01 c0 add %eax,%eax 400f1c: 39 03 cmp %eax,(%rbx) 400f1e: 74 05 je 400f25 phase_20x29 400f20: e8 15 05 00 00 callq 40143a explode_bomb破解步骤调用read_six_numbers读取6个数字到栈中第一个数字必须为1(%rsp) 1后续每个数字是前一个的2倍a[i] 2*a[i-1]因此正确数列是1 2 4 8 16 32。这个阶段考察栈帧布局和数组访问循环结构的汇编实现基本数学运算的指令形式5. Phase 3跳转表第三关引入了switch-case结构400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) 400f6f: 77 3c ja 400fad phase_30x6a 400f71: 8b 44 24 08 mov 0x8(%rsp),%eax 400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)关键发现第一个输入必须≤7无符号比较使用跳转表实现多分支地址0x402470每个case对应不同的返回值使用GDB查看跳转表(gdb) x/8gx 0x402470 0x402470: 0x0000000000400f7c 0x0000000000400fb9 0x402480: 0x0000000000400f83 0x0000000000400f8a ...例如输入0会跳转到0x400f7c400f7c: b8 cf 00 00 00 mov $0xcf,%eax # 207因此一组可行解是0 207。这个阶段重点理解跳转表的内存布局掌握间接跳转指令分析多分支结构的底层实现6. Phase 4递归函数第四关引入了递归调用401048: e8 81 ff ff ff callq 400fce func4 40104d: 85 c0 test %eax,%eax 40104f: 75 07 jne 401058 phase_40x4cfunc4的C语言等效代码int func4(int x, int y, int z) { int t ((z - y) (z - y)31) 1; int q t y; if (q x) { if (q x) return 0; return 2*func4(x, q1, z) 1; } return 2*func4(x, y, q-1); }通过分析可知需要func4返回0第二个输入必须为0第一个输入可以是7满足q x因此一组解为7 0。这个阶段考察递归函数的栈帧变化算术右移指令的应用递归终止条件分析7. Phase 5字符串变换第五关实现了一个字符转换40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx 401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx 4010b3: be 5e 24 40 00 mov $0x40245e,%esi关键点输入必须是6字符每个字符低4位作为索引查表地址0x4024b0结果需匹配flyers地址0x40245e查表字符串(gdb) x/s 0x4024b0 maduiersnfotvbyl...计算各字符位置f在第9位 → 输入字符低4位9如Il在第15位 → 低4位15如Oy在第14位 → 低4位14如N因此一组可行解是IONEFG。这个阶段重点字符编码处理查表转换模式识别位操作的实际应用8. Phase 6链表操作第六关最为复杂涉及链表排序401183: ba d0 32 60 00 mov $0x6032d0,%edx ... 4011da: bd 05 00 00 00 mov $0x5,%ebp 4011df: 48 8b 43 08 mov 0x8(%rbx),%rax 4011e3: 8b 00 mov (%rax),%eax 4011e5: 39 03 cmp %eax,(%rbx)破解步骤输入6个1-6且互不相同的数字每个数字x被转换为7-x按转换后的值对链表节点排序要求排序后节点值递减查看链表节点(gdb) x/24wx 0x6032d0 0x6032d0 node1: 0x0000014c 0x00000001 0x006032e0 0x00000000 0x6032e0 node2: 0x000000a8 0x00000002 0x006032f0 0x00000000 ...节点值依次为332, 168, 924, 691, 477, 443。要使它们按7-x的顺序递减排列原始输入应为4 3 2 1 6 5。9. Secret Phase二叉树搜索隐藏关卡通过phase_defused函数触发401630: e8 0d fc ff ff callq 401242 secret_phase触发条件在phase_4答案后追加字符串DrEvil输入数字作为二叉搜索树键值fun7函数实现递归搜索int fun7(Node* node, int val) { if (!node) return -1; if (node-val val) return 0; if (node-val val) return 2*fun7(node-right, val) 1; return 2*fun7(node-left, val); }需要返回2通过二叉树结构分析可知解为22。这个阶段综合考察二叉树的内存表示递归算法的逆向分析隐藏机制的发现方法10. 调试技巧与心得在拆弹过程中我总结了几个实用技巧内存查看技巧x/20xw $rsp查看栈内容x/gx查看8字节数据x/s查看字符串函数分析套路先找函数参数%rdi, %rsi等分析返回值%eax跟踪关键跳转指令常见模式识别循环结构通常有%rax作计数器递归函数会多次call自身字符串操作常用movzbl指令避坑指南注意有符号/无符号跳转jg/ja函数调用前后保存寄存器值栈帧变化时注意%rsp调整整个实验下来最大的收获是对汇编代码不再恐惧。通过GDB实际观察每条指令对寄存器和内存的影响那些晦涩的机器指令逐渐变得有规律可循。特别是看到自己推导的密码成功拆弹时那种成就感是看多少理论都替代不了的。
CSAPP BombLab 实战:从汇编指令到密码破解的逆向工程之旅
发布时间:2026/7/15 11:25:33
1. BombLab实验概述BombLab是《深入理解计算机系统》CSAPP课程中的经典实验通过逆向工程拆解一个二进制炸弹程序。这个程序包含6个常规关卡和1个隐藏关卡每个阶段都需要输入特定密码才能通过否则会触发爆炸。实验目的是锻炼学生阅读汇编代码、使用调试工具和理解程序底层机制的能力。实验环境需要Ubuntu系统和以下工具GDBGNU调试器用于动态分析程序objdump反汇编工具查看机器码对应的汇编指令strings提取二进制文件中的字符串2. 实验工具准备2.1 GDB调试基础在开始拆弹前需要掌握几个关键GDB命令# 启动调试 gdb bomb # 设置断点 (gdb) break phase_1 # 单步执行 (gdb) stepi (gdb) nexti # 查看寄存器 (gdb) info registers # 查看内存内容 (gdb) x/20xw $rsp # 查看栈内容 (gdb) x/s 0x402400 # 查看字符串 # 反汇编当前函数 (gdb) disas2.2 objdump反汇编使用objdump获取完整的汇编代码objdump -d bomb bomb.s这会生成包含所有函数汇编代码的bomb.s文件方便全局分析。3. Phase 1字符串比对第一个关卡是最简单的热身核心逻辑在phase_1函数400ee0: 48 83 ec 08 sub $0x8,%rsp 400ee4: be 00 24 40 00 mov $0x402400,%esi 400ee9: e8 4a 04 00 00 callq 401338 strings_not_equal 400eee: 85 c0 test %eax,%eax 400ef0: 74 05 je 400ef7 phase_10x17 400ef2: e8 43 05 00 00 callq 40143a explode_bomb关键点分析将地址0x402400存入%esi作为比较字符串调用strings_not_equal函数比较输入字符串与该地址内容测试返回值非零则引爆使用GDB查看目标字符串(gdb) x/s 0x402400 0x402400: Border relations with Canada have never been better.因此第一关密码就是这个字符串。这个阶段主要考察识别内存中的字符串常量理解函数调用约定参数通过%rdi和%rsi传递掌握基础的GDB内存查看命令4. Phase 2数列验证第二关开始涉及循环和栈操作400f05: e8 52 05 00 00 callq 40145c read_six_numbers 400f0a: 83 3c 24 01 cmpl $0x1,(%rsp) 400f0e: 74 20 je 400f30 phase_20x34 400f10: e8 25 05 00 00 callq 40143a explode_bomb ... 400f17: 8b 43 fc mov -0x4(%rbx),%eax 400f1a: 01 c0 add %eax,%eax 400f1c: 39 03 cmp %eax,(%rbx) 400f1e: 74 05 je 400f25 phase_20x29 400f20: e8 15 05 00 00 callq 40143a explode_bomb破解步骤调用read_six_numbers读取6个数字到栈中第一个数字必须为1(%rsp) 1后续每个数字是前一个的2倍a[i] 2*a[i-1]因此正确数列是1 2 4 8 16 32。这个阶段考察栈帧布局和数组访问循环结构的汇编实现基本数学运算的指令形式5. Phase 3跳转表第三关引入了switch-case结构400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) 400f6f: 77 3c ja 400fad phase_30x6a 400f71: 8b 44 24 08 mov 0x8(%rsp),%eax 400f75: ff 24 c5 70 24 40 00 jmpq *0x402470(,%rax,8)关键发现第一个输入必须≤7无符号比较使用跳转表实现多分支地址0x402470每个case对应不同的返回值使用GDB查看跳转表(gdb) x/8gx 0x402470 0x402470: 0x0000000000400f7c 0x0000000000400fb9 0x402480: 0x0000000000400f83 0x0000000000400f8a ...例如输入0会跳转到0x400f7c400f7c: b8 cf 00 00 00 mov $0xcf,%eax # 207因此一组可行解是0 207。这个阶段重点理解跳转表的内存布局掌握间接跳转指令分析多分支结构的底层实现6. Phase 4递归函数第四关引入了递归调用401048: e8 81 ff ff ff callq 400fce func4 40104d: 85 c0 test %eax,%eax 40104f: 75 07 jne 401058 phase_40x4cfunc4的C语言等效代码int func4(int x, int y, int z) { int t ((z - y) (z - y)31) 1; int q t y; if (q x) { if (q x) return 0; return 2*func4(x, q1, z) 1; } return 2*func4(x, y, q-1); }通过分析可知需要func4返回0第二个输入必须为0第一个输入可以是7满足q x因此一组解为7 0。这个阶段考察递归函数的栈帧变化算术右移指令的应用递归终止条件分析7. Phase 5字符串变换第五关实现了一个字符转换40108b: 0f b6 0c 03 movzbl (%rbx,%rax,1),%ecx 401099: 0f b6 92 b0 24 40 00 movzbl 0x4024b0(%rdx),%edx 4010b3: be 5e 24 40 00 mov $0x40245e,%esi关键点输入必须是6字符每个字符低4位作为索引查表地址0x4024b0结果需匹配flyers地址0x40245e查表字符串(gdb) x/s 0x4024b0 maduiersnfotvbyl...计算各字符位置f在第9位 → 输入字符低4位9如Il在第15位 → 低4位15如Oy在第14位 → 低4位14如N因此一组可行解是IONEFG。这个阶段重点字符编码处理查表转换模式识别位操作的实际应用8. Phase 6链表操作第六关最为复杂涉及链表排序401183: ba d0 32 60 00 mov $0x6032d0,%edx ... 4011da: bd 05 00 00 00 mov $0x5,%ebp 4011df: 48 8b 43 08 mov 0x8(%rbx),%rax 4011e3: 8b 00 mov (%rax),%eax 4011e5: 39 03 cmp %eax,(%rbx)破解步骤输入6个1-6且互不相同的数字每个数字x被转换为7-x按转换后的值对链表节点排序要求排序后节点值递减查看链表节点(gdb) x/24wx 0x6032d0 0x6032d0 node1: 0x0000014c 0x00000001 0x006032e0 0x00000000 0x6032e0 node2: 0x000000a8 0x00000002 0x006032f0 0x00000000 ...节点值依次为332, 168, 924, 691, 477, 443。要使它们按7-x的顺序递减排列原始输入应为4 3 2 1 6 5。9. Secret Phase二叉树搜索隐藏关卡通过phase_defused函数触发401630: e8 0d fc ff ff callq 401242 secret_phase触发条件在phase_4答案后追加字符串DrEvil输入数字作为二叉搜索树键值fun7函数实现递归搜索int fun7(Node* node, int val) { if (!node) return -1; if (node-val val) return 0; if (node-val val) return 2*fun7(node-right, val) 1; return 2*fun7(node-left, val); }需要返回2通过二叉树结构分析可知解为22。这个阶段综合考察二叉树的内存表示递归算法的逆向分析隐藏机制的发现方法10. 调试技巧与心得在拆弹过程中我总结了几个实用技巧内存查看技巧x/20xw $rsp查看栈内容x/gx查看8字节数据x/s查看字符串函数分析套路先找函数参数%rdi, %rsi等分析返回值%eax跟踪关键跳转指令常见模式识别循环结构通常有%rax作计数器递归函数会多次call自身字符串操作常用movzbl指令避坑指南注意有符号/无符号跳转jg/ja函数调用前后保存寄存器值栈帧变化时注意%rsp调整整个实验下来最大的收获是对汇编代码不再恐惧。通过GDB实际观察每条指令对寄存器和内存的影响那些晦涩的机器指令逐渐变得有规律可循。特别是看到自己推导的密码成功拆弹时那种成就感是看多少理论都替代不了的。