吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5208|回复: 7
收起左侧

[原创] 【Reversing.kr】SimpleVM

[复制链接]
whklhh 发表于 2017-10-13 00:44
本帖最后由 whklhh 于 2017-10-13 00:53 编辑

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

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

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

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

dump脚本:


[C++] 纯文本查看 复制代码
#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等几个字符串就是加密的提示内容了

附加的时候发现有两个进程
代码中又出现了这种结构
[C++] 纯文本查看 复制代码
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入程序中进行计算指令次数

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

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

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

python2爆破代码:
[Python] 纯文本查看 复制代码
#!/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……(:з」∠)

SimpleVM.rar

4.72 KB, 下载次数: 3, 下载积分: 吾爱币 -1 CB

免费评分

参与人数 3威望 +1 吾爱币 +9 热心值 +3 收起 理由
Three_fish + 1 + 1 谢谢@Thanks!
曹大哥丶 + 1 + 1 用心讨论,共获提升!
Sound + 1 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

头像被屏蔽
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
提示: 作者被禁止或删除 内容自动屏蔽
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 05:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表