深入浅出angr(六)
字数 793 2025-08-05 19:10:02

深入浅出angr(六)教学文档

前言

angr是一款强大的二进制分析框架,特别适合解决混淆的逆向工程题目。当面对以下情况时,angr能发挥巨大优势:

  • 程序混淆严重,手动去混淆困难
  • 动态调试耗时耗力
  • 需要快速找到程序执行路径

快速寻找find和avoid地址的技巧

方法概述

在解决混淆题目时,确定正确的执行路径(find)和需要避免的路径(avoid)是关键。对于大量混淆的程序,手动分析每个分支效率低下,可以通过以下方法高效获取avoid地址:

  1. 二进制模式匹配:通过搜索特定指令的机器码来识别分支点
  2. 地址计算:将文件偏移转换为内存地址

实战示例:hackcon2016_angry-reverser

e = open('./yolomolo', 'rb').read()
avoids = []

index = 0
while True:
    index = e.find(b'\xB9\x00\x00\x00\x00', index+1)  # 搜索mov ecx, 0指令
    if index == -1:
        break
    addr = 0x400000 + index  # 计算内存地址
    avoids.append(addr)

完整angr解决方案

import angr
import claripy

def main():
    flag = claripy.BVS('flag', 20*8, explicit_name=True)
    buf = 0x606000
    crazy = 0x400646
    find = 0x405a6e
    
    # 获取avoid地址列表
    e = open('./yolomolo', 'rb').read()
    avoids = []
    index = 0
    while True:
        index = e.find(b'\xB9\x00\x00\x00\x00', index+1)
        if index == -1: break
        avoids.append(0x400000 + index)

    # 设置angr项目
    proj = angr.Project('./yolomolo')
    state = proj.factory.blank_state(addr=crazy, add_options={angr.options.LAZY_SOLVES})
    
    # 设置输入约束
    state.memory.store(buf, flag, endness='Iend_BE')
    state.regs.rdi = buf
    for i in range(19):
        state.solver.add(flag.get_byte(i) >= 0x30)
        state.solver.add(flag.get_byte(i) <= 0x7f)

    # 执行符号执行
    simgr = proj.factory.simulation_manager(state)
    simgr.explore(find=find, avoid=avoids)
    
    # 输出结果
    found = simgr.found[0]
    return found.solver.eval(flag, cast_to=bytes)

线性程序解决方案

angr特别适合解决线性结构的程序,如ekopartyctf2016_rev250。

关键步骤

  1. 环境设置:处理动态库依赖

    LD_LIBRARY_PATH=./ ./FUck_binary
    
  2. 识别关键地址

    • find地址:0x403a40
    • avoid地址:0x403ABA, 0x403A7E等
  3. 自动获取avoid地址

avoids = []
def get_avoids():
    file_bytes = open('./FUck_binary','rb').read()
    index = 0
    while True:
        index = file_bytes.find(b'\x66\x90', index+1)  # 搜索nop指令
        if index == -1: break
        if index < 0x3a7e: continue  # 过滤无效地址
        avoids.append(0x400000 + index)

完整解决方案

import angr
import claripy

BUF_LEN = 100

def main():
    # 获取avoid地址
    get_avoids()
    
    # 设置angr项目
    p = angr.Project('FUck_binary')
    flag = claripy.BVS('flag', BUF_LEN*8)
    state = p.factory.entry_state(stdin=flag)
    
    # 设置输入约束(可打印字符)
    for c in flag.chop(8):
        state.solver.add(state.solver.And(c <= '~', c >= ' '))
    
    # 执行符号执行
    ex = p.factory.simulation_manager(state)
    ex.explore(find=0x403a40, avoid=avoids)
    
    # 输出结果
    found = ex.found[0]
    print(found.posix.dumps(0))

关键技巧总结

  1. 输入约束设置

    • 限制为可打印字符:state.solver.And(c <= '~', c >= ' ')
    • 限制ASCII范围:flag.get_byte(i) >= 0x30flag.get_byte(i) <= 0x7f
  2. 调试技巧

    import logging
    logging.getLogger('angr.sim_manager').setLevel(logging.DEBUG)
    
  3. 地址转换

    • 文件偏移到内存地址:0x400000 + index
  4. 指令模式识别

    • mov ecx, 0\xB9\x00\x00\x00\x00
    • nop指令 → \x66\x90

适用场景

angr最适合以下类型的逆向题目:

  1. 线性结构的程序
  2. 混淆严重的程序
  3. 分支众多的程序
  4. 需要快速求解的程序

通过合理使用angr的符号执行能力,可以大幅提高逆向工程效率,避免手动分析的繁琐过程。

深入浅出angr(六)教学文档 前言 angr是一款强大的二进制分析框架,特别适合解决混淆的逆向工程题目。当面对以下情况时,angr能发挥巨大优势: 程序混淆严重,手动去混淆困难 动态调试耗时耗力 需要快速找到程序执行路径 快速寻找find和avoid地址的技巧 方法概述 在解决混淆题目时,确定正确的执行路径(find)和需要避免的路径(avoid)是关键。对于大量混淆的程序,手动分析每个分支效率低下,可以通过以下方法高效获取avoid地址: 二进制模式匹配 :通过搜索特定指令的机器码来识别分支点 地址计算 :将文件偏移转换为内存地址 实战示例:hackcon2016_ angry-reverser 完整angr解决方案 线性程序解决方案 angr特别适合解决线性结构的程序,如ekopartyctf2016_ rev250。 关键步骤 环境设置 :处理动态库依赖 识别关键地址 : find地址:0x403a40 avoid地址:0x403ABA, 0x403A7E等 自动获取avoid地址 : 完整解决方案 关键技巧总结 输入约束设置 : 限制为可打印字符: state.solver.And(c <= '~', c >= ' ') 限制ASCII范围: flag.get_byte(i) >= 0x30 和 flag.get_byte(i) <= 0x7f 调试技巧 : 地址转换 : 文件偏移到内存地址: 0x400000 + index 指令模式识别 : mov ecx, 0 → \xB9\x00\x00\x00\x00 nop指令 → \x66\x90 适用场景 angr最适合以下类型的逆向题目: 线性结构的程序 混淆严重的程序 分支众多的程序 需要快速求解的程序 通过合理使用angr的符号执行能力,可以大幅提高逆向工程效率,避免手动分析的繁琐过程。