查看: 60|回复: 0

[Reverse] 深入浅出angr(五)

[复制链接]

22

主题

23

帖子

0

精华

VIP

Rank: 16

学币
10
荣耀
0
rank
0
违规
0

VIP

    发表于 2020-6-15 00:45:59 | 显示全部楼层 |阅读模式

    前言
      我们时常会遇到各种平台下的程序,有时我们为了搭一个动态调试的环境需要耗费不少的时间,更有甚者无功而返,那么这时不妨用angr试试,毕竟多一分尝试,多一分希望。
    如何跨平台使用angr解题  通常来说,我们会在linux下安装angr框架,那么如果遇到不是linux的程序该怎么办呢?程序无法运行还能使用angr解决么!答案当然是可以。因为angr是符号执行框架,不是真正的在执行程序。我们只需要进行正确的条件设置就可以了。
    ARM程序  这里以 android_arm_license_validation为例

    深入浅出angr(五)

    深入浅出angr(五)
      最关键的是设置合理的初始状态,从IDA的分析来看,最后的比较部分在sub_1760函数,因此我们以此作为初始状态,同时看到该函数有一个v6参数,经过分析,可以知道该参数经过加密变换,最终用来校验的字符串,也就是说最后的校验过程我们可以不用了解,只需要使用angr将其爆破出来即可。
      我们需要设置一个空的状态
    state = b.factory.blank_state(addr=0x401760)
      而后设置需要传入的参数,选取任意一个地址,用来存放变量。
    >>> concrete_addr = 0xfff00000
    >>> code = claripy.BVS('code', 10*8)
    >>> state.memory.store(concrete_addr, code, endness='Iend_BE')

    深入浅出angr(五)

    深入浅出angr(五)
      注意ARM程序是大端对齐。
      为了正确的传入参数,我们需要了解arm程序传参方式。
      参考文章
      因此我们设置r0为输入的地址
      state.regs.r0 = concrete_addr
      后面就是类似的。

    深入浅出angr(五)

    深入浅出angr(五)
      当然如果,加密过程也同样非常复杂,那么我们就需要从加密函数开始,设置好参数,利用angr符号执行。
    windows dll  这里以mma_howtouse为例
      程序是windows的dll,如果采用正常的方式解题,那么搭建动态调试的环境势必要花费一番功夫。
      IDA载入,首先我们应该注意的是dll程序的基址是0x10000000,而不是0x400000。

    深入浅出angr(五)

    深入浅出angr(五)
      通过导出表,我们可以定位到fnhowtouse函数,进行了handler的绑定,然后通过(*(&v2 + a1))();进行函数调用。

    深入浅出angr(五)

    深入浅出angr(五)
      为了调用fnhowtouse函数,可以使用callable方法

    深入浅出angr(五)

    深入浅出angr(五)
      首先加载howtouse.dll
    p = angr.Project('howtouse.dll', load_options={'main_opts': {'base_addr': 0x10000000}})
    howtouse = p.factory.callable(0x10001130)
      通过callable方法将fnhowtouse转化为python可以调用的函数

    深入浅出angr(五)

    深入浅出angr(五)
      然后我们便可以通过howtouse(i)进行函数调用,而无需依赖环境。通过题目,我们可以知道howtouse(i)结果就是flag,因此我们可以通过
    claripy.backends.concrete.convert(howtouse(i))
      将结果转换为BVV类型,并通过.value将值取出。
    因此代码可以如下进行组织:
    getch = lambda i: chr(claripy.backends.concrete.convert(howtouse(i)).value)
    
    # Let's call this 45 times, and that's the result!
    return ''.join(getch(i) for i in range(45))
    windows驱动  这里以flareon2015_10为例
      程序是windows驱动,2.5M还是蛮大的,IDA载入分析。

    深入浅出angr(五)

    深入浅出angr(五)
      What!这次又是个什么鬼图形,感觉出题人都要玩出花了。当然啦,angr最喜欢解决这种线性的题目了。
      首先程序的入口。

    深入浅出angr(五)

    深入浅出angr(五)
      可以知道sub_29cd20是有用的函数

    深入浅出angr(五)

    深入浅出angr(五)
      好吧。

    深入浅出angr(五)

    深入浅出angr(五)
      挨个查看一下,发现一个可疑的case

    深入浅出angr(五)

    深入浅出angr(五)
      话说这程序导致我的IDA都有点不正常了。

    深入浅出angr(五)

    深入浅出angr(五)
      接下来查看sub_2D2E0函数

    深入浅出angr(五)

    深入浅出angr(五)
      真漂亮,还好不是在比赛的时候遇到这种题目。。
      我仔细盘了一下代码。还真有趣。
      看到sub_13590函数

    深入浅出angr(五)

    深入浅出angr(五)
      一百多行代码,F5之后就两行。

    深入浅出angr(五)

    深入浅出angr(五)
      sub_2D2E0函数的前半部分都在为0x29F210地址的数据赋值

    深入浅出angr(五)

    深入浅出angr(五)
      只有在最后几行,调用了sub_110F0函数,并传入了byte_29F210和edx,ecx作为参数

    深入浅出angr(五)

    深入浅出angr(五)
      查一下edx对应[ebp+var_3C]的交叉引用,可以知道长度为40

    深入浅出angr(五)

    深入浅出angr(五)
      查一下edx对应[ebp+var_30]的交叉引用可以知道key

    深入浅出angr(五)

    深入浅出angr(五)
      具体查看sub_110F0函数,这回比较正常

    深入浅出angr(五)

    深入浅出angr(五)
      查看sub_11010函数

    深入浅出angr(五)

    深入浅出angr(五)
      很明显是一个不太标准的Tea解密函数。
      在查sub_110F0的交叉引用时,发现还有其他几个地方引用了该函数,不过我仔细查看了一下,这三个地方引用sub_110F0函数都是对相同的数据进行解密,只是数据存放的位置不同罢了。所以也就先不去关心了。
      所以把数据dump下来,解密就可以了。
      代码如下:
    void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */
    uint32_t delta=0x61C88647;          /* a key schedule constant */
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */
    for (i=0; i<32; i++) {                         /* basic cycle start */
    v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
    sum += delta;
    }                                              /* end cycle */
    v[0]=v0; v[1]=v1;
    }
    
    int main()
    {
    uint32_t k[4]={858927408,926299444,1111570744,1178944579};
    uint32_t v0[] = {0xfadc7f56,0xc49927aa};
    uint32_t v1[] = {0x92cf7c6c,0x1a476161};
    uint32_t v2[] = {0xfd63b919,0x20b6f20c};
    uint32_t v3[] = {0xfd5c2dc0,0x965471d9};
    uint32_t v4[] = {0xfff7434f,0x315d4cbb};
    // v为要加密的数据是两个32位无符号整数
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位
    setbuf(stdin,0);
    setbuf(stdout,0);
    setbuf(stderr,0);
    decrypt(v0,k);
    decrypt(v1,k);
    decrypt(v2,k);
    decrypt(v3,k);
    decrypt(v4,k);
    char *flag;
    sprintf(flag,"%x%x%x%x%x%x%x%x%x%x",v0[0],v0[1],v1[0],v1[1],v2[0],v2[1],v3[0],v3[1],v4[0],v4[1]);
    
    printf("%s",flag);
    
    return 0;
    }
      最后的结果需要转换一下。
      再让我们看看如何使用angr的hook来完成解密工作。
      大致思路,就是在程序调用解密函数前,将对应的数据存放到相应的位置即可。
      因此我们的Hook函数可以这么写
    def before_tea_decrypt(state):
    # Here we want to set the value of the byte array starting from 0x29f210
    # I got those bytes by using cross-reference in IDA
    all_bytes = bytes([0x56, 0x7f, 0xdc, 0xfa, 0xaa, 0x27, 0x99, 0xc4, 0x6c, 0x7c,
    0xfc, 0x92, 0x61, 0x61, 0x47, 0x1a, 0x19, 0xb9, 0x63, 0xfd,
    0xc, 0xf2, 0xb6, 0x20, 0xc0, 0x2d, 0x5c, 0xfd, 0xd9, 0x71,
    0x54, 0x96, 0x4f, 0x43, 0xf7, 0xff, 0xbb, 0x4c, 0x5d, 0x31])
    state.memory.store(ARRAY_ADDRESS, all_bytes)
      这时hook的长度应该为0
    p.hook(0xadc31, before_tea_decrypt, length=0),有点类似于插桩
      我们不会运行该程序,而是选取有用的部分运行。
    我们只需要运行2D2E0函数即可。
      这时需要用到callable方法:
      它是将程序的二进制函数,变成像python本地的函数一样调用。
      如下初始化
    proc_big_68 = p.factory.callable(0x2D2E0, cc=p.factory.cc(func_ty=prototype), toc=None, concrete_only=True)
      其中prototype是函数原型的申明
      可以像这样声明:
      prototype = SimTypeFunction((SimTypeInt(False),), SimTypeInt(False))
      因此完整的callable调用如下
    # Declare the prototype of the target function
    prototype = SimTypeFunction((SimTypeInt(False),), SimTypeInt(False))
    # Initialize the function instance
    proc_big_68 = p.factory.callable(BIG_PROC, cc=p.factory.cc(func_ty=prototype), toc=None, concrete_only=True)
    # Call the function and get the final state
    proc_big_68.perform_call(0)
    state = proc_big_68.result_state
      最后我们可以通过state.solver.eval(state.memory.load(ARRAY_ADDRESS, 40), cast_to=bytes)获取flag
      如果学会了,是不是非常简单呢
    总结  对于跨平台的程序,最重要的了解该平台调用约定,需要对程序的运行有所了解。


    温馨提示:
    1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
    2.回复帖子不仅是对作者的最好奖励,还可以获得学币奖励,请尊重作者的劳动成果,拒绝做伸手党!
    3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
    学逆向论坛-免费的逆向学习论坛
    微信公众号
    快速回复 返回顶部 返回列表