ANGR初识

首页: https://angr.io
项目存储库: GitHub - angr/angr: A powerful and user-friendly binary analysis platform!
文档: https://docs.angr.io
API 文档: angr documentation
练习项目: https://github.com/angr/angr-doc/tree/master
angr 是一款开源的python框架,是一个多架构开源二进制分析工具包,能够对二进制文件执行动态符号执行(如 Mayhem、KLEE 等)和各种静态分析。
直接 pip install angr就可以安装。

一、angr架构

CLE:
    CLE是angr加载二进制文件的组件,在加载二进制文件时会分析并读取binary的信息,包括指令地址、shared library、arch information等;
import angr
b = angr.Project("./test")
VEX:
    它会将指令转化为中间语言IR,分析IR并且模拟,搞清楚它是什么并且做了什么;
Claripy:
    它是angr的求解引擎,根据程序所需要的输入设置符号变量以及收集限制式等等,在argr中多用于符号化。

二、核心概念

2.1、顶层接口

Project类是angr的主类,也是angr的开始,通过初始化该类的对象,可以将你想要分析的二进制文件加载进来,就像这样:
import angr
proj = angr.Project("/bin/true")
参数:
  • thing:待分析的文件路径,它是唯一必须传入的参数
  • auto_load_libs:是否用CLE自动解析共享库依赖关系,默认为on。
  • except_missing_libs:当二进制文件有无法解析的共享库依赖项时,将引发异常,默认为on。
  • force_load_libs:将字符串列表传递给force_load_libs,所列出的内容将作为未解析的共享库依赖项处理,
  • skip_libs:将字符串列表传递给skip_libs,以防止将该名称的任何库解析为依赖项。
  • main_opts是一个从选项名到选项值的映射
  • lib_opts是一个从库名到将选项名映射到选项值的字典的映射 例如angr.Project('examples/fauxware/fauxware', main_opts={'backend': 'blob', 'arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}}
任何附加的参数都会被传递到angr的加载器,即CLE.loader中(CLE 即 CLE Loads Everything的缩写)。
  
Project类中有许多方法和属性,例如架构、程序入口点、加载的文件名、大小端等等:
>>> print(proj.arch, hex(proj.entry), proj.filename, proj.arch.bits, proj.arch.memory_endness )
<Arch AMD64 (LE)> 0x4023c0 /bin/true 64 Iend_LE

2.2、Blocks

project.factory.block()用于 从给定的地址提取基本代码块。angr是以基本块为单位分析代码。
返回一个Block对象。
>>> block = proj.factory.block(proj.entry) # lift a block of code from the program's entry point
<Block for 0x401670, 42 bytes>
   
>>> block.pp()       # 打印反汇编内容,地址显示16进制
4017b0  xor     ebp, ebp
4017b2  mov     r9, rdx
4017b5  pop     rsi
4017b6  mov     rdx, rsp
4017b9  and     rsp, 0xfffffffffffffff0
4017bd  push    rax
4017be  push    rsp
4017bf  lea     r8, [0x4049f0]
4017c6  lea     rcx, [0x404980]
4017cd  lea     rdi, [0x4016f0]
4017d4  call    qword ptr [0x606fd8]
   
>>> block.instructions      # 有多少条指令
11
>>> block.instruction_addrs  # 指令的地址,返回10进制,若要返回16进制,在此之前加上 import monkeyhex # this will format numerical results in hexadecimal
(4200368, 4200370, 4200373, 4200374, 4200377, 4200381, 4200382, 4200383, 4200390, 4200397, 4200404)
此外,您可以使用 Block 对象来获取代码块的其他表示形式:
>>> block.capstone                   # capstone disassembly
<DisassemblerBlock for 0x4017b0>

>>> block.vex                        # VEX IRSB (that's a Python internal address, not a program address)
IRSB <0x2a bytes, 11 ins., <Arch AMD64 (LE)>> at 0x4017b0

2.3、状态State

Project实际上只是将二进制文件加载进来了,要执行它,实际上是对SimState对象进行操作,它是程序的状态。用docker来比喻,Project相当于开发环境,State则是使用开发环境制作的镜像。

2.3.1、创建状态

要创建状态,需要使用Project对象中的factory,它还可以用于创建模拟管理器和基本块。
state = proj.factory.entry_state()
返回值:
    返回一个Simstate,SimState包含程序的内存、寄存器、文件系统、符号变量和约束内容等……任何可以通过执行更改的“实时数据”均在SimState。
预设状态有四种方式如下:   
  • entry_state():构造一个准备在主二进制文件的入口点执行的状态。
  • blank_state(addr=)构造了一个“空白石板”空白状态,其大部分数据未初始化,状态中下一条指令为addr处的指令当访问未初始化的数据时,将返回一个不受约束的符号值。
  • full_init_state()构造一个状态,该状态可以通过需要在主二进制的入口点之前运行的任何初始化程序执行,例如共享库构造函数或预初始化程序。当完成这些后,它将跳转到入口点。
  • call_state()构造准备执行给定函数的状态
entry_state和blank_state是常用的两种方式,后者通常用于跳过一些极大降低angr效率的指令,它们间的对比如下:
>>> state = p.factory.entry_state()
>>> print(state.regs.rax, state.regs.rip)  # state.regs.rip:get the current instruction pointer
<BV64 0x1c> <BV64 0x4023c0>

>>> state = p.factory.blank_state(addr=0x4023c0)
>>> print(state.regs.rax, state.regs.rip)
<BV64 reg_rax_42_64{UNINITIALIZED}> <BV64 0x4023c0>
在blank_state方式中,我们仍将地址设定为程序的入口点,然而rax中的值由于没有初始化,它现在是一个名字,也即符号变量,这是符号执行的基础,后续在细说。
此外,可以看到寄存器中的数据类型并不是int,而是BV64,它是一个位向量(Bit Vector)。

2.3.2、BV与BVS

位向量(Bit Vector,BV)就是一串比特的序列,这于python中的int不同,例如python中的int提供了整数溢出上的包装。而位向量可以理解为CPU中使用的一串比特流,需要注意的是, angr封装的位向量有两个属性:值以及它的长度
以下是 如何从 Python 整数转换为 位向量 并再次转换回来每个位向量都有一个 .length 属性,描述其位宽
>>> 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
4660
位向量相互之间能够进行运算,但 参与运算的位向量的长度必须相同
>>> one = state.solver.BVV(1,64)
>>> one_hundred = state.solver.BVV(100,64)
>>> short_nine = state.solver.BVV(9,27)
>>> print(one,one_hundred,short_nine)
<BV64 0x1> <BV64 0x64> <BV27 0x9>

>>> print(short_nine+1)
<BV27 0xa>
>>> print(one+one_hundred)
<BV64 0x65>
>>> print(one+short_nine)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/claripy/operations.py", line 50, in _op
    raise ClaripyOperationError(msg)
claripy.errors.ClaripyOperationError: args' length must all be equal   # 当长度不一样时,claripy会提示“length must all be equal”,同时我们也得知,位向量运算的底层模块时claripy
如果一定要进行长度不相等位向量之间的运算,可以扩展位向量使用zero_extend会用零扩展高位,而sign_extend会在此基础上带符号地进行扩展
>>> print(one+short_nine.zero_extend(64-27))
<BV64 0xa>
接下来使用 BVS(Bit Vectort Symbol)创建一些符号变量:
>>> x = state.solver.BVS('x',64)   # BVS的参数分别是符号变量名和长度
>>> y = state.solver.BVS('y',64)
>>> z = state.solver.BVS('notz',64)
>>> print(x,y,z)
<BV64 x_42_64> <BV64 y_43_64> <BV64 notz_44_64>
此时对符号变量进行运算,做比较判断,都不会得到一个具体的值,而是将这些操作统统保存到符号变量中:
>>> print(x+1)
<BV64 x_42_64 + 0x1>

2.3.3、状态操作

状态包含了程序运行时的一切信息,寄存器、内存的值、文件系统以及符号变量等
寄存器访问:可以通过state.regs.寄存器名来访问和修改寄存器:
  • state.regs.rip  get the current instruction pointer
  • state.regs.rax
  • state.regs.rbp = state.regs.rsp # 将寄存器rsp的值给rbp
栈访问:栈访问涉及两个寄存器:ebp和esp,以及两个指令:push和pop,对于寄存器的访问与其他寄存器相同
  • state.stack_push(value)
  • state.stack_pop()
   内存访问:使用以下两个指令对内存读写:
  • 读内存-state.memory.load(addr, size=None, condition=None, fallback=None, **kwargs) 程序所使用的大小端endness可以用proj.arch.memory_endness查询,因此在对默认值没有把握时,请让endness=p.arch.memory_endness。
  • 写内存-state.memory.store(addr, data, size=None, condition=None, **kwargs)
文件访问:angr提供SimFile类用来模拟文件,通过将SimFile对象插入到状态的文件系统中,在使用angr分析程序时就可以使用该文件。
    filename = 'test.txt'
    simfile = angr.storage.SimFile(name=filename, content=data, size=0x40)
    state.fs.insert(filename, simfile)
上述指令能创建一个SimFile对象,文件名为test.txt,内容为data,输入的内容长度为0x40,单位为字节。之后,使用state.fs.insert方法,将SimFile对象插入到状态的文件系统中,在模拟运行程序时就可以使用这个文件了。

2.4、符号约束与求解

2.4.1、符号约束

每个符号本质上可以看做是一颗抽象语法树(AST), x = state.solver.BVS( ' x ' ,64 )生成的符号变量<BV64 x_42_64>可以看作是只有一层的AST,对它进行操作实际上是在扩展AST,这样的AST的构造规则如下:
  • 如果AST只有根节点的话,name它必定是符号变量BVS或位向量BV
  • 如果AST有多层,那么叶子节点为符号变量和位向量,其他节点为运算符
其中一个节点的左右孩子可以使用args来访问,节点本身存放的信息则使用op来访问。可以通过下面的例子来理解:
接下来对符号变量进行 比较判断:
结果不是一个位向量,而是一个符号化的布尔类型。
这些布尔类型的值可以通过is_true和is_false来判断,但对于上述有符号变量参与的布尔类型,它永远为false
此外需要注意的是,直接使用比较符号比较两个位向量,通常是默认不带符号的,例子如下:
符号约束与状态相关,或者说一个state除了包含内存、寄存器中的值这些信息外,还包含了符号约束(也就是要到达当前状态符号变量所必须满足的条件)。
除了运行程序,SM根据分支搜集起来的符号约束之外,用户 可以自动手动添加约束
此时,x必须满足大于5小于8,而y必须满足小于x。

2.4.2、符号求解

可以使用 state.solver.eval(x)来求解当前状态(即state)中的符号约束下x的值
此外,很明显能够看到,x应该是有多个值的,可以solver中的其他方法取出来:
  • solver.eval(x):给出表达式的一个可能解
  • sovler.eval_one(x):给出表达式的解,如果有多个解,将抛出错误
  • solver.eval_upto(x,n) 给出表达式的至多n个解
  • sovler.eval_taleast(x,n):给出表达式的n个解,如果解的数量少于n,则抛出错误
  • solver.eval_exact(x,n):给出表达式的n个解,如果解的个数不为n,则抛出错误
  • sovler.min(x) : 给出表达式的最小解
  • sovler.max(x) : 给出表达式的最大解
这些方法还有两个可省略的 参数
  • extra_constraints:可以作为约束进行求解,但不会被添加到当前状态
  • cast_to:将传递结果转换成指定数据类型, 目前只能是int和bytes ,例如state.solver.eval(state.solver.BVV(0x41424344, 32), cast_to=bytes) 将返回b'ABCD'
此外,如果将两个互相矛盾的约束加入到一个state当中,那么这个state就会被放到unsat这个stash里面,对这样的state进行求解会导致异常,可以使用 state.satisfiable()来检查是否有解。

2.5、模拟管理器(Simulation Manager)

proj.factory.entry_state()只是预设了程序分析开始时的状态,要分析程序就必须要让它到达下一个状态,这就需要模拟管理器的帮助(简称SM)。
使用以下指令能创建一个SM,它需要传入一个state或者state的列表作为参数
simgr = proj.factory.simulation_manager(state
SM中有许多列表,这些列表被称为stash它保存了处于某种状态的state,stash有如下几种:
  • active:此存储区包含默认情况下将逐步执行的状态,除非指定了备用存储区;
  • deadended:当一个状态由于某种原因不能继续执行时,例如没有合法指令,或者有非法指针,它就会进入死区隐藏;
  • pruned:与solve的策略有关,当发现一个不可解的节点后,其后面所有的节点都优化掉放在pruned里;
  • unconstrained:如果创建SM时启用了save_unconstrained,则被认定为不受约束的state会放在这,不受约束的state是指由用户数据或符号控制的指令指针(例如eip)
  • unsat:如果创建SM时启用了save_unsat,则被认为不可满足(即,它们有相互矛盾的约束,比如输入必须同时为“AAAA”和“BBBB”)的state会放在这里。
默认情况下,state会被存放在active中
stash中的state可以通过move()方法来转移,将fulter_func筛选出来的state从from_stash转移到to_stash:
simgr.move(from_stash='deadended', to_stash='more_then_50', filter_func=lambda s: '100' in s.posix.dumps(1))
stash是一个列表,可以使用python支持的方式去遍历其中的元素,也可以使用常见的列表操作。但angr提供了一种更高级的方式,在stash名字前加上one_,可以得到stash中的第一个状态,加上mp_,可以得到一个mulpyplexed版本的stash。
解释一下上面代码中的 posix.dumps
  • state.posix.dumps(0):表示到达当前状态所对应的程序输入
  • state.posix.dumps(1): 表示到达当前状态所对应的程序输出
上述代码就是将deadended中输出的字符串包含'100'的state转移到more_then_50这个stash中。
  
可以通过 step()方法来让处于active的state执行一个基本块, 这种操作不会改变state本身:
>>> state = p.factory.entry_state()
>>> simgr = p.factory.simgr(state)
>>> print(state.regs.rax, state.regs.rip)
<BV64 0x1c>  <BV64 0x4023c0>

>>> print(simgr.one_active)
<SimState @ 0x4023c0>

>>> simgr.step()
<SimulationManager with 1 active>

>>> print(simgr.one_active)
<SimState @ 0x529240>
>>> print(state.regs.rax, state.regs.rip)
<BV64 0x1c>  <BV64 0x4023c0>

2.6、Analyses

angr 预先打包了几个内置分析: Analyses - angr documentation (docs-angr-io.translate.goog)
>>> proj .analyses .         # Press TAB here in ipython to get an autocomplete-listing of everything:
作为一个非常简短的示例:以下是构建和使用快速控制流图的方法:
# Originally, when we loaded this binary it also loaded all its dependencies into the same virtual address space
# This is undesirable for most analysis.
>>> proj = angr.Project('/bin/true', auto_load_libs=False)
>>> cfg = proj.analyses.CFGFast()
<CFGFast Analysis Result at 0x7f03988ec7b8>

# cfg.graph is a networkx DiGraph full of CFGNode instances
# You should go look up the networkx APIs to learn how to use this!
>>> cfg.graph
<networkx.classes.digraph.DiGraph at 0x7f0398e7d6d8>
>>> len(cfg.graph.nodes())
1094

# To get the CFGNode for a given address, use cfg.get_any_node
>>> entry_node = cfg.get_any_node(proj.entry)
>>> len(list(cfg.graph.successors(entry_node)))
1

2.7、探索技术(explorer techniques)

Simulation Managers - angr documentation (docs-angr-io.translate.goog)
可以 使用 explorer() 方法去执行某个状态,直到找到目标指令或者active中没有状态为止,它有如下 参数
  • find:传入目标指令的 地址或地址列表,或者一个用于判断的函数,函数以state为形参,返回布尔值
  • avoid:传入要避免的指令的地址或地址列表,或者一个用于判断的函数,用于减少路径
此外还有一些搜索策略,之后会集中讲解, 默认使用DFS(深度优先搜索)
explorer找到的符合find的状态会被保存在 simgr.found 这个列表当中,可以遍历其中元素获取状态。

2.7.1、传入一个用于判断的函数

看一个简单的crackme示例: https://github.com/angr/angr-doc/tree/master/examples/CSCI-4968-MBE/challenges/crackme0x00a
# 1、首先,我们加载二进制文件
>>> proj = angr.Project('examples/CSCI-4968-MBE/challenges/crackme0x00a/crackme0x00a')

# 2、接下来,我们创建一个SimulationManager
>>> simgr = proj.factory.simgr()

# 3、现在,符号执行直到找到与我们的条件匹配的state(即“win”条件)
>>> simgr.explore(find=lambda s: b"Congrats" in s.posix.dumps(1))  # 传入一个用于判断的函数
<SimulationManager with 1 active, 1 found>

# 4、现在,我们可以将flag从该state中取出!
>>> s = simgr.found[0]
>>> print(s.posix.dumps(1))
b'Enter password: Congrats!\n'
>>> print(s.posix.dumps(1).decode())   # bytes转成str
Enter password: Congrats!

>>> flag = s.posix.dumps(0)
>>> print(flag)
g00dJ0B!

2.7.2、传入目标指令的地址

https://github.com/angr/angr-doc/blob/master/examples/CSCI-4968-MBE/challenges/crackme0x00a/solve.py
#!/usr/bin/env python3

# Author: David Manouchehri <manouchehri@protonmail.com>
# Modern Binary Exploitation
# http://security.cs.rpi.edu/courses/binexp-spring2015/

import angr

FIND_ADDR = 0x08048533 # mov dword [esp], str.Congrats_ ; [0x8048654:4]=0x676e6f43 LEA str.Congrats_ ; "Congrats!" @ 0x8048654
AVOID_ADDR = 0x08048554 # mov dword [esp], str.Wrong_ ; [0x804865e:4]=0x6e6f7257 LEA str.Wrong_ ; "Wrong!" @ 0x804865e

def main():
    proj = angr.Project('crackme0x00a', load_options={"auto_load_libs": False})
    sm = proj.factory.simulation_manager()
    sm.explore(find=FIND_ADDR, avoid=AVOID_ADDR)  
    return sm.found[0].posix.dumps(0).split(b'\0')[0] # stdin

def test():
    assert main() == b'g00dJ0B!'

if __name__ == '__main__':
    print(main())
​

2.8、符号执行

angr通常作为符号执行工具使用。
符号执行就是给程序传递一个符号而不是具体值,让这个符号伴随程序执行,当碰见分支时,符号会全部进入各个分支。angr会保存所有分支以及分支后的所有分支,并且在分支时,保存进入该分支时的判断条件,通常这些判断条件是对符号的约束。
angr运行到目标状态时,就可以调用求解器对一路上搜集到的约束进行求解,最终得到某个符号能到达当前状态的输入值。
例如,程序接收一个int类型的输入,当这个输入大于0小于5时,就会执行某条保存在该程序中,我们希望执行的指令(例如一个后门函数backdoor),具体而言如下图所示

angr会沿着分支按照某种策略(默认DFS)进行状态搜索,当达到目标状态(也就是backdoor能够执行的状态),此时angr已经收集了两个约束(x>0 以及x<=5),那么angr就通过这两个约束对x进行求解,解出来的x值就是能够让程序执行backdoor的输入。

三、官方示例

利用angr分析程序时有个一般的流程:
  1. 导入angr
  2. 导入二进制文件,创建Project
  3. 预设状态state
  4. 定义符号变量bvs并与二进制文件相联系
  5. 建立simgr,用于管理state
  6. 运行,探索满足条件的路径
  7. 约束求解获取执行结果
直接用了 https://github.com/angr/angr-doc/tree/master/examples/sym-write 里面提供的例子
1、issue.c
#include <stdio.h>

char u=0;
int main(void)
{
    int i, bits[2]={0,0};
    for (i=0; i<8; i++) {
        bits[(u&(1<<i))!=0]++;
    }
    if (bits[0]==bits[1]) {
        printf("you win!");
    }
    else {
        printf("you lose!");
    }
    return 0;
}
2、编译: gcc issue.c -o issue
3、编写python脚本
# -*- coding: utf-8 -*- 
import angr
import claripy
 
def main():
    proj = angr.Project('./issue',load_options={"auto_load_libs":False}) # 创建一个工程并导入二进制文件——issue,选择不自动加载依赖项(可选)
 
    #以下是对二进制文件的一些基本操作(可选)
    print(proj.arch)                # 架构 <Arch X86 (LE)>
    #proj.arch.memory_endess        # 大小端
    #proj.entry                     # 二进制程序入口点 134513520
    #proj.filename                  # 程序名称以及位置 './issue'
    #proj.loader                    # 是通过CLE模块将二进制对象加载并映射带单个内存空间  <Loaded issue, maps [0x8048000:0x8407fff]>
    #proj.loader.min_addr           # proj.loader 的低位地址 134512640
    #proj.loader.max_addr           # proj.loader 的高位地址 138444799

    #proj.loader.all_objects        # CLE加载的对象的完整列表
    #proj.loader.shared_objects     # 这是一个从共享对象名称到对象的字典映射
    #proj.loader.all_elf_objects    # 这是从ELF文件中加载的所有对象
    #proj.loader.all_pe_objects     # 加载一个windows程序
    #proj.loader.main_object             # 加载main对象
    #proj.loader.main_object.execstack   # 这个二进制文件是否有可执行堆栈
    #proj.loader.main_object.pic         # 这个二进制位置是否独立
    #proj.loader.extern_object      # 这是“externs对象”,我们使用它来为未解析的导入和angr内部提供地址
    #proj.loader.kernel_object      # 此对象用于为模拟的系统调用提供地址

    #proj.loader.find_object_containing(0x400000) # 获得对给定地址的对象的引用
   
    #以下是对确定的对象进行基本操作(可选)
    obj = proj.loader.main_object  # 指定main对象
    print(obj.entry)      # 获取地址
    #obj.min_addr, obj.max_addr # 地址的地位和高位
    #obj.segments#检索该对象的段
    #obj.sections#检索该对象的节
    #obj.find_segment_containing(obj.entry) # 通过地址获得单独的段
    #obj.find_section_containing(obj.entry)# 通过地址获得单独的节
    #addr = obj.plt['abort'] # 通过符号获取地址
    #obj.reverse_plt[addr]   # 通过地址获取符号
    #obj.linked_base
    #obj.mapped_base # 显示对象的预链接基以及CLE实际映射到内存中的位置
   
    state = proj.factory.entry_state(add_options={angr.options.SYMBOLIC_WRITE_ADDRESSES})
    # 返回一个simstate,SimState包含程序的内存、寄存器、文件系统数据以及符号变量等……任何可以通过执行更改的“实时数据”均在SimState。
    #.entry_state的替换:
    # .entry_state()构造一个准备在主二进制文件的入口点执行的状态。
    #.blank_state()构造了一个“空白石板”空白状态,其大部分数据未初始化。当访问未初始化的数据时,将返回一个不受约束的符号值。
    #.full_init_state()构造一个状态,该状态可以通过需要在主二进制的入口点之前运行的任何初始化程序执行,例如共享库构造函数或预初始化程序。当完成这些后,它将跳转到入口点。
    #.call_state()构造准备执行给定函数的状态。
 
    #通过state来访问一些寄存器的数值(可选)
    #state.regs.rip
    #state.regs.rax
    #state.regs.rbp = state.regs.rsp # 将寄存器rsp的值给rbp
    #注意:这儿采用的bitvectors,并不是python值,后面会说明python和bitvectors的转换
 
    u = claripy.BVS("u",8)#建立一个名称为u,8位宽的符号变量
    #claripy.BVS和state.solver.BVS有什么区别还不清楚,需要验证
    #可以通过.eval(A)的方法将A(bitvectors)转化位python int型
    #a = state.solver.FPV(3.2, state.solver.fp.FSORT_DOUBLE)#通过FPV来创建浮点型向量
    #raw_to_bv和raw_to_fp方法将位向量解释为浮点数,反之亦然:

    state.memory.store(0x601041,u) # 使用.memory.store(addr,val)方法将数据val保存在地址位addr的内存中,0x601041代表的是二进制文件中的某个变量的地址
 
    simgr = proj.factory.simulation_manager(state) # 使用的模拟管理器来管理状态或状态列表。state被组织成stash,可以forward, filter, merge, and move 。
    #simgr.active # 存储操作
    #simgr.step() # 执行一个基本块的符号执行,即就是所有状态向前推进一个基本块(类似单步操作,这儿是单块操作)
    #simgr.active # 以列表的形式更新存储
    #simgr.run() # .run()方法直接执行程序,直到一切结束,运行simgr会查看返回deadended数目
 
    while len(simgr.active)!=2:
        simgr.step()#循环运行直到通过.active来判断是否进入了分支,这是因为angr在遇到分支时,会将每个分支作为一个状态来保存。
 
    return simgr.active[0].state.se.eval(u) # 返回结果为win的u值,要是想返回lose的u值,将active[0]中0变为1即可
 
if __name__ == "__main__":
    print(repr(main()))  # repr() 函数将对象转化为供解释器读取的形式。
​
4、solve.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Author: xoreaxeaxeax
Modified by David Manouchehri <manouchehri@protonmail.com>
Original at https://lists.cs.ucsb.edu/pipermail/angr/2016-August/000167.html
The purpose of this example is to show how to use symbolic write addresses.
"""
import angr
import claripy

def main():
    p = angr.Project('./issue', load_options={"auto_load_libs": False})

    # By default, all symbolic write indices are concretized.
    state = p.factory.entry_state(add_options={angr.options.SYMBOLIC_WRITE_ADDRESSES})

    u = claripy.BVS("u", 8)
    state.memory.store(0x804a021, u)

    sm = p.factory.simulation_manager(state)

    def correct(state):
        try:
            return b'win' in state.posix.dumps(1)  # state.posix.dumps(1):表示到达当前状态所对应的程序输出
        except:
            return False
    def wrong(state):
        try:
            return b'lose' in state.posix.dumps(1)
        except:
            return False

    sm.explore(find=correct, avoid=wrong)  # 探索

    # Alternatively, you can hardcode the addresses.
    # sm.explore(find=0x80484e3, avoid=0x80484f5)

    return sm.found[0].solver.eval_upto(u, 256)

def test():
    good = set()
    for u in range(256):
        bits = [0, 0]
        for i in range(8):
            bits[u&(1<<i)!=0] += 1
        if bits[0] == bits[1]:
            good.add(u)

    res = main()
    assert set(res) == good


if __name__ == '__main__':
    print(repr(main()))

四、参考

angr_ctf——从0学习angr(一):angr简介与核心概念 - Uiharu - 博客园 (cnblogs.com)

angr_ctf——从0学习angr(二):状态操作和约束求解 - Uiharu - 博客园 (cnblogs.com)

angr_ctf——从0学习angr(三):Hook与路径爆炸 - Uiharu - 博客园 (cnblogs.com)

angr_ctf——从0学习angr(四):库操作和溢出漏洞利用 - Uiharu - 博客园 (cnblogs.c

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/112265.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python的web自动化学习(五)Selenium的隐式等待(元素定位)

引言&#xff1a; WebDriver隐式等待是一种全局性的等待方式&#xff0c;它会在查找元素时设置一个固定的等待时间。当使用隐式等待时&#xff0c;WebDriver会在查找元素时等待一段时间&#xff0c;如果在等待时间内找到了元素&#xff0c;则立即执行下一步操作&#xff1b;如果…

Windows下多Chrome谷歌浏览器版本共存

场景 某些年代久远的 WEB 应用&#xff0c;必须在指定的浏览器或版本才能正常运行&#x1f602;&#xff0c;此时就需要多个版本 chrome 浏览器共存。 解决方案 下载指定版本 可以从 https://www.chromedownloads.net/ 下载需要的版本&#xff0c;此处下载的是87.0.4280.14…

【MySQL索引与优化篇】数据库设计实操(含ER模型)

数据库设计实操&#xff08;含ER模型&#xff09; 文章目录 数据库设计实操&#xff08;含ER模型&#xff09;1. ER模型1.1 概述1.2 建模分析1.3 ER 模型的细化1.4 ER 模型图转换成数据表1. 一个实体转换成一个数据库表2. 一个多对多的关系转换成一个数据表3. 通过外键来表达1对…

VS2019 C# mysql数据库使用EF

mysql 安装mysql-8.0.18-winx64 mysql-connector-net-8.0.18.msi mysql数据库.net开发驱动&#xff0c; 要在工程中引入connector安装后目录中的mysql.data.dll;如果直接在nutget中下载mysql.data.dll&#xff0c;那么就不用下载.net开发驱动包 mysql-for-visualstudio-1.…

oracel处理XML时,报ORA-31011、ORA-19202。

原字段为clob&#xff0c; 查询 SELECT XMLTYPE(字段) FROM TABLE_A报错如下&#xff1a; ORA-31011: XML 语法分析失败 ORA-19202: XML 处理 LPX-00217: invalid character 12 (U000C) Error at line 1559时出错 ORA-06512: 在 "SYS.XMLTYPE", line 272 ORA-0651…

嵌入式到底如何理解呢?

今日话题&#xff0c;嵌入式到底如何理解呢&#xff1f;以我个人的理解&#xff0c;可以用一个客观的比喻来描述&#xff0c;就是将某个系统嵌入到特定的环境中&#xff0c;以实现特定的功能。这个过程包括将现实世界中的人、物的意图和逻辑关系&#xff0c;通过计算和运算的方…

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A:基于计算机视觉的坑洼道路检测和识别 python 代码解析

【2023年MathorCup高校数学建模挑战赛-大数据竞赛】赛道A&#xff1a;基于计算机视觉的坑洼道路检测和识别 python 代码解析 1 题目 坑洼道路检测和识别是一种计算机视觉任务&#xff0c;旨在通过数字图像&#xff08;通常是地表坑洼图像&#xff09;识别出存在坑洼的道路。这…

栅栏密码fence cypher

f{_wi3_hlyk_lnceaonr_cirgu04fep} flag{you_kn0w_r4il_f3nce_cipher}

Android NDK开发详解之调试和性能分析的系统跟踪概览

Android NDK开发详解之调试和性能分析的系统跟踪概览 系统跟踪指南 “系统跟踪”就是记录短时间内的设备活动。系统跟踪会生成跟踪文件&#xff0c;该文件可用于生成系统报告。此报告有助于您了解如何最有效地提升应用或游戏的性能。 有关进行跟踪和性能分析的全面介绍&#x…

JavaScript基础

JavaScript是一种轻量级、解释型的Web开发语言&#xff0c;获得了所有浏览器的支持&#xff0c;是目前广泛使用的编程语言之一。本章将简要介绍JavaScript基本语法和用法。 1、编写JavaScript脚本 在HTML页面中嵌入JavaScript脚本需要使用<script>标签&#xff0c;在&l…

跟着Nature Communications学作图:纹理柱状图+添加显著性标签!

&#x1f4cb;文章目录 复现图片设置工作路径和加载相关R包读取数据集数据可视化计算均值和标准差 计算均值和标准差方差分析组间t-test 图a可视化过程图b可视化过程合并图ab 跟着「Nature Communications」学作图&#xff0c;今天主要通过复刻NC文章中的一张主图来巩固先前分享…

MySQL数据库入门到精通——运维篇(1)

MySQL数据库入门到精通——运维篇&#xff08;1&#xff09; 1. 日志1.1 错误日志1.2 二进制日志1.3 查询日志1.4 慢查询日志 2. 主从复制2.1 主从复制的概述2.2 主从复制的原理2.3 主从复制的搭建2.3.1 服务器准备2.3.2 主库配置2.3.3 从库配置2.3.4 测试 1. 日志 在任何一种…

力扣:147. 对链表进行插入排序(Python3)

题目&#xff1a; 给定单个链表的头 head &#xff0c;使用 插入排序 对链表进行排序&#xff0c;并返回 排序后链表的头 。 插入排序 算法的步骤: 插入排序是迭代的&#xff0c;每次只移动一个元素&#xff0c;直到所有元素可以形成一个有序的输出列表。每次迭代中&#xff0c…

C++归并排序算法的应用:计算右侧小于当前元素的个数

题目 给你一个整数数组 nums &#xff0c;按要求返回一个新数组 counts 。数组 counts 有该性质&#xff1a; counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。 示例 1&#xff1a; 输入&#xff1a;nums [5,2,6,1] 输出&#xff1a;[2,1,1,0] 解释&#xff1a; 5 …

Latex排版SIGGRAPH总结(持续总结中...)

本文学习总结自&#xff1a;How to use the ACM SIGGRAPH / TOG LaTeX template 相关文件&#xff1a;百度网盘 首先解压 “my paper” 中的文件&#xff0c;并用Latex打开mypaper.tex. 多行连等公式 \begin{equation}表示编号公式&#xff0c;\[ \]表示无编号公式 无编号\b…

折纸达珠峰高度(forwhile循环)

对折0.1mm厚度的纸张多少次&#xff0c;高度可达珠峰高度8848180mm。 (本笔记适合熟悉循环和列表的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《 python 完全自学教程》&#xff0c;不仅…

数据库面试题整理

目录 MySQL事务隔离级别有哪几种&#xff1f;MySQL的常用的存储引擎有哪些&#xff1f;特点是什么&#xff0c;分别适合什么场景下使用MySQL有数据缓存吗&#xff1f;原理是怎么样的&#xff1f;InnoDB的缓冲池默认是开启的吗&#xff1f;基本原理是什么&#xff1f;会有脏数据…

【MATLAB第81期】基于MATLAB的LSTM长短期记忆网络预测模型时间滞后解决思路(更新中)

【MATLAB第81期】基于MATLAB的LSTM长短期记忆网络预测模型时间滞后解决思路&#xff08;更新中&#xff09; 在LSTM预测过程中&#xff0c;极易出现时间滞后&#xff0c;类似于下图&#xff0c;与一个以上的样本点结果错位&#xff0c;产生滞后的效果。 在建模过程中&#xf…

7 款用于训练 AI 模型的合成数据工具

什么是合成数据&#xff1f; 合成数据是计算机模拟或算法生成的注释信息&#xff0c;作为真实世界数据的替代品。换句话说&#xff0c;合成数据是在数字世界中创建的&#xff0c;而不是从现实世界中收集或测量的。 合成数据的用例 为机器人开发软件只是合成数据的众多用例之…

el-tabel表格加个多选框

<template><div><el-checkbox v-model"checked" :disabled"checkedDis" change"onAllSelectChange">多选框</el-checkbox>点击多选框&#xff0c;禁用列表复选框<el-table ref"multipleTable" :data"…