前置知识
常见的汇编指令,特别是各种跳转指令的区别。
http://unixwiz.net/techtips/x86-jumps.html
基础的gdb调试操作,汇编调试(tui)。
phase_1
1 2 3 4 5 6 7 8 9 10 11 (gdb) disassemble phase_1 Dump of assembler code for function phase_1: 0x0000000000400ee0 <+0>: sub $0x8,%rsp 0x0000000000400ee4 <+4>: mov $0x402400,%esi 0x0000000000400ee9 <+9>: call 0x401338 <strings_not_equal> 0x0000000000400eee <+14>: test %eax,%eax 0x0000000000400ef0 <+16>: je 0x400ef7 <phase_1+23> 0x0000000000400ef2 <+18>: call 0x40143a <explode_bomb> 0x0000000000400ef7 <+23>: add $0x8,%rsp 0x0000000000400efb <+27>: ret End of assembler dump.
<+4>处将0x402400,赋值给%esi,接着调用了一个strings_not_equal函数,该函数显然是用来进行字符串比较的。我们通过查看strings_not_equal的汇编代码,大致可以推测将比较结果放入了%eax中。如果两个字符串相等则跳转到<+23>,如果不相等则会执行explode_bomb函数,炸弹会爆炸。
0x402400很像一个地址,不难推测出这是一个字符串的地址。我们使用调试工具读取其内容。(x命令用来查看内存地址中的值,s是指定使用读取字符串的方式)
1 2 3 4 5 6 7 8 9 10 11 (gdb) x/s 0x402400 0x402400: "Border relations with Canada have never been better." which to blow yourself up. Have a nice day! 73 input = read_line(); /* Get input */ (gdb) n Border relations with Canada have never been better. 74 phase_1(input); /* Run the phase */ (gdb) n 75 phase_defused(); /* Drat! They figured it out! (gdb) n 77 printf("Phase 1 defused. How about the next one?\n");
phase_2
老样子,我们使用gdb的调试命令来查看汇编代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (gdb) disassemble phase_2 Dump of assembler code for function phase_2: 0x0000000000400efc <+0>: push %rbp 0x0000000000400efd <+1>: push %rbx 0x0000000000400efe <+2>: sub $0x28,%rsp 0x0000000000400f02 <+6>: mov %rsp,%rsi 0x0000000000400f05 <+9>: call 0x40145c <read_six_numbers> 0x0000000000400f0a <+14>: cmpl $0x1,(%rsp) 0x0000000000400f0e <+18>: je 0x400f30 <phase_2+52> 0x0000000000400f10 <+20>: call 0x40143a <explode_bomb> 0x0000000000400f15 <+25>: jmp 0x400f30 <phase_2+52> 0x0000000000400f17 <+27>: mov -0x4(%rbx),%eax 0x0000000000400f1a <+30>: add %eax,%eax 0x0000000000400f1c <+32>: cmp %eax,(%rbx) 0x0000000000400f1e <+34>: je 0x400f25 <phase_2+41> 0x0000000000400f20 <+36>: call 0x40143a <explode_bomb> 0x0000000000400f25 <+41>: add $0x4,%rbx 0x0000000000400f29 <+45>: cmp %rbp,%rbx 0x0000000000400f2c <+48>: jne 0x400f17 <phase_2+27> 0x0000000000400f2e <+50>: jmp 0x400f3c <phase_2+64> 0x0000000000400f30 <+52>: lea 0x4(%rsp),%rbx 0x0000000000400f35 <+57>: lea 0x18(%rsp),%rbp 0x0000000000400f3a <+62>: jmp 0x400f17 <phase_2+27> 0x0000000000400f3c <+64>: add $0x28,%rsp 0x0000000000400f40 <+68>: pop %rbx 0x0000000000400f41 <+69>: pop %rbp 0x0000000000400f42 <+70>: ret End of assembler dump.
这一次的代码比上一次的更加复杂,而且结构更加复杂,从<+48>可以跳转到<+27>,代表这个结构是非线性的。
首先,我们需要将原始代码分成几个独立的代码块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <+0>: 0x0000000000400efc <+0>: push %rbp 0x0000000000400efd <+1>: push %rbx 0x0000000000400efe <+2>: sub $0x28,%rsp 0x0000000000400f02 <+6>: mov %rsp,%rsi 0x0000000000400f05 <+9>: call 0x40145c <read_six_numbers> 0x0000000000400f0a <+14>: cmpl $0x1,(%rsp) 0x0000000000400f0e <+18>: je 0x400f30 <phase_2+52> 0x0000000000400f10 <+20>: call 0x40143a <explode_bomb> <+52>: 0x0000000000400f30 <+52>: lea 0x4(%rsp),%rbx 0x0000000000400f35 <+57>: lea 0x18(%rsp),%rbp 0x0000000000400f3a <+62>: jmp 0x400f17 <phase_2+27> <+27>: 0x0000000000400f17 <+27>: mov -0x4(%rbx),%eax 0x0000000000400f1a <+30>: add %eax,%eax 0x0000000000400f1c <+32>: cmp %eax,(%rbx) 0x0000000000400f1e <+34>: je 0x400f25 <phase_2+41> 0x0000000000400f10 <+20>: call 0x40143a <explode_bomb> <+41>: 0x0000000000400f25 <+41>: add $0x4,%rbx 0x0000000000400f29 <+45>: cmp %rbp,%rbx 0x0000000000400f2c <+48>: jne 0x400f17 <phase_2+27> 0x0000000000400f2e <+50>: jmp 0x400f3c <phase_2+64> <+64>: 0x0000000000400f3c <+64>: add $0x28,%rsp 0x0000000000400f40 <+68>: pop %rbx 0x0000000000400f41 <+69>: pop %rbp 0x0000000000400f42 <+70>: ret
拆解后我们可以观察到,程序开始要求读入六个数字,并且将rsp减去0x28,也就是申请了40个字节的栈空间。不难猜测,读取的六个数字存放在了申请的栈空间中(当然,更为严谨的做法是继续观察read_six_numbers的汇编代码)。接着,将0x1和栈底的元素比较,如果相等则跳转到<+52>块,否则炸弹爆炸。所以第一个输入的值input[0] = 1。
我们继续往下看,<+52>的代码块。它实际上是循环体的初始化,rbp代表第6个int的尾地址,也就是循环的终止点。而rbx是循环的初始值。初始化完成后,跳转到<+27>。
<+27>是循环体。每次取出rbx的前一个值赋值给eax,同时将eax*=2,并且和将eax和rbx作比较。如果不等,则失败。
<+41>是朝下一次循环迭代。最后,遍历完成6个数后,跳转到<+64>。
最后,我们不难写出与这个汇编代码等效的C代码。
1 2 3 4 5 6 7 8 int a[6 ] = {*, *, *, *, *, *};if (a[0 ] != 1 ) explode_bomb;for (int i = 1 ; i < 6 ; i ++) { int x = a[i], y = a[i - 1 ]; y *= 2 ; if (x != y) explode_bomb; } success;
由于a[0]必须是1,且a[i]必须是a[i-1]的两倍,我们容易得到答案,即输入为1,2,4,8,16,32。
phase_3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 (gdb) disassemble phase_3 Dump of assembler code for function phase_3: 0x0000000000400f43 <+0>: sub $0x18,%rsp 0x0000000000400f47 <+4>: lea 0xc(%rsp),%rcx 0x0000000000400f4c <+9>: lea 0x8(%rsp),%rdx 0x0000000000400f51 <+14>: mov $0x4025cf,%esi 0x0000000000400f56 <+19>: mov $0x0,%eax 0x0000000000400f5b <+24>: call 0x400bf0 <__isoc99_sscanf@plt> 0x0000000000400f60 <+29>: cmp $0x1,%eax 0x0000000000400f63 <+32>: jg 0x400f6a <phase_3+39> 0x0000000000400f65 <+34>: call 0x40143a <explode_bomb> 0x0000000000400f6a <+39>: cmpl $0x7,0x8(%rsp) 0x0000000000400f6f <+44>: ja 0x400fad <phase_3+106> 0x0000000000400f71 <+46>: mov 0x8(%rsp),%eax 0x0000000000400f75 <+50>: jmp *0x402470(,%rax,8) 0x0000000000400f7c <+57>: mov $0xcf,%eax 0x0000000000400f81 <+62>: jmp 0x400fbe <phase_3+123> 0x0000000000400f83 <+64>: mov $0x2c3,%eax 0x0000000000400f88 <+69>: jmp 0x400fbe <phase_3+123> 0x0000000000400f8a <+71>: mov $0x100,%eax 0x0000000000400f8f <+76>: jmp 0x400fbe <phase_3+123> 0x0000000000400f91 <+78>: mov $0x185,%eax 0x0000000000400f96 <+83>: jmp 0x400fbe <phase_3+123> 0x0000000000400f98 <+85>: mov $0xce,%eax 0x0000000000400f9d <+90>: jmp 0x400fbe <phase_3+123> 0x0000000000400f9f <+92>: mov $0x2aa,%eax 0x0000000000400fa4 <+97>: jmp 0x400fbe <phase_3+123> 0x0000000000400fa6 <+99>: mov $0x147,%eax 0x0000000000400fab <+104>: jmp 0x400fbe <phase_3+123> 0x0000000000400fad <+106>: call 0x40143a <explode_bomb> 0x0000000000400fb2 <+111>: mov $0x0,%eax 0x0000000000400fb7 <+116>: jmp 0x400fbe <phase_3+123> 0x0000000000400fb9 <+118>: mov $0x137,%eax 0x0000000000400fbe <+123>: cmp 0xc(%rsp),%eax 0x0000000000400fc2 <+127>: je 0x400fc9 <phase_3+134> 0x0000000000400fc4 <+129>: call 0x40143a <explode_bomb> 0x0000000000400fc9 <+134>: add $0x18,%rsp 0x0000000000400fcd <+138>: ret
有了上面两次的经验,我们将所有看起来像地址的16进制数进行取数/取字符串。
1 2 (gdb) x/s 0x4025cf 0x4025cf: "%d %d"
输入为两个整数,我们输入两个整数后,执行到<+29>处,查看寄存器rex的值为2,推断出里面存储的是输入数值的个数。还有一个地址0x402470,我们一并进行分析。有点不明所以,我们稍后再看。
1 2 (gdb) x/x 0x402470 0x402470: 0x7c
我们对汇编代码进行格式化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <+0>: 0x0000000000400f43 <+0>: sub $0x18 ,%rsp 0x0000000000400f47 <+4>: lea 0xc(%rsp),%rcx 0x0000000000400f4c <+9>: lea 0x8(%rsp),%rdx 0x0000000000400f51 <+14>: mov $0x4025cf ,%esi 0x0000000000400f56 <+19>: mov $0x0 ,%eax 0x0000000000400f5b <+24>: call 0x400bf0 <__isoc99_sscanf@plt> 0x0000000000400f60 <+29>: cmp $0x1 ,%eax 0x0000000000400f63 <+32>: jg 0x400f6a <phase_3+39> 0x0000000000400f65 <+34>: call 0x40143a <explode_bomb> <+39>: 0x0000000000400f6a <+39>: cmpl $0x7 ,0x8(%rsp) if 7 < 0x8(%rsp) 0x0000000000400fad <+106>: call 0x40143a <explode_bomb> <+46>: 0x0000000000400f71 <+46>: mov 0x8(%rsp),%eax 0x0000000000400f75 <+50>: jmp *0x402470(,%rax,8) 0x0000000000400f7c <+57>: mov $0xcf ,%eax 0x0000000000400f81 <+62>: jmp 0x400fbe <phase_3+123> 0x0000000000400f83 <+64>: mov $0x2c3 ,%eax 0x0000000000400f88 <+69>: jmp 0x400fbe <phase_3+123> 0x0000000000400f8a <+71>: mov $0x100 ,%eax 0x0000000000400f8f <+76>: jmp 0x400fbe <phase_3+123> 0x0000000000400f91 <+78>: mov $0x185 ,%eax 0x0000000000400f96 <+83>: jmp 0x400fbe <phase_3+123> 0x0000000000400f98 <+85>: mov $0xce ,%eax 0x0000000000400f9d <+90>: jmp 0x400fbe <phase_3+123> 0x0000000000400f9f <+92>: mov $0x2aa ,%eax 0x0000000000400fa4 <+97>: jmp 0x400fbe <phase_3+123> 0x0000000000400fa6 <+99>: mov $0x147 ,%eax 0x0000000000400fab <+104>: jmp 0x400fbe <phase_3+123> 0x0000000000400fad <+106>: call 0x40143a <explode_bomb> 0x0000000000400fb2 <+111>: mov $0x0 ,%eax 0x0000000000400fb7 <+116>: jmp 0x400fbe <phase_3+123> 0x0000000000400fb9 <+118>: mov $0x137 ,%eax <+123>: 0x0000000000400fbe <+123>: cmp 0xc(%rsp),%eax 0x0000000000400fc2 <+127>: je 0x400fc9 <phase_3+134> 0x0000000000400fc4 <+129>: call 0x40143a <explode_bomb> 0x0000000000400fc9 <+134>: add $0x18 ,%rsp 0x0000000000400fcd <+138>: ret
<+0>在做的事情是读入两个数,如果不是,则爆炸。
<+39>将读入的第一个数和7作比较,如果比7大,则爆炸。
<+46>将第一个数赋值给eax,并且依照其值进行接下来的跳转。如果第一个数是0,则跳转到<+57>;如果是1,则跳转到<+64>,……依此类推。每个分支做的事是给eax赋不同的值,并且最后跳转到<+123>。
<+123>比较第二个数与eax的值,如果不等则爆炸,如果相等则成功。
这段代码事实上是switch语句所表达的逻辑。我们可以轻松的写出c代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 switch (x) { case 0: x = 0xcf; break ; case 1: x = 0x2c3; break ; case 2: x = 0x100; break ; case 3: x = 0x185; break ; case 4: x = 0xce; break ; case 5: x = 0x2aa; break ; case 6: x = 0x147; break ; default: explode_bomb; } if (x != y) explode_bomb;success;
所以,我们输入0~6之间的一个数与其对应的值即可。0 207是一组解。
phase_4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 (gdb) disassemble phase_4 Dump of assembler code for function phase_4: 0x000000000040100c <+0>: sub $0x18 ,%rsp 0x0000000000401010 <+4>: lea 0xc(%rsp),%rcx 0x0000000000401015 <+9>: lea 0x8(%rsp),%rdx 0x000000000040101a <+14>: mov $0x4025cf ,%esi 0x000000000040101f <+19>: mov $0x0 ,%eax 0x0000000000401024 <+24>: call 0x400bf0 <__isoc99_sscanf@plt> 0x0000000000401029 <+29>: cmp $0x2 ,%eax 0x000000000040102c <+32>: jne 0x401035 <phase_4+41> 0x000000000040102e <+34>: cmpl $0xe ,0x8(%rsp) 0x0000000000401033 <+39>: jbe 0x40103a <phase_4+46> 0x0000000000401035 <+41>: call 0x40143a <explode_bomb> 0x000000000040103a <+46>: mov $0xe ,%edx 0x000000000040103f <+51>: mov $0x0 ,%esi 0x0000000000401044 <+56>: mov 0x8(%rsp),%edi 0x0000000000401048 <+60>: call 0x400fce <func4> 0x000000000040104d <+65>: test %eax,%eax 0x000000000040104f <+67>: jne 0x401058 <phase_4+76> 0x0000000000401051 <+69>: cmpl $0x0 ,0xc(%rsp) 0x0000000000401056 <+74>: je 0x40105d <phase_4+81> 0x0000000000401058 <+76>: call 0x40143a <explode_bomb> 0x000000000040105d <+81>: add $0x18 ,%rsp 0x0000000000401061 <+85>: ret
令人意外的是,居然是前4个炸弹中代码最短的一个。不用高兴,我们发现<+60>是一个新的函数调用,所以继续查看func4的汇编代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 (gdb) disassemble func4 Dump of assembler code for function func4: 0x0000000000400fce <+0>: sub $0x8 ,%rsp 0x0000000000400fd2 <+4>: mov %edx,%eax 0x0000000000400fd4 <+6>: sub %esi,%eax 0x0000000000400fd6 <+8>: mov %eax,%ecx 0x0000000000400fd8 <+10>: shr $0x1f ,%ecx 0x0000000000400fdb <+13>: add %ecx,%eax 0x0000000000400fdd <+15>: sar %eax 0x0000000000400fdf <+17>: lea (%rax,%rsi,1),%ecx 0x0000000000400fe2 <+20>: cmp %edi,%ecx 0x0000000000400fe4 <+22>: jle 0x400ff2 <func4+36> 0x0000000000400fe6 <+24>: lea -0x1(%rcx),%edx 0x0000000000400fe9 <+27>: call 0x400fce <func4> 0x0000000000400fee <+32>: add %eax,%eax 0x0000000000400ff0 <+34>: jmp 0x401007 <func4+57> 0x0000000000400ff2 <+36>: mov $0x0 ,%eax 0x0000000000400ff7 <+41>: cmp %edi,%ecx 0x0000000000400ff9 <+43>: jge 0x401007 <func4+57> 0x0000000000400ffb <+45>: lea 0x1(%rcx),%esi 0x0000000000400ffe <+48>: call 0x400fce <func4> 0x0000000000401003 <+53>: lea 0x1(%rax,%rax,1),%eax 0x0000000000401007 <+57>: add $0x8 ,%rsp 0x000000000040100b <+61>: ret End of assembler dump.
首先,我们来观察一下func4,它在其内部调用了本身,也就是说func4是一个递归函数。func4内部没有炸弹,我们关心如何跳出循环即可。所以我们进行倒推,最后一次跳转到<+57>是在<+43>处,此处是一个条件跳转,比较%edi和%ecx,要求%edi>=%ecx即可。
%edi是什么呢?func4中并未出现过%edi,所以我们回到phase_4,这是%edi的最后一次赋值。
1 0x0000000000401044 <+56>: mov 0x8(%rsp),%edi
%edi的真实面目是我们输入的第一个数!同时我们需要注意,如果从<+43>跳转到<+57>再返回。我们的%rax也就是函数返回值是0,<+36>对其进行了赋值。
继续往上追溯,第一次跳转到<+36>是在<+22>处,注意到从<+0>到<+22>均未出现条件跳转。所以我们直接将他们翻译成c代码(很容易)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 int func (int x, int y) { int res = x; res -= y; int tmp = (res >> 31 ); res = (res + tmp) >> 1 ; tmp = res + y * 1 ; if (input_1 <= tmp) { 跳转到<+36 > if (input_1 >= tmp) { 跳转到<+57 > return 0 ; } } }
这段代码的输入是?从phase_4寻找答案。
1 2 3 4 0x000000000040103a <+46 >: mov $0xe ,%edx0x000000000040103f <+51 >: mov $0x0 ,%esi0x0000000000401044 <+56 >: mov 0x8 (%rsp),%edi0x0000000000401048 <+60 >: call 0x400fce <func4>
x = 14, y = 0。所以tmp = (0+14)>>1 + 0*1 = 7。观察到input_1 <= tmp,input_1 >= tmp,所以input_1 == tmp。因此我们输入的第一个数可以是7。
观察phase_4发现,只有一处提及我们输入的第二个数,也就是<+69>。
1 2 0x0000000000401051 <+69 >: cmpl $0x0 ,0xc (%rsp)0x0000000000401056 <+74 >: je 0x40105d <phase_4+81 >
如果等于0,则跳转到ret,否则炸弹爆炸。
至此我们成功解决了第四个问题,(7 0)是一组可行的解。
phase_5
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 (gdb) disassemble phase_5 Dump of assembler code for function phase_5: 0x0000000000401062 <+0 >: push %rbx 0x0000000000401063 <+1 >: sub $0x20 ,%rsp 0x0000000000401067 <+5 >: mov %rdi,%rbx 0x000000000040106a <+8 >: mov %fs:0x28 ,%rax 0x0000000000401073 <+17 >: mov %rax,0x18 (%rsp) 0x0000000000401078 <+22 >: xor %eax,%eax 0x000000000040107a <+24 >: call 0x40131b <string_length> 0x000000000040107f <+29 >: cmp $0x6 ,%eax 0x0000000000401082 <+32 >: je 0x4010d2 <phase_5+112 > 0x0000000000401084 <+34 >: call 0x40143a <explode_bomb> 0x0000000000401089 <+39 >: jmp 0x4010d2 <phase_5+112 > 0x000000000040108b <+41 >: movzbl (%rbx,%rax,1 ),%ecx 0x000000000040108f <+45 >: mov %cl,(%rsp) 0x0000000000401092 <+48 >: mov (%rsp),%rdx 0x0000000000401096 <+52 >: and $0xf ,%edx 0x0000000000401099 <+55 >: movzbl 0x4024b0 (%rdx),%edx 0x00000000004010a0 <+62 >: mov %dl,0x10 (%rsp,%rax,1 ) 0x00000000004010a4 <+66 >: add $0x1 ,%rax 0x00000000004010a8 <+70 >: cmp $0x6 ,%rax 0x00000000004010ac <+74 >: jne 0x40108b <phase_5+41 > 0x00000000004010ae <+76 >: movb $0x0 ,0x16 (%rsp) 0x00000000004010b3 <+81 >: mov $0x40245e ,%esi 0x00000000004010b8 <+86 >: lea 0x10 (%rsp),%rdi 0x00000000004010bd <+91 >: call 0x401338 <strings_not_equal> 0x00000000004010c2 <+96 >: test %eax,%eax 0x00000000004010c4 <+98 >: je 0x4010d9 <phase_5+119 > 0x00000000004010c6 <+100 >: call 0x40143a <explode_bomb> 0x00000000004010cb <+105 >: nopl 0x0 (%rax,%rax,1 ) 0x00000000004010d0 <+110 >: jmp 0x4010d9 <phase_5+119 > 0x00000000004010d2 <+112 >: mov $0x0 ,%eax 0x00000000004010d7 <+117 >: jmp 0x40108b <phase_5+41 > 0x00000000004010d9 <+119 >: mov 0x18 (%rsp),%rax 0x00000000004010de <+124 >: xor %fs:0x28 ,%rax 0x00000000004010e7 <+133 >: je 0x4010ee <phase_5+140 > 0x00000000004010e9 <+135 >: call 0x400b30 <__stack_chk_fail@plt> 0x00000000004010ee <+140 >: add $0x20 ,%rsp 0x00000000004010f2 <+144 >: pop %rbx 0x00000000004010f3 <+145 >: ret
做到实验五了,对于长的像地址的十六进制数,我们应该能很敏锐地察觉到。<+55>处的0x4024b0和<+81>处的0x40245e显然是地址。我们以字符串方式读取这个两块地址。
1 2 3 4 (gdb) x/s 0x4024b0 0x4024b0 <array.3449 >: "maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?" (gdb) x/s 0x40245e 0x40245e : "flyers"
注意观察<+91>处调用了strings_not_equal函数,该函数看名字就知道,是用来做字符串比较的。通过之前的Machine-Level Programming III: Procedures 中的Pass data小节中,我们得知了%rdi和%rsi为函数传参时优先使用的两个寄存器。所以应该是把两个字符串的首地址传入strings_not_equal中,进行字符串比较。一个字符串是”flyers“,另外一个是栈上的字符串。
接着我们来看看,栈上的字符串是如何构建的。
1 2 3 4 5 6 7 8 9 0x000000000040108b <+41 >: movzbl (%rbx,%rax,1 ),%ecx0x000000000040108f <+45 >: mov %cl,(%rsp)0x0000000000401092 <+48 >: mov (%rsp),%rdx0x0000000000401096 <+52 >: and $0xf ,%edx0x0000000000401099 <+55 >: movzbl 0x4024b0 (%rdx),%edx0x00000000004010a0 <+62 >: mov %dl,0x10 (%rsp,%rax,1 )0x00000000004010a4 <+66 >: add $0x1 ,%rax0x00000000004010a8 <+70 >: cmp $0x6 ,%rax0x00000000004010ac <+74 >: jne 0x40108b <phase_5+41 >
这一段,是我们再熟悉不过的循环。其中%rax是循环变量,<+62>行是给栈上字符数组的第i个元素赋值。赋值成什么呢?注意到0x4024b0(%rdx),也就是一个字符串的第%rdx个字符,而%rdx个字符是我们输入的第%rdx个字符与上0xf后得到。所以我们需要输入6个字符,使其与上0xf后,转换为"maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?"这个字符串与之对应下标的字符。转换完成后,要与flyers相同。
flyers->(9,15,14,5,6,7)->)/.%&’
1 2 3 So you got that one. Try this one. )/.%&' Good work! On to the next...
phase_6
Too hard,待完成。