Bomb LAB实验报告

目录

[TOC]

前置准备

tar -xvf bomb.tar 解压

objdump -d -M intel bomb >bomb.s

通过总体观察 可以看出主要需要理解的是phase函数 phase_defused 是共用的 ,猜测应该是成功时一些输出,先不管。

另外在gdb中 也可以使用disass phase_?查看反汇编,可以不用来回切换屏幕。

程序开启了PIE保护 但是gdb调试时。默认是关闭aslr的。这使得调试的时候可以简单的加上一个基地址来下断点,另外可以用phase_1 + offset 这样相对取值的方式下断点。

phase_1

Dump of assembler code for function phase_1:
0x00005555555555a7 <+0>: endbr64
0x00005555555555ab <+4>: sub rsp,0x8
0x00005555555555af <+8>: lea rsi,[rip+0x1b9a] # 0x555555557150
0x00005555555555b6 <+15>: call 0x555555555a9f <strings_not_equal>
0x00005555555555bb <+20>: test eax,eax
0x00005555555555bd <+22>: jne 0x5555555555c4 <phase_1+29>
0x00005555555555bf <+24>: add rsp,0x8
0x00005555555555c3 <+28>: ret
0x00005555555555c4 <+29>: call 0x555555555bb3 <explode_bomb>
0x00005555555555c9 <+34>: jmp 0x5555555555bf <phase_1+24>
End of assembler dump.

process

[rip+0x1b9a]是常用的加载某个数据的方式,从后面strings_not_equal可以看出很显然是在比较字符串,那么就需要知道rdi是什么,很显然rdi应该是在main里就传入的

0x00005555555554a0 <+87>:    call   0x555555555c24 <read_line>
0x00005555555554a5 <+92>: mov rdi,rax
0x00005555555554a8 <+95>: call 0x5555555555a7 <phase_1>
0x00005555555554ad <+100>: call 0x555555555d6c <phase_defused>

后面的rdi也基本是这种形式 所以不用重复判断了

那么只需要用gdb 的 x/s指令读取字符串内容,复制了之后重新输入就行

pwndbg> x/s 0x555555557150
0x555555557150: "For NASA, space is still a high priority."

pwndbg是我平时一直在用的gdb,相比正常的只是多了一些废话,并不会很轮椅,所以我也就懒得调gdb配置文件了。

outcome

pwndbg> c
Continuing.
For NASA, space is still a high priority.
Phase 1 defused. How about the next one?

phase_2

Dump of assembler code for function phase_2:
0x00005555555555cb <+0>: endbr64
0x00005555555555cf <+4>: push rbp
0x00005555555555d0 <+5>: push rbx
0x00005555555555d1 <+6>: sub rsp,0x28
0x00005555555555d5 <+10>: mov rax,QWORD PTR fs:0x28
0x00005555555555de <+19>: mov QWORD PTR [rsp+0x18],rax
0x00005555555555e3 <+24>: xor eax,eax
0x00005555555555e5 <+26>: mov rsi,rsp
0x00005555555555e8 <+29>: call 0x555555555bdf <read_six_numbers>
0x00005555555555ed <+34>: cmp DWORD PTR [rsp],0x0
0x00005555555555f1 <+38>: js 0x5555555555fd <phase_2+50>
0x00005555555555f3 <+40>: mov rbp,rsp
0x00005555555555f6 <+43>: mov ebx,0x1
0x00005555555555fb <+48>: jmp 0x555555555615 <phase_2+74>
0x00005555555555fd <+50>: call 0x555555555bb3 <explode_bomb>
0x0000555555555602 <+55>: jmp 0x5555555555f3 <phase_2+40>
0x0000555555555604 <+57>: call 0x555555555bb3 <explode_bomb>
0x0000555555555609 <+62>: add ebx,0x1
0x000055555555560c <+65>: add rbp,0x4
0x0000555555555610 <+69>: cmp ebx,0x6
0x0000555555555613 <+72>: je 0x555555555621 <phase_2+86>
0x0000555555555615 <+74>: mov eax,ebx
0x0000555555555617 <+76>: add eax,DWORD PTR [rbp+0x0]
0x000055555555561a <+79>: cmp DWORD PTR [rbp+0x4],eax
0x000055555555561d <+82>: je 0x555555555609 <phase_2+62>
0x000055555555561f <+84>: jmp 0x555555555604 <phase_2+57>
0x0000555555555621 <+86>: mov rax,QWORD PTR [rsp+0x18]
0x0000555555555626 <+91>: xor rax,QWORD PTR fs:0x28
0x000055555555562f <+100>: jne 0x555555555638 <phase_2+109>
0x0000555555555631 <+102>: add rsp,0x28
0x0000555555555635 <+106>: pop rbx
0x0000555555555636 <+107>: pop rbp
0x0000555555555637 <+108>: ret
0x0000555555555638 <+109>: call 0x555555555220 <__stack_chk_fail@plt>
End of assembler dump.

process

0x00005555555555d1 <+6>:     sub    rsp,0x28
0x00005555555555d5 <+10>: mov rax,QWORD PTR fs:0x28
0x00005555555555de <+19>: mov QWORD PTR [rsp+0x18],rax

这可能在不熟悉canary保护的人看起来会觉得有点诡异。

这其实是canary保护的一个固定的形式,加载fs段里的随机值到栈rbp上方 ,函数退出时进行比较,和我现在要做的部分无关。

Q:为什么phase_1 没有这个保护?A:phase_1根本没用到栈。

具体关注开启栈帧和关闭栈帧中间的部分

read_six_numbers 可以猜测大概率是将rdi字符串拆成六个数值,然后存入rsi 。 rsi指向栈顶,这是栈上数组的表现。

这里可以具体看看read_six_numbers怎么实现的,但是我觉得不如猜测用空格隔开更方便。

后面取值用了大量的DWORD PTR 可以直接假设是int 类型的数组。下面记这个数组为 int buf[6]

js貌似不是一个很常见的指令,在这里它比较 buf[0] 与 0 ,如果buf[0]<0 则会跳转。这里不能跳转。

可以令buf[0] = 0

mov rbp,rsp 这里比较反常理,一般rbp是不会被使用到的,这里就当是被使用了。

+40 到 +84 是一个循环,但是确实有点杂乱

ebx 很显然就是数组下标,从 1-5 到5时退出循环。

那么后面的操作就是在把i + buf[i-1] 判断是否等于buf [i]

综上 buf 的一个解可以是buf [6]= {0,1,3,6,10,15}

outcome

pwndbg> c
Continuing.
0 1 3 6 10 15
That's number 2. Keep going!

phase_3

Dump of assembler code for function phase_3:
0x000055555555563d <+0>: endbr64
0x0000555555555641 <+4>: sub rsp,0x18
0x0000555555555645 <+8>: mov rax,QWORD PTR fs:0x28
0x000055555555564e <+17>: mov QWORD PTR [rsp+0x8],rax
0x0000555555555653 <+22>: xor eax,eax
0x0000555555555655 <+24>: lea rcx,[rsp+0x4]
0x000055555555565a <+29>: mov rdx,rsp
0x000055555555565d <+32>: lea rsi,[rip+0x1ccb] # 0x55555555732f
0x0000555555555664 <+39>: call 0x5555555552c0 <__isoc99_sscanf@plt>
0x0000555555555669 <+44>: cmp eax,0x1
0x000055555555566c <+47>: jle 0x55555555568c <phase_3+79>
0x000055555555566e <+49>: cmp DWORD PTR [rsp],0x7
0x0000555555555672 <+53>: ja 0x555555555712 <phase_3+213>
0x0000555555555678 <+59>: mov eax,DWORD PTR [rsp]
0x000055555555567b <+62>: lea rdx,[rip+0x1b3e] # 0x5555555571c0
0x0000555555555682 <+69>: movsxd rax,DWORD PTR [rdx+rax*4]
0x0000555555555686 <+73>: add rax,rdx
0x0000555555555689 <+76>: notrack jmp rax
0x000055555555568c <+79>: call 0x555555555bb3 <explode_bomb>
0x0000555555555691 <+84>: jmp 0x55555555566e <phase_3+49>
0x0000555555555693 <+86>: mov eax,0x33e
0x0000555555555698 <+91>: sub eax,0x152
0x000055555555569d <+96>: add eax,0x179
0x00005555555556a2 <+101>: sub eax,0xcc
0x00005555555556a7 <+106>: add eax,0xcc
0x00005555555556ac <+111>: sub eax,0xcc
0x00005555555556b1 <+116>: add eax,0xcc
0x00005555555556b6 <+121>: sub eax,0xcc
0x00005555555556bb <+126>: cmp DWORD PTR [rsp],0x5
0x00005555555556bf <+130>: jg 0x5555555556c7 <phase_3+138>
0x00005555555556c1 <+132>: cmp DWORD PTR [rsp+0x4],eax
0x00005555555556c5 <+136>: je 0x5555555556cc <phase_3+143>
0x00005555555556c7 <+138>: call 0x555555555bb3 <explode_bomb>
0x00005555555556cc <+143>: mov rax,QWORD PTR [rsp+0x8]
0x00005555555556d1 <+148>: xor rax,QWORD PTR fs:0x28
0x00005555555556da <+157>: jne 0x55555555571e <phase_3+225>
0x00005555555556dc <+159>: add rsp,0x18
0x00005555555556e0 <+163>: ret
0x00005555555556e1 <+164>: mov eax,0x0
0x00005555555556e6 <+169>: jmp 0x555555555698 <phase_3+91>
0x00005555555556e8 <+171>: mov eax,0x0
0x00005555555556ed <+176>: jmp 0x55555555569d <phase_3+96>
0x00005555555556ef <+178>: mov eax,0x0
0x00005555555556f4 <+183>: jmp 0x5555555556a2 <phase_3+101>
0x00005555555556f6 <+185>: mov eax,0x0
0x00005555555556fb <+190>: jmp 0x5555555556a7 <phase_3+106>
0x00005555555556fd <+192>: mov eax,0x0
0x0000555555555702 <+197>: jmp 0x5555555556ac <phase_3+111>
0x0000555555555704 <+199>: mov eax,0x0
0x0000555555555709 <+204>: jmp 0x5555555556b1 <phase_3+116>
0x000055555555570b <+206>: mov eax,0x0
0x0000555555555710 <+211>: jmp 0x5555555556b6 <phase_3+121>
0x0000555555555712 <+213>: call 0x555555555bb3 <explode_bomb>
0x0000555555555717 <+218>: mov eax,0x0
0x000055555555571c <+223>: jmp 0x5555555556bb <phase_3+126>
0x000055555555571e <+225>: call 0x555555555220 <__stack_chk_fail@plt>
End of assembler dump.

process

更加复杂!

首先调用了一个sscanf 。

这个函数和scanf差别就是,它读取数据是在第一个参数指向的位置

rdi仍然是readline传入的。

0x0000555555555655 <+24>:    lea    rcx,[rsp+0x4]
0x000055555555565a <+29>: mov rdx,rsp

这两步就是给出了”%d %d” 需要存储到的位置,第一个放到[rsp] 第二个放到[rsp+4]

cmp eax,0x1 后面是对于scanf一个检查 正常读取就可以不看

cmp DWORD PTR [rsp],0x7 要求第一个参数小于等于7

然后又来了个复杂的玩意!

0x0000555555555678 <+59>:    mov    eax,DWORD PTR [rsp]
0x000055555555567b <+62>: lea rdx,[rip+0x1b3e] # 0x5555555571c0
0x0000555555555682 <+69>: movsxd rax,DWORD PTR [rdx+rax*4]
0x0000555555555686 <+73>: add rax,rdx
0x0000555555555689 <+76>: notrack jmp rax

一通瞪眼法之后,我认为这个很像一个跳转表,rdx加载了

0x5555555571c0 , 0x5555555571c 后面存储了一系列相对偏移,然后加上这个相对偏移后,进行跳转。

那么[rsp]就是相对偏移的数组下标。那么就需要计算好这个switch! 可以先假设switch都是跳转到函数内的。这样就可以不盲目的计算,先看后面的逻辑。

发现在ret指令后面存了一堆jmp,很显然这不是正常的,可以直接假设跳转表就是跳到这里

0x00005555555556bb <+126>:   cmp    DWORD PTR [rsp],0x5
0x00005555555556bf <+130>: jg 0x5555555556c7 <phase_3+138>
0x00005555555556c1 <+132>: cmp DWORD PTR [rsp+0x4],eax

这三个指令显然就是最终的目标!

[rsp]>=5 时,直接bomb,直接假设[rsp]等于5,第二个参数就是运行的结果。

可以直接输入一个5,用gdb查看最后的rax,这样就跳过了繁琐的计算过程

下好断点

pwndbg> p $eax
$2 = -204

然后重新运行一遍程序

输入5 -204

outcome

pwndbg> c
Continuing.
Halfway there!

phase_4

Dump of assembler code for function phase_4:
0x0000555555555764 <+0>: endbr64
0x0000555555555768 <+4>: sub rsp,0x18
0x000055555555576c <+8>: mov rax,QWORD PTR fs:0x28
0x0000555555555775 <+17>: mov QWORD PTR [rsp+0x8],rax
0x000055555555577a <+22>: xor eax,eax
0x000055555555577c <+24>: lea rcx,[rsp+0x4]
0x0000555555555781 <+29>: mov rdx,rsp
0x0000555555555784 <+32>: lea rsi,[rip+0x1ba4] # 0x55555555732f
0x000055555555578b <+39>: call 0x5555555552c0 <__isoc99_sscanf@plt>
0x0000555555555790 <+44>: cmp eax,0x2
0x0000555555555793 <+47>: jne 0x55555555579b <phase_4+55>
0x0000555555555795 <+49>: cmp DWORD PTR [rsp],0xe
0x0000555555555799 <+53>: jbe 0x5555555557a0 <phase_4+60>
0x000055555555579b <+55>: call 0x555555555bb3 <explode_bomb>
0x00005555555557a0 <+60>: mov edx,0xe
0x00005555555557a5 <+65>: mov esi,0x0
0x00005555555557aa <+70>: mov edi,DWORD PTR [rsp]
0x00005555555557ad <+73>: call 0x555555555723 <func4>
0x00005555555557b2 <+78>: cmp eax,0x7
0x00005555555557b5 <+81>: jne 0x5555555557be <phase_4+90>
0x00005555555557b7 <+83>: cmp DWORD PTR [rsp+0x4],0x7
0x00005555555557bc <+88>: je 0x5555555557c3 <phase_4+95>
0x00005555555557be <+90>: call 0x555555555bb3 <explode_bomb>
0x00005555555557c3 <+95>: mov rax,QWORD PTR [rsp+0x8]
0x00005555555557c8 <+100>: xor rax,QWORD PTR fs:0x28
0x00005555555557d1 <+109>: jne 0x5555555557d8 <phase_4+116>
0x00005555555557d3 <+111>: add rsp,0x18
0x00005555555557d7 <+115>: ret
0x00005555555557d8 <+116>: call 0x555555555220 <__stack_chk_fail@plt>
End of assembler dump.

process

突然代码量就变少了!

还是和刚才一样 输入两个数字到[rsp] [rsp+4]

要求[rsp]<=15

然后跳到func4,要求其返回值等于7。可以先看外面的逻辑,再看func4具体干了什么

[rsp+4]只是单纯的要求等于7 ,为什么不只读入一个参数呢?

进入func4:

rdi = [rsp] rsi = 0 rdx = 0xe

可以看出这完全是一个递归!

先尝试直接让他跳出有没有解,但是直接跳出显然就不会等于7。

那只能尝试去翻译这个函数了。

func4(x,y,z){
int temp = z-y ;
int flag = (unsigned)temp >> 31 ;
temp+=flag;
temp>>1;
temp+=y;
if (temp<=x){
if (temp == x){
return 0;
}else{
y = temp+1;
return 2*func4(x,y,z) + 1 ;
}
}else{
z= temp -1;
return 2*func4(x,y,z);
}

}

前面那个部分的计算还是不像人类能理解的,于是我优化了一下。

经过一堆判断 我认为这个算法实际上执行了x+y / 2 。但是简单粗暴的相加/2 可能会有溢出。另外在负数时,需要向零截断,所以加上了一个flag。

那么就可以简化为

func4(x,y,z){
int temp = (y+z)/2;
if(temp==x) return 0;
if(temp<x) return 2*func4(x,temp+1,z) + 1;
if(temp>x) return 2*func4(x,y,temp-1);
}

执行func(x,0,15),x是输入值。

通过逆推。函数最终节点的返回值必然是0 , 需要构造出0 1 3 7这样的返回流程

也就是执行四次后,让temp等于x

1:7,2:11,3:13,4:14 输入14尝试一下!

outcome

14 7
So you got that one. Try this one.

phase_5

Dump of assembler code for function phase_5:
0x00005555555557dd <+0>: endbr64
0x00005555555557e1 <+4>: push rbx
0x00005555555557e2 <+5>: mov rbx,rdi
0x00005555555557e5 <+8>: call 0x555555555a7e <string_length>
0x00005555555557ea <+13>: cmp eax,0x6
0x00005555555557ed <+16>: jne 0x55555555581b <phase_5+62>
0x00005555555557ef <+18>: mov rax,rbx
0x00005555555557f2 <+21>: lea rdi,[rbx+0x6]
0x00005555555557f6 <+25>: mov ecx,0x0
0x00005555555557fb <+30>: lea rsi,[rip+0x19de] # 0x5555555571e0 <array.3471>
0x0000555555555802 <+37>: movzx edx,BYTE PTR [rax]
0x0000555555555805 <+40>: and edx,0xf
0x0000555555555808 <+43>: add ecx,DWORD PTR [rsi+rdx*4]
0x000055555555580b <+46>: add rax,0x1
0x000055555555580f <+50>: cmp rax,rdi
0x0000555555555812 <+53>: jne 0x555555555802 <phase_5+37>
0x0000555555555814 <+55>: cmp ecx,0x3e
0x0000555555555817 <+58>: jne 0x555555555822 <phase_5+69>
0x0000555555555819 <+60>: pop rbx
0x000055555555581a <+61>: ret
0x000055555555581b <+62>: call 0x555555555bb3 <explode_bomb>
0x0000555555555820 <+67>: jmp 0x5555555557ef <phase_5+18>
0x0000555555555822 <+69>: call 0x555555555bb3 <explode_bomb>
0x0000555555555827 <+74>: jmp 0x555555555819 <phase_5+60>
End of assembler dump.

process

循环还是比递归看着舒服。

涉及到了两个数组

一个是char input[7]; 一个是 int array[16]

先是检查了字符串长度是否为6。

然后根据input[7]里每个char的值,用0xf取掩码(相当于%16) ,当作array数组下标,进行取值。

最终要使得取出来的值相加等于0x3e

使用x/16wx 0x5555555571e0 可以方便的列出这个数组的内容。

事实上可以规定数据类型 然后用print 但是这太麻烦了。

0x5555555571e0 <array.3471>:    0x00000002      0x0000000a      0x00000006      0x00000001
0x5555555571f0 <array.3471+16>: 0x0000000c 0x00000010 0x00000009 0x00000003
0x555555557200 <array.3471+32>: 0x00000004 0x00000007 0x0000000e 0x00000005
0x555555557210 <array.3471+48>: 0x0000000b 0x00000008 0x0000000f 0x0000000d

随便取几个6起来为0x3e就行了

pwndbg> p 0x3e-0xf-0xd-0x8-0xb-0x5-0xa
$4 = 0

对应数组下标 1 11 12-15

可以取大写英文字母 AKLMNO

outcome

pwndbg> c
Continuing.
AKLMNO
Good work! On to the next...

phase_6

0x0000555555555829 <+0>:     endbr64
0x000055555555582d <+4>: push r14
0x000055555555582f <+6>: push r13
0x0000555555555831 <+8>: push r12
0x0000555555555833 <+10>: push rbp
0x0000555555555834 <+11>: push rbx
0x0000555555555835 <+12>: sub rsp,0x60
0x0000555555555839 <+16>: mov rax,QWORD PTR fs:0x28
0x0000555555555842 <+25>: mov QWORD PTR [rsp+0x58],rax
0x0000555555555847 <+30>: xor eax,eax
0x0000555555555849 <+32>: mov r13,rsp
0x000055555555584c <+35>: mov rsi,r13
0x000055555555584f <+38>: call 0x555555555bdf <read_six_numbers>
0x0000555555555854 <+43>: mov r14d,0x1
0x000055555555585a <+49>: mov r12,rsp
0x000055555555585d <+52>: jmp 0x555555555887 <phase_6+94>
0x000055555555585f <+54>: call 0x555555555bb3 <explode_bomb>
0x0000555555555864 <+59>: jmp 0x555555555896 <phase_6+109>
0x0000555555555866 <+61>: add rbx,0x1
0x000055555555586a <+65>: cmp ebx,0x5
0x000055555555586d <+68>: jg 0x55555555587f <phase_6+86>
0x000055555555586f <+70>: mov eax,DWORD PTR [r12+rbx*4]
0x0000555555555873 <+74>: cmp DWORD PTR [rbp+0x0],eax
0x0000555555555876 <+77>: jne 0x555555555866 <phase_6+61>
0x0000555555555878 <+79>: call 0x555555555bb3 <explode_bomb>
0x000055555555587d <+84>: jmp 0x555555555866 <phase_6+61>
0x000055555555587f <+86>: add r14,0x1
0x0000555555555883 <+90>: add r13,0x4
0x0000555555555887 <+94>: mov rbp,r13
0x000055555555588a <+97>: mov eax,DWORD PTR [r13+0x0]
0x000055555555588e <+101>: sub eax,0x1
0x0000555555555891 <+104>: cmp eax,0x5
0x0000555555555894 <+107>: ja 0x55555555585f <phase_6+54>
0x0000555555555896 <+109>: cmp r14d,0x5
0x000055555555589a <+113>: jg 0x5555555558a1 <phase_6+120>
0x000055555555589c <+115>: mov rbx,r14
0x000055555555589f <+118>: jmp 0x55555555586f <phase_6+70>
0x00005555555558a1 <+120>: mov esi,0x0
0x00005555555558a6 <+125>: mov ecx,DWORD PTR [rsp+rsi*4]
0x00005555555558a9 <+128>: mov eax,0x1
0x00005555555558ae <+133>: lea rdx,[rip+0x395b] # 0x555555559210 <node1>
0x00005555555558b5 <+140>: cmp ecx,0x1
0x00005555555558b8 <+143>: jle 0x5555555558c5 <phase_6+156>
0x00005555555558ba <+145>: mov rdx,QWORD PTR [rdx+0x8]
0x00005555555558be <+149>: add eax,0x1
0x00005555555558c1 <+152>: cmp eax,ecx
0x00005555555558c3 <+154>: jne 0x5555555558ba <phase_6+145>
0x00005555555558c5 <+156>: mov QWORD PTR [rsp+rsi*8+0x20],rdx
0x00005555555558ca <+161>: add rsi,0x1
0x00005555555558ce <+165>: cmp rsi,0x6
0x00005555555558d2 <+169>: jne 0x5555555558a6 <phase_6+125>
0x00005555555558d4 <+171>: mov rbx,QWORD PTR [rsp+0x20]
0x00005555555558d9 <+176>: mov rax,QWORD PTR [rsp+0x28]
0x00005555555558de <+181>: mov QWORD PTR [rbx+0x8],rax
0x00005555555558e2 <+185>: mov rdx,QWORD PTR [rsp+0x30]
0x00005555555558e7 <+190>: mov QWORD PTR [rax+0x8],rdx
0x00005555555558eb <+194>: mov rax,QWORD PTR [rsp+0x38]
0x00005555555558f0 <+199>: mov QWORD PTR [rdx+0x8],rax
0x00005555555558f4 <+203>: mov rdx,QWORD PTR [rsp+0x40]
0x00005555555558f9 <+208>: mov QWORD PTR [rax+0x8],rdx
0x00005555555558fd <+212>: mov rax,QWORD PTR [rsp+0x48]
0x0000555555555902 <+217>: mov QWORD PTR [rdx+0x8],rax
0x0000555555555906 <+221>: mov QWORD PTR [rax+0x8],0x0
0x000055555555590e <+229>: mov ebp,0x5
0x0000555555555913 <+234>: jmp 0x55555555591e <phase_6+245>
0x0000555555555915 <+236>: mov rbx,QWORD PTR [rbx+0x8]
0x0000555555555919 <+240>: sub ebp,0x1
0x000055555555591c <+243>: je 0x55555555592f <phase_6+262>
0x000055555555591e <+245>: mov rax,QWORD PTR [rbx+0x8]
0x0000555555555922 <+249>: mov eax,DWORD PTR [rax]
0x0000555555555924 <+251>: cmp DWORD PTR [rbx],eax
0x0000555555555926 <+253>: jle 0x555555555915 <phase_6+236>
0x0000555555555928 <+255>: call 0x555555555bb3 <explode_bomb>
0x000055555555592d <+260>: jmp 0x555555555915 <phase_6+236>
0x000055555555592f <+262>: mov rax,QWORD PTR [rsp+0x58]
0x0000555555555934 <+267>: xor rax,QWORD PTR fs:0x28
0x000055555555593d <+276>: jne 0x55555555594c <phase_6+291>
0x000055555555593f <+278>: add rsp,0x60
0x0000555555555943 <+282>: pop rbx
0x0000555555555944 <+283>: pop rbp
0x0000555555555945 <+284>: pop r12
0x0000555555555947 <+286>: pop r13
0x0000555555555949 <+288>: pop r14
0x000055555555594b <+290>: ret
0x000055555555594c <+291>: call 0x555555555220 <__stack_chk_fail@plt>

process

略显复杂 但还好 没有递归。

先大致看一下逻辑,可以分成四个部分。

第一部分

0x0000555555555829 <+0>:     endbr64
0x000055555555582d <+4>: push r14
0x000055555555582f <+6>: push r13
0x0000555555555831 <+8>: push r12
0x0000555555555833 <+10>: push rbp
0x0000555555555834 <+11>: push rbx
0x0000555555555835 <+12>: sub rsp,0x60
0x0000555555555839 <+16>: mov rax,QWORD PTR fs:0x28
0x0000555555555842 <+25>: mov QWORD PTR [rsp+0x58],rax
0x0000555555555847 <+30>: xor eax,eax
0x0000555555555849 <+32>: mov r13,rsp
0x000055555555584c <+35>: mov rsi,r13
0x000055555555584f <+38>: call 0x555555555bdf <read_six_numbers>
0x0000555555555854 <+43>: mov r14d,0x1
0x000055555555585a <+49>: mov r12,rsp
0x000055555555585d <+52>: jmp 0x555555555887 <phase_6+94>
0x000055555555585f <+54>: call 0x555555555bb3 <explode_bomb>
0x0000555555555864 <+59>: jmp 0x555555555896 <phase_6+109>
0x0000555555555866 <+61>: add rbx,0x1
0x000055555555586a <+65>: cmp ebx,0x5
0x000055555555586d <+68>: jg 0x55555555587f <phase_6+86>
0x000055555555586f <+70>: mov eax,DWORD PTR [r12+rbx*4]
0x0000555555555873 <+74>: cmp DWORD PTR [rbp+0x0],eax
0x0000555555555876 <+77>: jne 0x555555555866 <phase_6+61>
0x0000555555555878 <+79>: call 0x555555555bb3 <explode_bomb>
0x000055555555587d <+84>: jmp 0x555555555866 <phase_6+61>
0x000055555555587f <+86>: add r14,0x1
0x0000555555555883 <+90>: add r13,0x4
0x0000555555555887 <+94>: mov rbp,r13
0x000055555555588a <+97>: mov eax,DWORD PTR [r13+0x0]
0x000055555555588e <+101>: sub eax,0x1
0x0000555555555891 <+104>: cmp eax,0x5
0x0000555555555894 <+107>: ja 0x55555555585f <phase_6+54>
0x0000555555555896 <+109>: cmp r14d,0x5
0x000055555555589a <+113>: jg 0x5555555558a1 <phase_6+120>
0x000055555555589c <+115>: mov rbx,r14
0x000055555555589f <+118>: jmp 0x55555555586f <phase_6+70>

主要做了两个检查

0x000055555555586f <+70>:    mov    eax,DWORD PTR [r12+rbx*4]
0x0000555555555873 <+74>: cmp DWORD PTR [rbp+0x0],eax

检查每相邻两个数是不是不相等的

0x000055555555588e <+101>:   sub    eax,0x1
0x0000555555555891 <+104>: cmp eax,0x5
0x0000555555555894 <+107>: ja 0x55555555585f <phase_6+54>

检查每个数-1是不是在0 到 5之间 就是要输入1到6

对于这种循环,有个技巧,就是rbx一般是for (int i …)里的i,当然不保证一定正确,但是确实有这个规律

第二部分

0x00005555555558a1 <+120>:   mov    esi,0x0
0x00005555555558a6 <+125>: mov ecx,DWORD PTR [rsp+rsi*4]
0x00005555555558a9 <+128>: mov eax,0x1
0x00005555555558ae <+133>: lea rdx,[rip+0x395b] # 0x555555559210 <node1>
0x00005555555558b5 <+140>: cmp ecx,0x1
0x00005555555558b8 <+143>: jle 0x5555555558c5 <phase_6+156>
0x00005555555558ba <+145>: mov rdx,QWORD PTR [rdx+0x8]
0x00005555555558be <+149>: add eax,0x1
0x00005555555558c1 <+152>: cmp eax,ecx
0x00005555555558c3 <+154>: jne 0x5555555558ba <phase_6+145>
0x00005555555558c5 <+156>: mov QWORD PTR [rsp+rsi*8+0x20],rdx
0x00005555555558ca <+161>: add rsi,0x1
0x00005555555558ce <+165>: cmp rsi,0x6
0x00005555555558d2 <+169>: jne 0x5555555558a6 <phase_6+125>

这里出现了个node1 。通过后面反复的[rdx+0x8],可以推测这是一个链表

并且节点结构应该是

struct node{

int data;

int padding;

struct node * next ;

}

可以从内存里面验证这个观点

pwndbg> x/100wx 0x555555559210
0x555555559210 <node1>: 0x000001c1 0x00000001 0x55559220 0x00005555
0x555555559220 <node2>: 0x0000023f 0x00000002 0x55559230 0x00005555
0x555555559230 <node3>: 0x000003b6 0x00000003 0x55559240 0x00005555
0x555555559240 <node4>: 0x00000379 0x00000004 0x55559250 0x00005555
0x555555559250 <node5>: 0x0000007d 0x00000005 0x55559110 0x00005555


pwndbg> x/4wx 0x555555559110
0x555555559110 <node6>: 0x0000006b 0x00000006 0x00000000 0x00000000

可以发现题目还非常好心的把padding标上的标号。但其实我并没有在phase中看到有这个操作。

这里具体做的事情,就是按照标号,取出链表节点.

0x00005555555558a1 <+120>:   mov    esi,0x0
0x00005555555558a6 <+125>: mov ecx,DWORD PTR [rsp+rsi*4]
0x00005555555558a9 <+128>: mov eax,0x1
0x00005555555558ae <+133>: lea rdx,[rip+0x395b] # 0x555555559210 <node1>
0x00005555555558b5 <+140>: cmp ecx,0x1
0x00005555555558b8 <+143>: jle 0x5555555558c5 <phase_6+156>
0x00005555555558ba <+145>: mov rdx,QWORD PTR [rdx+0x8]
0x00005555555558be <+149>: add eax,0x1
0x00005555555558c1 <+152>: cmp eax,ecx

然后再把对应指针存储到rsp+0x20开始的数组

6*4 = 0x18 为什么要空 0x08? 这通常是为了进行对齐

第三部分

0x00005555555558d4 <+171>:   mov    rbx,QWORD PTR [rsp+0x20]
0x00005555555558d9 <+176>: mov rax,QWORD PTR [rsp+0x28]
0x00005555555558de <+181>: mov QWORD PTR [rbx+0x8],rax
0x00005555555558e2 <+185>: mov rdx,QWORD PTR [rsp+0x30]
0x00005555555558e7 <+190>: mov QWORD PTR [rax+0x8],rdx
0x00005555555558eb <+194>: mov rax,QWORD PTR [rsp+0x38]
0x00005555555558f0 <+199>: mov QWORD PTR [rdx+0x8],rax
0x00005555555558f4 <+203>: mov rdx,QWORD PTR [rsp+0x40]
0x00005555555558f9 <+208>: mov QWORD PTR [rax+0x8],rdx
0x00005555555558fd <+212>: mov rax,QWORD PTR [rsp+0x48]
0x0000555555555902 <+217>: mov QWORD PTR [rdx+0x8],rax
0x0000555555555906 <+221>: mov QWORD PTR [rax+0x8],0x0

最轻松的一部分,可以看出就是按照数组顺序,重新连接了单链表。

第四部分

0x000055555555590e <+229>:   mov    ebp,0x5
0x0000555555555913 <+234>: jmp 0x55555555591e <phase_6+245>
0x0000555555555915 <+236>: mov rbx,QWORD PTR [rbx+0x8]
0x0000555555555919 <+240>: sub ebp,0x1
0x000055555555591c <+243>: je 0x55555555592f <phase_6+262>
0x000055555555591e <+245>: mov rax,QWORD PTR [rbx+0x8]
0x0000555555555922 <+249>: mov eax,DWORD PTR [rax]
0x0000555555555924 <+251>: cmp DWORD PTR [rbx],eax
0x0000555555555926 <+253>: jle 0x555555555915 <phase_6+236>
0x0000555555555928 <+255>: call 0x555555555bb3 <explode_bomb>
0x000055555555592d <+260>: jmp 0x555555555915 <phase_6+236>

这个部分也不难,ebp是for里面的i (i=5 ;i>0;i–),rbx是上一个部分已经存储的第一个节点指针,可以当成head

那么就是进行5次比较,

判断当前节点数据是否小于等于下一节点数据。

综上,四个部分的结果就是按照标号,重新排列了链表,要求最后的链表是递增的。

outcome

6 5 1 2 4 3
Congratulations! You've defused the bomb!

验证

按照实验要求,我需要使用argv[1]传入输入的内容

建立一个文本整合上述输入即可

❯ ./bomb solution_24300240142_彭宇.txt
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Phase 1 defused. How about the next one?
That's number 2. Keep going!
Halfway there!
So you got that one. Try this one.
Good work! On to the next...
Congratulations! You've defused the bomb!
❯ cat solution_24300240142_彭宇.txt
For NASA, space is still a high priority.
0 1 3 6 10 15
5 -204
14 7
AKLMNO
6 5 1 2 4 3