Reversing.kr题目之SimpleVM详解

作者:网友投稿 时间:2018-03-30 16:57

字号

Reversing.kr是一个韩国的逆向题练习网站,题目的质量还是比较好的,比较费时间去破解,这里给出我在刷到SimpleVM这道题目的时候的一些心得和体会。

SimpleVM ###0

拿到文件之后拖入ida分析,发现报错,illegal program entry point(C023DC)。我们继续打开文件,发现程序在00C023C7的时候就中断了,所以会报这个不合法的入口点,我们通过readelf命令可以清楚的查看到program header的信息(FileSize 为13C7):

Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000000 0x00c01000 0x00c01000 0x013c7 0x013c7 RWE 0x1000 LOAD 0x00019c 0x0804b19c 0x0804b19c 0x00000 0x00000 RW 0x1000

但是有一点是这样的,Linux中文件/内存映射总是页面大小的倍数,在x86上通常是4k。此处的映射长度0x13c7将四舍五入为页面大小的倍数,这意味着0×2000字节将被映射。所以其实我们通过010editor去查看该文件是可以看到(c023dc-c01000) = 13dc是有数据的。我们去更改一下FileSize和MemSize,之后拖入ida发现是正确的。我们在C023DC出查看代码如下:

; DATA XREF: LOAD:00C01018↑o LOAD:00C023DC mov dword_C01BF0, 252E8h LOAD:00C023DC ; --------------------------------------------------------------------------- LOAD:00C023E6 db 0E9h LOAD:00C023E7 db 5 LOAD:00C023E8 db 0F8h LOAD:00C023E9 db 0FFh LOAD:00C023E9 LOAD ends

后面的db没有被翻译出来,但是我们通过E9命令这是一个跳转的jmp命令,从而进入正常的程序流程。

###1

我们利用ida的linux_server进行虚拟机的远程调试,直接启动,ctrl+s查看段信息如下:

Reversing.kr题目之SimpleVM详解

看LOAD段完全没有任何的突破口,但是观察到debug001和debug002这两个段。我们进去看一下,发现前面几个字节0x7f,0×45,0x4c,0×46,一个明显的elf文件我们得把他dump下来,基本的ida script模板如下:

static main(void) { auto fp, begin, end, dexbyte; fp = fopen("D:\\dump.txt", "wb"); begin = 0x8048000; end = 0x804c000; for ( dexbyte = begin; dexbyte < end; dexbyte ++ ) fputc((dexbyte), fp); }

得到一个dump文件。

###2

我们将dump文件拖入ida分析,shift+f12查看字符串,好的“input”字符串没有被加密,直接出来,我们定位到该函数sub_8048556().我们发现所有的函数都被重新加密了,我们看input的这个函数,

sub_8048460(1, (int)"Input : ", 8);

明显可以得出它是类似与printf()的一个函数,我这里采用了一个动态调试的方法去跟踪该函数。首先记录下该函数的位置为8048460,接着利用ida运行起来这个文件,定位到8048460这个点。快捷键c一下,code出来为jmp off_804B018,跟进可知off_804B018的值为F7E1D3C0,显然这是一个so文件的函数,我们ctrl+s发现这个函数存在libc_2.23.so中。去查看Modules窗口查看libc_2.23.so,好像并不能查看name,无妨,我们从虚拟机中拷贝一份libc_2.23.so拖入ida分析,通过F7E1D3C0与libc_2.23.so的首地址的偏移量去查找,发现这个函数就是libc中的write函数。利用这个方法,我们可以得出大部分的系统调用函数出来,大致如下:

sub_8048460 -- write() sub_8048470 -- pipe() sub_8048480 -- fork() sub_8048400 -- read()

同时通过动态调试将类似与下方的代码分析称相应的字符串输出即可:

for ( i = 1; i <= 6; ++i ) { v8 = byte_804B074[i - 1] ^ i; write(1, (int)&v8, 1); }

整个函数翻译如下:

unsigned int sub_8048556() { v16 = __readgsdword(0x14u); if ( getuid() ) { v9 = 0; for ( i = 1; i <= 14; ++i ) { v8 = access_denyed[i - 1] ^ i; write(1, (int)&v8, 1); } } else { write(1, (int)"Input : ", 8); if ( pipe((int)&v3) != -1 && pipe((int)&v5) != -1 ) { v7 = fork(); if ( v7 == -1 ) { v9 = 0; for ( i = 1; i <= 6; ++i ) { v8 = error[i - 1] ^ i; write(1, (int)&v8, 1); } } else if ( v7 ) { v13 = 0; v14 = 0; v15 = 0; read(v3, (int)&v13, 9); read(v3, (int)&dword_804B0A0, 200); for ( i = 0; i <= 199; ++i ) *(_BYTE *)(i + 134525088) ^= 0x20u; dword_804B0A0 = v13; dword_804B0A4 = v14; for ( i = 0; i <= 199; ++i ) *(_BYTE *)(i + 134525088) ^= 0x10u; if ( sub_8048C6D() == 1 ) { if ( dword_804B190 ) { v9 = 0; for ( i = 1; i <= 9; ++i ) { v8 = Correct[i - 1] ^ i; write(1, (int)&v8, 1); } } else { v9 = 0; for ( i = 1; i <= 6; ++i ) { v8 = Wrong[i - 1] ^ i; write(1, (int)&v8, 1); } } } else { v9 = 0; for ( i = 1; i <= 6; ++i ) { v8 = Wrong[i - 1] ^ i; write(1, (int)&v8, 1); } } } else { v10 = 0; v11 = 0; v12 = 0; read(0, (int)&v10, 10); if ( (_BYTE)v12 ) { v9 = 0; for ( i = 1; i <= 6; ++i ) { v8 = Wrong[i - 1] ^ i; write(1, (int)&v8, 1); } } else { write(v4, (int)&v10, 9); for ( i = 0; i <= 199; ++i ) { v0 = sub_80489AA(*(unsigned __int8 *)(i + 134525088), 3); *(_BYTE *)(i + 134525088) = v0; } sub_8048410(dword_804B180); write(v4, (int)&dword_804B0A0, 200); } } } else { v9 = 0; for ( i = 1; i <= 6; ++i ) { v8 = error[i - 1] ^ i; write(1, (int)&v8, 1); } } } v2 = __readgsdword(0x14u); result = v2 ^ v16; if ( v2 != v16 ) result = sub_8048420(); return result; } ###3

好了,代码差不多出来了,现在先分析代码的功能。首先分析:

if ( pipe((int)&v3) != -1 && pipe((int)&v5) != -1 ) { v7 = fork();
责任编辑:CQITer新闻报料:400-888-8888   本站原创,未经授权不得转载
继续阅读
热新闻
推荐
关于我们联系我们免责声明隐私政策 友情链接