angr 入门介绍(二)
字数 1035 2025-08-24 20:49:22

Angr符号执行框架入门教程(二)

2.1 使用Angr解决Enigma 2017 Crackme 0

目标分析

我们需要解决Enigma 2017 Crackme 0,通过分析可以确定:

  • 红色路径:执行了wrong函数,不感兴趣
  • 绿色路径:感兴趣的代码路径
  • 蓝色部分:Angr开始执行分析的指令地址

关键点:

  1. 可以丢弃所有到达wrong函数的状态
  2. 需要避免0x8048670路径(需要提供输入否则关闭程序)
  3. fromhex()函数返回0的状态是我们感兴趣的
  4. 输入字符串指针在调用fromhex()前被压栈

解决方案脚本

import angr
import claripy

def main():
    path_to_binary = "./crackme_0"
    project = angr.Project(path_to_binary)
    
    # 关键地址定义
    start_addr = 0x8048692  # "PUSH EAX"指令地址
    avoid_addr = [0x8048541, 0x8048624, 0x8048599, 0x8048585, 0x8048670]
    success_addr = 0x80486d3  # 成功路径地址
    
    # 初始化状态
    initial_state = project.factory.blank_state(addr=start_addr)
    
    # 创建符号位向量
    password_length = 32  # 32字节字符串
    password = claripy.BVS("password", password_length * 8)
    fake_password_address = 0xffffcc80  # 任意栈地址
    
    # 存储符号位向量并设置eax
    initial_state.memory.store(fake_password_address, password)
    initial_state.regs.eax = fake_password_address
    
    # 开始仿真
    simulation = project.factory.simgr(initial_state)
    simulation.explore(find=success_addr, avoid=avoid_addr)
    
    # 输出结果
    if simulation.found:
        solution_state = simulation.found[0]
        solution = solution_state.solver.eval(password, cast_to=bytes)
        print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
    else:
        print("[-] Bro, try harder.")

if __name__ == '__main__':
    main()

3. 处理符号内存和动态内存

05_angr_symbolic_memory

程序分析

  1. 接收4个8字节字符串输入
  2. 输入保存在:[0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8]
  3. complex_function函数循环操作字符串
  4. 处理后字符串与"NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN"比较
  5. 匹配则打印"Good Job"

解决方案脚本

import angr
import claripy
import sys

def main():
    path_to_binary = "05_angr_symbolic_memory"
    project = angr.Project(path_to_binary)
    
    # 从scanf后的指令开始
    start_address = 0x8048601
    initial_state = project.factory.blank_state(addr=start_address)
    
    # 创建4个64位符号位向量
    password0 = claripy.BVS('password0', 64)
    password1 = claripy.BVS('password1', 64)
    password2 = claripy.BVS('password2', 64)
    password3 = claripy.BVS('password3', 64)
    
    # 存储到内存地址
    password0_address = 0xa1ba1c0
    initial_state.memory.store(password0_address, password0)
    initial_state.memory.store(password0_address + 0x8, password1)
    initial_state.memory.store(password0_address + 0x10, password2)
    initial_state.memory.store(password0_address + 0x18, password3)
    
    # 开始仿真
    simulation = project.factory.simgr(initial_state)
    
    def is_successful(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return b'Good Job.\n' in stdout_output
    
    def should_abort(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return b'Try again.\n' in stdout_output
    
    simulation.explore(find=is_successful, avoid=should_abort)
    
    if simulation.found:
        solution_state = simulation.found[0]
        solution0 = solution_state.solver.eval(password0, cast_to=bytes)
        solution1 = solution_state.solver.eval(password1, cast_to=bytes)
        solution2 = solution_state.solver.eval(password2, cast_to=bytes)
        solution3 = solution_state.solver.eval(password3, cast_to=bytes)
        solution = solution0 + b" " + solution1 + b" " + solution2 + b" " + solution3
        print("[+] Success! Solution is: {}".format(solution.decode("utf-8")))
    else:
        raise Exception('Could not find the solution')

if __name__ == '__main__':
    main()

06_angr_symbolic_dynamic_memory

程序分析

  1. 使用malloc分配两个9字节buffer
  2. 输入两个8字节字符串
  3. 循环8次处理字符串
  4. 每次迭代用complex_function"加密"字符串
  5. 处理后字符串与内置字符串比较

关键点

  • 使用伪造地址代替malloc返回的地址
  • 关系转换:
    • BEFORE: buffer0 -> malloc地址0 -> 字符串0
    • AFTER: buffer0 -> 伪造地址0 -> 符号位向量0

解决方案脚本

import angr
import claripy
import sys

def main():
    path_to_binary = "./06_angr_symbolic_dynamic_memory"
    project = angr.Project(path_to_binary)
    
    # 从scanf后的指令开始
    start_address = 0x8048699
    initial_state = project.factory.blank_state(addr=start_address)
    
    # 创建两个64位符号位向量
    password0 = claripy.BVS('password0', 64)
    password1 = claripy.BVS('password1', 64)
    
    # 伪造堆地址和指针地址
    fake_heap_address0 = 0xffffc93c
    pointer_to_malloc_memory_address0 = 0xabcc8a4
    fake_heap_address1 = 0xffffc94c
    pointer_to_malloc_memory_address1 = 0xabcc8ac
    
    # 存储伪造地址和符号位向量
    initial_state.memory.store(pointer_to_malloc_memory_address0, 
                             fake_heap_address0, 
                             endness=project.arch.memory_endness)
    initial_state.memory.store(pointer_to_malloc_memory_address1, 
                             fake_heap_address1, 
                             endness=project.arch.memory_endness)
    initial_state.memory.store(fake_heap_address0, password0)
    initial_state.memory.store(fake_heap_address1, password1)
    
    # 开始仿真
    simulation = project.factory.simgr(initial_state)
    
    def is_successful(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return b'Good Job.\n' in stdout_output
    
    def should_abort(state):
        stdout_output = state.posix.dumps(sys.stdout.fileno())
        return b'Try again.\n' in stdout_output
    
    simulation.explore(find=is_successful, avoid=should_abort)
    
    if simulation.found:
        solution_state = simulation.found[0]
        solution0 = solution_state.solver.eval(password0, cast_to=bytes)
        solution1 = solution_state.solver.eval(password1, cast_to=bytes)
        print("[+] Success! Solution is: {0} {1}".format(
            solution0.decode('utf-8'), solution1.decode('utf-8')))
    else:
        raise Exception('Could not find the solution')

if __name__ == '__main__':
    main()

关键知识点总结

  1. 符号位向量创建:使用claripy.BVS(name, size)创建符号变量
  2. 内存操作
    • state.memory.store(address, value)存储值到内存
    • 动态内存可以伪造地址代替malloc返回地址
  3. 寄存器操作state.regs.<register> = value设置寄存器值
  4. 路径探索
    • simulation.explore(find, avoid)探索路径
    • 可以通过输出内容判断成功/失败路径
  5. 解具体值state.solver.eval(symbol, cast_to=bytes)将符号值转为具体值

这些技术可以应用于各种逆向工程和漏洞分析场景,特别是需要自动化求解输入条件的情况。

Angr符号执行框架入门教程(二) 2.1 使用Angr解决Enigma 2017 Crackme 0 目标分析 我们需要解决Enigma 2017 Crackme 0,通过分析可以确定: 红色路径:执行了wrong函数,不感兴趣 绿色路径:感兴趣的代码路径 蓝色部分:Angr开始执行分析的指令地址 关键点: 可以丢弃所有到达wrong函数的状态 需要避免0x8048670路径(需要提供输入否则关闭程序) fromhex()函数返回0的状态是我们感兴趣的 输入字符串指针在调用fromhex()前被压栈 解决方案脚本 3. 处理符号内存和动态内存 05_ angr_ symbolic_ memory 程序分析 接收4个8字节字符串输入 输入保存在:[ 0xA1BA1C0, 0xA1BA1C8, 0xA1BA1D0, 0xA1BA1D8 ] complex_ function函数循环操作字符串 处理后字符串与"NJPURZPCDYEAXCSJZJMPSOMBFDDLHBVN"比较 匹配则打印"Good Job" 解决方案脚本 06_ angr_ symbolic_ dynamic_ memory 程序分析 使用malloc分配两个9字节buffer 输入两个8字节字符串 循环8次处理字符串 每次迭代用complex_ function"加密"字符串 处理后字符串与内置字符串比较 关键点 使用伪造地址代替malloc返回的地址 关系转换: BEFORE: buffer0 -> malloc地址0 -> 字符串0 AFTER: buffer0 -> 伪造地址0 -> 符号位向量0 解决方案脚本 关键知识点总结 符号位向量创建 :使用 claripy.BVS(name, size) 创建符号变量 内存操作 : state.memory.store(address, value) 存储值到内存 动态内存可以伪造地址代替malloc返回地址 寄存器操作 : state.regs.<register> = value 设置寄存器值 路径探索 : simulation.explore(find, avoid) 探索路径 可以通过输出内容判断成功/失败路径 解具体值 : state.solver.eval(symbol, cast_to=bytes) 将符号值转为具体值 这些技术可以应用于各种逆向工程和漏洞分析场景,特别是需要自动化求解输入条件的情况。