ANGR初识

news2024/11/28 15:53:43
首页: 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.coloradmin.cn/o/1160155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c语言从入门到实战——函数

函数 前言1. 函数的概念2. 库函数2.1 标准库和头⽂件2.2 库函数的使用方法2.2.1 功能2.2.2 头文件包含2.2.3 实践2.2.4 库函数文档的一般格式 3. 自定义函数3.1 函数的语法形式3.2 函数的举例 4. 形参和实参4.1 实参4.2 形参4.3 实参和形参的关系 5. return 语句6. 数组做函数参…

为什么需要Code Review?

1. Code Review 是什么&#xff1f; 代码审查&#xff08;Code Review&#xff09;是软件开发过程中对代码进行系统性检查和评审的一项活动。它是指团队成员之间相互检查彼此编写的代码&#xff0c;以确保代码质量、可读性和符合编码标准等。 2. Code Review 的必要性 ● 提…

音视频rtsp rtmp gb28181在浏览器上的按需拉流

按需拉流是从客户视角来看待音视频的产品功能&#xff0c;直观&#xff0c;好用&#xff0c;为啥hls flv大行其道也是这个原因&#xff0c;不过上述存在的问题是延迟没法降到实时毫秒级延迟&#xff0c;也不能随心所欲的控制。通过一段时间的努力&#xff0c;结合自己闭环技术栈…

设计融合_ c#

单例模式 using System; namespace DesignIntegration{ public class TimeManager{ private static TimeManager _instance; private static readonly Object locker new Object(); private TimeManager() { } public static TimeManager…

图像特征Vol.1:计算机视觉特征度量|第二弹:【统计区域度量】

目录 一、前言二、统计区域度量2.1&#xff1a;图像矩特征2.1.1&#xff1a;原始矩/几何矩2.1.2&#xff1a;中心距2.1.3&#xff1a;归一化的中心矩2.1.4&#xff1a;不变矩——Hu矩2.1.5&#xff1a;OpenCv实现矩特征及其应用 2.2&#xff1a;点度量特征2.3&#xff1a;全局直…

Simulink HDL--如何生成Verliog代码

Simulink生成HDL的方法可以快速设计出工程&#xff0c;并结合FPGA验证&#xff0c;相比于手写HDL代码虽然存在代码优化不足的问题。但是方法适合做工程的快速验证和基本框架搭建。本文将介绍Simulink HDL生成Verliog代码的基本操作 1、逻辑分析仪功能 Simulink生成HDL前需要通…

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

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

在Blender中使用python脚本

目录 使用&#xff1a; 快速创建10个圆球&#xff1a; python代码&#xff1a; 形态键快速创建和重命名&#xff1a; 可能存在的问题 python代码&#xff1a; Blender 强大的特点是其对 Python 脚本的完美支持。 这意味着可以编写 Python 脚本来自动化各种任务和操作&am…

密码学 - SHA-2

实验八 SHA-2 1.实验目的 熟悉SHA – 2算法的运行过程&#xff0c;能够使用C语言编写实现SHA-2算法程序&#xff0c;增加对摘要函数的理解。 2、实验任务 &#xff08;1&#xff09;理解SHA-2轮函数的定义和常量的定义。 &#xff08;2&#xff09;利用VC语言实现SHA-2算…

[ACTF2023]复现

MDH 源题&#xff1a; from hashlib import sha256 from secret import flagr 128 c 96 p 308955606868885551120230861462612873078105583047156930179459717798715109629 Fp GF(p)def gen():a1 random_matrix(Fp, r, c)a2 random_matrix(Fp, r, c)A a1 * a2.Treturn…

【湘粤鄂车牌】

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

虹科示波器 | 汽车免拆检修 | 2013款大众途观车发动机加速无力

一、故障现象 一辆2013款大众途观车&#xff0c;搭载CGM发动机&#xff0c;累计行驶里程约为12.6万km。车主进厂反映&#xff0c;发动机加速无力。 二、故障诊断 接车后试车&#xff0c;发动机怠速运转正常&#xff1b;原地将加速踏板踩到底&#xff0c;发动机转速最高只能达到…

做Web自动化前,你必须掌握的几个技能!

学习web自动化的前提条件&#xff1a;手工测试&#xff08;了解各种测试的知识&#xff09;、学习编程语言、学习Web基础、学习自动化测试工具 、学习自动化测试框架 、需要掌握前端的一些知识&#xff0c;无论学习语言还是前端知识&#xff0c;都是为了接下来的脚本和框架做铺…

antd5上传图片显示405解决

antd5上传图片&#xff0c;默认使用上传方式会调用本地的接口。 405 Method Not Allowed 状态码 405 Method Not Allowed 表明服务器禁止了使用当前 HTTP 方法的请求。 Upload {...props}beforeUpload{(file) > {//自定义上传图片的逻辑//最后返回falsereturn false }} &…

Python JSON 使用指南:解析和转换数据

JSON 是一种用于存储和交换数据的语法。JSON 是文本&#xff0c;使用 JavaScript 对象表示法编写。 Python 中的 JSON Python 有一个内置的 json 包&#xff0c;可用于处理 JSON 数据。 示例&#xff1a;导入 json 模块&#xff1a; import json解析 JSON - 从 JSON 转换为…

【UE】从UI中拖拽生成物体

目录 效果 步骤 一、准备工作 二、创建UI 三、创建Actor 四、拖拽生成Actor的逻辑 效果 步骤 一、准备工作 1. 首先新建一个第三人称模板工程 2. 新建一个游戏模式基础&#xff0c;这里命名为“BP_UIGameMode” 在世界场景设置中设置游戏模式覆盖为“BP_UIGameMode”…

代码随想录算法训练营第6天|242.有效的字母异位词 349. 两个数组的交集 202. 快乐数 1. 两数之和

JAVA代码编写 242. 有效的字母异位词 给定两个字符串 *s* 和 *t* &#xff0c;编写一个函数来判断 *t* 是否是 *s* 的字母异位词。 **注意&#xff1a;**若 *s* 和 *t* 中每个字符出现的次数都相同&#xff0c;则称 *s* 和 *t* 互为字母异位词。 示例 1: 输入: s "a…

pytorch复现4_Resnet

ResNet在《Deep Residual Learning for Image Recognition》论文中提出&#xff0c;是在CVPR 2016发表的一种影响深远的网络模型&#xff0c;由何凯明大神团队提出来&#xff0c;在ImageNet的分类比赛上将网络深度直接提高到了152层&#xff0c;前一年夺冠的VGG只有19层。Image…

为什么需要企业云性能监控?

云计算已经成为企业信息技术的核心组成部分&#xff0c;提供了灵活性、可扩展性和成本效益。然而&#xff0c;随着企业的应用程序和数据迁移到云中&#xff0c;监控和管理云性能变得至关重要。在这篇文章中&#xff0c;我们将探讨企业云性能监控的重要性是什么! 为什么需要企业…

瞄准一款好用到爆的在线数据库设计工具Itbuilder,被惊艳了!

在线数据库设计工具都不陌生&#xff0c;这与日常开发工作息息相关&#xff0c;每天都会用到。一款好用的在线数据库设计工具可以帮我们省去很多事情&#xff0c;Itbuilder在线数据库设计工具简单工作台&#xff0c;有利于新手操作&#xff0c;丰富的功能&#xff0c;可以满足开…