[Python] Need an example to understand angr state_plugins CallStack usage (angr 9.2.148)

Discussão em 'Python' iniciado por Stack, Abril 26, 2025.

  1. Stack

    Stack Membro Participativo

    I'm trying to understand how state_plugins.CallStack works. I want to obtain the assembly code and pseudocode for each frame in the call stack.

    However, the function address in stack.frame[-1] is always 0. For other frames, the function addresses cannot be correctly resolved to function names during disassembly. Why?

    I couldn't find any relevant example code to properly handle callstack-based function name resolution. Can anyone provide a working example that correctly extracts and disassembles callstack frames, including resolving function names for call instructions?

    Below are outputs and source code: Python script:

    from angr import SimStatePlugin, SimProcedure, Project, SimState
    import claripy

    # import logging

    class TrackedMalloc(SimProcedure):
    def run(self, size):
    ptr = claripy.BVV(self.state.heap._malloc(size), self.state.arch.bits)

    callstack = list(self.state.callstack)
    print(f"callstack:: {callstack}/ {type(callstack)}") # angr.state_plugins.callstack.CallStack
    for i, frame in enumerate(callstack):
    print(f"-- {i} / {type(frame)}--")
    print(f"call_site_addr = {frame.call_site_addr:x}, func_addr = {frame.func_addr:x}, stack_ptr = {frame.stack_ptr:x}, ret_addr = {frame.ret_addr:x}")

    return ptr

    proj = Project("../examples/demo3.so", auto_load_libs=False)

    func_addr = proj.loader.find_symbol('test_double_free').rebased_addr

    entry_state = proj.factory.blank_state(
    addr=func_addr,
    )

    proj.hook_symbol('malloc', TrackedMalloc())

    simgr = proj.factory.simgr(entry_state)
    simgr.run()


    C for binary file:

    #include <stdio.h>
    #include <stdlib.h>

    void _double_free()
    {
    char* ptr = (char*)malloc(3);
    free(ptr);
    free(ptr);
    }

    void test_double_free()
    {
    _double_free();
    }


    And Python script to print callstack:

    def _print_near_instructions(state, addr, num_before=10, num_after=10):
    """打印地址附近的指令"""
    try:
    # 获取反汇编信息
    cfg = proj.analyses.CFG()
    insns = []

    # 尝试获取前几条指令
    for offset in range(-num_before * state.arch.instruction_alignment,
    (num_after + 1) * state.arch.instruction_alignment,
    state.arch.instruction_alignment):
    try:
    insn = proj.factory.block(addr + offset).capstone.insns
    if insn:
    insns.extend(insn)
    except:
    pass

    # 去重并排序
    unique_insns = {i.address:i for i in insns}.values()
    sorted_insns = sorted(unique_insns, key=lambda x: x.address)

    # 打印
    for insn in sorted_insns:
    arrow = "=>" if addr == insn.address else " "
    log.debug(" %s %#x: %-8s %s", arrow, insn.address,
    insn.mnemonic, insn.op_str)
    except:
    log.debug(" [Could not disassemble near %#x]", addr)

    def _print_callstack(state, callstack):
    log.debug("\nCall stack (most recent first):")
    log.debug("#%-2s %-20s %s", " ", "Address", "Function")
    log.debug("-" * 60)

    for i, frame in enumerate(callstack):
    # 获取函数名和文件名
    func_name = "??"
    filename = "??"
    try:
    func_addr = frame.func_addr
    log.debug(f"finding func addr: {func_addr:x}")
    log.debug(vars(frame))
    sym = proj.loader.find_symbol(func_addr)
    if sym:
    func_name = sym.name
    # 获取所在文件
    obj = proj.loader.find_object_containing(func_addr)
    if obj:
    filename = obj.binary.split('/')[-1]
    except:
    log.exception(f"unknown func addr: {frame.func_addr}")
    pass

    # 获取源代码位置(如果有调试信息)
    line_info = ""
    try:
    addr = frame.func_addr - proj.loader.main_object.mapped_base
    line = proj.loader.main_object.addr_to_line(addr)
    if line:
    line_info = f" at {line[1]}:{line[2]}"
    except:
    pass

    log.debug("#%-2d %#-18x %s (%s)%s",
    i, frame.func_addr, func_name, filename, line_info)

    # 打印返回地址附近的指令
    if frame.ret_addr:
    _print_near_instructions(state, frame.ret_addr)

    def _print_stack(state, num_words=16):
    """改进的堆栈内容打印函数,更可读的格式"""
    sp = state.solver.eval(state.regs.sp)
    log.debug("Stack trace (SP = %#x):", sp)
    log.debug("Address Value Possible Meaning")
    log.debug("------------ ---------------- --------------------")

    arch_bytes = state.arch.bytes
    for i in range(num_words):
    addr = sp + i * arch_bytes
    try:
    # 读取内存值
    val = state.memory.load(addr, arch_bytes, endness=state.arch.memory_endness)
    val_num = state.solver.eval(val)

    # 尝试解析值的含义
    meaning = ""
    try:
    # 检查是否是代码地址
    if proj.loader.find_object_containing(val_num):
    sym = proj.loader.find_symbol(val_num)
    meaning = f"code:{sym.name}" if sym else f"code@%#x" % val_num
    # 检查是否是数据地址
    elif proj.loader.find_object_containing(val_num - proj.loader.main_object.mapped_base):
    meaning = "data"
    except:
    pass

    # 格式化输出
    log.debug("%#-12x %#-16x %s", addr, val_num, meaning)
    except:
    log.debug("%#-12x <unreadable>", addr)

    Continue reading...

Compartilhe esta Página