Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CMU 15213: Bomb Lab #10

Open
kiki-zjq opened this issue Jan 2, 2024 · 0 comments
Open

CMU 15213: Bomb Lab #10

kiki-zjq opened this issue Jan 2, 2024 · 0 comments

Comments

@kiki-zjq
Copy link
Owner

kiki-zjq commented Jan 2, 2024

CMU 15213 Bomb Lab

在 Bomb Lab 中我们需要通过 GDB 工具来对代码进行分析,来决定正确的输入,以此避免调用到 explode_bomb 函数

调试的关键指令

  • b <func_name>: 在对应的 func_name 处打上断点,例如 b explode_bomb
  • b *addr: 在对应的 address 处打上断点,打上断点后该 addr 那一行的代码未执行,例如 b *0x00005555555567b6
  • i b: 展示目前打上的所有断点
  • delete <NO>: 删除某一个指定断点
  • info registers: 展示所有的 register info
  • p *($rsi)
  • p *(0x7fffffffe610)
  • p *(int *)($rsi)
  • p $ebx
  • info registers rcx

Phase_1

0x00005555555567a7 <+0>:	endbr64 
0x00005555555567ab <+4>:	sub    $0x8,%rsp
0x00005555555567af <+8>:	lea    0x59ea(%rip),%rsi        # 0x55555555c1a0 <sval>
0x00005555555567b6 <+15>:	call   0x555555557035 <strings_not_equal>
0x00005555555567bb <+20>:	test   %al,%al
0x00005555555567bd <+22>:	jne    0x5555555567c4 <phase_1+29>
0x00005555555567bf <+24>:	add    $0x8,%rsp
0x00005555555567c3 <+28>:	ret    
**0x00005555555567c4 <+29>:	call   0x5555555572d9 <explode_bomb>**
0x00005555555567c9 <+34>:	jmp    0x5555555567bf <phase_1+24>

很单纯的一题

  • +29 行会调用 explode_bomb
  • +22 行如果满足条件就会跳到 +29
  • 为了避免 +22 的条件被满足,我们需要输入一个 string,满足
  • 如果 strings_not_equal (我们的输入不是正确的答案),%al 会存储 0x1
  • 如果 strings_not_equal 不满足(我们的输入的是正确的答案),%al 会存储 0x0
  • test %al, %al%al & %al == 0 的时候,会设置 ZF
  • jne 的判断条件是 ~ZF 即不相等 / 非零
  • 因此逻辑是当我们的 strings_not_equal 不满足,ZF 会变成 1,那么 jne 就不会触发,就避免了爆炸

解法

  • b *0x00005555555567bb 打上断点一
  • 运行到断点一,x/s 0x55555555c1a0 或者 x/s $rsi 显示内存中的 string
  • 得到的 string 即为最终的答案

Phase_2

   0x00005555555567cb <+0>:	endbr64 
   0x00005555555567cf <+4>:	push   %rbx
   0x00005555555567d0 <+5>:	sub    $0x40,%rsp
   0x00005555555567d4 <+9>:	mov    %fs:0x28,%rax
   0x00005555555567dd <+18>:	mov    %rax,0x38(%rsp)
   0x00005555555567e2 <+23>:	xor    %eax,%eax
   0x00005555555567e4 <+25>:	mov    %rsp,%rsi
   0x00005555555567e7 <+28>:	call   0x55555555731b <read_six_numbers>
   0x00005555555567ec <+33>:	cmpq   $0x2,(%rsp)
   0x00005555555567f1 <+38>:	jne    **0x5555555567fa <phase_2+47>**
   0x00005555555567f3 <+40>:	mov    $0x1,%ebx
   0x00005555555567f8 <+45>:	jmp    0x555555556805 <phase_2+58>
   **0x00005555555567fa <+47>:	call   0x5555555572d9 <explode_bomb>**
   0x00005555555567ff <+52>:	jmp    0x5555555567f3 <phase_2+40>
   0x0000555555556801 <+54>:	add    $0x1,%rbx
   0x0000555555556805 <+58>:	cmp    $0x5,%rbx
   0x0000555555556809 <+62>:	ja     0x555555556820 <phase_2+85>
   0x000055555555680b <+64>:	mov    -0x8(%rsp,%rbx,8),%rax
   0x0000555555556810 <+69>:	add    %rax,%rax
   0x0000555555556813 <+72>:	cmp    %rax,(%rsp,%rbx,8)
   0x0000555555556817 <+76>:	je     0x555555556801 <phase_2+54>
   **0x0000555555556819 <+78>:	call   0x5555555572d9 <explode_bomb>**
   0x000055555555681e <+83>:	jmp    0x555555556801 <phase_2+54>
   0x0000555555556820 <+85>:	mov    0x38(%rsp),%rax
   0x0000555555556825 <+90>:	sub    %fs:0x28,%rax
   0x000055555555682e <+99>:	jne    0x555555556836 <phase_2+107>
   0x0000555555556830 <+101>:	add    $0x40,%rsp
   0x0000555555556834 <+105>:	pop    %rbx
   0x0000555555556835 <+106>:	ret    
   0x0000555555556836 <+107>:	call   0x555555556370 <__stack_chk_fail@plt>
  • 这一题我们需要输入 6 个数字
  • +33 行我们对比 cmpq $0x2,(%rsp) ,我们会执行 (%rsp) - $0x2,如果两者相同,就会 set ZF = 1,当 ZF = 1 的时候,+38 行的 jne 就不会被满足,就躲开了第一个炸弹
  • +40 行我们把 0x1 存储到 %ebx 中,然后我们会跳到 +58,在这个对比中我们 p $rbx 就可以看出来 $rbx 里面存储的是 0x1 (+40 行操作导致的结果)
  • 0x1 < 0x5 不满足 +62 的 ja,因此我们会继续执行
  • +64 行有一个 -0x8(%rsp,%rbx,8),计算的逻辑是 -0x8 + %rsp + 8 * %rbx
  • +64 行的行为是将上述位置的数字给移动到 %rax 中。注意,%rsp + 0 拿到的是我们的第 0 个输入,因此当 %rbx = 1 的时候,上面相当于计算出了 -0x8 + %rsp + 8 * %rbx = %rsp,对应的就是我们的第 0 个输入 (2)
  • +69 行我们执行 %rax += %rax
  • +72 行我们执行了一个对比,(%rsp, %rbx, 8) = %rsp + 8 * %rbx,当 %rbx 为 1 的时候,我们得到的其实就是第 1 个输入
  • 因此我们的 input[1] = input[0] + input[0] = 4
  • 随后 +76 行是一个循环逻辑,我们会跳回到 +54

通过上面的逻辑,我们差不多可以写出伪代码了

if (input[0] != 2) bomb()

for (int i = 1; i <= 5; i++) {
	if (input[i - 1] + input[i - 1] != input[i]) bomb()
}

最终的结果就是 2 4 8 16 32 64

Phase_3

   0x000055555555683b <+0>:	endbr64 
   0x000055555555683f <+4>:	sub    $0x28,%rsp
   0x0000555555556843 <+8>:	mov    %fs:0x28,%rax
   0x000055555555684c <+17>:	mov    %rax,0x18(%rsp)
   0x0000555555556851 <+22>:	xor    %eax,%eax
   0x0000555555556853 <+24>:	lea    0x7(%rsp),%rcx
   0x0000555555556858 <+29>:	lea    0x8(%rsp),%rdx
   0x000055555555685d <+34>:	lea    0x10(%rsp),%r8
   0x0000555555556862 <+39>:	lea    0x28e3(%rip),%rsi        # 0x55555555914c
   0x0000555555556869 <+46>:	call   0x555555556430 <__isoc99_sscanf@plt>
   0x000055555555686e <+51>:	cmp    $0x2,%eax 
   0x0000555555556871 <+54>:	jle    0x555555556897 <phase_3+92>
   0x0000555555556873 <+56>:	mov    0x8(%rsp),%rax
   0x0000555555556878 <+61>:	sub    $0x6,%rax
   0x000055555555687c <+65>:	cmp    $0x7,%rax
   0x0000555555556880 <+69>:	ja     0x55555555698b <phase_3+336>
   0x0000555555556886 <+75>:	lea    0x28d3(%rip),%rdx        # 0x555555559160
   0x000055555555688d <+82>:	movslq (%rdx,%rax,4),%rax
   0x0000555555556891 <+86>:	add    %rdx,%rax
   0x0000555555556894 <+89>:	notrack jmp *%rax
   **0x0000555555556897 <+92>:	call   0x5555555572d9 <explode_bomb>**
   0x000055555555689c <+97>:	jmp    0x555555556873 <phase_3+56>
   0x000055555555689e <+99>:	cmpq   $0x67,0x10(%rsp)
   0x00005555555568a4 <+105>:	jne    0x5555555568b0 <phase_3+117>
   0x00005555555568a6 <+107>:	mov    $0x62,%eax
   0x00005555555568ab <+112>:	jmp    0x555555556995 <phase_3+346>
   0x00005555555568b0 <+117>:	call   0x5555555572d9 <explode_bomb>
   0x00005555555568b5 <+122>:	mov    $0x62,%eax
   0x00005555555568ba <+127>:	jmp    0x555555556995 <phase_3+346>
   0x00005555555568bf <+132>:	cmpq   $0x53,0x10(%rsp)
   0x00005555555568c5 <+138>:	jne    0x5555555568d1 <phase_3+150>
   0x00005555555568c7 <+140>:	mov    $0x7a,%eax
   0x00005555555568cc <+145>:	jmp    0x555555556995 <phase_3+346>
   0x00005555555568d1 <+150>:	call   0x5555555572d9 <explode_bomb>
   0x00005555555568d6 <+155>:	mov    $0x7a,%eax
   0x00005555555568db <+160>:	jmp    0x555555556995 <phase_3+346>
   0x00005555555568e0 <+165>:	cmpq   $0x73,0x10(%rsp)
   0x00005555555568e6 <+171>:	jne    0x5555555568f2 <phase_3+183>
   0x00005555555568e8 <+173>:	mov    $0x77,%eax
   0x00005555555568ed <+178>:	jmp    0x555555556995 <phase_3+346>
   0x00005555555568f2 <+183>:	call   0x5555555572d9 <explode_bomb>
   0x00005555555568f7 <+188>:	mov    $0x77,%eax
   0x00005555555568fc <+193>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556901 <+198>:	cmpq   $0x64,0x10(%rsp)
   0x0000555555556907 <+204>:	jne    0x555555556913 <phase_3+216>
   0x0000555555556909 <+206>:	mov    $0x70,%eax
   0x000055555555690e <+211>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556913 <+216>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556918 <+221>:	mov    $0x70,%eax
   0x000055555555691d <+226>:	jmp    0x555555556995 <phase_3+346>
   0x000055555555691f <+228>:	cmpq   $0x3b,0x10(%rsp)
   0x0000555555556925 <+234>:	jne    0x55555555692e <phase_3+243>
   0x0000555555556927 <+236>:	mov    $0x66,%eax
   0x000055555555692c <+241>:	jmp    0x555555556995 <phase_3+346>
   0x000055555555692e <+243>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556933 <+248>:	mov    $0x66,%eax
   0x0000555555556938 <+253>:	jmp    0x555555556995 <phase_3+346>
   0x000055555555693a <+255>:	cmpq   $0x31,0x10(%rsp)
   0x0000555555556940 <+261>:	jne    0x555555556949 <phase_3+270>

   0x0000555555556942 <+263>:	mov    $0x6f,%eax
   0x0000555555556947 <+268>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556949 <+270>:	call   0x5555555572d9 <explode_bomb>
   0x000055555555694e <+275>:	mov    $0x6f,%eax
   0x0000555555556953 <+280>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556955 <+282>:	cmpq   $0x3c,0x10(%rsp)
   0x000055555555695b <+288>:	jne    0x555555556964 <phase_3+297>
   0x000055555555695d <+290>:	mov    $0x6a,%eax
   0x0000555555556962 <+295>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556964 <+297>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556969 <+302>:	mov    $0x6a,%eax
   0x000055555555696e <+307>:	jmp    0x555555556995 <phase_3+346>
   0x0000555555556970 <+309>:	cmpq   $0x50,0x10(%rsp)
   0x0000555555556976 <+315>:	jne    0x55555555697f <phase_3+324>
   0x0000555555556978 <+317>:	mov    $0x66,%eax
   0x000055555555697d <+322>:	jmp    0x555555556995 <phase_3+346>
   0x000055555555697f <+324>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556984 <+329>:	mov    $0x66,%eax
   0x0000555555556989 <+334>:	jmp    0x555555556995 <phase_3+346>
   **0x000055555555698b <+336>:	call   0x5555555572d9 <explode_bomb>**
   0x0000555555556990 <+341>:	mov    $0x64,%eax
   0x0000555555556995 <+346>:	cmp    %al,0x7(%rsp)
   0x0000555555556999 <+350>:	jne    0x5555555569b0 <phase_3+373>
   0x000055555555699b <+352>:	mov    0x18(%rsp),%rax
   0x00005555555569a0 <+357>:	sub    %fs:0x28,%rax
   0x00005555555569a9 <+366>:	jne    0x5555555569b7 <phase_3+380>
   0x00005555555569ab <+368>:	add    $0x28,%rsp
   0x00005555555569af <+372>:	ret    
   0x00005555555569b0 <+373>:	call   0x5555555572d9 <explode_bomb>
   0x00005555555569b5 <+378>:	jmp    0x55555555699b <phase_3+352>
   0x00005555555569b7 <+380>:	call   0x555555556370 <__stack_chk_fail@plt>
  • x/s 0x55555555914c 会得到 "%ld %c %ld”,这预示了我们的入参的形式
  • +51 和 +54 这两行比较了我们的入参数目,如果 ≤ 2,就会直接跳到 +92,然后就爆炸了
  • b *0x0000555555556878 打上 +61 行的断点,执行到这一行后 p $rax,显示的就是我们的 input[0]
  • +61 +65 +69 的逻辑是这个值 -6,然后再和 0x7 进行比较,如果 above 的话,就会触发炸弹
  • 因此我们知道我们的 input[0] ≤ 13 (最后应该是对于不同的 input[0],我们可以得到不同的一组答案)
  • 这一题的本质结构是一个 jump table
  • b *0x000055555555688d 打上 +82 行的断点
  • p/a $rdx 显示 0x555555559160,p/a $rax 显示 0x6,stepi 追踪这几行的变化,可以得到 $rax 最终变成 0x0000555555556955
  • 当我们输入的是 12 的时候,会跳到 0x0000555555556955 <+282>: cmpq $0x3c,0x10(%rsp) 它要对比 $0x3c 和我们输入的第三个参数 —— 因此 input[2] = 60
  • +290 行我们执行了 mov $0x6a, %eax, 给 eax 寄存器写入了值
  • 随后我们直接跳到 +346 行,此时对比的 %al 实际上就是 $eax 的低位 —— 也就是刚刚写入的 0x6a = 106. 而另一方 0x7(%rsp) 就是我们输入的第二个参数
  • 所以这一行要求我们输入的第二个参数的 ascii == 106 → 也就是 j

答案是 12 j 60

Phase_4

0x00005555555569ff <+0>:	endbr64 
   0x0000555555556a03 <+4>:	sub    $0x28,%rsp
   0x0000555555556a07 <+8>:	mov    %fs:0x28,%rax
   0x0000555555556a10 <+17>:	mov    %rax,0x18(%rsp)
   0x0000555555556a15 <+22>:	xor    %eax,%eax
   0x0000555555556a17 <+24>:	lea    0x10(%rsp),%rcx
   0x0000555555556a1c <+29>:	lea    0x8(%rsp),%rdx
   0x0000555555556a21 <+34>:	lea    0x2c30(%rip),%rsi        # 0x555555559658
   0x0000555555556a28 <+41>:	call   0x555555556430 <__isoc99_sscanf@plt>
   0x0000555555556a2d <+46>:	cmp    $0x2,%eax
   0x0000555555556a30 <+49>:	jne    0x555555556a42 <phase_4+67>
   0x0000555555556a32 <+51>:	mov    0x8(%rsp),%rax
   0x0000555555556a37 <+56>:	test   %rax,%rax
   0x0000555555556a3a <+59>:	js     0x555555556a42 <phase_4+67>
   0x0000555555556a3c <+61>:	cmp    $0xe,%rax
   0x0000555555556a40 <+65>:	jle    0x555555556a47 <phase_4+72>
   0x0000555555556a42 <+67>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556a47 <+72>:	mov    $0xe,%edx
   0x0000555555556a4c <+77>:	mov    $0x0,%esi
   0x0000555555556a51 <+82>:	mov    0x8(%rsp),%rdi
   0x0000555555556a56 <+87>:	call   0x5555555569bc <func4>
   0x0000555555556a5b <+92>:	cmp    $0x25,%rax
   0x0000555555556a5f <+96>:	jne    0x555555556a69 <phase_4+106>
   0x0000555555556a61 <+98>:	cmpq   $0x25,0x10(%rsp)
   0x0000555555556a67 <+104>:	je     0x555555556a6e <phase_4+111>
   0x0000555555556a69 <+106>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556a6e <+111>:	mov    0x18(%rsp),%rax
   0x0000555555556a73 <+116>:	sub    %fs:0x28,%rax
   0x0000555555556a7c <+125>:	jne    0x555555556a83 <phase_4+132>
   0x0000555555556a7e <+127>:	add    $0x28,%rsp
   0x0000555555556a82 <+131>:	ret    
   0x0000555555556a83 <+132>:	call   0x555555556370 <__stack_chk_fail@plt>
  • +46 行检验我们的输入长度是否为 2
  • +65 行告诉我们我们输入的第一个参数一定要 ≤ 0xe
  • +87 行调用了一个函数 func4,它的返回值会被写入到 %rax 中
  • +92 行对比 $0x25 和 %rax 中的值 —— 经过测试得到 input[0] = 10 的时候这一行可以满足
  • +98 行直接告诉你 input[1] = 0x25 = 37
# func4

0x00005555555569bc <+0>:	endbr64 
   0x00005555555569c0 <+4>:	push   %rbx
   0x00005555555569c1 <+5>:	mov    %rdx,%rax
   0x00005555555569c4 <+8>:	sub    %rsi,%rax
   0x00005555555569c7 <+11>:	mov    %rax,%rbx
   0x00005555555569ca <+14>:	shr    $0x3f,%rbx
   0x00005555555569ce <+18>:	add    %rax,%rbx
   0x00005555555569d1 <+21>:	sar    %rbx
   0x00005555555569d4 <+24>:	add    %rsi,%rbx
   0x00005555555569d7 <+27>:	cmp    %rdi,%rbx
   0x00005555555569da <+30>:	jg     0x5555555569e3 <func4+39>
   0x00005555555569dc <+32>:	jl     0x5555555569f1 <func4+53>
   0x00005555555569de <+34>:	mov    %rbx,%rax
   0x00005555555569e1 <+37>:	pop    %rbx
   0x00005555555569e2 <+38>:	ret    
   0x00005555555569e3 <+39>:	lea    -0x1(%rbx),%rdx
   0x00005555555569e7 <+43>:	call   0x5555555569bc <func4>
   0x00005555555569ec <+48>:	add    %rax,%rbx
   0x00005555555569ef <+51>:	jmp    0x5555555569de <func4+34>
   0x00005555555569f1 <+53>:	lea    0x1(%rbx),%rsi
   0x00005555555569f5 <+57>:	call   0x5555555569bc <func4>
   0x00005555555569fa <+62>:	add    %rax,%rbx
   0x00005555555569fd <+65>:	jmp    0x5555555569de <func4+34>

最终结果就是 10 37

Phase_5

0x0000555555556a88 <+0>:	endbr64 
   0x0000555555556a8c <+4>:	push   %rbx
   0x0000555555556a8d <+5>:	mov    %rdi,%rbx
   0x0000555555556a90 <+8>:	call   0x55555555701c <string_length>
   0x0000555555556a95 <+13>:	cmp    $0x6,%rax
   0x0000555555556a99 <+17>:	jne    0x555555556aa7 <phase_5+31>
   0x0000555555556a9b <+19>:	mov    $0x0,%edx
   0x0000555555556aa0 <+24>:	mov    $0x0,%eax
   0x0000555555556aa5 <+29>:	jmp    0x555555556ac8 <phase_5+64>
   0x0000555555556aa7 <+31>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556aac <+36>:	jmp    0x555555556a9b <phase_5+19>

   0x0000555555556aae <+38>:	shl    $0x4,%rdx
   0x0000555555556ab2 <+42>:	movzbl (%rbx,%rax,1),%ecx
   0x0000555555556ab6 <+46>:	and    $0xf,%ecx
   0x0000555555556ab9 <+49>:	lea    0x26c0(%rip),%rsi        # 0x555555559180 <array.0>
   0x0000555555556ac0 <+56>:	add    (%rsi,%rcx,8),%rdx
   0x0000555555556ac4 <+60>:	add    $0x1,%rax
   0x0000555555556ac8 <+64>:	cmp    $0x5,%rax
   0x0000555555556acc <+68>:	jle    0x555555556aae <phase_5+38>

   0x0000555555556ace <+70>:	cmp    $0x198b91,%rdx
   0x0000555555556ad5 <+77>:	jne    0x555555556ad9 <phase_5+81>
   0x0000555555556ad7 <+79>:	pop    %rbx
   0x0000555555556ad8 <+80>:	ret    
   0x0000555555556ad9 <+81>:	call   0x5555555572d9 <explode_bomb>
   0x0000555555556ade <+86>:	jmp    0x555555556ad7 <phase_5+79>
  • +13 对比输入的长度和 0x6 —— 因此我们输入的长度为 6

  • 然后我们会跳到 38 ~ 68 行的循环中

  • display $rdx display $rcx ,我们可以发现通过 +42行,%rcx 会依次变成 string 每一个字符的 ASCII (例如我的 string[0] == ‘c’,那么 %rcx 就会变成 99)

  • 在 +49 行执行 x/128x 0x555555559180 可以打印出数组内容

0x555555559180 <array.0>:	0x02	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x555555559188 <array.0+8>:	0x0a	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x555555559190 <array.0+16>:	0x06	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x555555559198 <array.0+24>:	0x01	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591a0 <array.0+32>:	0x0c	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591a8 <array.0+40>:	0x00	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591b0 <array.0+48>:	0x09	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591b8 <array.0+56>:	0x03	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591c0 <array.0+64>:	0x04	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591c8 <array.0+72>:	0x07	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591d0 <array.0+80>:	0x0e	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591d8 <array.0+88>:	0x05	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591e0 <array.0+96>:	0x0b	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591e8 <array.0+104>:	0x08	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591f0 <array.0+112>:	0x0f	0x00	0x00	0x00	0x00	0x00	0x00	0x00
0x5555555591f8 <array.0+120>:	0x0d	0x00	0x00	0x00	0x00	0x00	0x00	0x00
  • 我的第一个输入如果是 c,那么就会在 +42 变成 99,然后在 +46 变成 0x3,对应到数组里的 0x01,然后给累加到 rdx 上

  • 经过了六次循环,我们得到了一个最终的 %rdx 的值,在 +70 行我们对比这个值和 0x198b91

  • 因此这道题目实际上的目的就是我们输入一个 string,其中的每一个字符都会对应到一个数字,然后我们希望最终的数字组合起来的到 0x198b61

  • 答案是 cfmlfc

Phase_6

太长了,分几段来观察汇编代码

   0x0000555555556ae0 <+0>:	endbr64 
   0x0000555555556ae4 <+4>:	push   %r12
   0x0000555555556ae6 <+6>:	push   %rbp
   0x0000555555556ae7 <+7>:	push   %rbx
   0x0000555555556ae8 <+8>:	sub    $0x70,%rsp
   0x0000555555556aec <+12>:	mov    %fs:0x28,%rax
   0x0000555555556af5 <+21>:	mov    %rax,0x68(%rsp)
   0x0000555555556afa <+26>:	xor    %eax,%eax
   0x0000555555556afc <+28>:	mov    %rsp,%rsi
   0x0000555555556aff <+31>:	call   0x55555555731b <read_six_numbers>
   0x0000555555556b04 <+36>:	mov    $0x0,%ebp
   0x0000555555556b09 <+41>:	jmp    0x555555556b30 <phase_6+80> // 一定会跳到 +80
   0x0000555555556b0b <+43>:	call   0x5555555572d9 <explode_bomb>

这一段感觉也没有啥太多内容,关键就是让我们知道我们需要 read_six_numbers

然后就跳到了 +80

   // 要求所有的数字都不相等
   0x0000555555556b10 <+48>:	jmp    0x555555556b44 <phase_6+100>
   0x0000555555556b12 <+50>:	add    $0x1,%rbx
   0x0000555555556b16 <+54>:	cmp    $0x5,%rbx 
   0x0000555555556b1a <+58>:	jg     0x555555556b2d <phase_6+77> // rbx > 0x5 会跳到77
   0x0000555555556b1c <+60>:	mov    (%rsp,%rbx,8),%rax // (%rsp,%rbx,8) 其实就是依次读取 arr[0], arr[1], arr[2]...
   0x0000555555556b20 <+64>:	cmp    %rax,(%rsp,%rbp,8)
   0x0000555555556b24 <+68>:	jne    0x555555556b12 <phase_6+50> // 要求 not equal,不然会爆炸
   0x0000555555556b26 <+70>:	call   0x5555555572d9 <explode_bomb>

   // 要求所有的数字都 1 ~ 6
   0x0000555555556b2b <+75>:	jmp    0x555555556b12 <phase_6+50> // 走不到这里
   0x0000555555556b2d <+77>:	mov    %r12,%rbp
   0x0000555555556b30 <+80>:	cmp    $0x5,%rbp
   0x0000555555556b34 <+84>:	jg     0x555555556b4d <phase_6+109> // 如果 rbp > 5, 会跳到 109
   0x0000555555556b36 <+86>:	mov    (%rsp,%rbp,8),%rax // (%rsp,%rbp,8) 这里给到的是 arr[i]
   0x0000555555556b3a <+90>:	sub    $0x1,%rax
   0x0000555555556b3e <+94>:	cmp    $0x5,%rax
   0x0000555555556b42 <+98>:	ja     0x555555556b0b <phase_6+43> // 如果 rax > 5,就会爆炸
   0x0000555555556b44 <+100>:	lea    0x1(%rbp),%r12 //
   0x0000555555556b48 <+104>:	mov    %r12,%rbx // rbx = r12
   0x0000555555556b4b <+107>:	jmp    0x555555556b16 <phase_6+54>
   0x0000555555556b4d <+109>:	mov    $0x0,%eax
   0x0000555555556b52 <+114>:	jmp    0x555555556b65 <phase_6+133>
  • +80 行对比了 0x5 和 rbp,但是 +36 才给 rbp 设置成了 0
  • +86 行当 rbp = i 的时候,就给 rax 赋值 arr[i]
  • 90 ~ 98 给出了第一个限制,arr[i] ≤ 6
  • +100 例如第一次 rbp = 0,于是 %r12 就得到了 0x1,然后 104 行才让 %rbx 也变成 0x1
  • +107 行跳到了 +54,我们可以发现这里其实也是一个循环, rbx > 0x5 的时候才会跳出去
  • +64 行要求 arr[i + j] ≠ arr[i] ,不然就会爆炸
  • 上面这一大段就是一个 O(n^2) 的循环,要求所有的数字都不一样
   0x0000555555556b54 <+116>:	mov    $0x7,%edx
   0x0000555555556b59 <+121>:	sub    (%rsp,%rax,8),%rdx
   0x0000555555556b5d <+125>:	mov    %rdx,(%rsp,%rax,8)
   0x0000555555556b61 <+129>:	add    $0x1,%rax
   0x0000555555556b65 <+133>:	cmp    $0x5,%rax
   0x0000555555556b69 <+137>:	jle    0x555555556b54 <phase_6+116>
  • 脱离了上面的循环后会直接从 +114 跳到 +133,进入到又一个新的循环
  • +109 行已经给 eax 设置成了 0x0,而新的循环要在 +133 对比 rax 和 0x5
  • +121 行依次对每个 %rdx = %rdx - arr[i] —— 注意 sub 和 add 操作是类似的 sub S,D → D = D - S
  • +125 行则有 arr[i] = %rdx
  • 因此这一个循环的意义是将 1 2 3 4 5 6 → 6 5 4 3 2 1
   0x0000555555556b6b <+139>:	mov    $0x0,%ecx
   0x0000555555556b70 <+144>:	jmp    0x555555556b89 <phase_6+169>

 
   0x0000555555556b72 <+146>:	mov    0x10(%rdx),%rdx // 这里相当于不断的读取链表的下一个节点
   0x0000555555556b76 <+150>:	add    $0x1,%rax
   0x0000555555556b7a <+154>:	cmp    %rax,(%rsp,%rcx,8)
   0x0000555555556b7e <+158>:	jg     0x555555556b72 <phase_6+146>
   // 

   0x0000555555556b80 <+160>:	mov    %rdx,0x30(%rsp,%rcx,8)
   0x0000555555556b85 <+165>:	add    $0x1,%rcx
   0x0000555555556b89 <+169>:	cmp    $0x5,%rcx # 这里是一个好的断点,我们在这里读取 x/8xb $rdx
   0x0000555555556b8d <+173>:	jg     0x555555556b9d <phase_6+189>
   0x0000555555556b8f <+175>:	mov    $0x1,%eax
   0x0000555555556b94 <+180>:	lea    0x6755(%rip),%rdx        # 0x55555555d2f0 <node1> # 这里是一个链表的头节点,将地址丢到 rdx
   0x0000555555556b9b <+187>:	jmp    0x555555556b7a <phase_6+154>
  • 接下来我们会从 +144 跳跃到 +169,然后这里又是一个循环,+139 的操作让 rcx 的初始值变成了 0
  • +180 行存储了一个链表的地址到 %rdx 中,然后 +187 行跳回到了 +154
  • +154 行在第一次循环中,我们对比 arr[i] 和 %rax,如果 arr[i] 更大,就会跳回到 +146,读取链表的下一个节点,并且给 rax + 1
  • 也就是说,如果此时 arr[i] = 1,那么我们最终的 rdx 指向的是链表的第 0 个节点 (从 0 开始);如果此时 arr[i] = 2,那么我们得到的 rdx 指向的就是链表的第 1 个节点
  • +160 行我们会将得到的 rdx 存储在 0x30(%rsp,%rcx,8) 中(看起来是一个数组的结构
   0x0000555555556b9d <+189>:	mov    0x30(%rsp),%rbx # 断点
   0x0000555555556ba2 <+194>:	mov    %rbx,%rcx
   0x0000555555556ba5 <+197>:	mov    $0x1,%eax
   0x0000555555556baa <+202>:	jmp    0x555555556bbc <phase_6+220>

   0x0000555555556bac <+204>:	mov    0x30(%rsp,%rax,8),%rdx
   0x0000555555556bb1 <+209>:	mov    %rdx,0x10(%rcx)
   0x0000555555556bb5 <+213>:	add    $0x1,%rax
   0x0000555555556bb9 <+217>:	mov    %rdx,%rcx
   0x0000555555556bbc <+220>:	cmp    $0x5,%rax
   0x0000555555556bc0 <+224>:	jle    0x555555556bac <phase_6+204>
  • 然后我们可以跳到 +189 行,注意 0x30(%rsp),就是我们刚刚存储 rdx 的数组
  • 然后我们会跳到 +220 行,此时 +204 ~ +224 又是一个循环
  • +204 将得到的值放到 rdx 中,+209 将 rdx 的值存储到 0x10(%rcx)

这一段看起来是一个数组的重排

 	 0x0000555555556bc2 <+226>:	movq   $0x0,0x10(%rcx)
   0x0000555555556bca <+234>:	mov    $0x0,%ebp
   0x0000555555556bcf <+239>:	jmp    0x555555556bd9 <phase_6+249>
   0x0000555555556bd1 <+241>:	mov    0x10(%rbx),%rbx
   0x0000555555556bd5 <+245>:	add    $0x1,%rbp // rbp += 1
   0x0000555555556bd9 <+249>:	cmp    $0x4,%rbp
   0x0000555555556bdd <+253>:	jg     0x555555556bf2 <phase_6+274> // 跳到 274 的话就肯定安全了
   0x0000555555556bdf <+255>:	mov    0x10(%rbx),%rax
   0x0000555555556be3 <+259>:	mov    (%rax),%rax
   0x0000555555556be6 <+262>:	cmp    %rax,(%rbx)
   0x0000555555556be9 <+265>:	jge    0x555555556bd1 <phase_6+241> // 要求 rax 的值 >= rbx 的值
   0x0000555555556beb <+267>:	call   0x5555555572d9 <explode_bomb>
  • +234 初始化了 ebp 为 0
  • +249 对比 rbp 和 0x4,因此这里本质也是一个循环
  • +255 读取了我们刚刚的数组的第一个值给 rax,+259 取得了这个地址上的值,然后和数组的第零个值对比
  • 如果 linked_arr[0] < linked_arr[1] 就会爆炸
  • 后面也是一样的操作

因此这一题的逻辑是,我们 input 的每一个数字最终都会对应到一个特定的链表的值

然后我们希望这个特定的值可以满足降序

最终答案是 5 4 1 6 3 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant