(6)第六关
我们先看一下第六关以及里面调用的函数的代码
00000000004010d9
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
//转化成特定进制
4010ec: 89 05 8e 16 2000 mov %eax, 0x20168e(%rip)
//注意执行这条指令时候rip为4010f2,而不是4010ec
# 602780
4010f2: bf 80 27 60 00 mov $0x602780, %edi
4010f7: e8 73 ff ff ff callq 40106f
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
40110e: 39 10 cmp %edx, (%rax)
401110: 74 05 je 401117
401112: e8 26 05 00 00 callq 40163d
401117: 48 83 c4 08 add $0x8, %rsp
40111b: c3 retq
被调用的fun 6:
000000000040106f
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
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
401096: 39 32 cmp %esi, (%rdx)
401098: 7f f0 jg 40108a
40109a: 48 89 cf mov %rcx, %rdi
40109d: eb 03 jmp
4010a2
40109f: 48 89 cf mov %rcx, %rdi
4010a2: 48 39 d7 cmp %rdx, %rdi
4010a5: 74 06 je 4010ad
4010a7: 4c 89 47 08 mov %r8, 0x8(%rdi)
4010ab: eb 03 jmp 4010b0
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
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
4010ce: 41 8b 30 mov (%r8), %esi
4010d1: 39 31 cmp %esi, (%rcx)
4010d3: 7f b8 jg 40108d
4010d5: eb cb jmp 4010a2
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
400db4: e8b7 00 00 00 callq 400e70
400db9: e8c7 09 00 00 callq 401785
400dbe: bfe8 19 40 00 mov $0x4019e8, %edi
400dc3: e8d8 fc ff ff callq 400aa0
400dc8: e892 08 00 00 callq 40165f
400dcd: 4889 c7 mov %rax, %rdi
400dd0: e8b7 00 00 00 callq 400e8c
400dd5: e8ab 09 00 00 callq 401785
400dda: bf47 19 40 00 mov $0x401947, %edi
400ddf: e8bc fc ff ff callq 400aa0
400de4: e876 08 00 00 callq 40165f
400de9: 4889 c7 mov %rax, %rdi
400dec: e808 01 00 00 callq 400ef9
400df1: e88f 09 00 00 callq 401785
400df6: bf65 19 40 00 mov $0x401965, %edi
400dfb: e8a0 fc ff ff callq 400aa0
400e00: e85a 08 00 00 callq 40165f
400e05: 4889 c7 mov %rax, %rdi
400e08: e8b4 01 00 00 callq 400fc1
400e0d: e873 09 00 00 callq 401785
400e12: bf18 1a 40 00 mov $0x401a18, %edi
400e17: e884 fc ff ff callq 400aa0
400e1c: e83e 08 00 00 callq 40165f
400e21: 4889 c7 mov %rax, %rdi
400e24: e8d9 01 00 00 callq 401002
400e29: e857 09 00 00 callq 401785
400e2e: bf40 1a 40 00 mov $0x401a40, %edi
400e33: e868 fc ff ff callq 400aa0
400e38: bf78 1a 40 00 mov $0x401a78, %edi
400e3d: e85e fc ff ff callq 400aa0
400e42: bfb8 1a 40 00 mov $0x401ab8, %edi
400e47: e854 fc ff ff callq 400aa0
400e4c: e80e 08 00 00 callq 40165f
400e51: 4889 c7 mov %rax, %rdi
400e54: e880 02 00 00 callq 4010d9
400e59: e827 09 00 00 callq 401785
400e5e: b800 00 00 00 mov $0x0, %eax
400e63: 5b pop %rbx
400e64: c3 retq
我们可以看到
0000000000401785
401785: 4883 ec 68 sub $0x68, %rsp
401789: 833d f0 14 20 00 06 cmpl $0x6, 0x2014f0(%rip)
# 602c80
401790: 755e jne 4017f0
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
4017b5: beca 1e 40 00 mov $0x401eca, %esi
4017ba: 488d 7c 24 10 lea 0x10(%rsp), %rdi
4017bf: e879 fa ff ff callq 40123d
4017c4: 85c0 test %eax, %eax
4017c6: 751e jne 4017e6
4017c8: bf40 1c 40 00 mov $0x401c40, %edi
4017cd: e8ce f2 ff ff callq 400aa0
4017d2: bf68 1c 40 00 mov $0x401c68, %edi
4017d7: e8c4 f2 ff ff callq 400aa0
4017dc: b800 00 00 00 mov $0x0, %eax
4017e1: e874 f9 ff ff callq 40115a
4017e6: bfa0 1c 40 00 mov $0x401ca0, %edi
4017eb: e8b0 f2 ff ff callq 400aa0
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 :
40115a: 53 push %rbx
40115b: e8 ff 04 00 00 callq 40165f
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
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
40117e: e8 ba 04 00 00 callq 40163d
401183: 89 de mov %ebx, %esi
401185: bf a0 25 60 00 mov $0x6025a0, %edi
40118a: e8 8d ff ff ff callq 40111c
40118f: 83 f8 03 cmp $0x3, %eax
401192: 74 05 je 401199
401194: e8 a4 04 00 00 callq 40163d
401199: bf 28 1b 40 00 mov $0x401b28, %edi
40119e: e8 fd f8 ff ff callq 400aa0
4011a3: e8 dd 05 00 00 callq 401785
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
【感谢热情的同学提供的帮助,帮我弄清了第六关的很多个点。第一次看的时候还是会有点晕,不过看完第六关第七关就好过多了!哈哈哈哈哈,认识大佬真是好。】