(6)第六关
我们先看一下第六关以及里面调用的函数的代码
00000000004010d9 <phase_6>:
4010d9: 48 83 ec 08 sub $0x8, %rsp
4010dd: ba 0a 00 00 00 mov $0xa, %edx
4010e2: be 00 00 00 00 mov $0x0, %esi
4010e7: e8 94 fa ff ff callq 400b80 <strtol@plt>
//转化成特定进制
4010ec: 89 05 8e 16 2000 mov %eax, 0x20168e(%rip)
//注意执行这条指令时候rip为4010f2,而不是4010ec
# 602780<node0>
4010f2: bf 80 27 60 00 mov $0x602780, %edi
4010f7: e8 73 ff ff ff callq 40106f <fun6>
4010fc: 48 8b 40 08 mov 0x8(%rax), %rax
401100: 48 8b 40 08 mov 0x8(%rax), %rax
401104: 48 8b 40 08 mov 0x8(%rax), %rax
401108: 8b 15 72 16 20 00 mov 0x201672(%rip), %edx
# 602780<node0>
40110e: 39 10 cmp %edx, (%rax)
401110: 74 05 je 401117<phase_6+0x3e>
401112: e8 26 05 00 00 callq 40163d <explode_bomb>
401117: 48 83 c4 08 add $0x8, %rsp
40111b: c3 retq
被调用的fun 6:
000000000040106f <fun6>:
40106f: 4c 8b 47 08 mov 0x8(%rdi), %r8
401073: 48 c7 47 08 00 00 00 movq $0x0, 0x8(%rdi)
40107a: 00
40107b: 48 89 f8 mov %rdi,%rax
40107e: 48 89 f9 mov %rdi, %rcx
401081: 4d 85 c0 test %r8, %r8
401084: 75 40 jne 4010c6<fun6+0x57>
401086: 48 89 f8 mov %rdi, %rax
401089: c3 retq
40108a: 48 89 d1 mov %rdx, %rcx
40108d: 48 8b 51 08 mov 0x8(%rcx), %rdx
401091: 48 85 d2 test %rdx, %rdx
401094: 74 09 je 40109f<fun6+0x30>
401096: 39 32 cmp %esi, (%rdx)
401098: 7f f0 jg 40108a<fun6+0x1b>
40109a: 48 89 cf mov %rcx, %rdi
40109d: eb 03 jmp
4010a2<fun6+0x33>
40109f: 48 89 cf mov %rcx, %rdi
4010a2: 48 39 d7 cmp %rdx, %rdi
4010a5: 74 06 je 4010ad<fun6+0x3e>
4010a7: 4c 89 47 08 mov %r8, 0x8(%rdi)
4010ab: eb 03 jmp 4010b0<fun6+0x41>
4010ad: 4c 89 c0 mov %r8, %rax
4010b0: 49 8b 48 08 mov 0x8(%r8), %rcx
4010b4: 49 89 50 08 mov %rdx, 0x8(%r8)
4010b8: 48 85 c9 test %rcx, %rcx
4010bb: 74 1a je 4010d7<fun6+0x68>
4010bd: 49 89 c8 mov %rcx, %r8
4010c0: 4889 c1 mov %rax, %rcx
4010c3: 48 89 c7 mov %rax, %rdi
4010c6: 48 89 ca mov %rcx, %rdx
4010c9: 48 85 c9 test %rcx, %rcx
4010cc: 74 d4 je 4010a2<fun6+0x33>
4010ce: 41 8b 30 mov (%r8), %esi
4010d1: 39 31 cmp %esi, (%rcx)
4010d3: 7f b8 jg 40108d<fun6+0x1e>
4010d5: eb cb jmp 4010a2<fun6+0x33>
4010d7: f3 c3 repz retq
查手册得,strtol是将字符串中的数字转化为特定进制,这里的参数edx为0xa,因此转化为10进制。我们随便输入一个数字 100,在 4010ec 处设置断点,发现eax中的数字就是100.
然后发现fun6的参数edi(0x602780)固定,也就是说我们不知道fun 6有什么功能,只需要获取在cmp的时候rax寄存器的值。
我们将其改成C代码,代码如下:
void func6(){
rdi = 0x602780;
r8 = *(rdi+8);
rax = rdi;
rcx = rdi;
if(r8 == 0){
rax = rdi;
return;
}
while(1){
rdx = rcx;
if(rcx != 0){
esi = *r8;
if(*rcx > esi){
while(1){
rdx = *(rcx+0x8)
if(rdx == 0) break;
else{
if(*rdx > esi){
rcx = rdx;
}
else break;
}
}
rdi = rcx;
}
}
if(rdi != rdx) *(rdi+8) = r8;
else rax=r8;
rcx = *(r8+8);
*(r8+8) = rdx;
if(rcx == 0) return;
r8 = rcx;
rcx = rax;
rdi = rax;
}
}
引不引爆的关键在40110e处,我们在此处设置断点,将edx,(rax)的内容都打印一下,我们输入100,测试到rax里面的值是600.而如下调试,答案又是673.观察伪代码,我们就可以看出答案应该是范围在600~673之间的一个数。
phase_6(rdi)
{
rsp-=0x8;
edx=0xa;
esi=0x0;
eax = strtol(rdi,rsi,rdx);
*(rip+0x20168e) = eax; // rip相对寻址。。
edi = 0x602780;
eax = fun6(rdi); // eax = 0x602780
eax = *(eax+8); // eax = 0x6027a0
eax = *(eax+8); // eax = 0x6027c0
eax = *(eax+8); // eax = 0x6027e0
edx = *(rip+0x201672); // edx为输入的值。
//*(rax) = 673
if(*(rax)-edx==0)
return;
explode_bomb();
// 答案: 600 ~ 673
}
最后,是隐藏关代码!
那么,是怎么发现这所谓的“隐藏关”呢?让我们回到最初的入口——主函数,看看如何让进入这隐藏关的。
0000000000400d24<main>: 【只粘贴部分代码】
400db4: e8b7 00 00 00 callq 400e70 <phase_1>
400db9: e8c7 09 00 00 callq 401785 <phase_defused>
400dbe: bfe8 19 40 00 mov $0x4019e8, %edi
400dc3: e8d8 fc ff ff callq 400aa0<puts@plt>
400dc8: e892 08 00 00 callq 40165f<read_line>
400dcd: 4889 c7 mov %rax, %rdi
400dd0: e8b7 00 00 00 callq 400e8c <phase_2>
400dd5: e8ab 09 00 00 callq 401785 <phase_defused>
400dda: bf47 19 40 00 mov $0x401947, %edi
400ddf: e8bc fc ff ff callq 400aa0<puts@plt>
400de4: e876 08 00 00 callq 40165f<read_line>
400de9: 4889 c7 mov %rax, %rdi
400dec: e808 01 00 00 callq 400ef9 <phase_3>
400df1: e88f 09 00 00 callq 401785 <phase_defused>
400df6: bf65 19 40 00 mov $0x401965, %edi
400dfb: e8a0 fc ff ff callq 400aa0<puts@plt>
400e00: e85a 08 00 00 callq 40165f<read_line>
400e05: 4889 c7 mov %rax, %rdi
400e08: e8b4 01 00 00 callq 400fc1 <phase_4>
400e0d: e873 09 00 00 callq 401785 <phase_defused>
400e12: bf18 1a 40 00 mov $0x401a18, %edi
400e17: e884 fc ff ff callq 400aa0<puts@plt>
400e1c: e83e 08 00 00 callq 40165f<read_line>
400e21: 4889 c7 mov %rax, %rdi
400e24: e8d9 01 00 00 callq 401002 <phase_5>
400e29: e857 09 00 00 callq 401785 <phase_defused>
400e2e: bf40 1a 40 00 mov $0x401a40, %edi
400e33: e868 fc ff ff callq 400aa0<puts@plt>
400e38: bf78 1a 40 00 mov $0x401a78, %edi
400e3d: e85e fc ff ff callq 400aa0<puts@plt>
400e42: bfb8 1a 40 00 mov $0x401ab8, %edi
400e47: e854 fc ff ff callq 400aa0<puts@plt>
400e4c: e80e 08 00 00 callq 40165f<read_line>
400e51: 4889 c7 mov %rax, %rdi
400e54: e880 02 00 00 callq 4010d9 <phase_6>
400e59: e827 09 00 00 callq 401785 <phase_defused>
400e5e: b800 00 00 00 mov $0x0, %eax
400e63: 5b pop %rbx
400e64: c3 retq
我们可以看到<phase_defused>函数在每次调用关卡的时候都会一起调用,所以我就猜测这个函数就是进入隐藏关的关键。因为进入隐藏关一定存在一些条件,这就需要每次通关后进行比较。从这一点分析,我决定去看看<phase_defused>函数。
0000000000401785<phase_defused>:
401785: 4883 ec 68 sub $0x68, %rsp
401789: 833d f0 14 20 00 06 cmpl $0x6, 0x2014f0(%rip)
# 602c80 <num_input_strings>
401790: 755e jne 4017f0 <phase_defused+0x6b>
401792: 488d 4c 24 10 lea 0x10(%rsp), %rcx
401797: 488d 54 24 0c lea 0xc(%rsp), %rdx
40179c: bec4 1e 40 00 mov $0x401ec4, %esi
4017a1: bf30 30 60 00 mov $0x603030, %edi
4017a6: b800 00 00 00 mov $0x0, %eax
4017ab: e800 f3 ff ff callq 400ab0<__isoc99_sscanf@plt>
4017b0: 83f8 02 cmp $0x2, %eax
4017b3: 7531 jne 4017e6<phase_defused+0x61>
4017b5: beca 1e 40 00 mov $0x401eca, %esi
4017ba: 488d 7c 24 10 lea 0x10(%rsp), %rdi
4017bf: e879 fa ff ff callq 40123d<strings_not_equal>
4017c4: 85c0 test %eax, %eax
4017c6: 751e jne 4017e6<phase_defused+0x61>
4017c8: bf40 1c 40 00 mov $0x401c40, %edi
4017cd: e8ce f2 ff ff callq 400aa0<puts@plt>
4017d2: bf68 1c 40 00 mov $0x401c68, %edi
4017d7: e8c4 f2 ff ff callq 400aa0<puts@plt>
4017dc: b800 00 00 00 mov $0x0, %eax
4017e1: e874 f9 ff ff callq 40115a <secret_phase>
4017e6: bfa0 1c 40 00 mov $0x401ca0, %edi
4017eb: e8b0 f2 ff ff callq 400aa0<puts@plt>
4017f0: 4883 c4 68 add $0x68, %rsp
4017f4: c3 retq
通过cmpl $0x6,0x2014f0(%rip) 这条语句,我们需要知道*(rip+0x2014f0)的值。通过gdb调试,发现每次通过这个值会+1,故知道第6关开启。在0x603030和0x603031处设置条件断点,gdb调试发现在第四关有值变化,猜测第四关输入会对这几个位置造成影响,得出rsp+0x10这里的字符数组的输入的位置是在第四关。
进一步,为4017b3设置断点,取当前eax,*(rsp+0xc), *(rsp+0x10)的值(rsp+0x10为字符串,rsp+0xc为int)基本确认第四关输入的值存储到0x603030之后。所以要开启隐藏关,只需要改变第四关的输入内容。
查看内存,发现有以下字符串:
可以得知我们需要输入第四关原本答案加上这个字符串,即“9austinpowers”才可以开启隐藏关的大门。
接下来就可以看看隐藏关的代码了
000000000040115a <secret_phase>: 40115a: 53 push %rbx 40115b: e8 ff 04 00 00 callq 40165f <read_line> 401160: ba 0a 00 00 00 mov $0xa, %edx 401165: be 00 00 00 00 mov $0x0, %esi 40116a: 48 89 c7 mov %rax, %rdi 40116d: e8 0e fa ff ff callq 400b80 <strtol@plt> 401172: 89 c3 mov %eax, %ebx 401174: 8d 43 ff lea -0x1(%rbx), %eax 401177: 3d e8 03 00 00 cmp $0x3e8, %eax 40117c: 76 05 jbe 401183 <secret_phase+0x29> 40117e: e8 ba 04 00 00 callq 40163d <explode_bomb> 401183: 89 de mov %ebx, %esi 401185: bf a0 25 60 00 mov $0x6025a0, %edi 40118a: e8 8d ff ff ff callq 40111c <fun7> 40118f: 83 f8 03 cmp $0x3, %eax 401192: 74 05 je 401199 <secret_phase+0x3f> 401194: e8 a4 04 00 00 callq 40163d <explode_bomb> 401199: bf 28 1b 40 00 mov $0x401b28, %edi 40119e: e8 fd f8 ff ff callq 400aa0 <puts@plt> 4011a3: e8 dd 05 00 00 callq 401785 <phase_defused> 4011a8: 5b pop %rbx 4011a9: c3 retq翻译secret_phase的代码
secret_phase() { eax = read_line(); edx = 0xa; esi = 0; rdi = rax; eax = strtol(rdi,rsi,rdx); ebx = eax; eax = ebx-1; if(eax-0x3e8>0) { explode_bomb(); } esi = ebx; edi = 0x6025a0; eax = fun7(rdi,rsi); if(eax-3!=0) { explode_bomb(); } edi = 0x401b28; //Wow! You've defused the secret stage! puts(rdi); phase_defused(); }
代码其实很简单,就是输入一个数字,转为整数后减1作为fun7的一个参数。
查看fun7代码,翻译如下
int fun7(rdi,rsi) { rsp-=0x8; if(rdi==0) { eax = 0xffffffff; } else { edx = *(rdi); if(edx-esi<=0) { eax = 0; if(edx-esi==0) { goto 401155; } else { rdi = *(rdi+0x10); eax = fun7(rdi,rsi); eax = rax+rax+1; goto 401155; } } else { rdi = *(rdi+0x8); eax = fun7(rdi,rsi); eax += eax; goto 401155; } } 401155: return eax; }
通过代码可以看出这是一个简单的递归函数,一个类似于二叉树的结构。要得到结果只要查看内存get到二叉树的结点答案应该就差不多可以看到了!
运用单步调试的方法:
可见答案为99.
综上所述:
基础的6关和隐藏关,答案如下:
Science isn't aboutwhy, it's about why not?
1 2 3 1 2 3
0 535
9(9austinpowers)
7 93
673
99
【感谢热情的同学提供的帮助,帮我弄清了第六关的很多个点。第一次看的时候还是会有点晕,不过看完第六关第七关就好过多了!哈哈哈哈哈,认识大佬真是好。】