Angr学习(一)

以前只是简单套用angr模板来解一些简单的题目,现在希望能深入了解一下angr, 所以做个记录。

注意,本文不是直接翻译,而会根据个人感觉进行删减!

本文主要参考自https://docs.angr.io/

===========================================================

下面是一些小tips,可以跳过。

  • angr的调试信息记录
import logging
logging.getLogger('angr').setLevel('DEBUG')

其中getLogger()的参数是希望记录的最高级别模块,如果只想记录某个模块的信息,比如angr.analyses.cfg,可以:

logging.getLogger('angr.analyses').setLevel('INFO')
  • 发现bug的方式

由于angr比较复杂,有时候会遇到一些bug,比如把一个指针符号化,这时可能会导致状态空间爆炸,为了避免于此,可以考虑设置状态检查。

========================================================

核心概念

--------------------------------------------------------------------------------------------------

顶层接口

使用angr的第一步通常都是加载一个二进制文件。

>>> import angr
>>> proj = angr.Project('/bin/true')

一个Project是你在angr中控制的基础,你可以基于它来进行可执行文件的分析与仿真。

  • 基本属性

一个project有多个属性:CPU架构,文件入口地址,文件名...

>>> import monkeyhex # this will format numerical results in hexadecimal
>>> proj.arch
<Arch AMD64 (LE)>
>>> proj.entry
0x401670
>>> proj.filename
'/bin/true'

arch是archinfo.Arch对象的一个实例,表示程序指令架构,在本例中是AMD64小端,一般会关心 arch.bits, arch.bytes,arch.name与arch.memory_endness。

  • 加载器

angr使用CLE模块将二进制文件映射到虚拟地址空间,得到的结果作为属性loader,可以通过它查看二进制文件加载的库以及加载地址空间的一些信息

>>> proj.loader
<Loaded true, maps [0x400000:0x5004000]>

>>> proj.loader.shared_objects # may look a little different for you!
{'ld-linux-x86-64.so.2': <ELF Object ld-2.24.so, maps [0x2000000:0x2227167]>,
 'libc.so.6': <ELF Object libc-2.24.so, maps [0x1000000:0x13c699f]>}

>>> proj.loader.min_addr
0x400000
>>> proj.loader.max_addr
0x5004000

>>> proj.loader.main_object  # we've loaded several binaries into this project. Here's the main one!
<ELF Object true, maps [0x400000:0x60721f]>

>>> proj.loader.main_object.execstack  # sample query: does this binary have an executable stack?
False
>>> proj.loader.main_object.pic  # sample query: is this binary position-independent?
True
  • factory

angr中有许多使用时需要一个实例化的project的类,可以使用project.factory而不用把project到处传递。

project.factory.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()                          # pretty-print a disassembly to stdout
0x401670:       xor     ebp, ebp
0x401672:       mov     r9, rdx
0x401675:       pop     rsi
0x401676:       mov     rdx, rsp
0x401679:       and     rsp, 0xfffffffffffffff0
0x40167d:       push    rax
0x40167e:       push    rsp
0x40167f:       lea     r8, [rip + 0x2e2a]
0x401686:       lea     rcx, [rip + 0x2db3]
0x40168d:       lea     rdi, [rip - 0xd4]
0x401694:       call    qword ptr [rip + 0x205866]

>>> block.instructions                  # how many instructions are there?
0xb
>>> block.instruction_addrs             # what are the addresses of the instructions?
[0x401670, 0x401672, 0x401675, 0x401676, 0x401679, 0x40167d, 0x40167e, 0x40167f, 0x401686, 0x40168d, 0x401694]

此外,你可以通过block得到代码块的其他表示形式

>>> block.capstone                       # capstone disassembly
<CapstoneBlock for 0x401670>
>>> block.vex                            # VEX IRSB (that's a python internal address, not a program address)
<pyvex.block.IRSB at 0x7706330>

states

Project对象只代表程序的一个初始化镜像,当你使用angr来执行时,你在使用一个表示模拟程序状态的一个特殊对象SimState

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

一个SimState包括程序内存,寄存器,文件系统数据...任何在运行中可以被改变的数据都可以在state中找到,如利用state.regs与state.mem:

>>> 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>

这些都是bitvector变量,每个bitvector都有.length属性来表示其位数。bitvector可以与python中的整数相互转换

>>> 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

你可以把这些bitvector赋给寄存器或内存,或者直接赋值整数,它将被自动转为合适大小的bitvector

>>> 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>

其中mem的使用方式为:

  1. 使用array[index]的形式来指定地址;
  2. 使用.<type>来指定内存需要解释为的类型(char, short, int, long, size_t, uint8_t, uint16_t...);
  3. 通过它,你可以存入一个值(bitvector或python int),.resolved来获取其bitvector值,.concrete来获取python int值。

最后,如果你读更多的寄存器,你可能会碰到

>>> state.regs.rdi
<BV64 reg_48_11_64{UNINITIALIZED}>

这也是一个64位的bitvector,但他没有一个具体的值。作为代替,他有一个名字。这被称为符号变量,是符号执行的基础。

仿真管理器

如果说state表示程序运行中的一个点,那么一定有一种方式来到达下一个点。一个仿真管理器是进行模拟/执行的基本接口。

首先,我们创建将要使用的仿真管理器。构造函数可以接受状态或状态列表。

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

一个仿真管理器可以包括数个状态存储,初始默认存储为active。

现在,让我们进行一些执行。

simgr.step()

注意,我们没有改变原本的state!您可以安全地使用单个state作为多轮执行的“基础”。

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

分析

angr预先打包了几个内置的分析,您可以使用这些分析从程序中提取一些有趣的信息。在这里,他们是:

>>> proj.analyses.            # Press TAB here in ipython to get an autocomplete-listing of everything:
 proj.analyses.BackwardSlice        proj.analyses.CongruencyCheck      proj.analyses.reload_analyses       
 proj.analyses.BinaryOptimizer      proj.analyses.DDG                  proj.analyses.StaticHooker          
 proj.analyses.BinDiff              proj.analyses.DFG                  proj.analyses.VariableRecovery      
 proj.analyses.BoyScout             proj.analyses.Disassembly          proj.analyses.VariableRecoveryFast  
 proj.analyses.CDG                  proj.analyses.GirlScout            proj.analyses.Veritesting           
 proj.analyses.CFG                  proj.analyses.Identifier           proj.analyses.VFG                   
 proj.analyses.CFGEmulated          proj.analyses.LoopFinder           proj.analyses.VSA_DDG               
 proj.analyses.CFGFast              proj.analyses.Reassembler

举一个非常简单的例子:下面是如何构造和使用一个快速的控制流程图:

# 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 0x2d85130>

# 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 0x2da43a0>
>>> len(cfg.graph.nodes())
951

# 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)))
2

通过api文档你将了解更多用法

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值