首先来看phase_2的代码:
0000000000400efc :
400efc: 55 push %rbp
400efd: 53 push %rbx
400efe: 48 83 ec 28 sub $0x28,%rsp
400f02: 48 89 e6 mov %rsp,%rsi
400f05: e8 52 05 00 00 callq 40145c
400f0a: 83 3c 24 01 cmpl $0x1,(%rsp)
400f0e: 74 20 je 400f30
400f10: e8 25 05 00 00 callq 40143a
400f15: eb 19 jmp 400f30
400f17: 8b 43 fc mov -0x4(%rbx),%eax #jump from 400f3a
400f1a: 01 c0 add %eax,%eax
400f1c: 39 03 cmp %eax,(%rbx)
400f1e: 74 05 je 400f25
400f20: e8 15 05 00 00 callq 40143a
400f25: 48 83 c3 04 add $0x4,%rbx
400f29: 48 39 eb cmp %rbp,%rbx
400f2c: 75 e9 jne 400f17
400f2e: eb 0c jmp 400f3c
400f30: 48 8d 5c 24 04 lea 0x4(%rsp),%rbx #jump from 400f0e
400f35: 48 8d 6c 24 18 lea 0x18(%rsp),%rbp
400f3a: eb db jmp 400f17
400f3c: 48 83 c4 28 add $0x28,%rsp
400f40: 5b pop %rbx
400f41: 5d pop %rbp
400f42: c3 retq
在400f02,rsi指向了和rsp一样的地方,然后调用read_six_numbers这个函数,从名字上看出这个函数是要读入6个数字的,它的代码是
000000000040145c :
40145c: 48 83 ec 18 sub $0x18,%rsp
401460: 48 89 f2 mov %rsi,%rdx
401463: 48 8d 4e 04 lea 0x4(%rsi),%rcx
401467: 48 8d 46 14 lea 0x14(%rsi),%rax
40146b: 48 89 44 24 08 mov %rax,0x8(%rsp)
401470: 48 8d 46 10 lea 0x10(%rsi),%rax
401474: 48 89 04 24 mov %rax,(%rsp)
401478: 4c 8d 4e 0c lea 0xc(%rsi),%r9
40147c: 4c 8d 46 08 lea 0x8(%rsi),%r8
401480: be c3 25 40 00 mov $0x4025c3,%esi
401485: b8 00 00 00 00 mov $0x0,%eax
40148a: e8 61 f7 ff ff callq 400bf0 <__isoc99_sscanf@plt>
40148f: 83 f8 05 cmp $0x5,%eax
401492: 7f 05 jg 401499
401494: e8 a1 ff ff ff callq 40143a
401499: 48 83 c4 18 add $0x18,%rsp
40149d: c3 retq
然后把0x4025c3这个地址赋给rsi,接着将rax赋值为0。然后调用ssanf这个函数,查找资料可知这个函数的原型是:int sscanf(const char *buffer,const char *format,[argument ]...);
第一个参数buffer表明要格式读取的字符串,这个已经在main调用read_line时存在rdi里
format这个参数存在rsi中,通过x/s命令读0x4025c3这个被赋给rsi的地址可看到是"%d %d %d %d %d %d"这样一个格式字符串,说明要读取6个整形数
之前将rdx这一系列参数的地址准备好都是为了sscanf的格式对应的后面的参数。看到这里就知道,read_six_numbers的作用实际上是把我们输入的6个数字存储到从phase_2内的rsp所指地址开始的连续6个int型整数中。注意在401492我们发现必须读6个或以上数字,否则直接触雷
在400f0a处,我们发现输入的第一个数字必须为1,否则无法跳过400f10的爆炸
成功后跳转到400f30,重要的是将rsp的4字节之后所处地址给了rbx,回到400f17,rax则存着rsp自身,将rax翻倍后需要与rbx相等,否则又会爆炸
这里能看出,我们输入的第二个数必须是第一个数的两倍,也就是2
接下来400f25回继续进行rbx后移4字节,rax存储rbx在后移前的地址,回头接着比较rax所指数的两倍是否和rbx所指的相等... 也就是,下一个参数要接着翻倍
这实际上是一个for循环,其退出条件在400f35和400f29处,可以看到当rbx已经偏移了5次到达原先地址+24字节时,循环结束,这时正好对比了我们输入的6个数字也就是说我们应该输入的是一个等比数列: 1 2 4 8 16 32 经验证确实如此,第二个phase就是这样