CTF——angr使用学习记录

CTF——angr使用学习记录,第1张

CTF——angr使用学习记录

 初始化:

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

载入文件,auto_load_libs设置为false,大概是启用angr自带的函数定义,避免一些逻辑过于复杂,机器跑不出来。

import claripy 

arg = claripy.BVS(‘arg1′, 8)

我们使用claripy这个模块来定义抽象的数据,claripy的BVS函数可以创建一个指定长度的抽象数据,BVS函数要求两个参数,第一个参数为变量名,第二个参数为变量长度。符号化变量;

state = p.factory.entry_state(args=argv) 

entry_state()函数接收一个list作为程序的命令行参数并且返回程序入口的状态

state.posix.files[0].read_from(1)

 表示从标准输入读取一个字节 

st = p.factory.blank_state(addr=)

创建一个 blank_state 对象,这个对象里面很多东西都是未初始化的,当程序访问未初始化的数据时,会返回一个不受约束的符号量

sm = p.factory.simulation_manager(state)

表示从state这个地址开始执行

sm.explore(find=0x400676,avoid=[0x40073d])

当探索到find地方,就是想要的答案,avoid就是需要避免的地方。

sm.found.posix.dumps(1) 

 最后的输出

sm.found.posix.dumps(0) 

found的输入

sm.found.solver.eval(arg1,cast_to = str)

使用约束求解引擎获取命令行参数,类型为字符串
相关使用方法:

寄存器的符号化:

import angr
import claripy
p = angr.Project('')
 
init_addr = 0x08048980  #scanf的下一条指令地址
state = p.factory.blank_state(addr=init_addr)  #创建一个状态,并将该地址赋给它,也就是跳过输入,直接执行下一条指令,此处使用.blank_state()而不再是.entry_state()
 
#定义三个位向量,即三个输入
p1 = claripy.BVS('p1',32)   #32位寄存器(符号向量)
p2 = claripy.BVS('p2',32)
p3 = claripy.BVS('p3',32)
 
state.regs.eax = p1    #.regs.eax 访问eax这个寄存器
state.regs.ebx = p2
state.regs.edx = p3
sm = p.factory.simulation_manager(state)
def good(state):
    return b'Good Job.' in state.posix.dumps(1)
def bad(state):
    return b'Try again.' in state.posix.dumps(1)
sm.explore(find = good, avoid = bad)
if sm.found:
    find_state = sm.found[0]
    flag1 = find_state.solver.eval(p1)#将探索成功时的第一个输入赋给flag1,下面两个类似
    flag2 = find_state.solver.eval(p2)
    flag3 = find_state.solver.eval(p3)
    print('{:x} {:x} {:x}'.format(flag1,flag2,flag3))

栈的符号化:

import angr
import claripy
import sys
def main(argv):
    p = angr.Project('')
    def good(state):
        return b'Good Job.' in state.posix.dumps(1)
    def bad(state):
        return b'Try again.' in state.posix.dumps(1)
    #创建开始状态
    start_addr = 0x08048697  #scanf之后的地址,之所以是这儿,是因为上一行'add  esp,10h'的作用是清理scanf的栈空间
    state = p.factory.blank_state(addr=start_addr)

    #因为跳过了scanf函数,所以我们需要模拟它的整个 *** 作(对栈的 *** 作)
    #state.stack_push(state.regs.ebp)
    state.regs.ebp = state.regs.esp    #初始化ebp、esp
    space = 0x8                         #一个变量占4个空间,所以两个就是8
    state.regs.esp -= space   #模拟scanf时栈的情况(剔除了对空间的浪费,即只开辟了两个变量的空间)
    ps1 = claripy.BVS('ps1',32)       #符号化两个输入
    ps2 = claripy.BVS('ps2',32)
    state.stack_push(ps1)     #将符号化的输入入栈
    state.stack_push(ps2)
    #至此对scanf的模拟过程就完成了
    
    #创建模拟管理器
    simulation = p.factory.simgr(state)
    
    #开始探索
    simulation.explore(find=good,avoid=bad)
    if simulation.found:
        solution_state = simulation.found[0]
        flag1 = solution_state.solver.eval(ps1)
        flag2 = solution_state.solver.eval(ps2)
        print('{} {}'.format(flag1,flag2))
  

if __name__ == '__main__':
    main(sys.argv)

内存的符号化:

import angr
import claripy
import sys

def main(argv):
    path = argv[1]
    p = angr.Project(path)

    start_addr = 0x08048601
    state = p.factory.blank_state(addr=start_addr)

    #创建四个位向量,模拟输入
    p1 = claripy.BVS('p1',64)   #一个变量输入8个字符,一个字符8位bit,总共64bit
    p2 = claripy.BVS('p2',64)
    p3 = claripy.BVS('p3',64)
    p4 = claripy.BVS('p4',64)

    #开始对输入进行模拟
    state.memory.store(0x0A1BA1C0,p1)#让四个位向量指向输入在内存中的地址
    state.memory.store(0x0A1BA1C8,p2)
    state.memory.store(0x0A1BA1D0,p3)
    state.memory.store(0x0A1BA1D8,p4)
    #scanf模拟结束

    sm = p.factory.simgr(state) #创建模拟管理器 
    
    def good(state):
        return b'Good Job.' in state.posix.dumps(1)
    def bad(state):
        return b'Try again.' in state.posix.dumps(1)

    sm.explore(find = good,avoid = bad)

    if sm.found:
        solution_state = sm.found[0]
        flag1 = solution_state.solver.eval(p1,cast_to=bytes)
        flag2 = solution_state.solver.eval(p2,cast_to=bytes)
        flag3 = solution_state.solver.eval(p3,cast_to=bytes)
        flag4 = solution_state.solver.eval(p4,cast_to=bytes)
        print("{} {} {} {}".format(flag1.decode('utf-8'),flag2.decode('utf-8'),flag3.decode('utf-8'),flag4.decode('utf-8')))
    else:
        print("NO")

if __name__ == '__main__':
    main(sys.argv)

 hook:

在 angr 中使用 hook 来把指定地址的二进制代码替换为 python 代码。angr 在模拟执行程序时,执行每一条指令前会检测该地址处是否已经被 hook ,如果是就不执行这条语句,转而执行hook 时指定的 python 处理代码。

#!/usr/bin/env python
# coding=utf-8
import angr
import claripy
def hook_demo(state):
    state.regs.eax = 0
    state.regs.ebx = 0xdeadbeef
p = angr.Project("./examples/sym-write/issue", load_options={"auto_load_libs": False})
p.hook(addr=0x08048485, hook=hook_demo, length=2) # 使用 p.hook 把 0x08048485 处的 2 字节的指令 为 hook_demo,之后执行 0x08048485就会去执行 hook_demo
state = p.factory.blank_state(addr=0x0804846B, add_options={"SYMBOLIC_WRITE_ADDRESSES"})#创建一个 state , 因为要往内存里面设置 符号量 ( BVS ),设置SYMBOLIC_WRITE_ADDRESSES
u = claripy.BVS("u", 8)
state.memory.store(0x0804A021, u) #新建一个 8 位长度的符号量,并把它存到 0x0804A021 (全局变量 u 的位置)
sm = p.factory.simgr(state)
sm.explore(find=0x080484DB)
st = sm.found[0]
print hex(st.se.eval(st.regs.ebx))

 p.hook(addr=0x08048485, hook=hook_demo, length=2)

  • addr 为待 hook 指令的地址

  • hook 为 hook 的处理函数,在执行到 addr 时,会执行 这个函数,同时把 当前的 state 对象作为参数传递过去

  • length 为 待 hook 指令的长度,在 执行完 hook 函数以后,angr 需要根据 length 来跳过这条指令,执行下一条指令

报错: 

The main binary is a position-independent executable. It is being loaded with a base address of 0x400000.

我们后面程序的地址要加上基址偏移。

参考:

​​​​​​大佬的代码

大佬的博客

大佬的博客

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5495922.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-13
下一篇 2022-12-12

发表评论

登录后才能评论

评论列表(0条)

保存