angr符号执行用例解析——csaw_wyvern

用例源码以及二进制文件链接:https://github.com/angr/angr-doc/tree/master/examples/csaw_wyvern

这是一个恐怖的世界,一条恶龙在这片领域盘旋。当然,这也是属于勇士的世界,力量和坚持是我们唯一的希望!

为了打败这条恶龙,我们要了解它的弱点,所以把它拖进IDA里分析一下。

这是一个C++的二进制文件,用到了较多的C++库函数。

执行二进制文件后,发现是需要我们输入一个字符串,直接去IDA里找,success or win等字符串。并找到它们调用的地点。

嗯 ,找到了 :[+] A great success! Here is a flag{

调用的地址是0x40E02C

那么find_addr=0x40E02C。

然而,用例并不是按照常规套路来找start地址和find地址的,而是基于unicorn引擎采用的动态符号执行分析的二进制文件。

 st = p.factory.full_init_state(args=['./wyvern'], add_options=angr.options.unicorn)

原因:该块构建了用于分析的初始程序状态。 因为我们必须深入到C ++标准库中才能正常工作,所以我们需要运行每个初始化工具。 full_init_state会做到这一点。 为了达到这个目的,我们将使用unicorn引擎!

看来还是由于库函数的原因,angr不能完全模拟执行,hook又很麻烦,所以干脆动态分析了。

判断输入字符串的长度代码:

 v6 = std::string::length(input) - 1LL != legend >> 2;

其中legend=0x73 右移2位 为0x1c=28,只有当v6为1时才能进入success的逻辑,所以输入应为29。

因此,我们可以将输入的长度约束为29,前28个为可打印字符,最后一个字符是换行符。

开始执行这个二进制文件,直到没有active路径,即执行至结束状态。

打印程序输出数据:

out = pp.posix.dumps(1)

过滤出包含flag的输出:

if 'flag{' in out:

            return filter(lambda s: 'flag{' in s, out.split())[0]

用例源码:

#!/usr/bin/env python
# coding: utf-8
import angr
import time

def main():
    # Load the binary. This is a 64-bit C++ binary, pretty heavily obfuscated.
    p = angr.Project('wyvern')

    # This block constructs the initial program state for analysis.
    # Because we're going to have to step deep into the C++ standard libraries
    # for this to work, we need to run everyone's initializers. The full_init_state
    # will do that. In order to do this peformantly, we will use the unicorn engine!
    st = p.factory.full_init_state(args=['./wyvern'], add_options=angr.options.unicorn)

    # It's reasonably easy to tell from looking at the program in IDA that the key will
    # be 29 bytes long, and the last byte is a newline.

    # Constrain the first 28 bytes to be non-null and non-newline:
    for _ in xrange(28):
        k = st.posix.files[0].read_from(1)
        st.solver.add(k != 0)
        st.solver.add(k != 10)

    # Constrain the last byte to be a newline
    k = st.posix.files[0].read_from(1)
    st.solver.add(k == 10)

    # Reset the symbolic stdin's properties and set its length.
    st.posix.files[0].seek(0)
    st.posix.files[0].length = 29

    # Construct a SimulationManager to perform symbolic execution.
    # Step until there is nothing left to be stepped.
    sm = p.factory.simulation_manager(st)
    sm.run()

    # Get the stdout of every path that reached an exit syscall. The flag should be in one of these!
    out = ''
    for pp in sm.deadended:
        out = pp.posix.dumps(1)
        if 'flag{' in out:
            return filter(lambda s: 'flag{' in s, out.split())[0]

    # Runs in about 15 minutes!

def test():
    assert main() == 'flag{dr4g0n_or_p4tric1an_it5_LLVM}'

if __name__ == "__main__":
    before = time.time()
    print main()
    after = time.time()
    print "Time elapsed: {}".format(after - before)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值