Frida在爆破Windows程序中的应用

*本文原创作者:geek痕,本文属FreeBuf原创奖励计划,未经许可禁止转载

谈到爆破,相信大部分网络安全从业者都并不陌生,爆破爆破,就是暴力破解嘛。通过枚举尝试尽可能多的可能解,再进行验证判断是否正确。在进行web的爆破时,我们通常会使用brupsuite等工具,那么,如果是二进制程序中的爆破呢?

本文将介绍一种方法,通过动态插桩(hook)的方式,实现二进制程序中的爆破。最近在学习逆向,刷一些ctf的题目,遇到了一道拖进ida死活分析不出算法,因为实在是太菜了,目标程序大概长这样:

有兴趣的可以先试试:地址如下:http://ctf5.shiyanbar.com/re/100w.exe

输入的口令正确则会弹出flag,输入错误则会弹出错误提示。

看到提示说是6位数字,而且在逆向的过程中发现有这样一段文字:

行吧…那就爆破一个试试。之前就听说过Frida牛逼的不行,跨平台的动态插桩框架,不过之前一直没亲自动手玩过,这次就试试吧。在实践过程中发现Frida的相关资料本身并不多,而且大多是针对Android移动平台的应用,于是决定写一篇文章分享一些桌面端Frida应用的技术。先上一段Frida的介绍:

So what is Frida, exactly? It’sGreasemonkey for native apps, or, put in more technical terms, it’s a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript or your own library into native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX. Frida also provides you with some simple tools built on top of the Frida API. These can be used as-is, tweaked to your needs, or serve as examples of how to use the API.

Frida是一个动态插桩的工具包。它可以让你将js脚本或那你自己的一些库插入到win、macos、linux、android、ios等平台的应用中。跨平台的实现方案听起来很牛逼有木有,这意味着熟练掌握这一个工具的性价比是很高的。乱扯了那么多,先来看下Frida使用的基本代码框架。以下是python的代码。首先,用pip安装一下:

pip install frida

然后下面这段代码是frida 的基本框架:

import frida
def on_message(message, data):
    print("[%s] => %s" % (message, data))
session = frida.attach('100fw.exe')#附加frida到目标进程
script = session.create_script('some js code here')
script.on('message', on_message)
script.load()
sys.stdin.read()
session.deatch()

代码比较简单,不多解释。重点是session.create_script里面的js代码。

首先,我们要能够模拟调用按钮点击后执行的函数。

找这个函数地址的思路有两个。一个,由于这个crackme是用易语言写的,所以用e-debug可以找到call的地址:

另外一个方法就是拖入od找字符串然后往上找到函数入口,下断点验证。不行再往上翻。

最后找到函数入口如下:

然后,我们用frida的js api写一个模拟调用的函数。

var f=new NativeFunction(ptr('0x0040173a'), 'void',[]);
rpc.exports={
    once:function(){
        f();
    }
};

NtiveFunction的后面两个参数中,第一个是返回值类型,第二个是参数列表的类型,这里都为空即可。

然后定义once模拟调用一次按钮点击事件。

最后,我们在python代码中调用frida为我们暴露出来的接口:

while(True):
    script.exports.once()

以上代码可以不断模拟点击目标程序中按钮的过程。

再然后,我们需要模拟往输入中填入各个值。那么要做的就是hook获取控件数值的相关函数。找的方法嘛..我用的是先把断点下到按钮事件函数那里,然后单步走起。看哪个函数返回了输入值的指针。

ok,找到函数地址为0X00401CE7(最靠近结果的call)

接下来我们hook这个函数的返回结果,让它依次遍历每一个可能的值:

var tmp=100000;
var NeedAdd=true;
var f3=ptr('0x00401CE7');
Interceptor.attach(f3,{
    onLeave:function(result){
    /*
        console.log('----------')
        console.log(result.toInt32());*/
        Memory.writeAnsiString(ptr(result.toInt32()),tmp.toString())//result为输入值的指针指向

        if(NeedAdd){tmp=tmp+1;NeedAdd=false;}
        else{NeedAdd=true;}
        /*
        console.log(Memory.readAnsiString(ptr(result.toInt32())));*///输出修改后的结果
    }
});

上面的代码有注释,这里解释下为什么用NeedAdd辅助来让tmp值每两次递增一次.因为…我比较菜hook点不是很合适,每一次调用都会有两次被hook到,所以..就出此下策了。

接下来,我们要hook掉消息框弹窗函数,获取提示内容以判断口令的正确与否。

眼看着这是最后一步了,但我却在这里踩了很多坑。

首先,获取信息框内容嘛,好啊,我hook MessageBox不就好了,于是用OD插件给API下断一通乱搞,获取到了弹窗内容美滋滋。跑起来一看,等等!难道要我每一次都点一下确认把消息框弄掉才能进行下一次尝试吗?不行!要把这个信息框干掉。然后想着直接跳过对MessageBox的call,结果程序崩了,调试一番才发现,堆栈不平衡,hook了好几个都不行。

就在这里卡了好一会,后来觉得沿着api的调用栈一直往上翻,一定能找到用户态最初的call,那个call的调用关系应该相对简单,堆栈平衡问题也比较容易处理,然后就一直找啊找,发现就在搜到的字符串附近有这样一段代码,顿时两眼放光:

这就好办了,结果跟进去一看,发现这只是一个索引,hook这个会影响很多函数:

那不如..直接把这个call给nop掉吧..把这个嘴给塞住。

测试之后发现经过add esp,0x28后堆栈刚好平衡,很好,perfect!

现在,我们可以写出最后一段代码了:

var f4=ptr('0x00401C03');
Interceptor.attach(f4,{
    onEnter:function(args){
        //console.log(args[0]);
        send(Memory.readAnsiString(ptr(args[0])));//读取相应内容并发送给终端显示

        return;
    }
});

这里解释一下,为什么把0x00401c03 nop掉之后仍然可以hook这里。

根据我的猜测,frida的hook应该是内存断点,获取的“参数”就是根据堆栈情况的相对位置确定的,所以我们可以“hook”这个地方,获取到前面push的内容,至于那个args[0],多试几个就好了。

其实,成功的时候call的地方不在这里,而我们没有处理成功弹窗的相关代码,成功后自然会弹出来,这里的显示有些多余,当作实验就好了吧。

下面是完整代码:

import frida
import sys

def on_message(message, data):
    print("[%s] => %s" % (message, data))

session = frida.attach('100fw.exe')
script = session.create_script('''
    var tmp=700000;
    var NeedAdd=true;
    var f1 = ptr('0x00401096');
    var f2 = ptr('0x0040169d');
    Interceptor.attach(f1, {
        onEnter:function(args){

        }
    });
    Interceptor.attach(f2, {
        onEnter:function(args){

        }
    });
    var f=new NativeFunction(ptr('0x0040173a'), 'void',[]);
    rpc.exports={
        once:function(){
            f();
        }
    };
    var f3=ptr('0x00401CE7');
    Interceptor.attach(f3,{
        onLeave:function(result){
        /*
            console.log('----------')
            console.log(result.toInt32());*/
            Memory.writeAnsiString(ptr(result.toInt32()),tmp.toString())

            if(NeedAdd){tmp=tmp+1;NeedAdd=false;}
            else{NeedAdd=true;}
            //console.log(Memory.readAnsiString(ptr(result.toInt32())));
            console.log(tmp);
        }
    });
    /*
    var f4=ptr('0x00401C03');
    Interceptor.attach(f4,{
        onEnter:function(args){
            //console.log(args[0]);
            send(Memory.readAnsiString(ptr(args[0])));

            return;
        }
    });
    */

''')
script.on('message', on_message)
script.load()
while(True):
    script.exports.once()
sys.stdin.read()
session.deatch()

最好运行成功之后是酱紫的:

再说几点注意吧,首先是运行的时候要先运行程序,再运行py脚本,不然会出现这个:

然后是我们要先在输入框中输入一个随意的六位数,这样系统才会分配一个储存的空间。不然会出现这样:

这个解决方案有个地方不足就是效率还是低了点,完整爆破需要一些时间。

我尝试过减少调试性的输出来提升效率,还是有一定效果的。然后因为爆破的时候cpu并没有跑满,所以多开几个实例来分段跑估计也能快不少。看了正解算法的确比较复杂,orz。

最后,本文旨在探讨Frida的使用技巧。总的来说,Frida的可玩性还是很高的,还有很多js api接口没有介绍,有兴趣的可以去官网看看文档。

写文章的经验不多,还请各位dalao拍砖!

*本文原创作者:geek痕,本文属FreeBuf原创奖励计划,未经许可禁止转载

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2018-09-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏dotnet & java

WCF入门 (14)

做了一份面试题,最后一题是数据库的,写个查询。要查出Score有两次及两次以上超过79的Name和他的最高得分,同时显示超过79分的次数。

9820
来自专栏Java帮帮-微信公众号-技术文章全总结

从零开始学Java-SpringMVC统一异常处理

看到 Exception 这个单词都心慌 如果有一天你发现好久没有看到Exception这个单词了,那你会不会想念她?我是不会的。她如女孩一样的令人心动又心慌...

35140
来自专栏小樱的经验随笔

CTF---隐写术入门第二题 小苹果

小苹果分值:10 来源: hanyuhang 难度:易 参与人数:2159人 Get Flag:862人 答题人数:996人 解题通过率:87% flag格...

32350
来自专栏极客慕白的成长之路

信息安全实验室招新试题和完全解析

写个网页应该是很简单的,不管是静态网页还是带特效的网页。但是有几个问题,需要说明一下。

13230
来自专栏FreeBuf

kill.exe溢出漏洞分析与EXP讨论

* 本文原创作者:zzz66686,本文属FreeBuf原创奖励计划,未经许可禁止转载 1. 前言 前几日,笔者在exploit-db上发现了一个kill.ex...

25490
来自专栏黑泽君的专栏

day38_Spring学习笔记_06_CRM_02

注意:当前员工的职务所属的部门,此部门下的所有职务。代码表示:post.department.postSet editStaff.jsp

11220
来自专栏企鹅号快讯

phpjiami 数种解密方法

Pwnhub公开赛出了个简单的PHP代码审计题目,考点有两个: 如果说仅为了做出题目拿到flag,这个题目太简单,后台也有数十名选手提交了答案和writeup。...

69270
来自专栏FreeBuf

oclhashcat:离线hash密码破解工具官方文档(中文版)

无聊中,就把hashcat的官方文档稍微翻译了下,方便初学的朋友查看。至于oclhashcat,它是一个离线的hash密码破解工具,与hashcat不同,它支持...

1K70
来自专栏用户2442861的专栏

python lmdb使用

学习LMDB的时候不禁想到知乎上的提问“有哪些名人长期生活在其他名人的光环下”,说实话感觉查它的人基本都是为了用Caffe……

84020
来自专栏前端说吧

vue - 生命周期第二次学习与理解

15050

扫码关注云+社区

领取腾讯云代金券