xuenixiang 发表于 2020-6-15 00:44:09

深入浅出angr(三)


前言  此篇来了解一下如何对内存寄存器进行直接存取
通过直接地址写入  对于.bss段等固定地址的变量我们可以利用claripy直接地址写入,进行初始化state。下面我通过一个例子进行简单说明。
例题来自于sym-write  载入IDA
  其中变量u位于.bss段,是未初始化的变量,我们可以在state状态,初始化simulation_manager的state时,将其设置.
  为了可以进行符号地址的写入,在设置state需要申明add_options={"SYMBOLIC_WRITE_ADDRESSES"}
  angr提供了SimMemory类对内存进行操作。
  并且提供了两种方法
  通过上下文,可以知道u是一个8bit的值。因此我们通过state.memory.store(0x804a021, u)将u存储到0x804a021处
  然后设置需要到达的路径即可
sm.explore(find=0x80484e3, avoid=0x80484f5)
  是不是很简单呢~
操纵内存以及寄存器数据  例题来自flareon2015_2
  IDA载入,发现是windows程序,而且逻辑也很简单。
  为了避免调用windows的API,我们需要从0x401084设为起始状态,并且设置好传入的参数。
  通过上下文,可以知道0x402159存放的是输入的数据,根据windows 32 位参数传递规则,我们可以如下进行构造。
s.memory.store(s.regs.esp+12, s.solver.BVV(40, s.arch.bits))
s.mem.dword = 0x402159
s.mem.dword = 0x4010e4
s.mem.dword = 0x401064  其中s.mem.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.dword = 0x402159
s.mem.dword = 0x4010e4
s.mem.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
return found_state.solver.eval(found_state.memory.load(0x402159, 40), cast_to=bytes).strip(b'\0')

def test():
assert main() == b'a_Little_b1t_harder_plez@flare-on.com'

if __name__ == '__main__':
print(main())经典例题一  angrbird这题比较经典。
  首先程序很直接的反调试,而且又是这种线性的混淆,这用angr来解决是再合适不过了。
  一条斜线。有点飘。
  其中刚开始的部分就是反调试,通常的做法是patch程序,不过这里用angr来解决。
  为了绕过反调试我们需要将入口地址设置在mov   rdx, cs:stdin也就是0x4007C2,同时结合IDA的分析,我们需要将mov   , offset off_606018布局到相应的内存中,这里应该是在设置函数表。
  同时我们还应该设置好fgets函数的参数mov   ecx, 其中的应该设置为21
  同时为了执行程序还应该设置mov   rbp, rsp
  这几步是必不可少的,因为跳过反调试,需要一定的代价。
因此初始化部分代码如下:
state.regs.rbp = state.regs.rsp
state.mem.uint32_t = 0x40
state.mem.uint64_t = 0x1000
state.mem.uint64_t = 0x1008
state.mem.uint64_t = 0x1010
state.mem.uint64_t = 0x1018  正确设置完初始状态后就是正常的执行了。
经典例题二  此题使用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方法:
  意思就是截取,所以我们每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) # ex.one_found is equiv. to ex.found

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 =
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)
#state.
ex = p.factory.simulation_manager(state)


ex.explore(find=(FIND_ADDR,), avoid=(AVOID_ADDR,))

found = ex.found
flag = found.solver.eval(state.memory.load(INPUT_ADDR, INPUT_LENGTH), cast_to=bytes)
print(flag)

if __name__ == '__main__':
main()总结  通过以上四道例题,相信大家对angr的内存寄存器有一定的了解,如果有必要,我们可以另设一个起始状态,然后设置好所需的参数。
  下面后介绍angr中的Hook


页: [1]
查看完整版本: 深入浅出angr(三)