计算机系统_炸弹(boom)实验/逆向工程实验(phase_6)+ 隐藏关

(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之间的一个数。

计算机系统_炸弹(boom)实验/逆向工程实验(phase_6)+ 隐藏关_第1张图片

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到二叉树的结点答案应该就差不多可以看到了!

运用单步调试的方法:

计算机系统_炸弹(boom)实验/逆向工程实验(phase_6)+ 隐藏关_第2张图片

可见答案为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

【感谢热情的同学提供的帮助,帮我弄清了第六关的很多个点。第一次看的时候还是会有点晕,不过看完第六关第七关就好过多了!哈哈哈哈哈,认识大佬真是好。】

你可能感兴趣的:(#,Linux)