• 我们在哪一颗星上见过 ,以至如此相互思念 ;我们在哪一颗星上相互思念过,以至如此相互深爱
  • 我们在哪一颗星上分别 ,以至如此相互辉映 ;我们在哪一颗星上入睡 ,以至如此唤醒黎明
  • 认识世界 克服困难 洞悉所有 贴近生活 寻找珍爱 感受彼此

第九章:二进制插桩

二进制分析实战 云涯 2周前 (11-17) 58次浏览

9.1 概念

在代码任意位置插入自定义代码,实时监控或修改。

静态插桩:直接修改二进制代码,例如修改call,插入监控代码。

动态插桩:运行时插入代码,不修改原代码。

while program_running:

    next_instruction = fetch()  # 获取下条指令 

    if is_monitor_point(next_instruction):  # 判断是否插桩点

        inject_code()  # 注入监测代码

    execute(next_instruction)  # 执行原指令

9.2 静态二进制插桩

9.2.1 int3方法

概念:假设我们要在高速公路(程序执行流)上设置检查站。INT3方法就像在某个检查点放置一个临时路障(0xCC指令),当车辆(CPU)经过时会停车(触发断点),此时我们记录车辆信息(数据采集)后移开路障,恢复通行。

示例:

程序执行 – 原始指令

mov eax, ebx    ; 机器码 8B C3

add eax, 5      ; 机器码 83 C0 05

 

程序执行 – int3指令(替换原始指令) – 操作系统触发中断 – 插桩处理器 – 插桩代码 – 原始指令(恢复执行)

步骤1:插入断点

int3          ; 替换mov为0xCC → 新机器码 CC C3

add eax, 5

步骤2:断点处理

当CPU执行到0xCC时:

触发断点异常

执行我们预装的”桩代码”:

printf(“eax当前值:%d\n”, eax);  // 记录寄存器值

memcpy(0x1234, original_opcode, 2); // 恢复原mov指令

 

9.2.2 跳板方法-jmp

概念:这次我们不破坏原有道路,而是在路边新建一个服务站(桩代码段)。当车辆经过特定路口时,指示牌(JMP指令)引导车辆进入服务站完成检查,再返回主路。

 

原始代码段(地址0x400500):

cmp edx, 10       ; 机器码 83 FA 0A

jle label         ; 机器码 7E 02

步骤1:插入跳板

jmp 0x500000     ; 新地址 → 机器码 E9 FB FA AF 00

nop              ; 90(填充字节)

 

步骤2:在新地址(0x500000)写入桩代码

// 桩代码区

pushfq                ; 保存标志寄存器

push rax

printf(“edx值:%d\n”, edx);  // 记录数据

pop rax

popfq

// 原始指令

cmp edx, 10          ; 还原被覆盖的指令

jle label            ; 原地址+5 → 0x400505

9.3 动态二进制插桩

9.3.1 DBI 系统的体系结构

包含:DBI引擎(大脑)、DBI工具(用户编写的自定义逻辑)、代码缓存(高速执行区)

工作流程:初始化(DBI引擎启动,加载DBI工具) – 插桩与编译(JIT编译器加工代码) – 在代码缓存中执行 – 控制流转移

9.3.2 Pin 介绍

Intel开发的一个DBI引擎(大脑)

 

两种模式:

JIT模式(运行时插桩和编译)

探针模式(预先一次性插桩所有代码,直接运行)

 

函数角色:

插桩例程:告诉pin在哪里以及插入什么

分析例程:实际的监控和分析逻辑

 

9.4 使用 Pin 进行分析

9.4.1 Profiler工具的数据结构和创建代码

目标:性能分析工具(Profiler)是如何用pin构建的

费曼方式的概念:搭建一个监控系统的框架——定义要监控什么(数据结构),设置监控选项(Knob),然后告诉监控系统什么时候开始工作(各种注册函数)

9.4.2 解析函数符号

目标:让Profiler能识别函数的名字,而不只是冷冰冰的地址。

原理:程序加载 – pin调用parse_funcsyms函数 – 该函数遍历所有节,和节中每个函数 – 把函数地址和名字存储到funcnames映射表里

9.4.3 插桩基本块

目标:统计程序执行了多少条指令

步骤:关注程序的基本块,忽略库代码 – 对每个块用instrument_bb来插桩 – 使用BBL_InsertCall插入回调函数,传递基本块中的指令数

9.4.4 检测控制流指令

目标:统计分析程序的控制流转移。使用INS_IsBranchOrCall判断是否是控制流指令。

三种场景:路遇红灯举例

跳转边:遇到了岔路口 – 绿灯 – 右转 (岔路口要转)

直行边:遇到了岔路口 – 红灯 – 选择等待 – 代表要继续执行  (岔路口选择直行,控制流不转弯)

函数调用:回家 – 突然想喝水 – 拐进便利店买水 – 买完水 – 回到原来路线回家

9.4.5 指令、控制转移及系统调用计数

目标:写出一个用来分析的函数,这个函数要被运行成千上万次,要求快速简洁

四个核心函数:

count_bb_insns:统计指令数(只是简单地把基本块的指令数加到总计数)

count_cflow:统计控制流转移,记录源地址和目标地址

count_call:统计函数调用

log_syscall:统计系统调用(通过PIN_GetSyscallNumber获取系统调用号)

9.4.6 测试Profiler

 

9.5 用 Pin 自动对二进制文件脱壳

9.5.1 可执行文件加壳器简介

构建自动脱壳器

9.5.2 脱壳器的配置代码及其使用的数据结构

原理:监视程序,当检测到“跳转到之前被写入的内存区域”时,就认为找到了OEP,然后转储内存

关键数据结构:

mem_access_t:记录内存字节的访问信息

w:是否被写入过

x:是否被执行过

val:存储的字节值

 

mem_cluster_t:合并相邻内存区域

base/size:内存块的起始地址和大小

w/x:访问权限

 

全局变量:

shadow_mem:影子内存,记录所有内存写入

clusters:存储发现的内存块

saved_addr:临时保存内存地址

 

main函数流程:初始化Pin – 注册内存和控制流插桩函数 – 启动被加壳程序

 

9.5.3 对内存写入插桩

目标:记录程序对所有内存的写入操作

挑战:Pin在内存写入之前知道地址,但是写入之后才知道具体写入值

解决:

写入前(IPOINT_BEFORE):调用queue_memwrite保存写入地址到saved_addr

写入后(IPOINT_AFTER或跳转边):调用log_memwrite记录实际写入的字节值

 

9.5.4 插桩控制流指令

目标:检测跳转到OEP的时刻

策略:只监控间接跳转指令

关键判断:如果跳转目标是曾被写入且未被处理过的内存,就可能是OEP!

实现:

使用INS_IsIndirectBranchOrCall检测间接控制流指令

插入check_indirect_ctransfer回调函数

这个函数会检查目标地址是否在之前被写入过的内存区域

9.5.5 跟踪内存写入

具体实现内存记录:

queue_memwrite函数:极其简单,只是保存地址:saved_addr = addr

log_memwrite函数:

从saved_addr开始,遍历所有被写入的字节

在shadow_mem中标记这些地址为”已写入”

使用PIN_SafeCopy安全地复制字节值

9.5.6 检测原始入口点并转储脱壳二进制文件

if (目标地址曾被写入过 AND 该地址不在已处理区域中):

# 很可能找到了OEP!

创建内存块包含目标地址周围的连续区域

将该区域标记为”可执行”

将内存块添加到已发现列表

转储该内存区域到文件

 

参考报告

DynamoRio Github地址:https://github.com/DynamoRIO/dynamorio

DynamoRio函数说明文档:https://dynamorio.org/files.html

利用 DynamoRIO 追踪和操控反分析技术:https://0xreverse.com/tracing-and-manipulating-anti-analysis-techniques-with-dynamorio

使用 DynamoRio 进行动态二进制插桩 (DBI):https://blog.talosintelligence.com/dynamic-binary-instrumentation-dbi-with-dynamorio/

 

利用动态二进制插桩框架Frida进行恶意软件分析:https://blogs.blackberry.com/en/2021/04/malware-analysis-with-dynamic-binary-instrumentation-frameworks

 

 

 

 

 

 


云涯历险记 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:第九章:二进制插桩
喜欢 (0)