【记录】Angr|Angr 标准库函数替换怎么看哪些库函数被Angr支持?
文章目录
- 前言
- 分析
- 什么是库函数替换?
- 为什么需要库函数替换?
- 如何查找支持的库函数
- 官方支持列表
- 目录结构说明
- 常用的替换包括哪些?
- 1. 字符串处理函数
- 2. 内存管理函数
- 3. 文件操作函数
- 高级技巧
- 1. 自定义库函数实现
- 2. 条件替换
- 常见问题与解决方案详解
- 1. 找不到合适的替换函数
- 问题表现
- 解决方案
- 1.1 源码分析法
- 1.2 自定义实现示例
- 1.3 使用近似替换
- 2. 替换后行为异常
- 问题表现
- 解决方案
- 2.1 函数签名检查
- 2.2 约束条件调整
- 2.3 状态空间控制
- 3. 性能问题
- 问题表现
- 解决方案
- 3.1 简化替换函数
- 3.2 深度限制
- 3.3 优化约束条件
- 实用建议
- 最佳实践总结
- 总结
前言
我在学习 Angr,这里会记录一些琐碎的、在中文引擎里一搜没找到解法的问题。Angr 是一种符号执行工具。
我的学习内容:
博主链接:Angr 使用技巧速通笔记(二) | TokameinE - 雨声淅淅沥沥,犬吠永不止息
Github 教程链接:https://github.com/jakespringer/angr_ctf
本博客是学习到 13_angr_static_binary 时产生的疑惑。
分析
什么是库函数替换?
在符号执行过程中,为了更好地控制执行流程和状态空间,Angr 会使用自己实现的版本来替换程序中的标准库函数调用。这些替换函数通常是原始函数的简化版本,专门为符号执行优化设计。
为什么需要库函数替换?
- 性能优化: 原始库函数可能过于复杂,会导致状态空间爆炸
- 行为控制: 可以更精确地控制函数行为,便于分析
- 符号执行支持: 确保函数可以正确处理符号值
- 跨平台兼容: 提供统一的函数行为,减少平台差异
如何查找支持的库函数
首先,Angr 标准库函数替换怎么看哪些库函数被Angr支持?这个问题 Angr_ctf 题解里面写了,提供了官方支持列表。
官方支持列表
Angr 的所有支持库函数都可以在其 GitHub 仓库中找到:
https://github.com/angr/angr/tree/master/angr/procedures
目录结构说明
angr/procedures/
├── libc/ # C标准库函数
├── posix/ # POSIX系统调用
├── win32/ # Windows API
├── linux_kernel/ # Linux内核函数
└── ...
进而不由产生其他疑问:
- 这么多库函数怎么知道自己想要的是哪个?
- 常用的替换包括哪些?
常用的替换包括哪些?
常用的替换有哪些?
Angr替换库函数的种类相对广泛,常见的包括:
- 内存相关函数:如
malloc
、free
、calloc
等。 - 字符串操作函数:如
strlen
、strcpy
、strncpy
等。 - 文件操作函数:如
open
、read
、write
、close
等。 - 系统调用:如
execve
、exit
等。 - 时间和日期相关:如
time
、gmtime
、localtime
等。
这些替换的函数一般都提供了简化版或精简版的实现,方便Angr进行符号执行和模拟。
1. 字符串处理函数
# 示例: 替换 strlen 函数
import angr
def analyze_strlen():
proj = angr.Project('./target_binary')
# 手动替换 strlen
proj.hook_symbol('strlen', angr.SIM_PROCEDURES['libc']['strlen']())
# 模拟执行
state = proj.factory.entry_state()
simgr = proj.factory.simulation_manager(state)
simgr.explore()
常见字符串函数替换:
- strlen: 字符串长度计算
- strcpy: 字符串复制
- strncpy: 限制长度的字符串复制
- strcat: 字符串拼接
2. 内存管理函数
# 示例: 替换 malloc 和 free
def analyze_memory():
proj = angr.Project('./target_binary')
# 替换内存分配函数
proj.hook_symbol('malloc', angr.SIM_PROCEDURES['libc']['malloc']())
proj.hook_symbol('free', angr.SIM_PROCEDURES['libc']['free']())
常见内存函数:
- malloc: 动态内存分配
- calloc: 分配并清零内存
- realloc: 重新分配内存
- free: 释放内存
3. 文件操作函数
# 示例: 模拟文件操作
def analyze_file_ops():
proj = angr.Project('./target_binary')
# 替换文件操作函数
proj.hook_symbol('fopen', angr.SIM_PROCEDURES['libc']['fopen']())
proj.hook_symbol('fread', angr.SIM_PROCEDURES['libc']['fread']())
proj.hook_symbol('fwrite', angr.SIM_PROCEDURES['libc']['fwrite']())
proj.hook_symbol('fclose', angr.SIM_PROCEDURES['libc']['fclose']())
高级技巧
1. 自定义库函数实现
有时候 Angr 提供的默认实现可能不满足需求,我们可以自定义实现:
class CustomStrlen(angr.SimProcedure):
def run(self, str_addr):
# 自定义 strlen 实现
strlen = 0
while True:
curr_char = self.state.memory.load(str_addr + strlen, 1)
curr_val = self.state.solver.eval(curr_char)
if curr_val == 0:
break
strlen += 1
return strlen
# 使用自定义实现
proj.hook_symbol('strlen', CustomStrlen())
2. 条件替换
根据不同条件使用不同的替换实现:
def conditional_hook(state):
# 根据状态决定是否替换
if state.solver.eval(state.regs.rax) > 0x1000:
return angr.SIM_PROCEDURES['libc']['malloc']()
return None
proj.hook_symbol('malloc', conditional_hook)
常见问题与解决方案详解
后面这些解决方案由AI生成
1. 找不到合适的替换函数
问题表现
- 在 Angr 的标准库中找不到目标函数的实现
- 现有的替换函数不能完全满足分析需求
- 需要处理特殊的或非标准的库函数
解决方案
1.1 源码分析法
首先查看 Angr 源码中类似功能的实现:
# 示例:查找字符串比较相关的函数实现
# 路径:angr/procedures/libc/strcmp.py
class strcmp(angr.SimProcedure):
def run(self, a_addr, b_addr):
# 实现细节
pass
# 如果需要实现类似的函数,可以参考这个模板
1.2 自定义实现示例
# 自定义一个复杂的字符串处理函数
class CustomStrProcess(angr.SimProcedure):
def run(self, str_addr, len_param):
# 1. 参数处理
strlen = self.state.solver.eval(len_param)
if strlen > 1000: # 添加合理的限制
strlen = 1000
# 2. 内存读取
string = self.state.memory.load(str_addr, strlen)
# 3. 实现特定的处理逻辑
processed = self.process_string(string)
# 4. 处理返回值
return processed
def process_string(self, string):
# 实现具体的处理逻辑
pass
# 使用自定义函数替换
proj.hook_symbol('target_function', CustomStrProcess())
1.3 使用近似替换
如果无法完全匹配原函数功能,可以使用功能相近的替换:
# 示例:用 strncmp 替换 strcmp
proj.hook_symbol('strcmp', angr.SIM_PROCEDURES['libc']['strncmp'](length=100))
2. 替换后行为异常
问题表现
- 程序执行路径不符合预期
- 符号执行过程中出现异常
- 分析结果不准确
解决方案
2.1 函数签名检查
# 正确的函数签名示例
class CorrectMemcpy(angr.SimProcedure):
def run(self, dest_addr, src_addr, length):
# 确保参数类型正确
if not isinstance(length, claripy.ast.Base):
length = self.state.solver.BVV(length, self.state.arch.bits)
# 实现内存复制逻辑
return dest_addr
# 使用方法
proj.hook_symbol('memcpy', CorrectMemcpy())
2.2 约束条件调整
def add_constraints(state):
# 添加基本约束
buff_addr = state.regs.rdi # 假设缓冲区地址在 rdi 寄存器
# 限制缓冲区大小
state.add_constraints(state.mem[buff_addr].uint32_t != 0)
state.add_constraints(state.mem[buff_addr].uint32_t < 1024)
# 限制字符范围
for i in range(32):
curr_byte = state.memory.load(buff_addr + i, 1)
state.add_constraints(curr_byte >= 0x20)
state.add_constraints(curr_byte <= 0x7e)
# 在符号执行前添加约束
state = proj.factory.entry_state()
add_constraints(state)
2.3 状态空间控制
# 设置执行超时和内存限制
def explore_with_limits():
proj = angr.Project('./target_binary')
state = proj.factory.entry_state()
# 创建模拟管理器
simgr = proj.factory.simulation_manager(state)
# 设置探索限制
simgr.explore(
find=lambda s: b"success" in s.posix.dumps(1),
avoid=lambda s: b"fail" in s.posix.dumps(1),
n_find=1, # 只需要找到一个解
max_active=1000, # 限制活动状态数量
timeout=300 # 设置超时(秒)
)
3. 性能问题
问题表现
- 符号执行速度过慢
- 内存消耗过大
- 状态爆炸
解决方案
3.1 简化替换函数
# 简化版的 strlen 实现
class FastStrlen(angr.SimProcedure):
def run(self, str_addr):
# 设置最大搜索长度
MAX_LEN = 64
# 快速扫描定长
for i in range(MAX_LEN):
curr_char = self.state.memory.load(str_addr + i, 1)
is_null = self.state.solver.is_true(curr_char == 0)
if is_null:
return i
return MAX_LEN
# 使用简化版本
proj.hook_symbol('strlen', FastStrlen())
3.2 深度限制
def limited_exploration():
proj = angr.Project('./target_binary')
state = proj.factory.entry_state()
simgr = proj.factory.simulation_manager(state)
# 设置探索策略
simgr.use_technique(angr.exploration_techniques.LoopSeer(
loops=[0x400c40, 0x400d30], # 指定循环的地址
bound=10 # 限制循环执行次数
))
# 限制执行深度
simgr.use_technique(angr.exploration_techniques.LengthLimiter(
max_length=1000 # 最大执行步数
))
return simgr.run()
3.3 优化约束条件
def optimize_constraints():
proj = angr.Project('./target_binary')
state = proj.factory.entry_state()
# 1. 添加智能约束
input_size = 32
input_chars = [state.solver.BVS(f'c{i}', 8) for i in range(input_size)]
# 2. 使用批量约束而不是单字符约束
printable = state.solver.And(
state.solver.And(c >= 0x20, c <= 0x7e) for c in input_chars
)
state.add_constraints(printable)
# 3. 使用预设值减少求解空间
known_prefix = b"FLAG{"
for i, c in enumerate(known_prefix):
state.add_constraints(input_chars[i] == c)
return state
实用建议
- 日志与调试
很有用
# 开启详细日志
import logging
logging.getLogger('angr').setLevel(logging.DEBUG)
# 添加调试点
@proj.hook(0x400c40)
def debug_hook(state):
print(f"执行到地址 0x400c40")
print(f"寄存器状态: {state.regs}")
print(f"内存内容: {state.mem[state.regs.rsp].uint64_t.concrete}")
- 性能监控
import time
import resource
def monitor_execution():
start_time = time.time()
start_memory = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
# 执行分析
result = run_analysis()
end_time = time.time()
end_memory = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print(f"执行时间: {end_time - start_time:.2f} 秒")
print(f"内存使用: {(end_memory - start_memory) / 1024:.2f} MB")
return result
最佳实践总结
-
渐进式调试
- 先用简单的替换函数测试
- 逐步添加复杂功能
- 持续监控性能变化
-
模块化设计
- 将复杂的替换函数拆分为小模块
- 便于测试和维护
- 提高代码复用性
-
错误处理
- 添加适当的错误处理机制
- 设置合理的超时和限制
- 保存中间状态便于调试
这些解决方案和最佳实践可以帮助你处理 Angr 库函数替换过程中遇到的大多数问题。在实际应用中,可能需要根据具体情况组合使用多种方案。
总结
库函数替换是 Angr 符号执行中的重要机制,掌握它能够帮助我们更好地控制程序分析过程。本文详细介绍了:
- 库函数替换的基本概念
- 如何查找和使用支持的库函数
- 常用替换函数的详细说明
- 实战案例演示
- 高级使用技巧
- 常见问题的解决方案
希望这篇文章能够帮助读者更好地理解和使用 Angr 的库函数替换功能。
本账号所有文章均为原创,欢迎转载,请注明文章出处:https://shandianchengzi.blog.csdn.net/article/details/144883082。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。