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

本文介绍了一个ASIS CTF决赛中的license挑战题解析过程,包括逆向工程、符号执行和约束求解等内容,最终成功获取flag。

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

分析题目:

运行二进制文件,输出:

key file not found!

看来该二进制需要读取一个文件,拖到IDA里看它需要读取的文件路径及名称是什么。

找到以后发现是:"_a\nb\tc_"。

那创建一个名为"_a\nb\tc_"的文件。创建这个文件的时候是有知识点的呀,“\n”“\t”都是转义符,所以最后创建的文件名称是这个样子的:


这么奇怪的名字怎么创建呢?就是写个c代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
FILE* fstream;
char msg[100] = "Hello!I have read this file.";
fstream=fopen("_a\nb\tc_","at+");
if(fstream==NULL)
{
printf("open file _a\\nb\\tc_ failed!\n");
exit(1);
}
else
{
printf("open file _a\\nb\\tc_ succeed!\n");
}
fclose(fstream);
return 0;
}

再次执行 license 文件,这时候提示:wrong formatted key file。文件格式又不对,此时就是对文件的内容进行检测了,直接上符号执行,求解对文件内容的约束。

解决思路:

1.将文件内容初始化为符号变量,加载到license程序中。

2.找到输出success等字符串的调用地址。

3.找到从读取文件到success调用地址的路径。

4.确定flag的存放地址:flag_addr。

5.求解,读取flag_addr的内容。

第一步,初始化符号变量时,用例代码是将文件的格式确定了下来,这样可以减少符号执行的计算复杂度和计算时间。

文件格式为:5行,每行包含6个字符,这部分是通过IDA逆向源码获得。还是一个权衡的问题,如果自己懒得看这部分,直接将格式未知的符号文件传入,可能需要等待更长的时间才能得到结果。

输出success的地址为:

0x400E93


flag存放的地址为:上图中rsp+0x278-0xd8。

其实下面的用例代码做了很多优化,比如事先确定好文件格式,以及在求解后通过sim_procedures模拟strlen函数估计flag的长度。

事实证明求解flag长度这一部分是可以省略的,将flag_length_int替换为大于等于38的整数,也可以读取出flag。

import angr

def main():
    p = angr.Project("license", load_options={'auto_load_libs': False})

    # Create a blank state
    state = p.factory.blank_state()

    # Build the file whose name is weird
    license_name = "_a\nb\tc_"

    # This is the license file
    # From analyzing the binary, we know that the license file should have five 
    # lines in total, and each line has 6 characters. Not setting file content 
    # may also work, but in that case, angr will produce many more paths, and we 
    # will spent much more time in path trimming.

    bytes = None
    constraints = [ ]
    for i in xrange(5):
        line = [ ]
        for j in xrange(6):
            line.append(state.solver.BVS('license_file_byte_%d_%d' % (i, j), 8))
            state.add_constraints(line[-1] != 0x0a)
        if bytes is None:
            bytes = state.solver.Concat(*line)
        else:
            bytes = state.solver.Concat(bytes, state.solver.BVV(0x0a, 8), *line)
    content = angr.state_plugins.SimSymbolicMemory(memory_id="file_%s" % license_name)
    content.set_state(state)
    content.store(0, bytes)

    license_file = angr.storage.SimFile(license_name, 'rw', content=content, size=len(bytes) / 8)

    # Build the file system dict
    # This interface might change in the near future
    fs = {
        license_name: license_file
    }
    state.posix.fs = fs

    ex = p.surveyors.Explorer(
                            start=state,
                            find=(0x400e93, ),
                            avoid=(0x400bb1, 0x400b8f, 0x400b6d, 0x400a85,
                                   0x400ebf, 0x400a59)
                            )
    ex.run()

    # One path will be found
    found = ex.found[0]
    rsp = found.regs.rsp
    flag_addr = rsp + 0x278 - 0xd8 # Ripped from IDA
    # Perform an inline call to strlen() in order to determine the length of the 
    # flag
    FAKE_ADDR = 0x100000
    strlen = lambda state, arguments: \
        angr.SIM_PROCEDURES['libc']['strlen'](p, FAKE_ADDR, p.arch).execute(
            state, arguments=arguments
        )
    flag_length = strlen(found, arguments=[flag_addr]).ret_expr
    # In case it's not null-terminated, we get the least number as the length
    flag_length_int = min(found.solver.eval_upto(flag_length, 3))
    # Read out the flag!
    flag_int = found.solver.eval(found.memory.load(flag_addr, flag_length_int))
    flag = hex(flag_int)[2:-1].decode("hex")
    #print state.posix.fs[license_name]
    return flag

def test():
    assert main() == 'ASIS{8d2cc30143831881f94cb05dcf0b83e0}'

if __name__ == '__main__':
    print main()







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值