查看: 72|回复: 0

[Reverse] 深入浅出angr(三)

[复制链接]

22

主题

23

帖子

0

精华

VIP

Rank: 16

学币
10
荣耀
0
rank
0
违规
0

VIP

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

    前言
      此篇来了解一下如何对内存寄存器进行直接存取
    通过直接地址写入  对于.bss段等固定地址的变量我们可以利用claripy直接地址写入,进行初始化state。下面我通过一个例子进行简单说明。
    例题来自于sym-write  载入IDA

    深入浅出angr(三)

    深入浅出angr(三)
      其中变量u位于.bss段,是未初始化的变量,我们可以在state状态,初始化simulation_manager的state时,将其设置.

    深入浅出angr(三)

    深入浅出angr(三)
      为了可以进行符号地址的写入,在设置state需要申明add_options={"SYMBOLIC_WRITE_ADDRESSES"}
      angr提供了SimMemory类对内存进行操作。

    深入浅出angr(三)

    深入浅出angr(三)
      并且提供了两种方法

    深入浅出angr(三)

    深入浅出angr(三)

    深入浅出angr(三)

    深入浅出angr(三)
      通过上下文,可以知道u是一个8bit的值。因此我们通过state.memory.store(0x804a021, u)将u存储到0x804a021处

    深入浅出angr(三)

    深入浅出angr(三)
      然后设置需要到达的路径即可
    sm.explore(find=0x80484e3, avoid=0x80484f5)

    深入浅出angr(三)

    深入浅出angr(三)
      是不是很简单呢~
    操纵内存以及寄存器数据  例题来自flareon2015_2
      IDA载入,发现是windows程序,而且逻辑也很简单。

    深入浅出angr(三)

    深入浅出angr(三)
      为了避免调用windows的API,我们需要从0x401084设为起始状态,并且设置好传入的参数。
      通过上下文,可以知道0x402159存放的是输入的数据,根据windows 32 位参数传递规则,我们可以如下进行构造。
    s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))
    s.mem[s.regs.esp+8:].dword = 0x402159
    s.mem[s.regs.esp+4:].dword = 0x4010e4
    s.mem[s.regs.esp:].dword = 0x401064
      其中s.mem[s.regs.esp:].dword用来设置内存的值,并且大小为dword
      angr支持许多类型的数据,包括dword,word,long,int,uint8_t,uint32_t等等。我建议只要对自己的理解不产生影响,选取合适类型即可。我通常会选择uint8_t之类的数据类型,毕竟对linux编程比较熟悉。
      完整代码如下:
    #!/usr/bin/env python
    import angr
    
    def main():
    b = angr.Project("very_success", load_options={"auto_load_libs":False})
    # create a state at the checking function
    # Since this is a windows binary we have to start after the windows library calls
    # remove lazy solves since we don't want to explore unsatisfiable paths
    s = b.factory.blank_state(addr=0x401084)
    # set up the arguments on the stack
    s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))
    s.mem[s.regs.esp+8:].dword = 0x402159
    s.mem[s.regs.esp+4:].dword = 0x4010e4
    s.mem[s.regs.esp:].dword = 0x401064
    # store a symbolic string for the input
    s.memory.store(0x402159, s.solver.BVS("ans", 8*40))
    # explore for success state, avoiding failure
    sm = b.factory.simulation_manager(s)
    sm.explore(find=0x40106b, avoid=0x401072)
    # print(the string)
    found_state = sm.found[0]
    return found_state.solver.eval(found_state.memory.load(0x402159, 40), cast_to=bytes).strip(b'\0')
    
    def test():
    assert main() == b'[email protected]'
    
    if __name__ == '__main__':
    print(main())
    经典例题一  angrbird这题比较经典。
      首先程序很直接的反调试,而且又是这种线性的混淆,这用angr来解决是再合适不过了。

    深入浅出angr(三)

    深入浅出angr(三)
      一条斜线。有点飘。

    深入浅出angr(三)

    深入浅出angr(三)
      其中刚开始的部分就是反调试,通常的做法是patch程序,不过这里用angr来解决。
      为了绕过反调试我们需要将入口地址设置在mov     rdx, cs:stdin也就是0x4007C2,同时结合IDA的分析,我们需要将mov     [rbp+var_70], offset off_606018布局到相应的内存中,这里应该是在设置函数表。
      同时我们还应该设置好fgets函数的参数mov     ecx, [rbp+n]其中的[rbp+n]应该设置为21
      同时为了执行程序还应该设置mov     rbp, rsp
      这几步是必不可少的,因为跳过反调试,需要一定的代价。
    因此初始化部分代码如下:
    state.regs.rbp = state.regs.rsp
    state.mem[state.regs.rbp - 0x74].uint32_t = 0x40
    state.mem[state.regs.rbp - 0x70].uint64_t = 0x1000
    state.mem[state.regs.rbp - 0x68].uint64_t = 0x1008
    state.mem[state.regs.rbp - 0x60].uint64_t = 0x1010
    state.mem[state.regs.rbp - 0x58].uint64_t = 0x1018
      正确设置完初始状态后就是正常的执行了。

    深入浅出angr(三)

    深入浅出angr(三)
    经典例题二  此题使用angr有两种方法,分别是在不同的地方进行条件约束,我个人认为对angr的应用会有所帮助。仅做简单记录。
      google2016_unbreakable_1
    方法一  通过命令行输入,并设置条件约束。
    p = angr.Project('unbreakable',load_options={"auto_load_libs": False})
    
    argv = claripy.BVS("argv",0x43*8)
    
    state = p.factory.entry_state(args={"./unbreakable",argv},add_options={angr.options.LAZY_SOLVES})
    state.libc.buf_symbolic_bytes=0x43 + 1
    
    for byt in argv.chop(8):
    state.add_constraints(state.solver.And(byt >= ord(' '),byt <= ord('~')))
      其中的state.libc.buf_symbolic_bytes=0x43 + 1是非常有必要的,我看官方所说,angr默认的symbolic_bytes只有60bytes,对于这题来说太小了。也就是说如果命令行传入的大小大于默认的值,所以需要手动调整大小。
      而且条件约束也是十分有必要的,这里说明一下chop方法:

    深入浅出angr(三)

    深入浅出angr(三)
      意思就是截取,所以我们每8bits截取,然后进行条件约束。
    代码如下:
    import angr
    import claripy
    
    START_ADDR = 0x4005bd # first part of program that does computation
    AVOID_ADDR = 0x400850 # address of function that prints wrong
    FIND_ADDR = 0x400830 # address of function that prints correct
    INPUT_ADDR = 0x6042c0 # location in memory of user input
    INPUT_LENGTH = 0xf2 - 0xc0 + 1 # derived from the first and last character
    # reference in data
    
    def extract_memory(state):
    """Convience method that returns the flag input memory."""
    return state.solver.eval(state.memory.load(INPUT_ADDR, INPUT_LENGTH), cast_to=bytes)
    
    def main():
    p = angr.Project('unbreakable',load_options={"auto_load_libs": False})
    
    argv = claripy.BVS("argv",0x43*8)
    
    state = p.factory.entry_state(args={"./unbreakable",argv},add_options={angr.options.LAZY_SOLVES})
    state.libc.buf_symbolic_bytes=0x43 + 1
    
    for byt in argv.chop(8):
    state.add_constraints(state.solver.And(byt >= ord(' '),byt <= ord('~')))
    
    
    ex = p.factory.simulation_manager(state)
    
    
    ex.explore(find=(FIND_ADDR,), avoid=(AVOID_ADDR,))
    
    flag = extract_memory(ex.found[0]) # ex.one_found is equiv. to ex.found[0]
    
    print(flag)
    
    if __name__ == '__main__':
    main()
    方法二  第二种方法跳过命令行输入的过程,直接将值存储到引用的内存当中,并设置条件约束。比较简单。代码如下:
    import angr
    import claripy
    
    START_ADDR = 0x4005bd # first part of program that does computation
    AVOID_ADDR = 0x400850 # address of function that prints wrong
    FIND_ADDR = 0x400830 # address of function that prints correct
    INPUT_ADDR = 0x6042c0 # location in memory of user input
    INPUT_LENGTH = 0xf2 - 0xc0 + 1 # derived from the first and last character
    def main():
    p = angr.Project('unbreakable')
    state = p.factory.blank_state(addr=START_ADDR, add_options={angr.options.LAZY_SOLVES})
    flag_chars = [state.solver.BVS('flag_%d' % i, 8) for i in range(0x43)]
    for flag_chr in flag_chars:
    state.add_constraints(state.solver.And(flag_chr >= ord(' '),flag_chr <= ord('~')))
    
    for i in range(0x43):
    state.memory.store(INPUT_ADDR+i,flag_chars[i])
    #state.
    ex = p.factory.simulation_manager(state)
    
    
    ex.explore(find=(FIND_ADDR,), avoid=(AVOID_ADDR,))
    
    found = ex.found[0] 
    flag = found.solver.eval(state.memory.load(INPUT_ADDR, INPUT_LENGTH), cast_to=bytes)
    print(flag)
    
    if __name__ == '__main__':
    main()
    总结  通过以上四道例题,相信大家对angr的内存寄存器有一定的了解,如果有必要,我们可以另设一个起始状态,然后设置好所需的参数。
      下面后介绍angr中的Hook



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