2026-03-03 完成 😄
1 | ❯ ./bomb ans.txt |
其中可以将答案写在文件里面,然后运行 ./bomb ans.txt,可以不用手动输入。
其次注意,必须在文件末尾留一个空行,不然会爆炸…
参考答案
我的一份答案 ans.txt
1 | Border relations with Canada have never been better. |
我的一份注释 https://gist.github.com/z0z0r4/32628db54848b181a0dc4b5a0347bd58
phase 1
0x402400 里面存了 Border relations with Canada have never been better.
这个字符串就是答案。
phase 2
读取六个数字,第一个数字必须为 1,然后要求前一个数字是后一个数字的两倍,即等比数列。
答案是 1 2 4 8 16 32。
phase 3
phase_3 读取两个数字到 %rcx 和 %rdx,要求 %rcx 即第一个数字的取值范围为 0~7,然后 jmp *0x402470(,%rax,8) 会按照下面的跳表,跳到 8 种 case。
1 | // 0x402470: 0x0000000000400f7c 0x0000000000400fb9 |
%rax = 0->0x400f7c%rax = 1->0x400fb9%rax = 2->0x400f83%rax = 3->0x400f8a%rax = 4->0x400f91%rax = 5->0x400f98%rax = 6->0x400f9f%rax = 7->0x400fa6
8 种 case 都是对 %eax 的赋值,以及跳转到再后面的代码
1 | // case 0 |
%rax = 0->0x400f7c-> 207%rax = 1->0x400fb9-> 311%rax = 2->0x400f83-> 707%rax = 3->0x400f8a-> 256%rax = 4->0x400f91-> 389%rax = 5->0x400f98-> 206%rax = 6->0x400f9f-> 682%rax = 7->0x400fa6-> 327
只要把上面对应的组合输入进去就是答案。
但我还是不知道标 unknown 的有什么用,没有看到跳转到 0x400fad 的,可能疏忽了
phase_3 注释
0000000000400f43 <phase_3>:
400f43: 48 83 ec 18 sub $0x18,%rsp
400f47: 48 8d 4c 24 0c lea 0xc(%rsp),%rcx // %rcx = %rsp + 0xc int 4 bytes, num2
400f4c: 48 8d 54 24 08 lea 0x8(%rsp),%rdx // %rdx = %rsp + 0x8 int 4 bytes, num1
400f51: be cf 25 40 00 mov $0x4025cf,%esi // %esi = 0x4025cf (%d %d) (%rdx %rcx)
400f56: b8 00 00 00 00 mov $0x0,%eax // %eax = 0 (the count of successfully parsed items)
400f5b: e8 90 fc ff ff call 400bf0 __isoc99_sscanf@plt
400f60: 83 f8 01 cmp $0x1,%eax // cmp %eax, 0x1
400f63: 7f 05 jg 400f6a <phase_3+0x27> // %eax > 0x1
400f65: e8 d0 04 00 00 call 40143a <explode_bomb> // only one item, explode the bomb
400f6a: 83 7c 24 08 07 cmpl $0x7,0x8(%rsp) // cmp 0x8(%rsp) (num1), 0x7
400f6f: 77 3c ja 400fad <phase_3+0x6a> // num1 > 0x7, if so jump to 400fad, which explodes the bomb
400f71: 8b 44 24 08 mov 0x8(%rsp),%eax // %eax = 0x8(%rsp) (%eax = num1)
400f75: ff 24 c5 70 24 40 00 jmp *0x402470(,%rax,8) // jump to the address stored at 0x402470 + %rax * 8, which is a jump table
// 0x402470: 0x0000000000400f7c 0x0000000000400fb9
// 0x402480: 0x0000000000400f83 0x0000000000400f8a
// 0x402490: 0x0000000000400f91 0x0000000000400f98
// 0x4024a0: 0x0000000000400f9f 0x0000000000400fa6
// case 0
400f7c: b8 cf 00 00 00 mov $0xcf,%eax // %eax = 0xcf = 207
400f81: eb 3b jmp 400fbe <phase_3+0x7b>
// case 2
400f83: b8 c3 02 00 00 mov $0x2c3,%eax // %eax = 0x2c3 = 707
400f88: eb 34 jmp 400fbe <phase_3+0x7b>
// case 3
400f8a: b8 00 01 00 00 mov $0x100,%eax // %eax = 0x100 = 256
400f8f: eb 2d jmp 400fbe <phase_3+0x7b>
// case 4
400f91: b8 85 01 00 00 mov $0x185,%eax // %eax = 0x185 = 389
400f96: eb 26 jmp 400fbe <phase_3+0x7b>
// case 5
400f98: b8 ce 00 00 00 mov $0xce,%eax // %eax = 0xce = 206
400f9d: eb 1f jmp 400fbe <phase_3+0x7b>
// case 6
400f9f: b8 aa 02 00 00 mov $0x2aa,%eax // %eax = 0x2aa = 682
400fa4: eb 18 jmp 400fbe <phase_3+0x7b>
// case 7
400fa6: b8 47 01 00 00 mov $0x147,%eax // %eax = 0x147 = 327
400fab: eb 11 jmp 400fbe <phase_3+0x7b>
// unknown
400fad: e8 88 04 00 00 call 40143a <explode_bomb>
400fb2: b8 00 00 00 00 mov $0x0,%eax // %eax = 0
400fb7: eb 05 jmp 400fbe <phase_3+0x7b>
// 1
400fb9: b8 37 01 00 00 mov $0x137,%eax // %eax = 0x137 = 311
400fbe: 3b 44 24 0c cmp 0xc(%rsp),%eax // cmp 0xc + %rsp, %eax
400fc2: 74 05 je 400fc9 <phase_3+0x86> // if equal, jump to 400fc9, which ends the phase
400fc4: e8 71 04 00 00 call 40143a <explode_bomb> // if not equal, explode the bomb
400fc9: 48 83 c4 18 add $0x18,%rsp // clean up the stack
400fcd: c3 ret
phase 4
phase 4 给定 二分查找,用一个寄存器 %eax 就可以简单的记录数字树中的路径,向左则 %eax * 2,向右则 %eax * 2 + 1,*2 可以每跳转一次左移一位,最后递归结束后可以得到完整的路径(注意是调用递归后才记录,所以是倒序的,人得从右往左读)
phase 4 要求 %eax 初始化时 0,%eax 结果是 0,也就是一直向左,不向右 +1。
最后要求第二个数字是 0(但我没看懂为什么这里突兀的多这一步,和整个前面的没法衔接起来…)
phase_4 注释
1 | 0000000000400fce <func4>: |
phase 5
phase 5 读取六个字符的字符串,然后取每个字符的低四位(一个 char 有 8 位),四位的取值范围是 0~15,将字符映射到 0x4024b0 所存的 maduiersnfotvbylSo 的对应字符,然后要求最后结果拼起来是 0x40245e 对应的 flyers。
phase_5 注释
1 | 0000000000401062 <phase_5>: |
其中还有 canary,在 phase_5 的头尾,这些应该会在 attack lab 用到。
phase 6
phase 6 要求输入六个不重复、范围在 1~6 的数字,将其逐个 7 - n 映射下,然后按照映射后的数字来重新组合链表,结果要求链表必须是递减的。
我觉得逻辑不是很难,不像上面的进入树的痕迹记录那样的 trick 让我眼前一新(也是记住了),但跳转太多、指令太多了,花了不少时间。
1 |
|
secret phase
read_line会将输入都存入一个数组里
需要在 phase_4 的末尾增加一个 DrEvil 来触发 secret_phase。
secret_phase 要求传入一个数字,在给定的二叉树中查找这个数字,和 phase_4 一样记录痕迹,要求痕迹为 2(实际上就是 000…010)
只需要按照这个树找到任意符合的路径就可以了(左右/左右左)
答案可以是 20 和 22。
1 |
|
TODO
我希望在后续补充记录一些用到的 gdb 调试和寄存器、指令的内容,可能新开 post 或者以后再下面补充。