首先来看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 retqrsp+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