Intel Pin 插桩

简介

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动态二进制插桩技术详解 - 先知社区

相关推荐
skilllite作者7 小时前
Warp 新手极速上手与部署指南
java·前端·笔记·安全·agentskills
Le_ee7 小时前
winXP栈溢出漏洞深度学习
网络安全·安全性测试·栈溢出·ollydbg
链上日记7 小时前
WEEX行业视角:从近期安全事件看,2026 年或成为行业安全分水岭
大数据·安全·区块链
skilllite作者8 小时前
LangChain-SkillLite 快速入门
网络·人工智能·安全·langchain·openclaw·agentskills
renhongxia18 小时前
AI技术分享:如何做好职场内部技术培训
人工智能·安全·docker·语言模型·容器
CDN3608 小时前
告别“慢”与“不安全”:360CDN动态API加速与HTTPS配置实战
网络协议·安全·https
liann1198 小时前
3.3_tasklist和netstat命令详解
运维·windows·计算机网络·安全·信息与通信
aodunsoft8 小时前
安全月报 | 傲盾DDoS攻击防御2026年4月简报
网络·安全·ddos
虚幻如影8 小时前
web端安全测试报告模板
linux·服务器·安全