内容纲要

安装

sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper

virtualenvwrapper是一个Python虚拟环境,使用虚拟环境的主要原因是angr会修改libz3和libVEX,可能会影响其他程序的正常使用。

#新建一个Python虚拟机环境
export WORKON_HOME=~/Envs
mkdir -p $WORKON_HOME
source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
mkvirtualenv angr

angr安装

运行

source /usr/share/virtualenvwrapper/virtualenvwrapper.sh 
mkvirtualenv angr

pip:

pip install angr
workon angr

概念

angr核心概念及模块解读

练习

题目:https://github.com/jakespringer/angr_ctf

b站讲解:https://www.bilibili.com/video/BV167411o7WK?from=search&seid=4817568224623525586

00_angr_find

主函数

image-20210530173519637

Good job地址

image-20210530173616343

import angr
#建立一个项目
p=angr.Project("./00_angr_find")
#对该项目入口点初始化
init_state=p.factory.entry_state()
sm=p.factory.simulation_manager(init_state)
#找到我们需要到达地址的那条路径
sm.explore(find=0x08048678)
#输出信息:<SimulationManager with 1 active, 16 deadended, 1 found>
#found是数组,打印信息,found[0]
found_state=sm.found[0]
#变成string,输入是0,输出是1
found_state.posix.dumps(0)
#输ru信息:b'JXWVXRKX'
found_state.posix.dumps(1)
#输出信息:b'Enter the password: '

如图:

image-20210530174418194

image-20210530174432688

image-20210530174451615

image-20210530174459088

01_angr_avoid

这题是为了更快的找到路径,运用了avoid

image-20210531001552572

有一个avoid函数

image-20210531001612958

import angr
p=angr.Project("01_angr_avoid")
init_state=p.factory.entry_state()
sm=p.factory.simulation_manager(init_state)
sm.explore(find=0x080485E0,avoid=0x080485A8)
found_state=sm.found[0]
found_state.posix.dumps(0)

image-20210531001846158

这里有8个avoid

看一下avoid函数的引用,多的吓人

image-20210531001924490

如果不写avoid

image-20210531002157127

当然这个题也会出来,但是我等了半天,所以写avoid可以适当的减少路径从而减少时间

02_angr_find_condition

vim的使用方法

这题是通过字符串匹配来找到正确输入

查看字符串有good job和try again

image-20210531004254255

vim 02.py

import angr
import sys

def main(argv):
    bin_path=argv[1]    #路径
    p=angr.Project(bin_path)    #project创建
    init_state=p.factory.entry_state()  #初始
    sm=p.factory.simulation_manager(init_state)
    def is_good(state):
        return b'Good' in state.posix.dumps(1)    #注意这里要得到输出,所以是dumps(1)

    def is_bad(state):
        return b'Try' in state.posix.dumps(1)

    sm.explore(find=is_good,avoid=is_bad)
    if sm.found:
        found_state=sm.found[0]
        print("sulution:{}".format(found_state.posix.dumps(0)))

if __name__=='__main__':
    main(sys.argv)

image-20210531005355495

esc

:wq

image-20210531005333103

03_angr_symbolic_registers

寄存器符号化

image-20210611004756664

image-20210611004809105

输入三个参数,假设要跳过输入参数的部分,从非入口地址开始进行

image-20210611005032163

跳过get_user_input,从0x08048980开始

image-20210611011155670

def main(argv):
    bin_path=argv[1]
    p=angr.Project(bin_path)
    #从跳过get_user_input的下一条地址开始
    start_addr=0x08048980
    #addr要写=start_addr
    init_state=p.factory.blank_state(addr=start_addr)

    #angr/claripy 是一个抽象的约束解决包装器,用法类似于z3
    #32位寄存器,建立符号向量
    pass1=claripy.BVS('pass1',32)
    pass2=claripy.BVS('pass2',32)
    pass3=claripy.BVS('pass3',32)
    #给寄存器赋值
    init_state.regs.eax=pass1
    init_state.regs.ebx=pass2
    init_state.regs.edx=pass3

    sm=p.factory.simulation_manager(init_state)

    def is_good(state):
        return b'Good' in state.posix.dumps(1)

    def is_bad(state):
        return b'Try' in state.posix.dumps(1)

    sm.explore(find=is_good,avoid=is_bad)

    if sm.found:
        found_state=sm.found[0]

        #求解,因为没有输入,所以无法输出我们的输入,因此需要对此求解
        pw1=found_state.solver.eval(pass1)
        pw2=found_state.solver.eval(pass2)
        pw3=found_state.solver.eval(pass3)
        #输出十六进制——:x
        print("Solution:{:x} {:x} {:x}".format(pw1,pw2,pw3))
    else:
        raise Exception("No solution found")

if __name__=='__main__':
    main(sys.argv)

image-20210611011747044

claripy

Create a 32-bit symbolic bitvector "x": claripy.BVS('x', 32)

Create a 32-bit bitvector with the value 0xc001b3475: claripy.BVV(0xc001b3a75, 32)

Create a 32-bit "strided interval" (see VSA documentation) that can be any divisible-by-10 number between 1000 and 2000: claripy.SI(name='x', bits=32, lower_bound=1000, upper_bound=2000, stride=10)

claripy参考资料

04_angr_symbolic_stack

image-20210620030617946

栈上输入

在调用输入函数后的位置,找到栈赋值的地址:0x8048697

image-20210620031228859

讲的很清楚,由于开始执行的地址是在输入(scanf)后,跳过了栈构造步骤,所以我们需要对栈ebpesp的构造进行同步模拟

image-20210620031517963

由于angr不支持多参数,所以我们要构造符号来存放参数

image-20210620031955597

注入符号要在正确的位置,从汇编来看,一个为ebp-0xc,一个为ebp-0x10,则在符号位置的前面地址应该padding掉,32位即4字节,0xc-4=0x8,即需padding8个字节

同时要注意esp的指向

image-20210620032359263

到了注入的位置,把之前建立的符号注入

import angr
import claripy
import sys

def main(argv):
    bin_path=argv[1]
    project=angr.Project(bin_path)
    start_address=0x8048697                         #①
    initial_state=project.factory.blank_state(addr=start_address)
    initial_state.regs.ebp=initial_state.regs.esp   #②
    pass1=claripy.BVS('pass1',32)                 #③
    pass2=claripy.BVS('pass2',32)
    padding_length_in_bytes=8                       #④
    initial_state.regs.esp-=padding_length_in_bytes
    initial_state.stack_push(pass1)                 #⑤
    initial_state.stack_push(pass2)
    simulation=project.factory.simgr(initial_state)

    def is_good(state):
        return b'Good' in state.posix.dumps(1)

    def is_bad(state):
        return b'Try' in state.posix.dumps(1)

    simulation.explore(find=is_good,avoid=is_bad)
    if simulation.found:
        found_state=simulation.found[0]
        solution1=found_state.solver.eval(pass1)
        solution2=found_state.solver.eval(pass2)
        print("{} {}".format(solution1,solution2))
    else:
        raise Exception('no')

if __name__=="__main__":
    main(sys.argv)

image-20210620035336924

05_angr_symbolic_memory

image-20210620152213696

存入内存,这里有四个参数

跳过输入,所以起始地址为0x08048601

每个输入为8字节,所以要开辟64位的符号

传参是从n到1,所以第一个参数地址为C0,第二个为C8,以此类推

image-20210620152506744

现在的cast_to没有str类型了,只有int和bytes,所以这里要改为cast_to=bytes,同时进行字节转换(.decode("utf-8"))

import angr
import claripy
import sys

def main(argv):
    path=argv[1]
    project=angr.Project(path)
    start_address=0x8048601
    initial_state=project.factory.blank_state(addr=start_address)
    pass1=claripy.BVS('pass1',64)
    pass2=claripy.BVS('pass2',64)
    pass3=claripy.BVS('pass3',64)
    pass4=claripy.BVS('psaa4',64)
    pass1_address=0xA1BA1C0
    pass2_address=0xA1BA1C8
    pass3_address=0xA1BA1D0
    pass4_address=0xA1BA1D8
    initial_state.memory.store(pass1_address,pass1)
    initial_state.memory.store(pass2_address,pass2)
    initial_state.memory.store(pass3_address,pass3)
    initial_state.memory.store(pass4_address,pass4)
    simulation=project.factory.simgr(initial_state)

    def is_good(state):
        return b'Good' in state.posix.dumps(1)

    def is_bad(state):
        return b'Try' in state.posix.dumps(1)

    simulation.explore(find=is_good,avoid=is_bad)
    if simulation.found:
        solution_state=simulation.found[0]
        solution1=solution_state.se.eval(pass1,cast_to=bytes).decode("utf-8")     #se=solver
        solution2=solution_state.se.eval(pass2,cast_to=bytes).decode("utf-8")
        solution3=solution_state.se.eval(pass3,cast_to=bytes).decode("utf-8")
        solution4=solution_state.se.eval(pass4,cast_to=bytes).decode("utf-8")
        print("{} {} {} {}".format(solution1,solution2,solution3,solution4))
    else:
        raise Exception('no')

if __name__=='__main__':
    main(sys.argv)

image-20210620152340450

06_angr_symbolic_dynamic_memory

image-20210622002430622

动态分配内存

image-20210622002634566

涉及到栈和堆向下向上的位置关系,所以找到esp,往下就是堆的位置,随便找两个地方写符号

image-20210622005345815

image-20210622011207889

注意端序问题

import angr
import sys
import claripy

def main(argv):
    path=argv[1]
    project=angr.Project(path)
    start_address=0x08048696
    initial_state=project.factory.blank_state(addr=start_address)
    pass1=claripy.BVS('pass1',64)
    pass2=claripy.BVS('pass2',64)
    #print("ESP:",initial_state.regs.esp)     0x7fff0000
    p1=0x7fff0000-0x100
    p2=0x7fff0000-0x200
    buff1=0x0ABCC8A4
    buff2=0x0ABCC8AC
    initial_state.memory.store(buff1,p1,endness=project.arch.memory_endness)    #将p1写入buff1,指定小端序
    initial_state.memory.store(buff2,p2,endness=project.arch.memory_endness)
    initial_state.memory.store(p1,pass1)    #将pass1写入p1
    initial_state.memory.store(p2,pass2)
    simulation=project.factory.simgr(initial_state)
    def is_good(state):
        return b'Good' in state.posix.dumps(1)
    def is_bad(state):
        return b'Try' in state.posix.dumps(1)
    simulation.explore(find=is_good,avoid=is_bad)
    if simulation.found:
        solution_state=simulation.found[0]

    solution1=solution_state.se.eval(pass1,cast_to=bytes)
    solution2=solution_state.se.eval(pass2,cast_to=bytes)
    print("{} {}".format(solution1,solution2))

if __name__=='__main__':
    main(sys.argv)

07_angr_symbolic_file

image-20210622230437543

打开一个文件并将里面的数据写入到txt文件中