171012 逆向-Reversing.kr(SimpleVM)

本文详细记录了对reversing.kr上的SimpleVM挑战的逆向分析过程。作者首先介绍了无法直接分析的困境,然后通过dump内存再加载到IDA进行分析。在分析过程中,发现了fork、pipe和加密解密机制,以及使用边信道攻击方法来逐字符爆破解密。挑战涉及父子进程交互、管道通信和复杂的解密逻辑,整个过程展现了逆向分析的技巧和思路。

1625-5 王子昂 总结《2017年10月12日》 【连续第377天总结】
A. reversing.kr
B.

SimpleVM

本来以为这个标题跟南邮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
这里写图片描述

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

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

附加的时候发现有两个进程
代码中又出现了这种结构

v6 = sub()
if(v6 == -1)
    xxx
else if (v6)
    xxx
else
    xxx

那么就该想起fork函数了
执行它的时候会再复制一个一模一样的子进程
然后父进程中的返回值为子进程的pid,子进程中的返回值为0
返回值为-1时为fork失败
根据fork的返回值来选择父进程和子进程的不同代码

然后分别分析:
这里写图片描述
read函数是通过IDA中观察函数堆栈的返回地址猜出来的
输入内容放在v9中,v9和v10各4个字节,v11作为溢出标志
如果溢出的话就输入Wrong
否则解密关键字符串0x804B0A0(长度200)

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

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

也因此,父进程写入,子进程读取或者子进程写入,父进程读取都是可行的
这里的v3就是管道写入句柄了

然后父进程通过read(第一个参数为v2,管道读取句柄)来先后获取输入值和0x804B0A0字符串
这里写图片描述
父进程再次对0x804B0A0进行解密,然后将输入的8个比特放在开头,再异或10(从之后的分析可以看出来这个其实是加密)

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

那么接下来去check里看看吧:
这里写图片描述
结构看起来不复杂,但是每个case里面一个子函数很让人头疼……

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

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

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

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

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

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

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

如果某个字符合格了,那么程序就可以晚落入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()[18:], 10)
        fi.close()
        print n
        if(n-l > 30 and l):#如果两次运行指令差别过大,说明字符正确
            flag += i
            break
        else:
            l = n
print flag

得到结果
这里写图片描述

这个SimpleVM一定都不Simple……(:з」∠)

C. 明日计划
reversing.kr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值