Angr學習


Angr學習

聽聞Angr好久了,自己愣是沒嘗試過,這次網鼎杯有道vm虛擬機題目, 本來是手動盤的, 聽說angr一把梭,故借此機會學習下angr

angr我覺得比較重要的有三個點

  1. 起始找尋位置
  2. 需要到達的路徑
  3. 避免走的路徑

Angr 基本介紹

  • angr 來源於CGC項目,最初用於自動攻防。
  • 平台無關(platform-agnostic)的二進制分析框架
  • ( Computer Security Lab ) UCSB,Shellphish

Angr可以干什么?

  • Disassembly and intermediate-representation lifting
  • Program instrumentation
  • Symbolic execution
  • Control-flow analysis
  • Data-dependency analysis
  • Value-set analysis (VSA)

Angr安裝

Angr文件下載地址 :

https://github.com/angr/angr

官方安裝指導:

https://docs.angr.io/introductory-errata/install

angr 是一個 python 庫,所以它必須安裝到你的 python 環境中才能使用。它是為 Python 3 構建的:由於迫在眉睫的 EOL 和我們團隊的小規模,Python 2 支持是不可行的。

我們強烈推薦使用python 虛擬環境來安裝和使用 angr。angr 的幾個依賴項(z3、pyvex)需要從它們的原始代碼分叉的本地代碼庫,如果你已經安裝了 libz3 或 libVEX,你絕對不想用我們的覆蓋官方共享對象。一般來說,不要指望支持在 virtualenv 之外安裝 angr 所引起的問題。

所有 python 依賴項都應該由 pip 和/或 setup.py 腳本處理。但是,您將需要構建一些 C 才能從這里到最后,因此您需要一個良好的構建環境以及 python 開發頭文件。在依賴項安裝過程的某個時刻,您將安裝 python 庫 cffi,但是(至少在 linux 上)它不會運行,除非您安裝操作系統的 libffi 包。

# dependency
sudo apt-get install python3-dev libffi-dev build-essential virtualenvwrapper
# install
# we'd better use it in virtual environment
mkvirtualenv --python=$(which python3) angr && pip install angr
# more see https://docs.angr.io/INSTALL.html

Docker install

# install docker
curl -sSL https://get.docker.com/ | sudo sh

# pull the docker image
sudo docker pull angr/angr

# run it
sudo docker run -it angr/angr

ubuntu 16.04 安裝

virtualenvwrapper是一個Python虛擬環境,使用虛擬環境的主要原因是angr會修改libz3和libVEX,可能會影響其他程序的正常使用。

新建一個Python虛擬機環境:

$ export WORKON_HOME=~/Envs
$ mkdir -p $WORKON_HOME
$ source /usr/share/virtualenvwrapper/virtualenvwrapper.sh
$ mkvirtualenv angr

由於我使用的是MAC OSX環境,所以直接使用命令:

python3 -m pip install angr

4YMv5R.png

angr Management安裝

angr Management文件下載地址 :

https://github.com/angr/angr-management

直接安裝版本下載地址:

https://github.com/angr/angr-management/releases

4YQFqe.png

From PyPI

python3 -m pip install angr-management

4YQnRP.png

啟動命令

python3 -m angrmanagement

4YQlqg.png

4YQ3ZQ.png

基本操作

Project

用來加載 binary,是使用 angr 的基礎。

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

基本屬性

查看對應binary的基本屬性

  • arch

  • entry

  • filename, absolute filename of the binary

  • loader,

    • pic,位置獨立
    • execstack,棧是否可以執行
    • min_addr
    • max_addr
    • main_object,Project 加載的二進制文件,即主二進制文件。
    • shared_objects,共享目標文件信息,名字以及映射地址
>>> proj.arch
<Arch AMD64 (LE)>
>>> proj.entry
0x401670
>>> proj.filename
'/bin/true'
>>> 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

加載選項

基本選項

  • auto_load_libs 是否自動加載程序的依賴。
  • except_missing_libs,當加載一個程序的依賴不成功時,就會產生異常。
  • force_load_libs,強制加載的庫。
  • skip_libs,防止加載的庫。
  • custom_ld_path,優先的共享庫的搜尋路徑。

高級選項

  • main_ops 是選項到選項值的映射。
  • lib_opts 是庫名到一個字典的映射,這個字典將名字映射到其對應的值上。
angr.Project(main_opts={'backend': 'ida', 'custom_arch': 'i386'}, lib_opts={'libc.so.6': {'backend': 'elf'}})

Loader

loader (CLE Load Everything,CLE)用於將一個 binary 加載到對應的虛擬地址空間。每類 binary 都有對應的加載器后端 (cle.Backend)。比如 cle.ELF 用來加載ELF文件。此外,angr 加載的 binary 都有自己的內存空間,但是並不是內存空間中每一個對象都會有對應的binary。

主加載對象信息

我們可以得出 loader 加載的主對象的基本信息

# This is the "main" object, the one that you directly specified when loading the project
>>> proj.loader.main_object
<ELF Object true, maps [0x400000:0x60105f]>
>>> obj = proj.loader.main_object

# The entry point of the object
>>> obj.entry
0x400580

>>> obj.min_addr, obj.max_addr
(0x400000, 0x60105f)

# Retrieve this ELF's segments and sections
>>> obj.segments
<Regions: [<ELFSegment offset=0x0, flags=0x5, filesize=0xa74, vaddr=0x400000, memsize=0xa74>,
           <ELFSegment offset=0xe28, flags=0x6, filesize=0x228, vaddr=0x600e28, memsize=0x238>]>
>>> obj.sections
<Regions: [<Unnamed | offset 0x0, vaddr 0x0, size 0x0>,
           <.interp | offset 0x238, vaddr 0x400238, size 0x1c>,
           <.note.ABI-tag | offset 0x254, vaddr 0x400254, size 0x20>,
            ...etc

# You can get an individual segment or section by an address it contains:
>>> obj.find_segment_containing(obj.entry)
<ELFSegment offset=0x0, flags=0x5, filesize=0xa74, vaddr=0x400000, memsize=0xa74>
>>> obj.find_section_containing(obj.entry)
<.text | offset 0x580, vaddr 0x400580, size 0x338>

# Get the address of the PLT stub for a symbol
>>> addr = obj.plt['__libc_start_main']
>>> addr
0x400540
>>> obj.reverse_plt[addr]
'__libc_start_main'

# Show the prelinked base of the object and the location it was actually mapped into memory by CLE
>>> obj.linked_base
0x400000
>>> obj.mapped_base
0x400000

其它加載對象信息

# All loaded objects
>>> proj.loader.all_objects
[<ELF Object fauxware, maps [0x400000:0x60105f]>,
 <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>,
 <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>,
 <ELFTLSObject Object cle##tls, maps [0x3000000:0x300d010]>,
 <KernelObject Object cle##kernel, maps [0x4000000:0x4008000]>,
 <ExternObject Object cle##externs, maps [0x5000000:0x5008000]>


# This is a dictionary mapping from shared object name to object
>>> proj.loader.shared_objects
{ 'libc.so.6': <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>
  'ld-linux-x86-64.so.2': <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>}

# Here's all the objects that were loaded from ELF files
# If this were a windows program we'd use all_pe_objects!
>>> proj.loader.all_elf_objects
[<ELF Object true, maps [0x400000:0x60105f]>,
 <ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>,
 <ELF Object ld-linux-x86-64.so.2, maps [0x2000000:0x22241c7]>]

# Here's the "externs object", which we use to provide addresses for unresolved imports and angr internals
>>> proj.loader.extern_object
<ExternObject Object cle##externs, maps [0x5000000:0x5008000]>

# This object is used to provide addresses for emulated syscalls
>>> proj.loader.kernel_object
<KernelObject Object cle##kernel, maps [0x4000000:0x4008000]>

# Finally, you can to get a reference to an object given an address in it
>>> proj.loader.find_object_containing(0x400000)
<ELF Object true, maps [0x400000:0x60105f]>

符號以及重定位信息

我們還可以使用 CLE 來操作二進制文件中的符號。

  • 查找符號,傳入符號名或者對應的地址。
>>> malloc = proj.loader.find_symbol('malloc')
>>> malloc
<Symbol "malloc" in libc.so.6 at 0x1054400>
  • 基本符號信息,符號名,所屬者,它的地址
>>> malloc.name
'malloc'

>>> malloc.owner_obj
<ELF Object libc.so.6, maps [0x1000000:0x13c42bf]>

# .rebased_addr is its address in the global address space. This is what is shown in the print output.
>>> malloc.rebased_addr
0x1054400
# .linked_addr is its address relative to the prelinked base of the binary. This is the address reported in, for example, readelf(1)
>>> malloc.linked_addr
0x54400
# .relative_addr is its address relative to the object base. This is known in the literature (particularly the Windows literature) as an RVA (relative virtual address).
>>> malloc.relative_addr
0x54400
  • 符號的導入導出信息
>>> malloc.is_export
True
>>> malloc.is_import
False

# On Loader, the method is find_symbol because it performs a search operation to find the symbol.
# On an individual object, the method is get_symbol because there can only be one symbol with a given name.
>>> main_malloc = proj.loader.main_object.get_symbol("malloc")
>>> main_malloc
<Symbol "malloc" in true (import)>
>>> main_malloc.is_export
False
>>> main_malloc.is_import
True
>>> main_malloc.resolvedby
<Symbol "malloc" in libc.so.6 at 0x1054400>

后端

backend name  description  requires custom_arch?
elf  Static loader for ELF files based on PyELFTools  no
pe  Static loader for PE files based on PEFile  no
mach-o  Static loader for Mach-O files. Does not support dynamic linking or rebasing.  no
cgc  Static loader for Cyber Grand Challenge binaries  no
backedcgc  Static loader for CGC binaries that allows specifying memory and register backers  no
elfcore  Static loader for ELF core dumps  no
ida  Launches an instance of IDA to parse the file  yes
blob  Loads the file into memory as a flat image  yes

Symbolic Function

默認情況下,angr 會嘗試將程序中調用的庫函數用自己模擬的函數來代替,這些函數一般對應的對象為SimProcedures 。我們可以從 angr.SIM_PROCEDURES 中找到所有的函數。這些函數的命名規范為package name(libc, posix, win32, etc...)+function name。

需要注意的是

  • auto_load_libsTrue 的時候,真正的庫函數會被執行。

hook

hook 指定的函數,使得angr執行自己給定的函數。

>>> stub_func = angr.SIM_PROCEDURES['stubs']['ReturnUnconstrained'] # this is a CLASS
>>> proj.hook(0x10000, stub_func())  # hook with an instance of the class

>>> proj.is_hooked(0x10000)            # these functions should be pretty self-explanitory
True
>>> proj.unhook(0x10000)
>>> proj.hooked_by(0x10000)
<ReturnUnconstrained>

# length keyword argument to make execution jump some number of bytes forward after your hook finishes.
>>> @proj.hook(0x20000, length=5)
... def my_hook(state):
...     state.regs.rax = 1

>>> proj.is_hooked(0x20000)
True

factory

原因

  • 很多 angr 中的類需要使用到 project 才能實例化,使用 factory可以避免傳遞 project 對象。
  • factory 也可以提供一些方便的構造器。

方法

  • block(addr)
    • 提取給定地址的基本塊,返回一個塊對象。
    • 需要注意的是Angr分析程序的單元是基本。
  • bitvector
  • 寄存器使用位向量來描述

基本塊

屬性

  • instructions

    • 對應基本塊的指令的個數。
  • instructions_addrs

  • 基本塊每個

  • capstone

  • capstone block對象

  • vex

方法

  • pp()
    • 漂亮地輸出對象基本塊的匯編代碼。

state

project 只是給出程序最初鏡像的信息,state 可以給出模擬程序執行到某條指令時的進程的具體狀態。在 angr 中,則使用 SimState 來描述。

  • state 中所有的信息均使用位向量.
  • 可以直接向寄存器和內存中存儲整數,angr 會將其轉換為位向量。

預置執行狀態

我們可以根據 factory 來設置程序執行到指定地址的默認狀態。

  • .blank_state() constructs a "blank slate" blank state, with most of its data left uninitialized. When accessing uninitialized data, an unconstrained symbolic value will be returned.
  • .entry_state() constructs a state ready to execute at the main binary's entry point.
  • .full_init_state() constructs a state that is ready to execute through any initializers that need to be run before the main binary's entry point, for example, shared library constructors or preinitializers. When it is finished with these it will jump to the entry point.
  • .call_state() constructs a state ready to execute a given function.
    • you should call it with .call_state(addr, arg1, arg2, ...), where addr is the address of the function you want to call and argN is the Nth argument to that function, either as a python integer, string, or array, or a bitvector.

基本狀態信息

寄存器

  • state.regs.rip

內存

模式:state.mem[addr].type.xxx

  • 要訪問的內存地址
  • type指定相應地址應該被解釋成的類型。
  • 空,可直接存儲數據。
  • 使用.resolved 來把數據輸出為位向量。
  • 使用.concrete 來把數據輸出為int值。
>>> import angr
>>> proj = angr.Project('/bin/true')
>>> state = proj.factory.entry_state()

# copy rsp to rbp
>>> state.regs.rbp = state.regs.rsp

# store rdx to memory at 0x1000
>>> state.mem[0x1000].uint64_t = state.regs.rdx

# dereference rbp
>>> state.regs.rbp = state.mem[state.regs.rbp].uint64_t.resolved

# add rax, qword ptr [rsp + 8]
>>> state.regs.rax += state.mem[state.regs.rsp + 8].uint64_t.resolved

文件系統

執行

基本執行

>>> proj = angr.Project('examples/fauxware/fauxware')
>>> state = proj.factory.entry_state()
>>> while True:
...     succ = state.step()
...     if len(succ.successors) == 2:
...         break
...     state = succ.successors[0]

>>> state1, state2 = succ.successors
>>> state1
<SimState @ 0x400629>
>>> state2
<SimState @ 0x400699>

低層次內存訪問

  • 默認大端序存儲。
>>> s = proj.factory.blank_state()
>>> s.memory.store(0x4000, s.solver.BVV(0x0123456789abcdef0123456789abcdef, 128))
>>> s.memory.load(0x4004, 6) # load-size is in bytes
<BV48 0x89abcdef0123>
>>> import archinfo
>>> s.memory.load(0x4000, 4, endness=archinfo.Endness.LE)
<BV32 0x67453201>

State Option

# Example: enable lazy solves, an option that causes state satisfiability to be checked as infrequently as possible.
# This change to the settings will be propagated to all successor states created from this state after this line.
>>> s.options.add(angr.options.LAZY_SOLVES)

# Create a new state with lazy solves enabled
>>> s = proj.factory.entry_state(add_options={angr.options.LAZY_SOLVES})

# Create a new state without simplification options enabled
>>> s = proj.factory.entry_state(remove_options=angr.options.simplification)

solver

solver 基本就是一個約束求解引擎。

操作位向量

位向量與 python 中的整形的轉換。

  • 將給定數值轉換為指定位數的位向量。
# 64-bit bitvectors with concrete values 1 and 100
>>> one = state.solver.BVV(1, 64)
>>> one
 <BV64 0x1>
>>> one_hundred = state.solver.BVV(100, 64)
>>> one_hundred
 <BV64 0x64>

# create a 27-bit bitvector with concrete value 9
>>> weird_nine = state.solver.BVV(9, 27)
>>> weird_nine
<BV27 0x9>
  • 位向量運算,位向量的位數必須一樣。
>>> one + one_hundred
<BV64 0x65>

# You can provide normal python integers and they will be coerced to the appropriate type:
>>> one_hundred + 0x100
<BV64 0x164>

# The semantics of normal wrapping arithmetic apply
>>> one_hundred - one*200
<BV64 0xffffffffffffff9c>

# use extend to extent the length of bitvector
# also there is sign_extend
>>> weird_nine.zero_extend(64 - 27)
<BV64 0x9>
>>> one + weird_nine.zero_extend(64 - 27)
<BV64 0xa>
  • 位向量符號
# Create a bitvector symbol named "x" of length 64 bits
>>> x = state.solver.BVS("x", 64)
>>> x
<BV64 x_9_64>
>>> y = state.solver.BVS("y", 64)
>>> y
<BV64 y_10_64>
  • 混合位向量符號的運算
>>> x + one
<BV64 x_9_64 + 0x1>

>>> (x + one) / 2
<BV64 (x_9_64 + 0x1) / 0x2>

>>> x - y
<BV64 x_9_64 - y_10_64>

AST 查看

>>> tree = (x + 1) / (y + 2)
>>> tree
<BV64 (x_9_64 + 0x1) / (y_10_64 + 0x2)>
>>> tree.op
'__div__'
>>> tree.args
(<BV64 x_9_64 + 0x1>, <BV64 y_10_64 + 0x2>)
>>> tree.args[0].op
'__add__'
>>> tree.args[0].args
(<BV64 x_9_64>, <BV64 0x1>)
>>> tree.args[0].args[1].op
'BVV'
>>> tree.args[0].args[1].args
(1, 64)

符號約束

  • 比較默認情況下按照無符號進行比較。
>>> x == 1
<Bool x_9_64 == 0x1>
>>> x == one
<Bool x_9_64 == 0x1>
>>> x > 2
<Bool x_9_64 > 0x2>
>>> x + y == one_hundred + 5
<Bool (x_9_64 + y_10_64) == 0x69>
>>> one_hundred > 5
<Bool True>
>>> one_hundred > -5
<Bool False>
  • 如何判斷
>>> yes = one == 1
>>> no = one == 2
>>> maybe = x == y
>>> state.solver.is_true(yes)
True
>>> state.solver.is_false(yes)
False
>>> state.solver.is_true(no)
False
>>> state.solver.is_false(no)
True
>>> state.solver.is_true(maybe)
False
>>> state.solver.is_false(maybe)
False

約束求解

基本步驟

  • 添加約束
  • 求解
>>> state.solver.add(x > y)
>>> state.solver.add(y > 2)
>>> state.solver.add(10 > x)
>>> state.solver.eval(x)
4

# get a fresh state without constraints
>>> state = proj.factory.entry_state()
>>> input = state.solver.BVS('input', 64)
>>> operation = (((input + 4) * 3) >> 1) + input
>>> output = 200
>>> state.solver.add(operation == output)
>>> state.solver.eval(input)
0x3333333333333381
# If we add conflicting or contradictory constraints
>>> state.solver.add(input < 2**32)
>>> state.satisfiable()
False

Simulation Managers

我們用 state 來描述程序執行到某個地址時程序的具體狀態。同時,我們使用 Simulation Managers 來管理程序如何由一個狀態到另一個狀態。它是 angr 中模擬控制程序的重要接口。

創建模擬管理器

>>> simgr = proj.factory.simgr(state) # TODO: change name before merge
<SimulationManager with 1 active>

查看狀態信息

對於一個管理器來說,它可以存儲多個狀態,自然也可以查看每個狀態的具體信息。其中 active 狀態由我們默認傳入的狀態初始化得到。

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

執行

執行一個基本塊,這並不會修改最初的時候傳入的狀態。

>>> simgr.step()

# Step until the first symbolic branch
>>> while len(simgr.active) == 1:
...    simgr.step()

>>> simgr
<SimulationManager with 2 active>
>>> simgr.active
[<SimState @ 0x400692>, <SimState @ 0x400699>]

# Step until everything terminates
>>> simgr.run()
>>> simgr
<SimulationManager with 3 deadended>

Stash Management

  • 轉移stash
>>> simgr.move(from_stash='deadended', to_stash='authenticated', filter_func=lambda s: 'Welcome' in s.posix.dumps(1))
>>> simgr
<SimulationManager with 2 authenticated, 1 deadended>
  • 列舉stash
>>> for s in simgr.deadended + simgr.authenticated:
...     print hex(s.addr)
0x1000030
0x1000078
0x1000078
# If you prepend the name of a stash with one_, you will be given the first state in the stash. 
>>> simgr.one_deadended
<SimState @ 0x1000030>
#  If you prepend the name of a stash with mp_, you will be given a mulpyplexed version of the stash.
>>> simgr.mp_authenticated
MP([<SimState @ 0x1000078>, <SimState @ 0x1000078>])
>>> simgr.mp_authenticated.posix.dumps(0)
MP(['\x00\x00\x00\x00\x00\x00\x00\x00\x00SOSNEAKY\x00',
    '\x00\x00\x00\x00\x00\x00\x00\x00\x00S\x80\x80\x80\x80@\x80@\x00'])

angr-0ctf_momo

1.需要逆向找到約束求解的三個條件

dx, dword ptr [edx*4 + 0x81fe260]
al, byte ptr [0x81fe6e0]
dl, byte ptr [0x81fe6e4]

2.需要掌握“逆向MoVfuscator編譯程序”能力

1.使用qira+ida進行人工分析,
2.或使用“movfuscator的反混淆器”
3.使用Makefile+二進制插樁
4.angr求解是建立在對程序逆向的理解程度

網鼎 vm

起始找尋位置,我們可以選main, 也可以選特定驗證函數, 但是需要注意的是, 數據的找尋

需要到達的路徑為: success, flag這種成功的路徑

避免走的路徑便為失敗路徑

所以我們需要在ida找着三條路.

起初我選了vm_operate這個函數作為起始找尋位置,但是沒有成功, 后面我選了main函數

需要到達的路徑便為輸出flag處

避免走的路徑則是exit這種

最后執行即可

#!/usr/bin/env python
# coding=utf-8
import angr

p = angr.Project('./signal.exe')
good = (0x0040179E)
bad = (0x004016E6)

start = 0x00401760

state = p.factory.blank_state(addr=start)
simgr = p.factory.simulation_manager(state)
simgr.explore(find=good, avoid=bad)
result = simgr.found[0]

for i in range(3):
    print (result.posix.dumps(i))

不用一會就拿到flag了

757515121f3d478

angr-ctf-0

學習angr的基本使用

import angr
import sys
binary = './angr_0'
good = 0x401329

p = angr.Project(binary)
state = p.factory.entry_state()
simulation = p.factory.simgr(state)
simulation.explore(find=good)
if simulation.found:
    solution_state = simulation.found[0]
    for i in range(3):
        print (solution_state.posix.dumps(i))
else:
    raise Exception("Could not find the solution")

angr-ctf-1

這道題需要找到avoid, 這里有個小技巧,就是跑進avoid里去選

4YlFS0.png

不然外部有多個avoid的話, 你要寫很多個avoid

import angr
import sys
binary = './angr_1'

good = (0x4012D0)
bad = (0x40127B)

p = angr.Project(binary)
state = p.factory.entry_state()
simulation = p.factory.simgr(state)
simulation.explore(find=good, avoid=bad)
if simulation.found:
    solution_state = simulation.found[0]
    for i in range(3):
        print (solution_state.posix.dumps(i))
else:
    raise Exception("Could not find the solution")

angr-ctf-2

該節學習angr的found_condition, 也就是說條件可以為一個函數,而不一定非得是一個地址, 這里自己寫good跟bad函數, 然后讓其通過判斷, 還記得文件描述符嗎

0 代表標准輸入

1 代表標准輸出

所以state.posix.dumps(1)才是輸出,我們要輸出包含good job

import angr
import sys
binary = './angr_2'

good = (0x401245,0x4013C7)
bad = (0x4013B0)

def is_good(state):
    return b'Good Job' in state.posix.dumps(1)
def is_bad(state):
    return b'Try again' in state.posix.dumps(1)
    
p = angr.Project(binary)
state = p.factory.entry_state()
simulation = p.factory.simgr(state)
simulation.explore(find=is_good, avoid=is_bad)
if simulation.found:
    solution_state = simulation.found[0]
    for i in range(3):
        print (solution_state.posix.dumps(i))
else:
    raise Exception("Could not find the solution")

angr-ctf-3

這道題由於angr不能處理scanf多參數問題, 這里需要自己設置寄存器的值,同時設置輸出形式

這道題有坑, 不能用函數形式的判斷, 我用函數形式拿不到結果,用地址可以跑出來

同時因為我們沒有用scanf,所以需要用solver求解

import angr
import sys
import claripy

binary = './angr_3'
start = 0x4015BD
good = (0x401621)
bad = (0x40160B)

def is_good(state):
    print(state.posix.dumps(1))
    return b'Good Job' in state.posix.dumps(sys.stdout.fileno())
def is_bad(state):
    print(state.posix.dumps(1))
    return b'Try again' in state.posix.dumps(sys.stdout.fileno())
    
p = angr.Project(binary)
state = p.factory.blank_state(addr=start)
bits = 32
password0 = claripy.BVS('password0', bits)
password1 = claripy.BVS('password1', bits)
password2 = claripy.BVS('password2', bits)
state.regs.eax = password0
state.regs.ebx = password1
state.regs.edx = password2


simulation = p.factory.simgr(state)
simulation.explore(find=good, avoid=bad)
if simulation.found:
    solution_state = simulation.found[0]
    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution2 = solution_state.se.eval(password2)
    solution = ' '.join(map('{:x}'.format, [ solution0, solution1, solution2]))
    print(solution)
else:
    raise Exception("Could not find the solution")

angr-ctf-4

這道題修復棧就可以了, 我們知道常規函數調用通常是

push ebp

mov ebp,esp

sub esp, 0x2333

所以我們偽造一個棧給他, 然后再將參數傳遞

import angr
import sys
import claripy

binary = './angr_4'
start = 0x4013FB
good = (0x401447)
bad = (0x401433)

def is_good(state):
    print(state.posix.dumps(1))
    return b'Good Job' in state.posix.dumps(sys.stdout.fileno())
def is_bad(state):
    print(state.posix.dumps(1))
    return b'Try again' in state.posix.dumps(sys.stdout.fileno())
    
p = angr.Project(binary)
state = p.factory.blank_state(addr=start)
state.stack_push(state.regs.ebp)
state.regs.ebp = state.regs.esp
state.regs.esp -= 0x8

bits = 32
password0 = claripy.BVS('password0', bits)
password1 = claripy.BVS('password1', bits)
state.stack_push(password0)
state.stack_push(password1)


simulation = p.factory.simgr(state)
simulation.explore(find=good, avoid=bad)
if simulation.found:
    solution_state = simulation.found[0]
    solution0 = solution_state.se.eval(password0)
    solution1 = solution_state.se.eval(password1)
    solution = ' '.join(map('{:}'.format, [ solution0, solution1]))
    print(solution)
else:
    raise Exception("Could not find the solution")


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM