Solution
前置工作:
在bomb文件夹中创建.gdbinit文件,在其中写入set args sol.txt b phase_1 b phase_2 b phase_3 b phase_4 b phase_5 b phase_6 b *(explode_bomb + 0x1D) commands j *(explode_bomb + 0x31) end r
设置以sol.txt作为输入,在各个phase处输入断点,并跳过
explode_bomb
中的send_msg
函数,来防止爆炸后与服务器通信导致掉分
上面的偏移由分析objdump -d bomb
后的explode_bomb
函数汇编代码得到
(注释里只写了完整的寄存器,方便阅读,实际操作位的可能不同)
phase_1
0000000000400f2d <phase_1>:
400f2d: 48 83 ec 08 sub $0x8,%rsp
400f31: be 10 26 40 00 mov $0x402610,%esi
400f36: e8 41 04 00 00 call 40137c <strings_not_equal>
400f3b: 85 c0 test %eax,%eax
400f3d: 74 05 je 400f44 <phase_1+0x17>
400f3f: e8 0c 07 00 00 call 401650 <explode_bomb>
400f44: 48 83 c4 08 add $0x8,%rsp
400f48: c3 ret
test %eax %eax
判断 strings_not_equal
的返回值来确定输入与答案是否相等
$%rdi$和$%rsi$分别是第一个和第二个参数,上面 mov ... %esi
的操作在传参,于是在 strings_not_equal
n内部查看一下$%rdi$和$%rsi$的值就行了,答案肯定在其中之一
使用 x /s regname
来查看寄存器的字符串表示,得到答案The future will be better tomorrow.
phase_2
0000000000400f49 <phase_2>:
400f49: 55 push %rbp // save %rbp
400f4a: 53 push %rbx // save %rbx
400f4b: 48 83 ec 28 sub $0x28,%rsp // allocate space on stack
400f4f: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax // set canary value, %rax = 0x28
400f56: 00 00
400f58: 48 89 44 24 18 mov %rax,0x18(%rsp) // *(%rsp + 0x18) = %rax
400f5d: 31 c0 xor %eax,%eax // %eax = 0
400f5f: 48 89 e6 mov %rsp,%rsi // %rsi = %rsp
400f62: e8 1f 07 00 00 call 401686 <read_six_numbers> // call read six numbers
400f67: 83 3c 24 00 cmpl $0x0,(%rsp) // compare *(%rsp) with 0
400f6b: 79 05 jns 400f72 <phase_2+0x29> // if not negative, skip <explode_bomb>
400f6d: e8 de 06 00 00 call 401650 <explode_bomb>
400f72: 48 89 e5 mov %rsp,%rbp // %rbp = %rsp
400f75: bb 01 00 00 00 mov $0x1,%ebx // %rbx = 1
[1]400f7a: 89 d8 mov %ebx,%eax // %rax = %rbx = 1
400f7c: 03 45 00 add 0x0(%rbp),%eax // %rax += *(%rbp)
400f7f: 39 45 04 cmp %eax,0x4(%rbp) // compare *(%rbp + 4) with %rax
400f82: 74 05 je 400f89 <phase_2+0x40> // if equal, skip <explode_bomb>
400f84: e8 c7 06 00 00 call 401650 <explode_bomb>
400f89: 83 c3 01 add $0x1,%ebx // %rbx += 1
400f8c: 48 83 c5 04 add $0x4,%rbp // %rbp += 4
400f90: 83 fb 06 cmp $0x6,%ebx // compare %rbx with 0x6
400f93: 75 e5 jne 400f7a <phase_2+0x31> // if not equal, jump to [1] (maybe a loop)
400f95: 48 8b 44 24 18 mov 0x18(%rsp),%rax // %rax = *(%rsp + 18)
400f9a: 64 48 33 04 25 28 00 xor %fs:0x28,%rax // check canary value, %rax ^ 0x28
400fa1: 00 00
400fa3: 74 05 je 400faa <phase_2+0x61> // if equal, that is the canary value has not been changed, skip <__stack_chk_fail>
400fa5: e8 e6 fb ff ff call 400b90 <__stack_chk_fail@plt> // exit abnormally
400faa: 48 83 c4 28 add $0x28,%rsp // restore the space on stack
400fae: 5b pop %rbx // restore %rbx
400faf: 5d pop %rbp // restore %rbp
400fb0: c3 ret
看到 read_six_numbers
,代表读进来六个数字
一开始先判断$*(%rsp)$的值的正负,后面 mov %rsp %rbp
并 add $0x4 %rbp
是在遍历栈上数据
后面的 add $0x1 %rbx
和 cmp $0x6 %ebx
并跳转不难看出这是一个循环
$%rax$的值首先为$%rbx$,每次$%rax += (%rbp)$,比较$(%rbp + 4)$和$%rax$
下面给出循环的伪代码:
given arr[1] to arr[6]
arr[0] = 0
x := 1
for i in 1 .. 6
x := i + arr[i - 1]
if x != arr[i]
bomb!
接着来分析一下 read_six_numbers
read_six_numbers
0000000000401686 <read_six_numbers>:
401686: 48 83 ec 08 sub $0x8,%rsp // allocate space on stack
40168a: 48 89 f2 mov %rsi,%rdx // %rdx = %rsi
40168d: 48 8d 4e 04 lea 0x4(%rsi),%rcx // %rcx = %rsi + 0x4
401691: 48 8d 46 14 lea 0x14(%rsi),%rax // %rax = %rsi + 0x14
401695: 50 push %rax // save %rax
401696: 48 8d 46 10 lea 0x10(%rsi),%rax // %rax = %rsi + 10
40169a: 50 push %rax // save %rax
40169b: 4c 8d 4e 0c lea 0xc(%rsi),%r9 // %r9 = %rsi + 0xc
40169f: 4c 8d 46 08 lea 0x8(%rsi),%r8 // %r8 = %rsi + 0x8
4016a3: be 21 29 40 00 mov $0x402921,%esi // %rsi = 0x402921
4016a8: b8 00 00 00 00 mov $0x0,%eax // %rax = 0
4016ad: e8 8e f5 ff ff call 400c40 <__isoc99_sscanf@plt> // call sscanf
4016b2: 48 83 c4 10 add $0x10,%rsp // restore the space on stack
4016b6: 83 f8 05 cmp $0x5,%eax // if %rax(the elements successfully read in) - 5
4016b9: 7f 05 jg 4016c0 <read_six_numbers+0x3a> // > 0, ok, skip <explode_bomb>
4016bb: e8 90 ff ff ff call 401650 <explode_bomb>
4016c0: 48 83 c4 08 add $0x8,%rsp // restore the space on stack
4016c4: c3 ret
这里 sscanf
应该是传8个参数,第1个是读入字符串的位置,第2个是format字符串,第3~8个是传入的地址
后面因为寄存器位置不够了,就push了两次,用栈上的空间
在运行到 call sscanf
这句的时候用 x /s $rdi($rsi)
查看一下,一个是我们输入的字符串,一个是 %d %d %d %d %d %d
这里居然没有弄混地址的顺序,好人啊
直接推一下得到答案(也可以每一次都用gdb看值,不过不如直接推方便)
$1 \space 2 \space 4 \space 7 \space 11 \space 16$
phase_3
0000000000400fb1 <phase_3>:
400fb1: 48 83 ec 18 sub $0x18,%rsp // allocate space on stack
400fb5: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax // set canary value
400fbc: 00 00
400fbe: 48 89 44 24 08 mov %rax,0x8(%rsp) // *(%rsp + 0x8) = %rax
400fc3: 31 c0 xor %eax,%eax // %rax = 0
400fc5: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx // %rcx = %rsp + 4
400fca: 48 89 e2 mov %rsp,%rdx // %rdx = %rsp
400fcd: be 2d 29 40 00 mov $0x40292d,%esi // %rsi = 0x40292d
400fd2: e8 69 fc ff ff call 400c40 <__isoc99_sscanf@plt> // call sscanf
400fd7: 83 f8 01 cmp $0x1,%eax // compare %rax with 1
400fda: 7f 05 jg 400fe1 <phase_3+0x30> // if greater than 1, skip <explode_bomb> (%rax > 1, scan 2)
400fdc: e8 6f 06 00 00 call 401650 <explode_bomb>
400fe1: 83 3c 24 07 cmpl $0x7,(%rsp) // compare *%rsp with 0x7
400fe5: 77 3b ja 401022 <phase_3+0x71> // if %rsp > 7, explode bomb(*%rsp must be in [0, 7])
400fe7: 8b 04 24 mov (%rsp),%eax // %rax = *%rsp
400fea: ff 24 c5 60 26 40 00 jmp *0x402660(,%rax,8) // jumplist, maybe a switch-case statement
400ff1: b8 86 00 00 00 mov $0x86,%eax // %rax = 134
400ff6: eb 3b jmp 401033 <phase_3+0x82>
400ff8: b8 d3 00 00 00 mov $0xd3,%eax // %rax = 211
400ffd: eb 34 jmp 401033 <phase_3+0x82>
400fff: b8 08 01 00 00 mov $0x108,%eax // %rax = 264
401004: eb 2d jmp 401033 <phase_3+0x82>
401006: b8 34 02 00 00 mov $0x234,%eax // %rax = 564
40100b: eb 26 jmp 401033 <phase_3+0x82>
40100d: b8 1e 01 00 00 mov $0x11e,%eax // %rax = 286
401012: eb 1f jmp 401033 <phase_3+0x82>
401014: b8 60 03 00 00 mov $0x360,%eax // %rax = 864
401019: eb 18 jmp 401033 <phase_3+0x82>
40101b: b8 67 02 00 00 mov $0x267,%eax // %rax = 615
401020: eb 11 jmp 401033 <phase_3+0x82>
401022: e8 29 06 00 00 call 401650 <explode_bomb>
401027: b8 00 00 00 00 mov $0x0,%eax // %rax = 0
40102c: eb 05 jmp 401033 <phase_3+0x82>
40102e: b8 17 01 00 00 mov $0x117,%eax // %rax = 279
401033: 3b 44 24 04 cmp 0x4(%rsp),%eax // compare %rax with *(%rsp + 4)
401037: 74 05 je 40103e <phase_3+0x8d> // if not equal, bomb!
401039: e8 12 06 00 00 call 401650 <explode_bomb>
40103e: 48 8b 44 24 08 mov 0x8(%rsp),%rax
401043: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
40104a: 00 00
40104c: 74 05 je 401053 <phase_3+0xa2>
40104e: e8 3d fb ff ff call 400b90 <__stack_chk_fail@plt>
401053: 48 83 c4 18 add $0x18,%rsp
401057: c3 ret
这里还是先看一眼sscanf里面要读入什么,单步执行到对应位置之后 x/s $rsi
输出 %d %d
表明读入两个整数
根据对上面汇编代码的分析,得出第一个输入必须在$0 \text{到} 7$内
后面用了一个跳转表的结构来跳转,按照下面这个表来跳转,于是按照对应第一个输入给定第二个输入就行了
对应数字 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
十六进制 | 0x117 | 0x86 | 0xd3 | 0x108 | 0x234 | 0x11e | 0x360 | 0x267 |
十进制 | 279 | 134 | 211 | 264 | 564 | 286 | 864 | 615 |
phase_4
0000000000401096 <phase_4>:
401096: 48 83 ec 18 sub $0x18,%rsp
40109a: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4010a1: 00 00
4010a3: 48 89 44 24 08 mov %rax,0x8(%rsp) // *(%rsp + 8) = %rax
4010a8: 31 c0 xor %eax,%eax // %rax = 0
4010aa: 48 8d 4c 24 04 lea 0x4(%rsp),%rcx // %rcx = %rsp + 4
4010af: 48 89 e2 mov %rsp,%rdx // %rdx = %rsp
4010b2: be 2d 29 40 00 mov $0x40292d,%esi // %rsi = 0x40292d
4010b7: e8 84 fb ff ff call 400c40 <__isoc99_sscanf@plt> // call sscanf
4010bc: 83 f8 02 cmp $0x2,%eax // compare %rax with 2
4010bf: 75 06 jne 4010c7 <phase_4+0x31> // if not equal, bomb!
4010c1: 83 3c 24 0e cmpl $0xe,(%rsp) // compare *%rsp with 14
4010c5: 76 05 jbe 4010cc <phase_4+0x36> // if *%rsp <= 14, skip <explode_bomb>
4010c7: e8 84 05 00 00 call 401650 <explode_bomb>
4010cc: ba 0e 00 00 00 mov $0xe,%edx // %rdx = 14
4010d1: be 00 00 00 00 mov $0x0,%esi // %rsi = 0
4010d6: 8b 3c 24 mov (%rsp),%edi // %rdi = *(%rsp)
4010d9: e8 7a ff ff ff call 401058 <func4> // call func4
4010de: 83 f8 07 cmp $0x7,%eax // compare %rax with 7
4010e1: 75 07 jne 4010ea <phase_4+0x54> // if not equal, bomb!
4010e3: 83 7c 24 04 07 cmpl $0x7,0x4(%rsp) // compare *(%rsp + 4) with 7
4010e8: 74 05 je 4010ef <phase_4+0x59> // if equal, skip <explode_bomb>
4010ea: e8 61 05 00 00 call 401650 <explode_bomb>
4010ef: 48 8b 44 24 08 mov 0x8(%rsp),%rax
4010f4: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
4010fb: 00 00
4010fd: 74 05 je 401104 <phase_4+0x6e>
4010ff: e8 8c fa ff ff call 400b90 <__stack_chk_fail@plt>
401104: 48 83 c4 18 add $0x18,%rsp
401108: c3 ret
这里sscanf也是读入两个数 %d %d
,第一个读进来的数要比14大
这里调用了func4,第一个参数的值是*(%rsp),也就是读进来的第一个数,第二个参数是0,第三个参数是14,func4返回值应当是7
这里其实可以暴力枚举做了,但是懒得搞
接着比较*(%rsp + 4)和7,表明第二个输入的应该是7
0000000000401058 <func4>:
401058: 48 83 ec 08 sub $0x8,%rsp
40105c: 89 d0 mov %edx,%eax // %rax = %rdx
40105e: 29 f0 sub %esi,%eax // %rax -= %rsi
401060: 89 c1 mov %eax,%ecx // %rcx = %rax
401062: c1 e9 1f shr $0x1f,%ecx // %rcx = %rcx >> 31(logically)
401065: 01 c8 add %ecx,%eax // %rax += %rcx
401067: d1 f8 sar $1,%eax // %rax >>= 1(arithmetically)
401069: 8d 0c 30 lea (%rax,%rsi,1),%ecx // %rcx = %rax + %rsi
40106c: 39 f9 cmp %edi,%ecx // compare %rcx with %rdi
40106e: 7e 0c jle 40107c <func4+0x24> // if %rcx <= %rdi, jump to [1]
401070: 8d 51 ff lea -0x1(%rcx),%edx // %rdx = %rcx - 1
401073: e8 e0 ff ff ff call 401058 <func4> // call func4
401078: 01 c0 add %eax,%eax // %rax *= 2
40107a: eb 15 jmp 401091 <func4+0x39> // exit
[1]40107c: b8 00 00 00 00 mov $0x0,%eax // %rax = 0
401081: 39 f9 cmp %edi,%ecx // compare %rcx with %rdi
401083: 7d 0c jge 401091 <func4+0x39> // if %rcx >= %rdx, exit
401085: 8d 71 01 lea 0x1(%rcx),%esi // %rsi = %rcx + 1
401088: e8 cb ff ff ff call 401058 <func4> // call func4
40108d: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax // %rax = %rax * 2 + 1
401091: 48 83 c4 08 add $0x8,%rsp
401095: c3 ret
是一个递归函数,跳出条件是$rdi\le rcx$
int func4(int rdi, int rsi, int rdx) {
int rcx = (rdx + rsi) / 2 + (rdx < rsi);
if (rcx < rdi) {
return func4(rdi, rcx + 1, rdx) * 2 + 1;
} else if (rdi == rcx) { // must not be equal
return 0;
} else {
return func4(rdi, rsi, rcx - 1) * 2;
}
}
传入$%rsi=0, %rdx=14$
$%rdi$是不改变的,通过条件判断来实现控制
最后返回0,要构造返回7,构造序列$0 \space 1 \space 3 \space 7$,是唯一的
也就是前面两次$%rci<%rdi$,最后$%rci=%rdi$
把结果算一下:
regs/times | 1 | 2 | 3 | 4 |
---|---|---|---|---|
$%rsi$ | 0 | 8 | 12 | 14 |
$%rdx$ | 14 | 14 | 14 | 14 |
$%rcx$ | 7 | 11 | 13 | 14 |
最后是$14$就行,结合前面的第二个输入,得到答案:$14 \space 7$
phase_5
0000000000401109 <phase_5>:
401109: 53 push %rbx
40110a: 48 89 fb mov %rdi,%rbx // %rbx = %rdi
40110d: e8 4c 02 00 00 call 40135e <string_length> // call string_length
401112: 83 f8 06 cmp $0x6,%eax // length must be 6
401115: 74 05 je 40111c <phase_5+0x13> // if equal, skip <explode_bomb>
401117: e8 34 05 00 00 call 401650 <explode_bomb>
40111c: 48 89 d8 mov %rbx,%rax // %rax = %rbx
40111f: 48 8d 7b 06 lea 0x6(%rbx),%rdi // %rdi = %rbx + 6
401123: b9 00 00 00 00 mov $0x0,%ecx // %rcx = 0
[1]401128: 0f b6 10 movzbl (%rax),%edx // %rdx = *(%rax), cast
40112b: 83 e2 0f and $0xf,%edx // %rdx &= 1111b
40112e: 03 0c 95 a0 26 40 00 add 0x4026a0(,%rdx,4),%ecx // %rcx += *(0x4026a0 + 4 * $rdx)
401135: 48 83 c0 01 add $0x1,%rax // %rax += 1
401139: 48 39 f8 cmp %rdi,%rax // compare %rax with %rdi
40113c: 75 ea jne 401128 <phase_5+0x1f> // if not equal, jump to [1]
40113e: 83 f9 34 cmp $0x34,%ecx // compare %rcx with 52
401141: 74 05 je 401148 <phase_5+0x3f> // if equal, skip <explode_bomb>
401143: e8 08 05 00 00 call 401650 <explode_bomb>
401148: 5b pop %rbx
401149: c3 ret
读一行,长度是$6$
于是可以穷举,但是懒得搞
这里$%rax$每次$+1$,和以往$+8$不同,加之有位扩展,判定%rax对应是char,这里的操作应当是循环遍历我们给的字符串
and操作取$%rdx$低四位,$%rcx$每次从$0x4026a0$加上偏移处取值累加,去看一眼
通过 x/16d 0x4026a0
得到:
0x4026a0 <array.3601>: 2 10 6 1
0x4026b0 <array.3601+16>: 12 16 9 3
0x4026c0 <array.3601+32>: 4 7 14 5
0x4026d0 <array.3601+48>: 11 8 15 13
有tui了就不能复制了,还得退出去看一眼
是个数组,凑一下就行了,$52=5*10+2$
于是低四位$0 \space 1 \space 1 \space 1 \space 1 \space 1$
答案可以是$@AAAAA$
phase_6
000000000040114a <phase_6>:
40114a: 41 55 push %r13
40114c: 41 54 push %r12
40114e: 55 push %rbp
40114f: 53 push %rbx
401150: 48 83 ec 68 sub $0x68,%rsp
401154: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
40115b: 00 00
40115d: 48 89 44 24 58 mov %rax,0x58(%rsp)
401162: 31 c0 xor %eax,%eax // %rax = 0
401164: 48 89 e6 mov %rsp,%rsi // %rsi = %rsp
401167: e8 1a 05 00 00 call 401686 <read_six_numbers> // call read_six_numbers
40116c: 49 89 e4 mov %rsp,%r12 // %r12 = %rsp
40116f: 41 bd 00 00 00 00 mov $0x0,%r13d // %r13 = 0
[3]401175: 4c 89 e5 mov %r12,%rbp // %rbp = %r12
401178: 41 8b 04 24 mov (%r12),%eax // %rax = *(%r12)
40117c: 83 e8 01 sub $0x1,%eax // %rax -= 1
40117f: 83 f8 05 cmp $0x5,%eax // compare %rax with 5
401182: 76 05 jbe 401189 <phase_6+0x3f> // if %rax <= 5, skip <explode_bomb> (*%r12) <= 6
401184: e8 c7 04 00 00 call 401650 <explode_bomb>
401189: 41 83 c5 01 add $0x1,%r13d // %r13 += 1
40118d: 41 83 fd 06 cmp $0x6,%r13d // compare %r13 with 6
401191: 74 3d je 4011d0 <phase_6+0x86> // if equal, jump to [1]
401193: 44 89 eb mov %r13d,%ebx // %rbx = %r13
[2]401196: 48 63 c3 movslq %ebx,%rax // %rax = %ebx(sign)
401199: 8b 04 84 mov (%rsp,%rax,4),%eax // %rax = *(%rsp + %rax * 4)
40119c: 39 45 00 cmp %eax,0x0(%rbp) // compare *(%rbp) with %rax
40119f: 75 05 jne 4011a6 <phase_6+0x5c> // if not equal, skip <explode_bomb>
4011a1: e8 aa 04 00 00 call 401650 <explode_bomb>
4011a6: 83 c3 01 add $0x1,%ebx // %rbx += 1
4011a9: 83 fb 05 cmp $0x5,%ebx // compare %rbx with 5
4011ac: 7e e8 jle 401196 <phase_6+0x4c> // if %rbx <= 5, jump to [2]
4011ae: 49 83 c4 04 add $0x4,%r12 // %r12 += 4
4011b2: eb c1 jmp 401175 <phase_6+0x2b> // jump to [3]
----------------------------------------------------------------------------------------------------------
[7]4011b4: 48 8b 52 08 mov 0x8(%rdx),%rdx // %rdx = *(%rdx + 8)
4011b8: 83 c0 01 add $0x1,%eax // %rax += 1
4011bb: 39 c8 cmp %ecx,%eax // compare %rax with %rcx
4011bd: 75 f5 jne 4011b4 <phase_6+0x6a> // if %rcx != %rax, jump to [7]
[8]4011bf: 48 89 54 74 20 mov %rdx,0x20(%rsp,%rsi,2) // *(%rsp + %rsi * 2 + 32) = %rdx
4011c4: 48 83 c6 04 add $0x4,%rsi // %rsi += 4
4011c8: 48 83 fe 18 cmp $0x18,%rsi // compare %rsi with 24
4011cc: 75 07 jne 4011d5 <phase_6+0x8b> // if %rsi != 24, jump to [5]
4011ce: eb 19 jmp 4011e9 <phase_6+0x9f> // else jump to [6]
[1]4011d0: be 00 00 00 00 mov $0x0,%esi // %rsi = 0
[5]4011d5: 8b 0c 34 mov (%rsp,%rsi,1),%ecx // %rcx = *(%rsp + %rsi)
4011d8: b8 01 00 00 00 mov $0x1,%eax // %rax = 1
4011dd: ba f0 42 60 00 mov $0x6042f0,%edx // %rdx = 0x6042f0
4011e2: 83 f9 01 cmp $0x1,%ecx // compare %rcx with 1
4011e5: 7f cd jg 4011b4 <phase_6+0x6a> // if %rcx > 1, jump to [7]
4011e7: eb d6 jmp 4011bf <phase_6+0x75> // else jump to [8]
----------------------------------------------------------------------------------------------------------
[6]4011e9: 48 8b 5c 24 20 mov 0x20(%rsp),%rbx // %rbx = *(%rsp + 32)
4011ee: 48 8d 44 24 20 lea 0x20(%rsp),%rax // %rax = %rsp + 32
4011f3: 48 8d 74 24 48 lea 0x48(%rsp),%rsi // %rsi = %rsp + 72
4011f8: 48 89 d9 mov %rbx,%rcx // %rcx = %rbx
[9]4011fb: 48 8b 50 08 mov 0x8(%rax),%rdx // %rdx = *(%rax + 8)
4011ff: 48 89 51 08 mov %rdx,0x8(%rcx) // *(%rcx + 8) = %rdx
401203: 48 83 c0 08 add $0x8,%rax // %rax += 8
401207: 48 89 d1 mov %rdx,%rcx // %rcx = %rdx
40120a: 48 39 f0 cmp %rsi,%rax // compare %rax with %rsi
40120d: 75 ec jne 4011fb <phase_6+0xb1> // if %rax != %rsi, jump to [9]
40120f: 48 c7 42 08 00 00 00 movq $0x0,0x8(%rdx) // *(%rdx + 8) = 0
401216: 00
401217: bd 05 00 00 00 mov $0x5,%ebp // %rbp = 5
[10]40121c: 48 8b 43 08 mov 0x8(%rbx),%rax // %rax = *(%rbx + 8)
401220: 8b 00 mov (%rax),%eax // %rax = *(%rax)
401222: 39 03 cmp %eax,(%rbx) // compare *(%rbx) with %rax
401224: 7d 05 jge 40122b <phase_6+0xe1> // if *(%rbx) >= %rax, skip <explode_bomb>
401226: e8 25 04 00 00 call 401650 <explode_bomb>
40122b: 48 8b 5b 08 mov 0x8(%rbx),%rbx // %rbx = *(%rbx + 8)
40122f: 83 ed 01 sub $0x1,%ebp // %rbp -= 1
401232: 75 e8 jne 40121c <phase_6+0xd2> // if %rbp != 0, jump to [10]
401234: 48 8b 44 24 58 mov 0x58(%rsp),%rax
401239: 64 48 33 04 25 28 00 xor %fs:0x28,%rax
401240: 00 00
401242: 74 05 je 401249 <phase_6+0xff>
401244: e8 47 f9 ff ff call 400b90 <__stack_chk_fail@plt>
401249: 48 83 c4 68 add $0x68,%rsp
40124d: 5b pop %rbx
40124e: 5d pop %rbp
40124f: 41 5c pop %r12
401251: 41 5d pop %r13
401253: c3 ret
第一个读进来的数要在$[1,6]$
后面遍历读进来的六个数,每一个数都不能和前面一个数一样
后面跳转到[3]表示每个数都应该在$[1,6]$,而且不能和之前的数一样,输入的数应当是$1~6$的一个排列,于是可以枚举,但是懒得搞
我们看到最后的情况是跳转到[1]处,mov $0x6042f0 %edx
这句把一个地址赋给了$%rdx$,打印一下发现类型是个 <node1>
,应该是结构体
分析一下[1]后面跳转的逻辑,只有当输入的数$\le 1$时,才会跳转到[8]处,否则跳转到[7]处
[8]处把$%rdx$的值存在栈上,最多存$5$个数,且把读入的数往后移动一位(如果这之前还有先到[7]则是把$*(%rdx + 8)$的值存在栈上)
[7]处把$%rdx$的值变成$%rdx$往后$8$字节的地址对应的值,随后比较$%rcx$与$%rax$,不相等就继续改变$%rdx$,而且每次使$%rax += 1$(之前以为必须相等,枚举了所有情况后发现都不对,发现其实这里可以存地址,结合之前的node1,有点像链表)
而且[7]最后一定会会连锁启动[8],相当于遍历输入的数了
$%rax$刚开始是$1$,每次经过[7]的时候会$+1$,而跳入的条件[7]的条件是$%rcx$的值$\gt 1$
于是发现[8]里面存的就是第$x$($x$是我们按次序输入的数)个node的地址
后面是改变next指针把链表重新连接了一下,按照我们给定的顺序,最后一个指向$0$就是nullptr
按照后面的逻辑推算一下,应当是按照node的第一个成员变量的值排序,让最大的在前面,于是我们去check一下所有的node就好啦!
node1 | node2 | node3 | node4 | node5 | node6 | |
---|---|---|---|---|---|---|
value | 918 | 189 | 96 | 704 | 808 | 696 |
rank | 1 | 5 | 6 | 3 | 2 | 4 |
答案是$1 \space 5 \space 4 \space 6 \space 2 \space 3$
secret_phase
直接 jump secret_phase
,不想找入口了
0000000000401292 <secret_phase>:
401292: 53 push %rbx
401293: e8 2d 04 00 00 call 4016c5 <read_line>
401298: ba 0a 00 00 00 mov $0xa,%edx // %rbx = 10
40129d: be 00 00 00 00 mov $0x0,%esi // %rsi = 0
4012a2: 48 89 c7 mov %rax,%rdi // %rdi = %rax
4012a5: e8 76 f9 ff ff call 400c20 <strtol@plt>
4012aa: 48 89 c3 mov %rax,%rbx // %rbx = %rax
4012ad: 8d 40 ff lea -0x1(%rax),%eax // %rax -= 1;
4012b0: 3d e8 03 00 00 cmp $0x3e8,%eax // compare %rax with 0x3e8
4012b5: 76 05 jbe 4012bc <secret_phase+0x2a> // if %rax <= 0x3e8, skip <explode_bomb>
4012b7: e8 94 03 00 00 call 401650 <explode_bomb>
4012bc: 89 de mov %ebx,%esi // %rsi = %rbx
4012be: bf 10 41 60 00 mov $0x604110,%edi // %rdi = 0x604110
4012c3: e8 8c ff ff ff call 401254 <fun7> // call fun7
4012c8: 83 f8 02 cmp $0x2,%eax // compare %rax with 2
4012cb: 74 05 je 4012d2 <secret_phase+0x40> // if %rax == 2, skip <explode_bomb>
4012cd: e8 7e 03 00 00 call 401650 <explode_bomb>
4012d2: bf 38 26 40 00 mov $0x402638,%edi // %rdi = 0x402638
4012d7: e8 94 f8 ff ff call 400b70 <puts@plt>
4012dc: e8 0a 05 00 00 call 4017eb <phase_defused>
4012e1: 5b pop %rbx
4012e2: c3 ret
输入一个long,范围在$[1, 0x3e9]$,又可以枚举
调用fun7,第二个参数是$10$
0000000000401254 <fun7>:
401254: 48 83 ec 08 sub $0x8,%rsp
401258: 48 85 ff test %rdi,%rdi
40125b: 74 2b je 401288 <fun7+0x34> // if %rdi == 0, bomb
40125d: 8b 17 mov (%rdi),%edx // %rdx = *(%rdi)
40125f: 39 f2 cmp %esi,%edx
401261: 7e 0d jle 401270 <fun7+0x1c> // if %rdx <= %rsi, jump to [1]
401263: 48 8b 7f 08 mov 0x8(%rdi),%rdi // %rdi = *(%rdi + 8)
401267: e8 e8 ff ff ff call 401254 <fun7> // %rax = call fun7
40126c: 01 c0 add %eax,%eax // %rax *= 2
40126e: eb 1d jmp 40128d <fun7+0x39> // exit
[1]401270: b8 00 00 00 00 mov $0x0,%eax
401275: 39 f2 cmp %esi,%edx
401277: 74 14 je 40128d <fun7+0x39> // if %rsi == %rdx, exit
401279: 48 8b 7f 10 mov 0x10(%rdi),%rdi // %rdi = *(%rdi + 16)
40127d: e8 d2 ff ff ff call 401254 <fun7> // %rax = call fun7
401282: 8d 44 00 01 lea 0x1(%rax,%rax,1),%eax // %rax = %rax * 2 + 1
401286: eb 05 jmp 40128d <fun7+0x39> // exit
401288: b8 ff ff ff ff mov $0xffffffff,%eax
40128d: 48 83 c4 08 add $0x8,%rsp
401291: c3 ret
#define B sizeof(unsigned char)
int fun7(int *rdi, int rsi) {
if (rdi == 0) {
return 0xffffffff;
}
if (*rdi < rsi) {
return fun7(*(rdi + 16 * B), rsi) * 2 + 1;
} else if (*rdi == rsi) {
return 0;
} else {
return fun7(*(rdi + 8 * B), rsi) * 2;
}
}
返回值是$2$,序列是$0 \space 1 \space 2$
这参数调用看着又像一个结构体指针
check一下
0x604110 <n1>: 0x0000000000000024 0x0000000000604130
0x604120 <n1+16>: 0x0000000000604150 0x0000000000000000
0x604130 <n21>: 0x0000000000000008 0x00000000006041b0
0x604140 <n21+16>: 0x0000000000604170 0x0000000000000000
0x604150 <n22>: 0x0000000000000032 0x0000000000604190
0x604160 <n22+16>: 0x00000000006041d0 0x0000000000000000
0x604170 <n32>: 0x0000000000000016 0x0000000000604290
0x604180 <n32+16>: 0x0000000000604250 0x0000000000000000
0x604190 <n33>: 0x000000000000002d 0x00000000006041f0
0x6041a0 <n33+16>: 0x00000000006042b0 0x0000000000000000
0x6041b0 <n31>: 0x0000000000000006 0x0000000000604210
0x6041c0 <n31+16>: 0x0000000000604270 0x0000000000000000
0x6041d0 <n34>: 0x000000000000006b 0x0000000000604230
0x6041e0 <n34+16>: 0x00000000006042d0 0x0000000000000000
0x6041f0 <n45>: 0x0000000000000028 0x0000000000000000
0x604200 <n45+16>: 0x0000000000000000 0x0000000000000000
0x604210 <n41>: 0x0000000000000001 0x0000000000000000
0x604220 <n41+16>: 0x0000000000000000 0x0000000000000000
0x604230 <n47>: 0x0000000000000063 0x0000000000000000
0x604240 <n47+16>: 0x0000000000000000 0x0000000000000000
0x604250 <n44>: 0x0000000000000023 0x0000000000000000
0x604260 <n44+16>: 0x0000000000000000 0x0000000000000000
0x604270 <n42>: 0x0000000000000007 0x0000000000000000
0x604280 <n42+16>: 0x0000000000000000 0x0000000000000000
0x604290 <n43>: 0x0000000000000014 0x0000000000000000
0x6042a0 <n43+16>: 0x0000000000000000 0x0000000000000000
0x6042b0 <n46>: 0x000000000000002f 0x0000000000000000
0x6042c0 <n46+16>: 0x0000000000000000 0x0000000000000000
0x6042d0 <n48>: 0x00000000000003e9 0x0000000000000000
0x6042e0 <n48+16>: 0x0000000000000000 0x0000000000000000
看起来前面几个结构体第二个和第三个成员变量(八个字节为单位)存储的是一个地址,后面几个则缺少了
然后发现上面return有点像segment tree(虽然好像没什么关系),合理猜测这些节点组成了二叉树,前面几个分别是指向左儿子和右儿子的指针,而叶子节点没有儿子
结构如下
graph TB;
n1:32-->n21:8
n1:32-->n22:50
n21:8-->n31:6
n21:8-->n32:22
n22:50-->n33:45
n22:50-->n34:107
n31:6-->n41:1
n31:6-->n42:7
n32:22-->n43:20
n32:22-->n44:35
n33:45-->n45:40
n33:45-->n46:47
n34:107-->n47:99
n34:107-->n48:1001
不知道为什么这里不能解析mermaid
对应顺序,左儿子,右儿子,左儿子,值是$20$
所以答案就是$20$
总结:
kitty真好用