Angr(十一)——官方文档(Part2)

通过阅读、整理angr的官方文档,加深对angr的理解,掌握更多、更通用angr的使用方法

参考链接:

angr Documentation

angr-doc

核心原理

程序状态

读、写内存与寄存器

    state.regs对象通过以各个寄存器为名称的属性,提供对各个寄存器的读、写方法;state.mem对象通过数组索引提供对内存的读、写方法,指定地址后可以通过属性来指定该内存应该应被解释为何种数据类型。

(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> proj = angr.Project('./fauxware')
>>> state = proj.factory.entry_state()
>>> state.regs.rbp = state.regs.rsp
>>> state.mem[0x1000].uint64_t = state.regs.rdx
>>> state.mem[0x1000].uint64_t.resolved
<BV64 0xe00018>
>>> state.regs.rdx
<BV64 0xe00018>

基础执行

    通过state.step()来展示符号执行的工作原理,此方法将进行一步符号执行,并返回一个SimSuccessors的对象。与正常执行不同,符号执行运行一步后可以产生多种后续状态(按照不同的分类方式)。SimSuccessors对象的.successors属性是包含符号执行一步后产生的所有正常后继状态的列表。

    以fauxware为例进行分析。用IDA反编译该程序:在main函数中读取用户输入的用户名(username)和密码(password),并作为参数传递到认证函数(authenticate)中进行身份校验。认证函数比较password和sneaky字符串,如果相同,则认为认证通过。

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BOOL4 v4; // [rsp+1Ch] [rbp-24h] BYREF
  char password[16]; // [rsp+20h] [rbp-20h] BYREF
  char username[16]; // [rsp+30h] [rbp-10h] BYREF

  username[8] = 0;
  password[8] = 0;
  puts("Username: ");
  read(0, username, 8uLL);
  read(0, &v4, 1uLL);
  puts("Password: ");
  read(0, password, 8uLL);
  read(0, &v4, 1uLL);
  v4 = authenticate(username, password);
  if ( !v4 )
    rejected(username);
  return accepted(username);
}

_BOOL8 __fastcall authenticate(const char *username, const char *password)
{
  char buf[12]; // [rsp+10h] [rbp-10h] BYREF
  int fd; // [rsp+1Ch] [rbp-4h]

  buf[8] = 0;
  if ( !strcmp(password, sneaky) )              // sneaky = "SOSNEAKY"
    return 1LL;
  fd = open(username, 0);
  read(fd, buf, 8uLL);
  return strcmp(password, buf) == 0;
}

     从程序入口点开始,通过step逐步执行,直到得到多个后继状态。

(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> proj = angr.Project('./fauxware')
>>> state = proj.factory.entry_state()
>>> while True:
...     succ = state.step()
...     if len(succ.successors) == 2:
...             break
...     state = succ.successors[0]
... 
WARNING | 2022-07-24 18:22:01,344 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-07-24 18:22:01,345 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-07-24 18:22:01,353 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-07-24 18:22:01,353 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-07-24 18:22:01,353 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-07-24 18:22:01,354 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff8c with 4 unconstrained bytes referenced from 0x400585 (_start+0x5 in fauxware (0x400585))
WARNING | 2022-07-24 18:22:01,865 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff70 with 8 unconstrained bytes referenced from 0x79d820 (strcmp+0x0 in libc.so.6 (0x9d820))
>>> state1, state2 = succ.successors
>>> state1
<SimState @ 0x400692>
>>> state2
<SimState @ 0x400699>

    此时,从其中的一个状态可以得到通过认证的输入。

>>> state1.posix.dumps(0)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00SOSNEAKY\x00'
>>> state2.posix.dumps(0)
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x01\x08\x01\x00\x00\x00\x00\x00'

    可以看到state1对应的输入为SOSNEAKY。


状态预设

    创建状态的构造函数:

.blank_state()

构造一个空状态,在该状态下,大部分数据都是未初始化的。

当访问未初始化的数据时,会返回一个不受约束的符号值。

.entry_state()构造一个准备从主二进制程序入口点开始执行的状态。
.full_init_state()构造一个准备从所有初始化程序开始执行的状态,完成后会跳转到入口点。
.call_state()构造一个准备从给定函数运行的状态。

低级内存接口

    通过state.mem接口可以方便的从内存中按照指定的数据类型访问数据,但是如果想要手动在某块内存中做存取操作,这个接口就比较麻烦。可以直接使用state.memory的.load(addr, size)和.store(addr, val)方法:

>>> value = state.solver.BVV(0x0123456789abcdef0123456789abcdef, 128)
>>> value
<BV128 0x123456789abcdef0123456789abcdef>
>>> state.memory.store(0x4000, value)
>>> state.memory.load(0x4000, 6)
<BV48 0x123456789ab>

    可以看到,数据以大端的方式加载和存储,因为state.memory的主要目的就是存取没有任何语义的数据块。但是,如果想对存储的数据进行字节序交换,可以选择传递参数endness,如果传入的是小端,则会发生字节序交换。endness参数的取值应该是archinfo包中Endness枚举的成员之一。此外,正在分析的程序的endness可以通过state.arch.memory_endness查看。

>>> import archinfo
>>> state.memory.store(0x4000, value, endness=archinfo.Endness.LE)
>>> state.memory.load(0x4000, 16)
<BV128 0xefcdab8967452301efcdab8967452301>
>>> state.memory.load(0x4000, 6, endness=archinfo.Endness.BE)
<BV48 0xefcdab896745>
>>> state.memory.load(0x4000, 6, endness=archinfo.Endness.LE)
<BV48 0x456789abcdef>
>>> state.arch.memory_endness
'Iend_LE'

    还有一个用于访问寄存器的低级接口:state.registers,它使用与state.memory完全相同的API。简短的解释是:它是一个寄存器文件,寄存器和偏移量之间的映射关系定义在archinfo中。


状态选项

    通过状态选项可以对angr内部进行许多小的调整。这些调整在某些情况下可以优化angr的行为,但在某些情况下会造成负面影响。

    对于每一个SimState对象,都有一组所有其启用的对象(state.option)。每个选项以某种方式控制angr执行引擎的行为。可以在option appendix中找到完整的选项域以及各选项在不同状态下的默认值。通过angr.options来访问可以添加到状态中的一些独立选项,这些选项都由大写字母命名。也有一些选项分组,捆绑在一起使用,由小写字母命名。

    当通过任何构造函数创建SimState对象时,可以传递参数add_options和remove_options,通过它们修改选项的默认值。

>>> state.options.add(angr.options.LAZY_SOLVES)
>>> state = proj.factory.entry_state(add_options={angr.options.LAZY_SOLVES})
>>> state = proj.factory.entry_state(remove_options=angr.options.simplification)

    在第一行中启用了LAZY_SOLVES选项,这个选项将会导致对约束条件是否满足的检测尽可能慢地执行,且这个改变将会对从这一行代码之后由这个状态产生的所有衍生状态有效。第二行是在初始化状态的时候启用了LAZY_SOLVES选项。第三行是在初始化状态的时候移除了simplification选项。


状态插件

    除了状态选项,所有SimState存储的内容实际上都存储在附加在state上的插件中。state的属性——memory、registers、mem、regs、solver等都是一个插件。这种设计的优点有:

  1. 使代码模块化
  2. 可以方便的实现新类型的数据存储方式
  3. 可以修改插件提供的功能

    例如,原本的memory插件模拟了线性内存空间。但是在分析时,可以选择启用抽象内存插件来支持state.memory。该插件使用新的数据类型表示地址来模拟独立于地址的内存映射。

  • globals插件

        state.globals插件实现了python dict的标准接口,从而可以在state中存储任意数据。

  • history插件

        state.history插件存储了state在执行过程中所走过路径的历史数据。它实际上是历史记录节点构成的链表每个结点表示一次执行,可以通过state.history.parent.parent...来遍历链表。

        为了能更方便的使用这个结构,history插件为某些历史值提供了一些有效的迭代器。这些值通常被存储为history.recent_NAME,它们的迭代器是history.NAME。以下面的代码段为例:

for addr in state.history.bbl_addrs: 
    print hex(addr)

        这段代码会输出跟踪的基本块地址信息。state.history.recent_bbl_addrs是在最近一步中执行的基本块列表,state.history.parent.recent_bbl_addrs是在之前一步中执行的基本块列表,以此类推。如果想要迅速的获取这些值,可以通过.hardcopy。例如state.history.bbl_addrs.hardcopy。

(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> proj = angr.Project('./fauxware')
>>> state = proj.factory.entry_state()
>>> while True:
...     succ = state.step()
...     if len(succ.successors) == 2:
...             break
...     state = succ.successors[0]
... 
WARNING | 2022-07-26 19:00:01,327 | angr.storage.memory_mixins.default_filler_mixin | The program is accessing memory with an unspecified value. This could indicate unwanted behavior.
WARNING | 2022-07-26 19:00:01,328 | angr.storage.memory_mixins.default_filler_mixin | angr will cope with this by generating an unconstrained symbolic variable and continuing. You can resolve this by:
WARNING | 2022-07-26 19:00:01,328 | angr.storage.memory_mixins.default_filler_mixin | 1) setting a value to the initial state
WARNING | 2022-07-26 19:00:01,328 | angr.storage.memory_mixins.default_filler_mixin | 2) adding the state option ZERO_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to make unknown regions hold null
WARNING | 2022-07-26 19:00:01,328 | angr.storage.memory_mixins.default_filler_mixin | 3) adding the state option SYMBOL_FILL_UNCONSTRAINED_{MEMORY,REGISTERS}, to suppress these messages.
WARNING | 2022-07-26 19:00:01,328 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff8c with 4 unconstrained bytes referenced from 0x400585 (_start+0x5 in fauxware (0x400585))
WARNING | 2022-07-26 19:00:01,625 | angr.storage.memory_mixins.default_filler_mixin | Filling memory at 0x7fffffffffeff70 with 8 unconstrained bytes referenced from 0x79d820 (strcmp+0x0 in libc.so.6 (0x9d820))
>>> state1 = succ.successors[0]
>>> state1
<SimState @ 0x400692>
>>> state1.history.recent_bbl_addrs
[4195982]
>>> quick = state1.history.bbl_addrs.hardcopy
>>> quick
[4195712, 4195648, 7478176, 4196320, 4195552, 4195756, 4195774, 4195561, 4195904, 4195936, 4195566, 4196480, 4196527, 4195571, 4196389, 4196422, 14680144, 4196125, 4195600, 7866736, 4196158, 4195632, 8454176, 4196180, 4195632, 8454176, 4196202, 4195600, 7866736, 4196212, 4195632, 8454176, 4196234, 4195632, 8454176, 4196256, 4195940, 4195664, 7985184, 4195982]
>>> slow = []
>>> for addr in state.history.bbl_addrs:
...     slow.append(addr)
... 
>>> print(slow)
[4195712, 4195648, 7478176, 4196320, 4195552, 4195756, 4195774, 4195561, 4195904, 4195936, 4195566, 4196480, 4196527, 4195571, 4196389, 4196422, 14680144, 4196125, 4195600, 7866736, 4196158, 4195632, 8454176, 4196180, 4195632, 8454176, 4196202, 4195600, 7866736, 4196212, 4195632, 8454176, 4196234, 4195632, 8454176, 4196256, 4195940, 4195664, 7985184]

        history历史记录中存储的数据有:

        1. history.descriptions:描述state上每轮执行的字符串列表。

>>> state1.history.descriptions
<angr.state_plugins.history.LambdaAttrIter object at 0x7f0050c2db00>
>>> desc = state1.history.descriptions.hardcopy
>>> for des in desc:
...     print(des)
... 
<IRSB from 0x400580: 1 sat>
<IRSB from 0x400540: 1 sat>
<SimProcedure __libc_start_main from 0x721ba0: 1 sat>
<IRSB from 0x4007e0: 1 sat>
<IRSB from 0x4004e0: 1 sat>
<IRSB from 0x4005ac: 1 sat 1 unsat>
<IRSB from 0x4005be: 1 sat>
<IRSB from 0x4004e9: 1 sat>
<IRSB from 0x400640: 1 sat 1 unsat>
<IRSB from 0x400660: 1 sat>
<IRSB from 0x4004ee: 1 sat>
<IRSB from 0x400880: 1 sat 1 unsat>
<IRSB from 0x4008af: 1 sat>
<IRSB from 0x4004f3: 1 sat>
<IRSB from 0x400825: 1 sat 1 unsat>
<IRSB from 0x400846: 1 sat>
<SimProcedure __libc_start_main from 0xe00050: 1 sat>
<IRSB from 0x40071d: 1 sat>
<IRSB from 0x400510: 1 sat>
<SimProcedure puts from 0x780970: 1 sat>
<IRSB from 0x40073e: 1 sat>
<IRSB from 0x400530: 1 sat>
<SimProcedure read from 0x810020: 1 sat>
<IRSB from 0x400754: 1 sat>
<IRSB from 0x400530: 1 sat>
<SimProcedure read from 0x810020: 1 sat>
<IRSB from 0x40076a: 1 sat>
<IRSB from 0x400510: 1 sat>
<SimProcedure puts from 0x780970: 1 sat>
<IRSB from 0x400774: 1 sat>
<IRSB from 0x400530: 1 sat>
<SimProcedure read from 0x810020: 1 sat>
<IRSB from 0x40078a: 1 sat>
<IRSB from 0x400530: 1 sat>
<SimProcedure read from 0x810020: 1 sat>
<IRSB from 0x4007a0: 1 sat>
<IRSB from 0x400664: 1 sat>
<IRSB from 0x400550: 1 sat>
<SimProcedure strcmp from 0x79d820: 1 sat>
<IRSB from 0x40068e: 2 sat>

        2. history.bbl_addrs:是state已经执行过的基本块的地址列表。不是所有的地址都对应于二进制代码,有些地址可能被SimProcedures进行了hook。

        3. history.jumpkinds:是state执行历史中每一个控制流转换的处理列表。

>>> jmpk = state1.history.jumpkinds.hardcopy
>>> for jmp in jmpk:
...     print(jmp)
... 
Ijk_Boring
Ijk_Call
Ijk_Boring
Ijk_Call
Ijk_Call
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Ret
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Call
Ijk_Call
Ijk_Boring
Ijk_Ret
Ijk_Boring

        这些字符串的含义如表格所示:

Ijk_Boring跳转(jump)至某地址
Ijk_Call调用(call)至某地址
Ijk_Ret返回(return)至某地址
Ijk_Sig各种信号(signal)
Ijk_Sys系统调用
Ijk_NoHookangr的hook

        4. history.jump_guards:是state来到当前路径所需的条件约束。

>>> guards = state1.history.jump_guards.hardcopy
>>> for guard in guards:
...     print(guard)
... 
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool True>
<Bool strncmp_ret_47_32{UNINITIALIZED} == 0x0>

        5.  history.events:是执行过程中发生的有趣事件的语义列表。比如:符号执行跳转条件、程序弹出消息框或exit代码执行完毕。

>>> events = state1.history.events.hardcopy
>>> for event in events:
...     print(event)
... 
<SimEvent unconstrained 0, with fields dict_keys(['name', 'bits'])>
<SimActionData __libc_start_main() reg/read: rdi  ---->>  <BV64 0x40071d>>
<SimActionData __libc_start_main() reg/read: rsi  ---->>  <BV64 mem_7fffffffffeff8c_38_32{UNINITIALIZED} .. 0x1>>
<SimActionData __libc_start_main() reg/read: rdx  ---->>  <BV64 0x7fffffffffeff90>>
<SimActionData __libc_start_main() reg/read: rcx  ---->>  <BV64 0x4007e0>>
<SimActionData __libc_start_main() reg/read: r8  ---->>  <BV64 0x400870>>
<SimActionData __libc_start_main() mem/write: 3221229360  <<----  <BV64 0x0>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff68>>
<SimActionData __libc_start_main() reg/write: edi  <<----  <BV32 0x1>>
<SimActionData __libc_start_main() reg/write: rsi  <<----  <BV64 0x7fffffffffeff90>>
<SimActionData __libc_start_main() reg/write: rdx  <<----  <BV64 0x7fffffffffeff98 + 0x8 * (mem_7fffffffffeff8c_38_32{UNINITIALIZED} .. 0x1)>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff68>>
<SimActionData __libc_start_main() mem/write: <BV64 0x7fffffffffeff68>  <<----  <BV64 0xe00050>>
<SimActionData __libc_start_main() reg/write: rip  <<----  <BV64 0x4007e0>>
<SimActionData __libc_start_main() mem/read: <BV64 0x7fffffffffeff68>  ---->>  <BV64 0xe00050>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/read: rax  ---->>  <BV64 0xffffffffffffffff>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff78>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff70>>
<SimActionData __libc_start_main() reg/write: rsp  <<----  <BV64 0x7fffffffffeff68>>
<SimActionData __libc_start_main() reg/write: edi  <<----  <BV32 0x1>>
<SimActionData __libc_start_main() reg/write: rsi  <<----  <BV64 0x7fffffffffeff90>>
<SimActionData __libc_start_main() reg/write: rdx  <<----  <BV64 0x7fffffffffeff98 + 0x8 * (mem_7fffffffffeff8c_38_32{UNINITIALIZED} .. 0x1)>>
<SimActionData __libc_start_main() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff68>>
<SimActionData __libc_start_main() mem/write: <BV64 0x7fffffffffeff68>  <<----  <BV64 0xe00058>>
<SimActionData __libc_start_main() reg/write: rip  <<----  <BV64 0x40071d>>
<SimActionData __libc_start_main() mem/read: <BV64 0x7fffffffffeff68>  ---->>  <BV64 0xe00058>>
<SimActionData puts() reg/read: rdi  ---->>  <BV64 0x400915>>
<SimActionData puts() mem/read: <BV64 0x400915>  ---->>  <BV1032 0x557365726e616d653a200050617373776f72643a200000011b033b4000000007000000d4fbffff5c00000038fdffff84000000c1fdffffa4000000d1fdffffc4000000f1fdffffe4000000b4feffff0401000044ffffff2c0100001400000000000000017a5200017810011b0c070890010000240000001c00000070fbffff8000>>
<SimActionData puts() mem/read: <BV64 0x400915>  ---->>  <BV80 0x557365726e616d653a20>>
<SimEvent fs_write 46, with fields dict_keys(['filename', 'data', 'size', 'pos'])>
<SimEvent fs_write 47, with fields dict_keys(['filename', 'data', 'size', 'pos'])>
<SimActionData puts() reg/write: eax  <<----  <BV32 0xb>>
<SimActionData puts() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData puts() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x40073e>>
<SimActionData puts() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData puts() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData puts() reg/write: rip  <<----  <BV64 0x40073e>>
<SimActionData read() reg/read: edi  ---->>  <BV32 0x0>>
<SimActionData read() reg/read: rsi  ---->>  <BV64 0x7fffffffffeff50>>
<SimActionData read() reg/read: rdx  ---->>  <BV64 0x8>>
<SimEvent unconstrained 57, with fields dict_keys(['name', 'bits'])>
<SimActionData read() mem/write: <BV64 0x7fffffffffeff50>  <<----  <BV64 packet_0_stdin_41_64>>
<SimActionData read() reg/write: rax  <<----  <BV64 0x8>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x400754>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData read() reg/write: rip  <<----  <BV64 0x400754>>
<SimActionData read() reg/read: edi  ---->>  <BV32 0x0>>
<SimActionData read() reg/read: rsi  ---->>  <BV64 0x7fffffffffeff3c>>
<SimActionData read() reg/read: rdx  ---->>  <BV64 0x1>>
<SimEvent unconstrained 68, with fields dict_keys(['name', 'bits'])>
<SimActionData read() mem/write: <BV64 0x7fffffffffeff3c>  <<----  <BV8 packet_1_stdin_42_8>>
<SimActionData read() reg/write: rax  <<----  <BV64 0x1>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x40076a>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData read() reg/write: rip  <<----  <BV64 0x40076a>>
<SimActionData puts() reg/read: rdi  ---->>  <BV64 0x400920>>
<SimActionData puts() mem/read: <BV64 0x400920>  ---->>  <BV1032 0x50617373776f72643a200000011b033b4000000007000000d4fbffff5c00000038fdffff84000000c1fdffffa4000000d1fdffffc4000000f1fdffffe4000000b4feffff0401000044ffffff2c0100001400000000000000017a5200017810011b0c070890010000240000001c00000070fbffff80000000000e10460e184a0f0b>>
<SimActionData puts() mem/read: <BV64 0x400920>  ---->>  <BV80 0x50617373776f72643a20>>
<SimEvent fs_write 79, with fields dict_keys(['filename', 'data', 'size', 'pos'])>
<SimEvent fs_write 80, with fields dict_keys(['filename', 'data', 'size', 'pos'])>
<SimActionData puts() reg/write: eax  <<----  <BV32 0xb>>
<SimActionData puts() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData puts() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x400774>>
<SimActionData puts() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData puts() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData puts() reg/write: rip  <<----  <BV64 0x400774>>
<SimActionData read() reg/read: edi  ---->>  <BV32 0x0>>
<SimActionData read() reg/read: rsi  ---->>  <BV64 0x7fffffffffeff40>>
<SimActionData read() reg/read: rdx  ---->>  <BV64 0x8>>
<SimEvent unconstrained 90, with fields dict_keys(['name', 'bits'])>
<SimActionData read() mem/write: <BV64 0x7fffffffffeff40>  <<----  <BV64 packet_2_stdin_43_64>>
<SimActionData read() reg/write: rax  <<----  <BV64 0x8>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x40078a>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData read() reg/write: rip  <<----  <BV64 0x40078a>>
<SimActionData read() reg/read: edi  ---->>  <BV32 0x0>>
<SimActionData read() reg/read: rsi  ---->>  <BV64 0x7fffffffffeff3c>>
<SimActionData read() reg/read: rdx  ---->>  <BV64 0x1>>
<SimEvent unconstrained 101, with fields dict_keys(['name', 'bits'])>
<SimActionData read() mem/write: <BV64 0x7fffffffffeff3c>  <<----  <BV8 packet_3_stdin_44_8>>
<SimActionData read() reg/write: rax  <<----  <BV64 0x1>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() mem/read: <BV64 0x7fffffffffeff18>  ---->>  <BV64 0x4007a0>>
<SimActionData read() reg/read: rsp  ---->>  <BV64 0x7fffffffffeff18>>
<SimActionData read() reg/write: rsp  <<----  <BV64 0x7fffffffffeff20>>
<SimActionData read() reg/write: rip  <<----  <BV64 0x4007a0>>
<SimActionData strcmp() reg/read: rdi  ---->>  <BV64 0x7fffffffffeff40>>
<SimActionData strcmp() reg/read: rsi  ---->>  <BV64 0x4008d0>>
<SimEvent unconstrained 112, with fields dict_keys(['name', 'bits'])>
<SimActionData strcmp() mem/read: <BV64 0x7fffffffffeff40>  ---->>  <BV1032 packet_2_stdin_43_64 .. 0x0 .. packet_0_stdin_41_64 .. 0x5800e00000000000 .. mem_7fffffffffeff70_45_64{UNINITIALIZED} .. 0xa90540000000000088fffeffffffff071c00000000000000c8fffeffffffff07000000000000000000000000000000001900000000000000d3fffeffffffff07000000000000000000>>
<SimEvent unconstrained 113, with fields dict_keys(['name', 'bits'])>
<SimActionConstraint strcmp() <SAO <Bool (if packet_2_stdin_43_64[63:56] == 0 then 0x7fffffffffeff40 else (if packet_2_stdin_43_64[55:48] == 0 then 0x7fffffffffeff41 else (if packet_2_stdin_43_64[47:40] == 0 then 0x7fffffffffeff42 else (if packet_2_stdin_43_64[39:32] == 0 then 0x7fffffffffeff43 else (if packet_2_stdin_43_64[31:24] == 0 then 0x7fffffffffeff44 else (if packet_2_stdin_43_64[23:16] == 0 then 0x7fffffffffeff45 else (if packet_2_stdin_43_64[15:8] == 0 then 0x7fffffffffeff46 else (if packet_2_stdin_43_64[7:0] == 0 then 0x7fffffffffeff47 else 0x7fffffffffeff48)))))))) - 0x7fffffffffeff40 == strlen_46_64>>>
<SimActionData strcmp() mem/read: <BV64 0x4008d0>  ---->>  <BV1032 0x534f534e45414b59000000000000000057656c636f6d6520746f207468652061646d696e20636f6e736f6c652c207472757374656420757365722100476f20617761792100557365726e616d653a200050617373776f72643a200000011b033b4000000007000000d4fbffff5c00000038fdffff84000000c1fdffffa4000000d1>>
<SimEvent unconstrained 116, with fields dict_keys(['name', 'bits'])>
<SimActionData strcmp() mem/read: <BV64 0x7fffffffffeff40>  ---->>  <BV64 packet_2_stdin_43_64>>
<SimActionData strcmp() mem/read: <BV64 0x4008d0>  ---->>  <BV64 0x534f534e45414b59>>
<SimActionConstraint strcmp() <SAO <Bool (strlen_46_64 == 0x8 || strlen_46_64 >= 0x8) && (packet_2_stdin_43_64[63:56] == 83 || strlen_46_64 < 0x0) && (packet_2_stdin_43_64[55:48] == 79 || strlen_46_64 < 0x1) && (packet_2_stdin_43_64[47:40] == 83 || strlen_46_64 < 0x2) && (packet_2_stdin_43_64[39:32] == 78 || strlen_46_64 < 0x3) && (packet_2_stdin_43_64[31:24] == 69 || strlen_46_64 < 0x4) && (packet_2_stdin_43_64[23:16] == 65 || strlen_46_64 < 0x5) && (packet_2_stdin_43_64[15:8] == 75 || strlen_46_64 < 0x6) && (packet_2_stdin_43_64[7:0] == 89 || strlen_46_64 < 0x7) && strncmp_ret_47_32{UNINITIALIZED} == 0x0 || !((strlen_46_64 == 0x8 || strlen_46_64 >= 0x8) && (packet_2_stdin_43_64[63:56] == 83 || strlen_46_64 < 0x0) && (packet_2_stdin_43_64[55:48] == 79 || strlen_46_64 < 0x1) && (packet_2_stdin_43_64[47:40] == 83 || strlen_46_64 < 0x2) && (packet_2_stdin_43_64[39:32] == 78 || strlen_46_64 < 0x3) && (packet_2_stdin_43_64[31:24] == 69 || strlen_46_64 < 0x4) && (packet_2_stdin_43_64[23:16] == 65 || strlen_46_64 < 0x5) && (packet_2_stdin_43_64[15:8] == 75 || strlen_46_64 < 0x6) && (packet_2_stdin_43_64[7:0] == 89 || strlen_46_64 < 0x7)) && strncmp_ret_47_32{UNINITIALIZED} == 0x1>>>
<SimActionData strcmp() reg/write: eax  <<----  <BV32 strncmp_ret_47_32{UNINITIALIZED}>>
<SimActionData strcmp() reg/read: rsp  ---->>  <BV64 0x7fffffffffefee8>>
<SimActionData strcmp() mem/read: <BV64 0x7fffffffffefee8>  ---->>  <BV64 0x40068e>>
<SimActionData strcmp() reg/read: rsp  ---->>  <BV64 0x7fffffffffefee8>>
<SimActionData strcmp() reg/write: rsp  <<----  <BV64 0x7fffffffffefef0>>
<SimActionData strcmp() reg/write: rip  <<----  <BV64 0x40068e>>
<SimActionConstraint 0x400690:17 <SAO <Bool strncmp_ret_47_32{UNINITIALIZED} == 0x0>>>

        6.  history.actions:通常情况下为空,但是如果将angr.options.refs选项添加到state中,则会记录程序执行过程中访问的寄存器、内存和立即数。

  • callstack插件

        angr会跟踪模拟执行程序的调用栈。每次执行call指令时,一个新的函数栈帧将会被添加到当前函数调用栈的栈顶。这使得angr能够可靠的存储当前模拟执行函数的局部数据。

        类似于history插件,callstack也是由节点构成的链表。但是angr没有为之提供迭代器,可以直接使用state.callstack来获取每一个尚未被弹出的栈帧,获取的顺序是从最近的一次调用到最早的一次。

>>> for frame in state1.callstack:
...     print(frame)
... 
Backtrace:
Frame 0: 0x4007a0 => 0x400664, sp = 0x7fffffffffeff18
Frame 1: 0xe00050 => 0x40071d, sp = 0x7fffffffffeff68
Frame 2: 0x400580 => 0x400540, sp = 0x7fffffffffeff78
Frame 3: 0x0 => 0x0, sp = 0xffffffffffffffff
Backtrace:
Frame 0: 0xe00050 => 0x40071d, sp = 0x7fffffffffeff68
Frame 1: 0x400580 => 0x400540, sp = 0x7fffffffffeff78
Frame 2: 0x0 => 0x0, sp = 0xffffffffffffffff
Backtrace:
Frame 0: 0x400580 => 0x400540, sp = 0x7fffffffffeff78
Frame 1: 0x0 => 0x0, sp = 0xffffffffffffffff
Backtrace:
Frame 0: 0x0 => 0x0, sp = 0xffffffffffffffff
>>> state1.callstack[0]
<CallStack (depth 4)>
>>> state1.callstack[1]
<CallStack (depth 3)>
>>> state1.callstack[2]
<CallStack (depth 2)>
>>> state1.callstack[3]
<CallStack (depth 1)>
>>> state1.callstack[1][0]
<CallStack (depth 3)>
>>> state1.callstack[1][1]
<CallStack (depth 2)>
>>> state1.callstack[1][2]
<CallStack (depth 1)>

        1. callstack.func_addr:获取当前正在执行的函数地址。

>>> hex(state1.callstack.func_addr)
'0x400664'

        2. callstack.call_site_addr:调用当前函数的基本块的地址。

>>> hex(state1.callstack.call_site_addr)
'0x4007a0'

        3. callstack.stack_ptr:当前函数开始执行时的栈顶指针。

>>> hex(state1.callstack.stack_ptr)
'0x7fffffffffeff18'

        4. callstack.ret_addr:当前函数返回的地址。

>>> hex(state1.callstack.ret_addr)
'0x4007b3'

关于输入输出:文件、文件系统和网络接口

    能够控制程序模拟执行时的看到的环境是十分重要的,包括如何从环境中引入符号数据。angr通过一系列强大的抽象,使得我们可以设置所需的环境。

    与文件系统、管道、网络接口、终端进行交互的根源是SimFile对象。一个SimFile对象是对存储的抽象,定义了一系列字节、符号或其他内容。SimFiles存在不同的种类,它们以不同的方式存储数据。例如:SimFile(基类是SimFileBase)将文件存储为线性地址空间中的数据;SimPackets存储一系列可变大小的读入数据。前者适用于需要对文件进行搜索的程序,是文件的默认存储方式;后者适用于使用scanf的程序,是stdin/stdout/stderr的默认存储方式。

    由于SimFiles有多样化的存储机制,因此与它们交互的接口也十分抽象。可以从某位置的文件中读取、可以向某位置的文件中写入、可以询问某文件当前存储了多少字节,也可以具体化文件生成测试用例。如果可以明确当前正在使用的是哪一个SimFile类,则可以对它进行更强大的控制。因此,鼓励在创建初始状态时手动创建需要交互的文件。

    具体而言,每一个SimFile类都会创建一个抽象的文件内位置(position)。每次读取和写入时都以position作为参数,并返回一个新的position。这样一来就可以从上次离开的位置继续操作文件。如果正在使用一个不明确的SimFile类,则需要将position视为一个完全不透明的对象,只能完成读\写功能。

    然而,这与程序和文件的交互方式匹配度很低。因此,angr还提供了一个SimFileDescriptor的抽象。该对象提供通常的读、写等接口。但是当基础存储不支持对应的操作时,仍然会返回error。

    可以通过state.posix.fd访问文件描述符编号和文件描述符对象间的映射关系。

使用方法

    创建SimFiles类实例的完整方法参见storage docs,通过一些示例来说明部分用法。

  • Example1:创建有明确内容的文件
(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr
>>> simfile = angr.SimFile('concretefile', content='hello world!\n')

        当一个SimFile实例没有与一个state相关联时,它是不可使用的。下面的操作不必在实际使用的场景下执行(在将SimFile传入构造函数或文件系统时会自动执行这些操作),现在只是进行模拟:

>>> project = angr.Project('./fauxware')
>>> state = project.factory.blank_state()
>>> simfile.set_state(state)

        SimFile默认的position是文件开始时的字节数。调用SimFile.read函数读取会返回一个三元组(读取的内容、实际读取的字节数、更新后的position)。读取的内容是bitvector类型。

>>> data, actual_size, new_pos = simfile.read(0, 5)
>>> data
<BV40 0x68656c6c6f>
>>> assert claripy.is_true(actual_size == 5)
>>> assert claripy.is_true(new_pos == 5)

        继续读取:

>>> data, actual_size, new_pos = simfile.read(new_pos, 1000)
>>> data
<BV8000 0x20776f726c64210a .. file_0_concretefile_d_38_7936{UNINITIALIZED}>
>>> assert len(data) == 1000*8
>>> assert claripy.is_true(actual_size == 8)
>>> assert claripy.is_true(new_pos == 13)
  • Example2:创建符号内容但有明确大小的文件
(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> project = angr.Project('./fauxware')
>>> state = project.factory.blank_state()
>>> simfile = angr.SimFile('symbolicfile', size=0x20)
>>> simfile.set_state(state)
>>> data, actual_size, new_pos = simfile.read(0, 0x30)
>>> data
<BV384 file_0_symbolicfile_0_38_384{UNINITIALIZED}>
>>> actual_size
<BV64 0x20>
>>> new_pos
<BV64 0x20>
>>> data.symbolic
True

        基本的SimFile提供与state.memory相同的接口,因此可以直接加载数据。

>>> simfile.load(0, actual_size)
<BV256 file_0_symbolicfile_0_38_384{UNINITIALIZED}[383:128]>
>>> assert simfile.load(0, actual_size) is data.get_bytes(0, 0x20)
  • Example3:创建受约束符号内容的文件
(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> project = angr.Project('./fauxware')
>>> bytes_list = [claripy.BVS('byte_%d' % i, 8) for i in range(32)]
>>> bytes_list
[<BV8 byte_0_38_8>, <BV8 byte_1_39_8>, <BV8 byte_2_40_8>, <BV8 byte_3_41_8>, <BV8 byte_4_42_8>, <BV8 byte_5_43_8>, <BV8 byte_6_44_8>, <BV8 byte_7_45_8>, <BV8 byte_8_46_8>, <BV8 byte_9_47_8>, <BV8 byte_10_48_8>, <BV8 byte_11_49_8>, <BV8 byte_12_50_8>, <BV8 byte_13_51_8>, <BV8 byte_14_52_8>, <BV8 byte_15_53_8>, <BV8 byte_16_54_8>, <BV8 byte_17_55_8>, <BV8 byte_18_56_8>, <BV8 byte_19_57_8>, <BV8 byte_20_58_8>, <BV8 byte_21_59_8>, <BV8 byte_22_60_8>, <BV8 byte_23_61_8>, <BV8 byte_24_62_8>, <BV8 byte_25_63_8>, <BV8 byte_26_64_8>, <BV8 byte_27_65_8>, <BV8 byte_28_66_8>, <BV8 byte_29_67_8>, <BV8 byte_30_68_8>, <BV8 byte_31_69_8>]
>>> bytes_ast = claripy.Concat(*bytes_list)
>>> bytes_ast
<BV256 byte_0_38_8 .. byte_1_39_8 .. byte_2_40_8 .. byte_3_41_8 .. byte_4_42_8 .. byte_5_43_8 .. byte_6_44_8 .. byte_7_45_8 .. byte_8_46_8 .. byte_9_47_8 .. byte_10_48_8 .. byte_11_49_8 .. byte_12_50_8 .. byte_13_51_8 .. byte_14_52_8 .. byte_15_53_8 .. byte_16_54_8 .. byte_17_55_8 .. byte_18_56_8 .. byte_19_57_8 .. byte_20_58_8 .. byte_21_59_8 .. byte_22_60_8 .. byte_23_61_8 .. byte_24_62_8 .. byte_25_63_8 .. byte_26_64_8 .. byte_27_65_8 .. byte_28_66_8 .. byte_29_67_8 .. byte_30_68_8 .. byte_31_69_8>
>>> mystate = proj.factory.entry_state(stdin=angr.SimFile('/dev/stdin', content=bytes_ast))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'proj' is not defined
>>> mystate = project.factory.entry_state(stdin=angr.SimFile('/dev/stdin', content=bytes_ast))
>>> for byte in bytes_list:
...     mystate.solver.add(byte >= 0x20)
...     mystate.solver.add(byte <= 0x7e)
... 
[<Bool byte_0_38_8 >= 32>]
[<Bool byte_0_38_8 <= 126>]
[<Bool byte_1_39_8 >= 32>]
[<Bool byte_1_39_8 <= 126>]
[<Bool byte_2_40_8 >= 32>]
[<Bool byte_2_40_8 <= 126>]
[<Bool byte_3_41_8 >= 32>]
[<Bool byte_3_41_8 <= 126>]
[<Bool byte_4_42_8 >= 32>]
[<Bool byte_4_42_8 <= 126>]
[<Bool byte_5_43_8 >= 32>]
[<Bool byte_5_43_8 <= 126>]
[<Bool byte_6_44_8 >= 32>]
[<Bool byte_6_44_8 <= 126>]
[<Bool byte_7_45_8 >= 32>]
[<Bool byte_7_45_8 <= 126>]
[<Bool byte_8_46_8 >= 32>]
[<Bool byte_8_46_8 <= 126>]
[<Bool byte_9_47_8 >= 32>]
[<Bool byte_9_47_8 <= 126>]
[<Bool byte_10_48_8 >= 32>]
[<Bool byte_10_48_8 <= 126>]
[<Bool byte_11_49_8 >= 32>]
[<Bool byte_11_49_8 <= 126>]
[<Bool byte_12_50_8 >= 32>]
[<Bool byte_12_50_8 <= 126>]
[<Bool byte_13_51_8 >= 32>]
[<Bool byte_13_51_8 <= 126>]
[<Bool byte_14_52_8 >= 32>]
[<Bool byte_14_52_8 <= 126>]
[<Bool byte_15_53_8 >= 32>]
[<Bool byte_15_53_8 <= 126>]
[<Bool byte_16_54_8 >= 32>]
[<Bool byte_16_54_8 <= 126>]
[<Bool byte_17_55_8 >= 32>]
[<Bool byte_17_55_8 <= 126>]
[<Bool byte_18_56_8 >= 32>]
[<Bool byte_18_56_8 <= 126>]
[<Bool byte_19_57_8 >= 32>]
[<Bool byte_19_57_8 <= 126>]
[<Bool byte_20_58_8 >= 32>]
[<Bool byte_20_58_8 <= 126>]
[<Bool byte_21_59_8 >= 32>]
[<Bool byte_21_59_8 <= 126>]
[<Bool byte_22_60_8 >= 32>]
[<Bool byte_22_60_8 <= 126>]
[<Bool byte_23_61_8 >= 32>]
[<Bool byte_23_61_8 <= 126>]
[<Bool byte_24_62_8 >= 32>]
[<Bool byte_24_62_8 <= 126>]
[<Bool byte_25_63_8 >= 32>]
[<Bool byte_25_63_8 <= 126>]
[<Bool byte_26_64_8 >= 32>]
[<Bool byte_26_64_8 <= 126>]
[<Bool byte_27_65_8 >= 32>]
[<Bool byte_27_65_8 <= 126>]
[<Bool byte_28_66_8 >= 32>]
[<Bool byte_28_66_8 <= 126>]
[<Bool byte_29_67_8 >= 32>]
[<Bool byte_29_67_8 <= 126>]
[<Bool byte_30_68_8 >= 32>]
[<Bool byte_30_68_8 <= 126>]
[<Bool byte_31_69_8 >= 32>]
[<Bool byte_31_69_8 <= 126>]
  • Example4:创建符号内容和明确内容混合的文件
(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> project = angr.Project('./fauxware')
>>> state = project.factory.blank_state()
>>> variable = claripy.BVS('var', 10*8)
>>> simfile = angr.SimFile('mixedfile', content=variable.concat(claripy.BVV('\n')), has_end=False)
WARNING | 2022-07-27 03:23:27,845 | claripy.ast.bv | BVV value is a unicode string, encoding as utf-8
>>> simfile.set_state(state)
>>> claripy.is_true(simfile.size == 11)
True
>>> data, actual_size, new_pos = simfile.read(0, 15)
>>> actual_size
15
>>> new_pos
15
>>> data.get_bytes(0, 10) == variable
<Bool True>
>>> data.get_bytes(10, 1)
<BV8 10>
>>> data.get_bytes(11, 4).symbolic
True
  • Example2:创建符号大小的文件
>>> symsize = claripy.BVS('size', 64)
>>> state.solver.add(symsize >= 10)
[<Bool size_40_64 >= 0xa>]
>>> state.solver.add(symsize < 20)
[<Bool size_40_64 < 0x14>]
>>> simfile = angr.SimFile('symsizefile', size=symsize)
>>> simfile.set_state(state)
>>> data, actual_size, new_pos = simfile.read(0, 30)
>>> set(state.solver.eval_upto(actual_size, 30)) == set(range(10, 20)) #读取时将覆盖所有可能性
True
>>> len(data) #由于最大尺寸不固定 所以返回数据的size为传入read的参数 需要与actual_size结合使用
240

        到目前为止的示例只使用了SimFile类,该类对随机访问的文件对象进行建模。但是在实际中,数据并不是全从文件中获取。流(标准IO,TCP)就是一个很好的例子:虽然它们像普通文件一样存储数据,但是不支持随机访问。因此,angr对流进行了更简单的抽象,也更有利于符号执行。

>>> simfile = angr.SimPackets('mypackets')
>>> simfile.set_state(state)

        上述两行代码只会形成单个包。对于SimPackets类,position表示的是包序号。

>>> simfile = angr.SimPackets('mypackets')
>>> simfile.set_state(state)
>>> data, actual_size, new_pos = simfile.read(0, 20, short_reads=True)
>>> len(data)
160
>>> set(state.solver.eval_upto(actual_size, 30)) == set(range(21))
True
>>> data
<BV160 packet_0_file_2_mypackets_43_160>

        SimPackets中的数据以元组的形式存储(包数据,包大小),通过.content来访问。

真实场景

    如果想使SimFile对象能够被程序使用,我们需要将它关联到文件系统或让它提供stdin、stdout服务。

    模拟文件系统的是state.fs插件。你可以从文件系统中存储、加载、删除文件,分别对应insert、get、delete方法。更多详细的方法可以查阅API手册

1. 首先使文件可以通过/tmp/myfile使用

(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> project = angr.Project('./fauxware')
>>> state = project.factory.blank_state()
>>> variable = claripy.BVS('var', 10*8)
>>> simfile = angr.SimFile('mixedfile', content=variable.concat(claripy.BVV('\n')), has_end=False)
WARNING | 2022-07-27 04:15:55,457 | claripy.ast.bv | BVV value is a unicode string, encoding as utf-8
>>> state.fs.insert('/tmp/file', simfile)
True
>>> state.fs.get('/tmp/file') is simfile
True

    在执行之后,我们将从结果状态中提取文件,并使用simfile.concretize()来获得到达当前状态时的测试用例。


复制与合并

    state支持快速复制,可以进一步探索更多的可能性:

(angr) root@ubuntu:/home/c1rcl3/Desktop/Angr/doc# python
Python 3.6.9 (default, Jun 29 2022, 11:45:57) 
[GCC 8.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import angr, claripy
>>> project = angr.Project('./fauxware')
>>> state = project.factory.blank_state()
>>> state1 = state.copy()
>>> state2 = state.copy()
>>> state1.mem[0x1000].uint32_t = 0x19191919
>>> state2.mem[0x1000].uint32_t = 0x91919191

    state也可以合并,merge函数返回一个三元组。第一个元素是合并后的state,第二个元素是描述state标志的符号变量,第三个元素是描述合并是否完成的布尔值。

>>> (s_merged, m, anything_merged) = state1.merge(state2)
>>> s_merged
<SimState @ 0x400580>
>>> m
[<Bool state_merge_0_38_16 == 0x0>, <Bool state_merge_0_38_16 == 0x1>]
>>> anything_merged
True
>>> aaaa_or_bbbb = s_merged.mem[0x1000].uint32_t
>>> aaaa_or_bbbb
<uint32_t <BV32 (if state_merge_0_38_16 == 0x1 then 145 else (if state_merge_0_38_16 == 0x0 then 25 else 0)) .. (if state_merge_0_38_16 == 0x1 then 145 else (if state_merge_0_38_16 == 0x0 then 25 else 0)) .. (if state_merge_0_38_16 == 0x1 then 145 else (if state_merge_0_38_16 == 0x0 then 25 else 0)) .. (if state_merge_0_38_16 == 0x1 then 145 else (if state_merge_0_38_16 == 0x0 then 25 else 0))> at 0x1000>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值