angr learning note

By xia0

angr学习笔记

安装

macOS平台上面安装angr的时候还是折腾了一会,刚开始网上都说angr对python3不兼容,然后用python2去安装,但是pip install angr却有问题,然后看官方的文档才发现angr现在主要支持python3,python2不再支持了,反正最新版的angr安装不了(virtualenv需要python3),要用python2的话,可以安装以前版本的。

It is built for Python 3: Python 2 support is not feasable due to the looming EOL and the small size of our team.

既然官方都这么说了,没理由不安装python3版本的,pip3 install angr一把梭吧。然后又报错了

SystemExit: error: [Errno 2] No such file or directory: 'libunicorn.dylib'

明显是在安装unicorn的时候没安装成功,导致找不到其动态库。angr文档中也提及到了这个问题

Building unicorn from source requires Python 2, so will fail inside a virtualenv where python gets you Python 3. If you encounter errors with pip install angr, you may need to first install unicorn separately, pointing it to your Python 2: UNICORN_QEMU_FLAGS="--python=/path/to/python2" pip install unicorn # Python 2 is probably /usr/bin/python on your macOS system

意思是unicorn兼容python2的,我本机之前已经安装了python2的unicorn,按照文档做还是不行,既然angr依赖unicorn,不如手动安装python3的unicorn。去unicorn的项目中发现可以绑定python3。

$ cd bindings/python
$ sudo make install3
$ python3 -c "import unicorn; print(unicorn.__version__)"
1.0.0

通过这样,我的python3环境中也安装了unicorn。这时候再执行pip3 install angr这下一路畅通,安装完成。

迫不及待尝试导入angr试下,结果又报错了

angr.state_plugins.unicorn_engine | failed loading "angr_native.dylib", unicorn support disabled (dlopen(/usr/local/lib/python3.7/site-packages/angr/lib/angr_native.dylib, 6): Library not loaded: libpyvex.dylib
  Referenced from: /usr/local/lib/python3.7/site-packages/angr/lib/angr_native.dylib
  Reason: image not found)

这个错误很明显,就是angr_native.dylib中加载libpyvex.dylib的时候找不到该动态库。看了下angr_native.dylib的macho格式里load command中加载libpyvex.dylib部分,发现是在其当前目录下去查找libpyvex.dyliblibunicorn.1.dylib。关于这个问题其实官方文档中也提及到这个问题

PYVEX=`python3 -c 'import pyvex; print(pyvex.__path__[0])'`
UNICORN=`python3 -c 'import unicorn; print(unicorn.__path__[0])'`
ANGR=`python3 -c 'import angr; print(angr.__path__[0])'`

install_name_tool -change libunicorn.1.dylib "$UNICORN"/lib/libunicorn.dylib "$ANGR"/lib/angr_native.dylib
install_name_tool -change libpyvex.dylib "$PYVEX"/lib/libpyvex.dylib "$ANGR"/lib/angr_native.dylib

就是通过重新指定加载libunicorn.dyliblibpyvex.dylib 的位置。执行完脚本以后,angr算是安装完成了。

使用

直接上例子来学习,官方的fauxware例子

C源码如下

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>

char *sneaky = "SOSNEAKY";

int authenticate(char *username, char *password)
{
    char stored_pw[9];
    stored_pw[8] = 0;
    int pwfile;

    // evil back d00r
    if (strcmp(password, sneaky) == 0) return 1;

    pwfile = open(username, O_RDONLY);
    read(pwfile, stored_pw, 8);

    if (strcmp(password, stored_pw) == 0) return 1;
    return 0;

}

int accepted()
{
    printf("Welcome to the admin console, trusted user!\n");
}

int rejected()
{
    printf("Go away!");
    exit(1);
}

int main(int argc, char **argv)
{
    char username[9];
    char password[9];
    int authed;

    username[8] = 0;
    password[8] = 0;

    printf("Username: \n");
    read(0, username, 8);
    read(0, &authed, 1);
    printf("Password: \n");
    read(0, password, 8);
    read(0, &authed, 1);

    authed = authenticate(username, password);
    if (authed) accepted();
    else rejected();
}

简单解释下程序就是程序有一个后门,当输入的password为SOSNEAKY即可通过。下面我们用angr来求解出成功的输入

import angr

filename = 'the/filepath/of/fauxware'

proj = angr.Project(filename, auto_load_libs=False)

st = proj.factory.entry_state()

while True:
    succ = st.step()
    if len(succ.successors) == 2:
        break

    st = succ.successors[0]

st1, st2 = succ.successors

print(st1, st2)

print(st1.posix.dumps(0))
print(st2.posix.dumps(0))

下面解释下这个python代码

proj = angr.Project(filename, auto_load_libs=False)加载一个二进制文件,然后得到一个程序开始处的状态st = proj.factory.entry_state()下面从这个状态开始,一直运行直到出现分支。最后获取此时的两个状态。打印出满足条件的输入值。

<SimState @ 0x400692> <SimState @ 0x400699>
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00SOSNEAKY\x00'
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x80\x80\x80@\x80\x80\x00\x00'

可以看出st1也就是成功授权的输入为SOSNEAKY。

基本概念

factory

该对象提供了大多数的功能,能够将二进制对象分解为很方便处理的对象。

  • blocks

    project.factory.block()给定一个地址,返回其basic block,这里的block与llvm中的block一致。获取了block就能进一步对其指令等操作。

    block = proj.factory.block(proj.entry)
    block.pp() # 打印block反汇编代码
    block.instructions # 指令个数
    block.instruction_addrs # 指令地址list
    
  • states

    angr里面很重要的一个对象,代表了程序的一个实例镜像,是模拟执行某个时刻的状态。

    state = proj.factory.entry_state()
    <SimState @ 0x401670>
    

    既然是程序模拟运行过程中的某个状态,那么SimState就包含了程序内存,寄存器,文件系统等数据。而且这些数据是可以被修改。

    # 访问寄存器和内存
    >>> state.regs.rip        # get the current instruction pointer
    <BV64 0x401670>
    >>> state.regs.rax
    <BV64 0x1c>
    >>> state.mem[proj.entry].int.resolved  # interpret the memory at the entry point as a C int
    <BV32 0x8949ed31>
    
    # 修改寄存器和内存
    >>> state.regs.rsi = state.solver.BVV(3, 64)
    >>> state.regs.rsi
    <BV64 0x3>
    
    >>> state.mem[0x1000].long = 4
    >>> state.mem[0x1000].long.resolved
    <BV64 0x4>
    

    这些数据都是bitvectors,所以需要一个转换

    >>> bv = state.solver.BVV(0x1234, 32)       # create a 32-bit-wide bitvector with value 0x1234
    <BV32 0x1234>                               # BVV stands for bitvector value
    >>> state.solver.eval(bv)                # convert to python int
    0x1234
    

    mem有几点需要注意:

    • .typechar, short, int, long, size_t, uint8_t, uint16_t
    • .resolved得到bitvector;.concrete得到int值
  • simulation managers

    state代表了某个时刻的程序状态,那么simulation managers就代表了程序如何进入下一个state。

    >>> simgr = proj.factory.simulation_manager(state)
    <SimulationManager with 1 active>
    >>> simgr.active
    [<SimState @ 0x401670>]
    

    这里我们用state去初始化了simulation managers得到了一组state。.active返回当前state。

    >>> simgr.step()
    >>> simgr.active
    [<SimState @ 0x1020300>]
    >>> simgr.active[0].regs.rip                 # new and exciting!
    <BV64 0x1020300>
    >>> state.regs.rip                           # still the same!
    <BV64 0x401670>
    

    通过上面的方法去执行程序,这样会得到下一个state,此时active的state已经变了,step方法会执行到下一个basic block。之前的state仍然保留不变。

待更新

参考