简介
Pin 是 Intel 开发的一款动态二进制插桩分析框架,支持多平台。它提供了一套强大的 API,允许用户在二进制代码的执行过程中插入自定义的监控代码(如数据流跟踪、程序行为分析等)。动态二进制插桩(Dynamic Binary Instrumentation,DBI)指的是:不需要修改源代码,就可以在二进制文件的任意位置插入任意的代码,以便于分析程序的行为。
Pintool 是基于 Pin 框架开发的工具,主要用于程序分析、调试和安全研究等领域。Pintool 利用 Pin 提供的强大 API,可以对已经编译好的二进制程序进行动态插桩,不需要访问源代码。它允许你在程序的任意位置插入自定义的 C/C++ 代码,从而对程序行为进行分析和修改。

环境搭建
- 下载
下载相应系统的 Pin:Pin - A Dynamic Binary Instrumentation Tool
- 构建
将下载解压后的 Pin 目录放入 下载的 pintool 目录中。
shell
cd pin-pin-external-3.31-gcc-linux/source/tools/ManualExamles
make obj-intel64/inscount0.so
make obj-ia32/inscount0.so TARGET=ia32
-
Pin 基础使用
pin -t your_pintool -- your_binary <arg>
Pintool
Pintool 是通过 Pin 的 API 接口来开发的工具,在pin\source\toolsManualExamples目录中已经内置了很多 Pintool。
一个 Pintool 的基本结构:
c
#include "pin.H" // PIN API 头文件
#include <iostream>
#include <fstream>
// 输出文件句柄(可选)
std::ofstream outfile;
// === 插桩回调函数 ===
// 每条指令前调用的函数
VOID Instruction(INS ins, VOID *v) {
// 插入在指令执行前调用的函数
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)+[] (ADDRINT ip) {
outfile << "Instruction at address: 0x" << std::hex << ip << "\n";
}, IARG_INST_PTR, IARG_END);
}
// === 程序结束时调用的函数 ===
VOID Fini(INT32 code, VOID *v) {
outfile << "Program finished with exit code: " << code << "\n";
outfile.close();
}
// === 初始化函数 ===
INT32 Usage() {
std::cerr << "This Pintool logs instruction addresses.\n";
return -1;
}
// === 主函数 ===
int main(int argc, char *argv[]) {
// 初始化 Pin
if (PIN_Init(argc, argv)) return Usage();
// 打开输出文件
outfile.open("ins.log");
// 注册插桩函数
INS_AddInstrumentFunction(Instruction, 0);
// 注册程序退出时回调
PIN_AddFiniFunction(Fini, 0);
// 启动被测程序
PIN_StartProgram(); // 不会返回
return 0;
}
编译:
shell
g++ -Wall -Werror -fPIC -shared -o pintool.so pintool.cpp -I$PIN_ROOT/source/include -I$PIN_ROOT/source/include/pin -pthread -ldl
插桩模式
在使用 Intel Pin 开发 Pintool 时,插桩模式是指你选择 "在哪些粒度点" 插入你自定义分析逻辑的方式。Pin 提供了多种插桩模式,常见的有以下几类:
| 插桩粒度 | API | 执行时机 |
|---|---|---|
| 指令级插桩(instruction) | INS_AddInstrumenFunction |
执行一条新指令 |
| 轨迹级插桩(trace) | TRACE_AddInstrumenFunction |
执行一个新trace |
| 镜像级插桩(image) | IMG_AddInstrumenFunction |
加载新镜像时 |
| 函数级插桩(routime) | RTN_AddInstrumenFunction |
执行一个新函数时 |
在各种粒度的插桩函数调用时,可以在代码中添加自己的处理函数程序被加载后,在被插桩的代码运行时,自己添加的函数会被调用。
Instruction
指令集插桩的对象就是所有指令,适用于需要细粒度跟踪指令执行的场景,缺点是开销大效率会比较低。
cpp
#include "pin.H"
#include <iostream>
using std::cout;
VOID Instruction(INS ins, VOID *v) {
// 为每条指令插入回调:打印地址
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)+[] (ADDRINT addr) {
cout << "[INS] Executing instruction at: 0x" << std::hex << addr << "\n";
}, IARG_INST_PTR, IARG_END);
}
int main(int argc, char *argv[]) {
if (PIN_Init(argc, argv)) return 1;
INS_AddInstrumentFunction(Instruction, 0);
PIN_StartProgram();
return 0;
}
Trace
快速插桩每个基本块,适用于需要跟踪程序执行路径或热点代码块。
cpp
#include "pin.H"
#include <iostream>
using std::cout;
VOID Trace(TRACE trace, VOID *v) {
// 遍历 Trace 中的每个基本块
for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
// 为每个基本块插入执行回调
BBL_InsertCall(bbl, IPOINT_ANYWHERE, (AFUNPTR)+[] () {
cout << "[TRACE] Executing a basic block\n";
}, IARG_END);
}
}
int main(int argc, char *argv[]) {
if (PIN_Init(argc, argv)) return 1;
TRACE_AddInstrumentFunction(Trace, 0);
PIN_StartProgram();
return 0;
}
Image
程序加载模块时触发,比如主程序、动态库加载。适用于做模块初始化,符号解析等。
cpp
#include "pin.H"
#include <iostream>
using std::string;
using std::cout;
VOID Image(IMG img, VOID *v) {
cout << "[IMG] Loaded image: " << IMG_Name(img) << "\n";
}
int main(int argc, char *argv[]) {
if (PIN_Init(argc, argv)) return 1;
IMG_AddInstrumentFunction(Image, 0);
PIN_InitSymbols(); // 启用符号支持
PIN_StartProgram();
return 0;
}
Routine
在函数入口或出口插桩,监控函数调用行为。适用于分析函数参数、返回值,做函数调用跟踪。
cpp
#include "pin.H"
#include <iostream>
using std::string;
using std::cout;
VOID Routine(RTN rtn, VOID *v) {
string name = RTN_Name(rtn);
if (name == "malloc") {
RTN_Open(rtn);
// 在 malloc 调用前插入
RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)+[] (size_t size) {
cout << "[RTN] malloc called with size: " << size << "\n";
}, IARG_FUNCARG_ENTRYPOINT_VALUE, 0, IARG_END);
RTN_Close(rtn);
}
}
int main(int argc, char *argv[]) {
if (PIN_Init(argc, argv)) return 1;
PIN_InitSymbols(); // 解析函数名所需
RTN_AddInstrumentFunction(Routine, 0);
PIN_StartProgram();
return 0;
}
sde
sde 基于 Pin 和 xed,在 pin 插桩分析的基础上多了仿真的功能,可以在特定指令集架构上运行给定程序并捕获各种性能详细信息,可以供开发人员提前熟悉和探索即将推出的新指令集,为在软件中使用新指令集做准备。供开发人员
| 功能 | 命令示例 | 含义 |
|---|---|---|
| 指令计数 | sde -icount -- ./a.out |
统计程序执行的总指令数 |
| 热点函数分析 | sde -top_blocks -- ./a.out |
统计最常执行的函数/地址 |
| 混合类型分布 | sde -mix -- ./a.out |
分析每种指令类型执行的比例 |
| 新指令支持测试 | sde -avx512 -- ./avx512_test |
在旧机器上测试 AVX-512 程序 |
Reference
ChrisTheCoolHut/PinCTF: Using Intel's PIN tool to solve CTF problems
Intel Pin基本用法 - BrieflyX's Base
Pin动态二进制插桩技术详解 - 先知社区