whklhh 发表于 2017-10-13 00:44

【Reversing.kr】SimpleVM

本帖最后由 whklhh 于 2017-10-13 00:53 编辑

Reversing.kr是韩国的一个逆向题目网站
有分国度的排行榜,还是挺有意思的
本题即来自于第十七关SimpleVM
http://reversing.kr/challenge.php可以下载到文件

本来以为这个标题跟南邮CTF平台上的题目一样,只是个简单的虚拟机壳题……
没想到南邮的是入门类型,这个是复杂入门类型啊QAQ

ELF文件,拖入IDA发现EP地址在映像范围之外,于是无法正常分析
gdb加载又没有任何函数可下断

参考了一下WP,发现可以直接用IDA附加进程,然后dump内存下来再分析

dump脚本:


#include <idc.idc>
static main(void)
{
      auto i,fp;
      fp = fopen("f:\\dump.dex","wb");
      for(i=0x8048000;i<0x804C000;i++)
      {
                fputc(Byte(i),fp);
      }
}


直接保存用IDA加载即可(如果直接命令行输入的话去掉#include和main,直接输入命令即可)
dump下来再拖入IDA分析,这次能得到反汇编代码了
不过导入表坏了╮(╯_╰)╭
只好连猜带蒙

首先根据明码字符串"input:"定位到这个地方,猜测该函数为输出printf



http://img.blog.csdn.net/20171012234531835?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hrbGhoaGg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast


错误提示Wrong是被加密过的,直接搜索没有结果

根据print的出现地方和参数,可以猜测出byte_804b06e等几个字符串就是加密的提示内容了

附加的时候发现有两个进程
代码中又出现了这种结构
v6 = sub()
if(v6 == -1)
    xxx
else if (v6)
    xxx
else
    xxx
那么就该想起fork函数了
执行它的时候会再复制一个一模一样的子进程
然后父进程中的返回值为子进程的pid,子进程中的返回值为0
返回值为-1时为fork失败
根据fork的返回值来选择父进程和子进程的不同代码

分别分析:
http://img.blog.csdn.net/20171012235830654?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hrbGhoaGg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
read函数是通过IDA中观察函数堆栈的返回地址猜出来的
输入内容放在v9中,v9和v10各4个字节,v11作为溢出标志
如果溢出的话就输入Wrong
否则解密关键字符串0x804B0A0(长度200)

然后先后两个printf将v9(输入字符串)和0x804B0A0送出,注意这里第一个参数是v3,而之前输出Wrong的第一个参数是1

其实这个printf应该是管道,父子进程通过管道交互
原理是管道调用相当于两个文件句柄,一个读一个写
由于fork完全复制,因此父子进程所拥有的管道的句柄是相同的

也因此,父进程写入,子进程读取或者子进程写入,父进程读取都是可行的
这里的v3就是管道写入句柄了
然后父进程通过read(第一个参数为v2,管道读取句柄)来先后获取输入值和0x804B0A0字符串
http://img.blog.csdn.net/20171013001038343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hrbGhoaGg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
父进程再次对0x804B0A0进行解密,然后将输入的8个比特放在开头,再异或10(从之后的分析可以看出来这个其实是加密)

成功的条件是chechk()函数返回1,并且key非0

那么接下来去check里看看吧:
http://img.blog.csdn.net/20171013001312946?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hrbGhoaGg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
结构看起来不复杂,但是每个case里面一个子函数很让人头疼……
程序不断读取字符串的数据,异或10解密后作为switch

子函数中的处理就很复杂了,每个都不一样,并且最后还会改变字符串的值
不过因为输入的值的ASCII一定在33以上,因此不可能作为swtich部分

尝试在IDA中对输入字符串下读取断点,发现是经过异或后写入字符串中
这种改变程序流程的难度就比较大了……因为逆运算很困难

穷举空间在93^8左右,本身计算也比较耗时,难度有点大

不过我们可以猜测一下流程
异或输入字符,写入字符串,之后再读取该值,正确的话会进入下一步case
错误的话则会直接落入swtich的default中跳出

那么这里就有一个东西可以利用了
运算次数

破解方法学名叫做“边信道攻击”
通过pintools可以注入dll入程序中进行计算指令次数

我们依次对输入字符串下读取断点,可以发现程序的读取过程是从前往后依次的,那么就允许来逐字符爆破了

逐字符爆破的穷举空间就只有93*8的大小了

如果某个字符合格了,那么程序就可以晚落入default中一段时间,通过计算指令次数就能知道这一点

python2爆破代码:
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import popen2,string

INFILE = "test"
CMD = "pin/pin/pin -t pin/pin/source/tools/ManualExamples/obj-ia32/inscount1.so -- IDA/SimpleVM <" + INFILE
choices = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&'()*+,-./:;<=>?@[\]^_`{|}~"#自定义爆破字典顺序,将数字和小写字母提前可以使得速度快一些~

def execlCommand(command):
    global f
    fin,fout = popen2.popen2(command)
    result1 = fin.readline()#获取程序自带打印信息,wrong或者correct
    print result1
    if(result1 != 'Input : Wrong\n'):#输出Correct时终止循环
      f = 0
    result2 = fin.readline()#等待子进程结束,结果输出完成
    fin.close()



def writefile(data):
    fi = open(INFILE,'w')
    fi.write(data)
    fi.close()

flag = ''
f = 1
while(f):
    l = 0#初始化计数器
    for i in choices:
      key = flag + i#测试字符串
      print ">",key
      writefile(key)
      execlCommand(CMD)
      fi = open('./inscount.out', 'r')
      n = int(fi.read(), 10)
      fi.close()
      print n
      if(n-l > 30 and l):#如果两次运行指令差别过大,说明字符正确
            flag += i
            break
      else:
            l = n
print flag

得到结果

http://img.blog.csdn.net/20171013003650117?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hrbGhoaGg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast
这个SimpleVM一点都不Simple……(:з」∠)

xiayang1314 发表于 2017-10-13 01:08

52pojieggh 发表于 2017-10-13 08:58

lyjhdl 发表于 2017-10-13 09:40

不较高深的,我慢慢研究。谢谢。

mayl8822 发表于 2017-10-13 10:20

感谢分享

debugbaby 发表于 2017-10-13 12:09


感谢分享

Poner 发表于 2017-10-13 16:46

那一年一个小小的fork()难倒多少程序员

爱蜂玩爱疯玩 发表于 2017-11-16 13:15

页: [1]
查看完整版本: 【Reversing.kr】SimpleVM