angr学习

2022-09-30

基础学习

关于angr的底层原理的基本学习,基本语法学习,可以转移至
https://bluesadi.github.io/0x401RevTrain-Tools/
(跟着做大概18道题,就能熟悉angr的基本使用了)
大概了解完之后,应该需要了解符号执行的常用术语

  1. 符号状态(symbolic state):{x->$x_{sym}$,y->$y_{sym}$,z->$2*y_{sym}$}
  2. 路径约束(path constraint):达到所需路径要满足的条件
  3. 遍历策略(通常是广度优先搜索BFS和深度优先搜索DFS)
  4. 约束求解(angr的约束求解器是z3的封装)
  5. 动态符号执行,也叫混合执行(主要是因为纯静态符号执行变量复杂,无法求解,实用化不高)动态符号执行通过维护实际状态和符号状态两个状态来解决这个问题
  6. 路径爆炸:指某种情况下符号执行的路径以指数级增长,这时候符号执行无法求解出正确的答案,解决办法目前有3个: 1. 手动添加约束 2. hook替换导致路径指数增长的位置 3. veritesting路径归并
  7. angr会根据导入表自动识别动态库函数,但是对于静态库函数,angr是无法识别的,需要手动替换像printf之类的函数,否则无法跑出结果。
  8. angr基于IR(Intermediate Representation)来运行,

https://docs.angr.io/?q=
(想做研究还是得看文档,看源码)

angr 模板

根据前面的学习,总结了angr的使用模板

import angr
import claripy


# step 1:创建一个project
## 二进制程序路径
bin_path = "xxxx" 
##创建一个project类型,里面包括了二进制文件的基本信息以及符号信息
proj = angr.Project(bin_path)  
## 添加hook
### hook某个汇编指令
def proj_hook(state):
    buffer_addr = 0x0804A054
    buffer = state.memory.load(buffer_addr,16)
    state.regs.eax = claripy.If(buffer==b'XYMKBKUHNIQYNQXE',claripy.BVV(1,32),claripy.BVV(0,32))
proj.hook(0x080486B3,proj_hook,5)
### hook某个函数
class my_sin_proc(angr.SimProcedure):
    def run(self,buffer_addr,length):
        buffer =self.state.memory.load(buffer_addr,length)
        return claripy.If(buffer==b"ORSDDWXHZURJRBDH",claripy.BVV(1,32),claripy.BVV(0,32))
proj.hook_symbol(symbol_name="check_equals_ORSDDWXHZURJRBDH",simproc=my_sin_proc())
### angr自带hook的类型,在存在静态编译的程序时,需要手动替换成angr提供的相关函数,在https://github.com/angr/angr/tree/master/angr/procedures中查看
proj.hook_symbol("__isoc99_scanf", angr.SIM_PROCEDURES['libc']['scanf']())

# step 2:创建符号变量
passwd = claripy.BVS("PASSWD",16*8) 

# step 3:创建state,state类型是angr中的表示程序运行状态的类,包含了寄存器信息以及内存、栈等等信息
state = proj.factory.entry_state() 
state = proj.factory.blank_state(addr= 0x00000000) ## 用于不想从程序开始就运行时
state = proj.factory.call_state(addr=0x00000000,arg1,arg2) ## call_state用于函数在动态库中
## 可以手动修改寄存器、栈、内存的值
state.regs.eax = passwd 
state.stack_push(passwd)
state.mem[0x0A1BA1C0].uint64_t= passwd0 ### 在内存某个位置存入值,动态内存随机申请的chunk地址,可以通过在bss段写入,修改指针完成符号变量写入
state.mem[0xA1BA1C0].uint64_t.resolved ### 提取内存某个位置的值
state.memory.store(0x0804A050,passwd,16) ### state.memory 的新用法
state.memory.load(0x0804A050,16) ### 提取内存的值。
## 伪造文件
sim_file = angr.SimFile(name="OJKSQYDP.txt",content=passwd,size=0x40)
state.fs.insert("OJKSQYDP.txt",sim_file)

# step 4:创建 模拟器运行
simgr = proj.factory.simgr(state)
## veritesting,路径归并,缓解路径爆炸问题,效果挺强力,但是和其他技术可能会冲突,慎用
simgr = proj.factory.simgr(state,veritesting = True)
## 单步调试的用法
while len(simgr.active):  
    print('--------------')
    for active in simgr.active:
        print(hex(active.addr))
        if b'Good Job.' in active.posix.dumps(1):
            print(active.posix.dumps(0))
    simgr.step()

## F9一步到位的做法
def is_successful(state):
    return b"Good Job." in state.posix.dumps(1)
def should_avoid(state):
    return b"Try again." in state.posix.dumps(1)
### 添加目标,减少不必要的state
simgr.explore(find=[0x0000000,is_successful],avoid=[0x0000000,should_avoid]) 

# step 5: 运行求解器求解
if simgr.found:
    found = simgr.found[0]
    solver = simgr.found[0].solver
    ## 手动添加约束条件
    found.add_constraints(found.memory.load(0x0804A050,16)==b"AUPDNNPROEZRJWKB") 
    print(solver.eval(passwd,cast_to=bytes))
    ## 打印出标准输入
    print(simgr.found[0].posix.dumps(0))

angr存在的问题

  1. 玄学问题,同一个块irsb下的不同地址不一定found的到
  2. 同样玄学,使用step()和explore()有时会出现不同的结果,即有一个找得到,另一个找不到
  3. 调试不好调
    调试不好调倒是其次,问题在于玄学上面,玄学就意味着不可预测,不可溯源,这点不解决的话,无法进行更深入层次的研究和使用了。

capstone

http://www.capstone-engine.org/documentation.html
反汇编框架,主要功能就是将字节码转化为汇编代码

keystone

https://www.keystone-engine.org/ 汇编框架,主要功能是将汇编代码转换为字节码。

z3

微软的一款强大的约束求解器。
https://github.com/Z3Prover/z3
本人之前也稍微写了些z3的使用blog,厚颜无耻贴出来
https://wsxk.github.io/python_z3/?query=z3