二进制分析工具angr的使用学习(1)

新手一枚,如有错误(不足)请指正,谢谢!!
参考文章:

  1. Angr文档

建议Ubuntu18.04及以上版本安装angr

1. angr安装-linux

  1. 安装依赖
sudo apt-get install python-dev libffi-dev build-essential virtualenvwrapper

在这里插入图片描述

  1. 安装angr
mkvirtualenv angr && pip install angr

在这里插入图片描述
出现错误,进行virtualenvwrapper初始化
输入命令

export WORKON_HOME=$HOME/Python-workhome
source /usr/share/virtualenvwrapper/virtualenvwrapper.sh

然后再输入安装angr的命令,可以安装了
在这里插入图片描述

2. angr学习

GitHub angr题库

00_angr_find

import angr
p = angr.Project('/home/cx330/Desktop/Debugging/learn/00_angr_find',auto_load_libs=False)
# 加载文件
# auto_load_libs=False   不加载共享库文件

state = p.factory.entry_state()
# 创建一个状态state
# entry_state()  构造一个已经准备好从函数入口点执行的状态。

sm = p.factory.simgr(state)
# 创建一个Simulation Managers  模拟管理器
# 也可改为
# sm = p.factory.simulation_manager(state)
# 目前没发现区别

good = 0x804867d
# 正确路径的地址

sm.explore(find = good)
# 让angr开始探索,这个路径
# 如果找到了满足find的状态,则将其保存在模拟管理器的found分类里

if sm.found:
    #如果found分类不为空
    find_state = sm.found[0]
    # 就把found分类里面的状态给find_state

flag = find_state.posix.dumps(0)
# state.posix.dumps(0) 获取输入

print(str(flag,'utf-8'))
# type(flag)为'bytes',将其转换为字符串再输出


####################################
# 其中good还可换成
good = lambda s:b'Good Job.' in s.posix.dumps(1)
# 意为寻找输出为'Good Job.'的状态,注意这里为字节型数据
# # state.posix.dumps(1) 获取输出

01_angr_avoid

import angr
p = angr.Project('/home/cx330/Desktop/Debugging/learn/01_angr_avoid',auto_load_libs=False)
# 加载文件
# auto_load_libs=False   不加载共享库文件

state = p.factory.entry_state()
# 创建一个状态state
# entry_state()  构造一个已经准备好从函数入口点执行的状态。

sm = p.factory.simulation_manager(state)
# 创建一个Simulation Managers  模拟管理器

good = lambda s:b'Good Job.' in s.posix.dumps(1)
# 意为寻找输出为'Good Job.'的状态,注意这里为字节型数据
# state.posix.dumps(1) 获取输出
# 也可换为     good = 0x080485E0  # 即为换成地址

bad = 0x080485A8
#需要避免的地址,即找到的状态需满足 不执行此地址

sm.explore(find = good,avoid = bad)
# 让angr开始探索,这个路径
# 如果找到了满足find和avoid的状态,则将其保存在模拟管理器的found分类里
# 如果有经过avoid的状态,将其保存再avoid分类里

if sm.found:#如果found分类里不为空,即存在满足条件的状态,将其去除
    find_state = sm.found[0]
flag = find_state.posix.dumps(0)
# 得到这种状态的输入
print(str(flag,'utf-8'))

02_angr_find_condition

import angr
p = angr.Project('/home/cx330/Desktop/Debugging/learn/02_angr_find_condition',auto_load_libs=False)
state = p.factory.entry_state()
sm = p.factory.simulation_manager(state)
def is_good(state):
    return b'Good Job' in state.posix.dumps(1)
sm.explore(find = is_good)
if sm.found:
    find_state = sm.found[0]
flag = find_state.posix.dumps(0)
print(str(flag,'utf-8'))

03_angr_symbolic_registers

老版angr不支持scanf多个参数(现已支持,因此完全可以用之前的脚本打通……)

这里使用老版的解法

main()函数中

.text:0804897B call    get_user_input
.text:08048980 mov     [ebp+var_14], eax
.text:08048983 mov     [ebp+var_10], ebx
.text:08048986 mov     [ebp+var_C], edx

可以看到scanf是把三个输入分别给了eax,ebx,edx

import angr,claripy
p = angr.Project('/home/cx330/Desktop/Debugging/learn/03_angr_symbolic_registers')
init_addr = 0x08048980
state = p.factory.blank_state(addr=init_addr)
# 构造一个空状态,即跳过了scanf,从其下部开始执行

p1 = claripy.BVS('p1',32)
p2 = claripy.BVS('p2',32)
p3 = claripy.BVS('p3',32)
# 定义三个位向量,分别表示三个输入

state.regs.eax = p1
state.regs.ebx = p2
state.regs.edx = p3
# 将三个位向量分别给eax,ebx,edx。(因为get_user_input这个函数就是这么干的)

sm = p.factory.simulation_manager(state)
# 构造一个模拟管理器
# def is_bad(state):
#        return b'Try again' in state.posix.dumps(1)
# 可以加上此约束,即在explore之中加上 avoid = is_bad
def is_good(state):
    return b'Good Job' in state.posix.dumps(1)
sm.explore(find = is_good)
if sm.found:
    find_state = sm.found[0]
    flag1 = find_state.solver.eval(p1)
    flag2 = find_state.solver.eval(p2)
    flag3 = find_state.solver.eval(p3)
    print('{:x} {:x} {:x}'.format(flag1,flag2,flag3))
    # 输出16进制的结果,因为原程序是按照16进制来读取的
else:
    print('No')
    # 没找到符合条件的状态则输出no

04_angr_symbolic_stack

模拟栈

.text:08048679 public handle_user
.text:08048679 handle_user proc near
.text:08048679
.text:08048679 var_10= dword ptr -10h
.text:08048679 var_C= dword ptr -0Ch
.text:08048679
.text:08048679 ; __unwind {
.text:08048679 push    ebp
.text:0804867A mov     ebp, esp
.text:0804867C sub     esp, 18h
.text:0804867F sub     esp, 4
.text:08048682 lea     eax, [ebp+var_10]
.text:08048685 push    eax
.text:08048686 lea     eax, [ebp+var_C]
.text:08048689 push    eax
.text:0804868A push    offset aUU      ; "%u %u"
.text:0804868F call    ___isoc99_scanf
.text:08048694 add     esp, 10h
.text:08048697 mov     eax, [ebp+var_C]
.text:0804869A sub     esp, 0Ch
.text:0804869D push    eax

假设在scanf函数下执行,08048694为scanf函数恢复栈,所以应从08048697开始模拟

import angr
p = angr.Project('/home/cx330/Desktop/Debugging/learn/04_angr_symbolic_stack')
# 载入程序

def good(state):
    return b"Good Job." in state.posix.dumps(1)

def bad(state):
    return b"Try again." in state.posix.dumps(1)

start_addr = 0x08048697
# 开始模拟的地址

state = p.factory.blank_state(addr = start_addr)
# 新建一个状态

state.stack_push(state.regs.ebp)
# 压入ebp,模拟地址0x08048679代码

state.regs.ebp = state.regs.esp
# 模拟地址0x0804867A代码

offest = 0x8
# 两个局部变量
# var_C= dword ptr -0Ch  也就是占的地址是 0x9 a b c
# var_10= dword ptr -10h 占的地址是 d e f 0x10
state.regs.esp -= offest
# 初始化栈

p1 = state.solver.BVS('p1',32)
p2 = state.solver.BVS('p2',32)
state.stack_push(p1)
state.stack_push(p2)
# 创建两个位向量,并将其压入栈

sm = p.factory.simgr(state)
sm.explore(find = good, avoid = bad)
# 创建模拟管理器并开始探索路径
if sm.found:
    found_state = sm.found[0]
    flag1 = found_state.solver.eval(p1)
    flag2 = found_state.solver.eval(p2)
    print('{} {}'.format(flag1,flag2))
    # 输出
else:
    print('No')

05_angr_symbolic_memory

import angr
p = angr.Project('/home/cx330/Desktop/Debugging/learn/05_angr_symbolic_memory')

def good(state):
    return b"Good Job." in state.posix.dumps(1)

def bad(state):
    return b"Try again." in state.posix.dumps(1)

start_addr = 0x08048601
# 依旧是选择在scanf函数的下方初始化状态

state = p.factory.blank_state(addr = start_addr)
p1 = state.solver.BVS('p1',64)
p2 = state.solver.BVS('p2',64)
p3 = state.solver.BVS('p3',64)
p4 = state.solver.BVS('p4',64)
state.memory.store(0x0A1BA1C0,p1)
state.memory.store(0x0A1BA1C8,p2)
state.memory.store(0x0A1BA1D0,p3)
state.memory.store(0x0A1BA1D8,p4)
# 初始化四个位向量,并存入内存

sm = p.factory.simgr(state)
sm.explore(find = good, avoid = bad)
if sm.found:
    found_state = sm.found[0]
    flag1 = found_state.solver.eval(p1,cast_to=bytes).decode('utf-8')
    flag2 = found_state.solver.eval(p2,cast_to=bytes).decode('utf-8')
    flag3 = found_state.solver.eval(p3,cast_to=bytes).decode('utf-8')
    flag4 = found_state.solver.eval(p4,cast_to=bytes).decode('utf-8')
    # 先解释成bytes类型再转换成str
    # 貌似不能直接转换str
    print('{} {} {} {}'.format(flag1,flag2,flag3,flag4))
else:
    print('No')

待更新

原创文章 77 获赞 29 访问量 1万+

猜你喜欢

转载自blog.csdn.net/Palmer9/article/details/103737973