CSAPP3e - x86-64 assembly code analysis - Bomb Lab: phase 5

时间:2022-05-04 15:16:53

首先来看phase_5做了什么:

0000000000401062 <phase_5>:
  401062:    53                       push   %rbx
  401063:    48 83 ec 20              sub    $0x20,%rsp
  401067:    48 89 fb                 mov    %rdi,%rbx
  40106a:    64 48 8b 04 25 28 00     mov    %fs:0x28,%rax
  401071:    00 00
  401073:    48 89 44 24 18           mov    %rax,0x18(%rsp)
  401078:    31 c0                    xor    %eax,%eax
  40107a:    e8 9c 02 00 00           callq  40131b <string_length>
  40107f:    83 f8 06                 cmp    $0x6,%eax
  401082:    74 4e                    je     4010d2 <phase_5+0x70> # length of the string must be 6
  401084:    e8 b1 03 00 00           callq  40143a <explode_bomb>
  401089:    eb 47                    jmp    4010d2 <phase_5+0x70>

这一段代码将rbx赋值成rdi,也就是我们输入的字符串所存的地址,然后在40106a处,它将rax赋值成一个特殊的量%fs:0x28,查阅资料这是一个sentinel,用于监视栈溢出

接下来rsp+24的位置被赋成这个值,然后eax被按位异或后调用string_length,这个函数后eax必须为6,这说明我们输入的字符是6个

然后短暂跳跃了一下,将eax赋成0:

  4010d2:    b8 00 00 00 00           mov    $0x0,%eax
  4010d7:    eb b2                    jmp    40108b <phase_5+0x29>
然后是一个循环:
  40108b:	0f b6 0c 03          	movzbl (%rbx,%rax,1),%ecx
  40108f:	88 0c 24             	mov    %cl,(%rsp)
  401092:	48 8b 14 24          	mov    (%rsp),%rdx
  401096:	83 e2 0f             	and    $0xf,%edx
  401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
  4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
  4010a4:	48 83 c0 01          	add    $0x1,%rax
  4010a8:	48 83 f8 06          	cmp    $0x6,%rax
  4010ac:	75 dd                	jne    40108b <phase_5+0x29>
ecx被赋值成rbx+rax所指向的值,注意到movzbl这个命令是在短变量赋给长变量时自动填充0的,然后将ecx的低8位赋给rsp所指向的地址,然后将这个值赋给rdx,然后将edx与0xf作按位合取,然后将rdx+0x4024b0所指向的值赋给edx,然后将这个值的低8位赋值给rsp+rax+16的地址

上述过程由于eax从1到6退出,共进行6次。它做了什么呢?rbx存储着我们输入字符串的存储地址,每次eax贡献一个偏移,也就是说我们的字符串内6个字符每次都赋给rdx,然后和0xf按位合取,也就是只取了最低4位的值,接着这个值作为偏移量取了0x4024b0后的某一个地址上的变量,那个变量的低8位被赋到rsp+rax+16的位置上。

循环结束时,rsp+16 ~ rsp+21 这6个字节上存储着从0x4024b0开始的一段地址内搬运来的6个变量,计算每个地址的偏移量等于我们输入的字符的低4位所代表的数

接下来,

  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 <strings_not_equal> # the processed string must be the same as that stored in 0x40245e
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 <phase_5+0x77> # eax must be 0
  4010c6:	e8 6f 03 00 00       	callq  40143a <explode_bomb>
  4010cb:	0f 1f 44 00 00       	nopl   0x0(%rax,%rax,1)		
  4010d0:	eb 07                	jmp    4010d9 <phase_5+0x77>
  4010d2:	b8 00 00 00 00       	mov    $0x0,%eax
  4010d7:	eb b2                	jmp    40108b <phase_5+0x29>
  4010d9:	48 8b 44 24 18       	mov    0x18(%rsp),%rax 
  4010de:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  4010e5:	00 00 
  4010e7:	74 05                	je     4010ee <phase_5+0x8c>
  4010e9:	e8 42 fa ff ff       	callq  400b30 <__stack_chk_fail@plt>
  4010ee:	48 83 c4 20          	add    $0x20,%rsp
  4010f2:	5b                   	pop    %rbx
  4010f3:	c3                   	retq   
rsp+22处被赋成0,标记着这个新串的结束。然后esi被赋成0x40245e,rdi被赋成rsp+16,也就是这个新串的开始地址

然后是strings_not_equal,我们知道它会在rsi和rdi所指向的两个字符串相等时返回0,从4010d9看这正是我们需要的。如果通过这一层,

我们就跳到4010d9,将rax赋成rsp+24所指向的值,这正是那个sentinel,然后rax与sentinel按位异或后比较是否和401078处的结果一致,我们知道正常情况下是的(代入计算的sentinel都是%fs:0x28),这样就能退出了

关键在于那个字符串是什么。我们操作得到的串需要等于0x40245e处的,查看知它是"flyers",

而4024b0处的字符串是:maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?

按照flyers中字母在这个字符串中的位置一个一个找下来,我们需要的偏移量分别是:9 15 14 5 6 7

转换成2进制:1001 1111 1100 0101 0110 0111

查ASCII码表,不难得知很多字符的低4位都能满足需要,我们只需要找一个就行,比如:"9?>567",这样就通过了phase_5