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_equaln内部查看一下$%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 %rbpadd $0x4 %rbp是在遍历栈上数据

后面的 add $0x1 %rbxcmp $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真好用


电波交流