avatar

目录
Intel Pin的基本使用

Intel Pin的基本使用

Pin是Intel开发的一款动态二进制插桩工具,可用于污点分析,Fuzz,漏洞挖掘等。

Pin工具下载地址:pintool.org

插桩粒度

  1. 指令级插桩(instruction instrumentatio),通过函数INS_AddInstrumentFunctio实现。
  2. 轨迹级插装(trace instrumentation),通过函数TRACE_AddInstrumentFunction实现。
  3. 镜像级插装(image instrumentation),使用IMG_AddInstrumentFunction函数,由于其依赖于符号信息去确定函数边界,因此必须在调用PIN_Init之前调用PIN_InitSymbols。
  4. 函数级的插装(routine instrumentation),使用RTN_AddInstrumentFunction函数。函数级插装比镜像级插装更有效,因为只有镜像中的一小部分函数被执行。

Pin工具测试

首先编译一个名为 insmix 的工具,这个工具是用来统计应用程序执行的 x86 指令的数量的。

bash
pin-gcc-linux/source/tools/Insmix/
make

执行成功后可在Insmix/obj-intel64下生成insmix.so和insmin.o

返回到 pin 的根目录,然后输入如下指令将 insmix 工具附加在 ls 指令上并由 pin 运行 ls 指令:

bash
./pin -t source/tools/Insmix/obj-intel64/insmix.so -- ls

在pin根目录下将生成insmix.out

image.png

可以看到pin可以输出非常详细的信息,甚至包含每个汇编指令的数量。

插桩分析基本框架

查看/pin-gcc-linux/source/tools/ManualExamples下的inscount0.cpp。

cpp
/*
* Copyright 2002-2019 Intel Corporation.
*
* This software is provided to you as Sample Source Code as defined in the accompanying
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
* section 1.L.
*
* This software and the related documents are provided as is, with no express or implied
* warranties, other than those that are expressly stated in the License.
*/

#include <iostream>
#include <fstream>
#include "pin.H"
using std::cerr;
using std::ofstream;
using std::ios;
using std::string;
using std::endl;

ofstream OutFile;

// The running count of instructions is kept here
// make it static to help the compiler optimize docount
static UINT64 icount = 0;

// This function is called before every instruction is executed
VOID docount() { icount++; }

// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)
{
// Insert a call to docount before every instruction, no arguments are passed
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)docount, IARG_END);
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool",
"o", "inscount.out", "specify output file name");

// This function is called when the application exits
VOID Fini(INT32 code, VOID *v)
{
// Write to a file since cout and cerr maybe closed by the application
OutFile.setf(ios::showbase);
OutFile << "Count " << icount << endl;
OutFile.close();
}

/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */

INT32 Usage()
{
cerr << "This tool counts the number of dynamic instructions executed" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}

/* ===================================================================== */
/* Main */
/* ===================================================================== */
/* argc, argv are the entire command line: pin -t <toolname> -- ... */
/* ===================================================================== */

int main(int argc, char * argv[])
{
// Initialize pin
if (PIN_Init(argc, argv)) return Usage();

OutFile.open(KnobOutputFile.Value().c_str());

// Register Instruction to be called to instrument instructions
INS_AddInstrumentFunction(Instruction, 0);

// Register Fini to be called when the application exits
PIN_AddFiniFunction(Fini, 0);

// Start the program, never returns
PIN_StartProgram();

return 0;
}

代码所展示的是pin的基本框架

  1. 调用PIN_Init进行初始化。
  2. 进入INS_AddInstrumentFunction注册插桩函数,INS_AddInstrumentFunction应该是一个回调函数,会在每次指令执行前调用。
  3. PIN_AddFiniFunction注册一个将在目标程序结束时被执行的函数。
  4. PIN_StartProgram开始启动。

所示的代码调用docount来记录目标程序执行的指令数量。(记录程序执行了的指令数量可以用于看不同输入的熵,从而判断某一输入是否执行了与其他输入不同的流程)

精确的指令插桩

查看/pin-gcc-linux/source/tools/ManualExamples下的safecopy.cpp。

cpp
/*
* Copyright 2002-2019 Intel Corporation.
*
* This software is provided to you as Sample Source Code as defined in the accompanying
* End User License Agreement for the Intel(R) Software Development Products ("Agreement")
* section 1.L.
*
* This software and the related documents are provided as is, with no express or implied
* warranties, other than those that are expressly stated in the License.
*/

#include <stdio.h>
#include "pin.H"
#include <iostream>
#include <fstream>
using std::cerr;
using std::endl;

std::ofstream* out = 0;

//=======================================================
// Analysis routines
//=======================================================

// Move from memory to register
ADDRINT DoLoad(REG reg, ADDRINT * addr)
{
*out << "Emulate loading from addr " << addr << " to " << REG_StringShort(reg) << endl;
ADDRINT value;
PIN_SafeCopy(&value, addr, sizeof(ADDRINT));
return value;
}

//=======================================================
// Instrumentation routines
//=======================================================

VOID EmulateLoad(INS ins, VOID* v)
{
// Find the instructions that move a value from memory to a register
if (INS_Opcode(ins) == XED_ICLASS_MOV &&
INS_IsMemoryRead(ins) &&
INS_OperandIsReg(ins, 0) &&
INS_OperandIsMemory(ins, 1))
{
// op0 <- *op1
INS_InsertCall(ins,
IPOINT_BEFORE,
AFUNPTR(DoLoad),
IARG_UINT32,
REG(INS_OperandReg(ins, 0)),
IARG_MEMORYREAD_EA,
IARG_RETURN_REGS,
INS_OperandReg(ins, 0),
IARG_END);

// Delete the instruction
INS_Delete(ins);
}
}

/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */

INT32 Usage()
{
cerr << "This tool demonstrates the use of SafeCopy" << endl;
cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
return -1;
}

/* ===================================================================== */
/* Main */
/* ===================================================================== */

int main(int argc, char * argv[])
{
// Write to a file since cout and cerr maybe closed by the application
out = new std::ofstream("safecopy.out");

// Initialize pin & symbol manager
if (PIN_Init(argc, argv)) return Usage();
PIN_InitSymbols();

// Register EmulateLoad to be called to instrument instructions
INS_AddInstrumentFunction(EmulateLoad, 0);

// Never returns
PIN_StartProgram();
return 0;
}

如下代码所示,可以控制在想要的位置寄存器进行插桩

还可以获取寄存器值,对内存进行读写等。

cpp
VOID EmulateLoad(INS ins, VOID* v)
{
// Find the instructions that move a value from memory to a register
if (INS_Opcode(ins) == XED_ICLASS_MOV &&
INS_IsMemoryRead(ins) &&
INS_OperandIsReg(ins, 0) &&
INS_OperandIsMemory(ins, 1))
{
// op0 <- *op1
INS_InsertCall(ins,
IPOINT_BEFORE,
AFUNPTR(DoLoad),
IARG_UINT32,
REG(INS_OperandReg(ins, 0)),
IARG_MEMORYREAD_EA,
IARG_RETURN_REGS,
INS_OperandReg(ins, 0),
IARG_END);

// Delete the instruction
INS_Delete(ins);
}
}

pin的基本操作就写到这里,使用pin对ctf题目进行分析也很有趣,之后会进行尝试。

文章作者: kabeor
文章链接: https://kabeor.github.io/Intel%20Pin%E7%9A%84%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 K's House

评论