pin

这是什么东西就不解释了,我也不是很清楚,网上的介绍也挺多的。

下面的演示以Windows为准

安装

Pin - A Dynamic Binary Instrumentation Tool

在其中找到Windows(MSVC)

image-20250523004544183

选择最新版下载

下载后解压,其中的pin.exe就是pin主程序了。

将本目录添加到path

然后安装Cygwin,在选择安装时额外添加make,gcc和g++,安装成功后,将bin目录添加到path

然后尝试编译pintool工具,看看有没有成功安装:

  • 打开x64 Native Tools Command Prompt for VS 2022/如果是32位使用32位的prompt
  • 跳转到\source\tools\MyPinTool目录
  • 执行make all TARGET=Intel64

会生成obj-intel64文件夹及其中的dll文件,然后尝试执行验证一下能不能用

到ManualExamples中再次make,cd到obj-intel64中pin -t inscount2.dll -o inscount0.log – divide_by_zero.exe,查看输出文件是否存在和是否可以执行。

image-20250525020741939

十分合理。

参考:

[Intel Pin — Windows&wsl 编译踩坑实录](https://hur1k.github.io/2023/02/26/Intel Pin — Windows&wsl 编译踩坑实录/)

Pin: Pin 3.21 User Guide

Intel-Pin的windows安装


项目

构建项目

在source/tools中新建一个文件夹PinTool1

在里面写一个cpp文件,我直接在ManualExample文件里面复制了,命名为pintool.cpp,然后同样复制一个makefile.rules和makefile文件,makefile.rule文件里面把TEST_TOOL_ROOTS后面的内容改为pintool:

1
TEST_TOOL_ROOTS := pintool

并把后面的附加内容删掉

1
2
3
4
ifeq ($(TARGET_OS),windows)
TEST_TOOL_ROOTS += w_malloctrace buffer_windows emudiv replacesigprobed dumpargv
APP_ROOTS += divide_by_zero
endif

然后直接make tools,这里不知道为什么如果使用make TARGET=Intel64会报错不支持的架构,但是直接make也可以生成64位的dll

然后依旧任意找个exe测试

1
pin -t obj-intel64/pintool.dll -o log.log -- divide_by_zero.exe

成功。

使用VS写代码

创建新项目,但是修改编译环境:

Intel Pin的安装与配置 | lzeroyuee’s blog

在目录%PinDir%\source\tools\MyPinTool下有示例代码,使用VS编译即可,编译中可能存在错误,对照错误提示修改,主要的编译环境配置如下:

  • C/C++ --> 常规 --> 附加包含目录添加如下路径:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    ..\..\include\pin
    ..\..\include\pin\gen
    ..\InstLib
    ..\..\..\extras\components\include
    ..\..\..\extras\stlport\include
    ..\..\..\extras
    ..\..\..\extras\libstdc++\include
    ..\..\..\extras\crt\include
    ..\..\..\extras\crt
    ..\..\..\extras\crt\include\kernel\uapi
    ..\..\..\extras\crt\include\kernel\uapi\asm-x86

    对x86需要额外添加:
    ..\..\..\extras\crt\include\arch-x86
    ..\..\..\extras\xed-ia32\include\xed

    对x64需要额外添加:
    ..\..\..\extras\crt\include\arch-x86_64
    ..\..\..\extras\xed-intel64\include\xed
  • 链接器 --> 高级 --> 映像具有安全异常处理程序设置为

  • 链接器 --> 输入 --> 附加依赖项添加如下项目:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    pin.lib
    xed.lib
    pinvm.lib
    pincrt.lib
    kernel32.lib
    crtbeginS.obj

    x86需要额外添加:
    ntdll-32.lib

    x64需要额外添加:
    ntdll-64.lib

然后就可以写代码了。编译的时候还是使用vs x64 native执行make指令来编译

使用

插桩粒度

trace instumentation

在一个代码序列(从某个指令开始到控制流语句)第一次执行前插桩,用TRACE_AddInstrumentFunction实现

instruction instrumentation

指令插桩,用INS_AddInstrumentFunction实现

image instrumentation

映像插桩,在映像加载时执行,使用IMG_AddInstrumentFunction实现,需要初始化符号信息:PIN_InitSysmbols

routine instrumentation

例程插桩,在映像加载时执行,使用RTN_AddInstrumentFunction实现

例子

翻译来自有毒的学Pin记录,部分例子省略,只摘抄了相对重要的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include "pin.H"
FILE* trace;
// 在每条指令执行前都会被调用,打印出当前指令的地址
VOID printip(VOID* ip) { fprintf(trace, "%p\n", ip); }
// 遇到一条新指令调用一次
VOID Instruction(INS ins, VOID* v)
{
// 在每条指令前插入对 printip 函数的调用,并传递 ip 参数
INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printip, IARG_INST_PTR, IARG_END);
}
// 结束函数
VOID Fini(INT32 code, VOID* v)
{
fprintf(trace, "#eof\n");
fclose(trace);
}
/* ===================================================================== */
/* Print Help Message */
/* ===================================================================== */
INT32 Usage()
{
PIN_ERROR("This Pintool prints the IPs of every instruction executed\n" + KNOB_BASE::StringKnobSummary() + "\n");
return -1;
}
/* ===================================================================== */
/* Main */
/* ===================================================================== */
int main(int argc, char* argv[])
{
trace = fopen("itrace.out", "w");
// 初始化
if (PIN_Init(argc, argv)) return Usage();
// 桩指令注册
INS_AddInstrumentFunction(Instruction, 0);
// 结束逻辑注册
PIN_AddFiniFunction(Fini, 0);
// 开始执行,不返回
PIN_StartProgram();
return 0;
}

INS_AddInstrumentFunction注册了回调,PIN_AddFiniFunction作为程序退出时的处理函数

INS_InsertCall说明了插入的功能在什么时候触发,函数指针是什么,以及传参

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
VOID Instruction(INS ins, VOID* v)
{
// 获取指令中的内存操作数计数
UINT32 memOperands = INS_MemoryOperandCount(ins);

// 遍历指令中的每个内存操作数
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
// 如果是内存读
if (INS_MemoryOperandIsRead(ins, memOp))
{
INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead, IARG_INST_PTR, IARG_MEMORYOP_EA, memOp,
IARG_END);
}

// 在某些架构下,内存操作数可以同时用作读和写,例如 IA-32 的 %eax,这种情况下只记录一次
// 如果是写
if (INS_MemoryOperandIsWritten(ins, memOp))
{
INS_InsertPredicatedCall(ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite, IARG_INST_PTR, IARG_MEMORYOP_EA, memOp,
IARG_END);
}
}
}

上面这个例子使用了内存模块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Pin 在 image 加载时调用该函数,在该例中没有进行插桩
VOID ImageLoad(IMG img, VOID* v) { TraceFile << "Loading " << IMG_Name(img) << ", Image id = " << IMG_Id(img) << endl; }
// Pin 在 image 卸载时调用该函数,对于将要卸载的image无法进行插桩
VOID ImageUnload(IMG img, VOID* v) { TraceFile << "Unloading " << IMG_Name(img) << endl; }
int main(int argc, char* argv[])
{
// 符号初始化
PIN_InitSymbols();
// pin 初始化
if (PIN_Init(argc, argv)) return Usage();
TraceFile.open(KnobOutputFile.Value().c_str());
// 注册加载桩函数
IMG_AddInstrumentFunction(ImageLoad, 0);
// 注册卸载桩函数
IMG_AddUnloadFunction(ImageUnload, 0);
// 注册退出函数
PIN_AddFiniFunction(Fini, 0);
// 开始执行,无返回
PIN_StartProgram();
return 0;
}
1
2
3
4
5
6
7
8
9
VOID Trace(TRACE trace, VOID* v)
{
// 访问 trace 中的每个 bbl
for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl))
{
// 在每个 bbl 前插入对 docount 函数的调用,传入指令数量
BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)docount, IARG_UINT32, BBL_NumIns(bbl), IARG_END);
}
}

时机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
typedef enum
{
IPOINT_INVALID,

IPOINT_BEFORE, ///< 在被插桩对象的第一条指令之前插入一次调用。总是有效。

/*!
* 在被插桩对象最后一条指令的顺序执行路径上(如果存在该顺序执行路径)插入一次调用。
* 对于例程 (RTN):对所有返回路径都进行插桩。总是有效。
* 对于指令 (INS):仅当 INS_IsValidForIpointAfter() 返回 true 时才有效。
* 对于基本块 (BBL):仅当 BBL_HasFallThrough() 返回 true 时才有效。
* 对于追踪 (TRACE):仅当 TRACE_HasFallThrough() 返回 true 时才有效。
*/
IPOINT_AFTER,

/*!
* 在被插桩对象的任意位置插入一次调用。
* 除了 INS_InsertIfCall() 和 INS_InsertThenCall() 之外,适用于所有插桩函数。
*/
IPOINT_ANYWHERE,

/*!
* 在被插桩对象中控制流指令的被采取分支上插入一次调用。
* 对于指令而言,仅当 INS_IsValidForIpointTakenBranch() 返回 true 时才有效。
*/
IPOINT_TAKEN_BRANCH
} IPOINT;

类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
typedef enum
{
IARG_INVALID,
IARG_ADDRINT, ///< Type: ADDRINT. Constant value (additional arg required)
IARG_PTR, ///< Type: "VOID *". Constant value (additional pointer arg required)
IARG_BOOL, ///< Type: BOOL. Constant (additional BOOL arg required)
IARG_UINT32, ///< Type: UINT32. Constant (additional integer arg required)
IARG_UINT64, ///< Type: UINT64. Constant (additional UINT64 arg required)

/*!
* Type: ADDRINT. The address of the instrumented instruction. This value does not change at IPOINT_AFTER.
* This is simply shorthand for <tt>IARG_ADDRINT, INS_Address(ins)</tt>.
*/
IARG_INST_PTR,

/*!
* Type: ADDRINT for integer registers. Value of a register (additional register arg required) @ref REG
* Basically, this cannot be used to retrieve the value of registers whose size is larger than ADDRINT
* (e.g. x87 FPU/XMM/YMM/ZMM/opmask) or registers which are not architectural (REG_PIN_*), but there are some
* exceptions to this rule.
*/
IARG_REG_VALUE,

/*!
* Type: @ref UINT8*.
* Pointer to buffer holding the content of the requested register. buffer size is the size of the requested register.
* Register is specified in additional argument.
* Register is updated back to the relevant application register (Meaning if you change values in the mentioned pointer they will
* be propagated to the relevant application register). If you're not interested in modifying the register value, but only read
* its value use IARG_REG_CONST_REFERENCE instead.
*
* Not supported in Probe mode or with the Buffering APIs.
*
* @note additional REG arg required!!!
* @note If the requested register is a tile register and AMX is in init state (i.e., not active) - a NULL pointer will be
* passed to the analysis routine as the reference argument.
*/
IARG_REG_REFERENCE,

/*!
* Type: @ref UINT8*.
* Pointer to buffer holding the content of the requested register. buffer size is the size of the requested register.
* Register is specified in additional argument.
* Registers are not updated back to the relevant application register (Meaning if you change values in the mentioned pointer
* they will not be propagated to the relevant application register). If you're interested in modifying the register value,
* use IARG_REG_REFERENCE instead.
* Not supported with the Buffering APIs.
*
* @note additional REG arg required!!!
* @note If the requested register is a tile register and AMX is in init state (i.e., not active) - a NULL pointer will be
* passed to the analysis routine as the reference argument.
*/
IARG_REG_CONST_REFERENCE,

IARG_MEMORYREAD_EA, ///< Type: ADDRINT. Effective address of a memory read, only valid if INS_IsMemoryRead is true and at IPOINT_BEFORE
IARG_MEMORYREAD2_EA, ///< Type: ADDRINT. Effective address of a 2nd memory read (e.g. 2nd operand in cmps on ia32), only valid at IPOINT_BEFORE
IARG_MEMORYWRITE_EA, ///< Type: ADDRINT. Effective address of a memory write, only valid at IPOINT_BEFORE
/*!
* Type: UINT32. Size in bytes of memory read.
* This IARG is applicable for all instructions for which @ref INS_hasKnownMemorySize returns TRUE.
* For other instructions e.g. vgather/vscatter see @ref IARG_MULTI_ELEMENT_OPERAND.
* For the varying memory size read instruction, XRSTOR, the size is based on the XSAVE area header. If the header
* indicates compact mode it will provide the compact size. Otherwise it will provide the standard size.
* The minimum size for the XRSTOR instruction (not including FXRSTOR) is 576 (i.e the XSAVE area size up
* to and including the header).
*/
IARG_MEMORYREAD_SIZE,
/*!
* Type: UINT32. Size in bytes of memory write.
* This IARG is applicable for all instructions for which @ref INS_hasKnownMemorySize returns TRUE.
* For other instructions e.g. vgather/vscatter see @ref IARG_MULTI_ELEMENT_OPERAND.
* For varying size memory write instructions, the supported instructions are the XSAVE family of instructions.
* For XSAVE, the size of the XSAVE area used is based on user request and supported features in the machine. This will
* provide the exact size used.
* For XSAVEOPT, the size is calculated the same as if XSAVE was used. This may provide in some cases an upper
* bound to the actual used memory.
* For XSAVEC, in IPOINT_BEFORE the size is calculated based on user request only. This may provide in some cases
* an upper bound to the actual size.
* If used in IPOINT_AFTER it will provide the exact compact size as defined in the XSAVE area header.
* The minimum size for the XSAVE family write instructions (not including FXSAVE) is 576 (i.e the XSAVE area
* size up to and including the header).
*/
IARG_MEMORYWRITE_SIZE,

// IARG_MEMORY*_PTR argument represents actual address of the memory access, even if the operand is rewritten
// or a memory translation callback is registered
/*!
* Actual address of the memory access; same as @ref IARG_MEMORYREAD_EA, unless the memory address is translated
* by @ref MEMORY_ADDR_TRANS_CALLBACK, or the memory operand is rewritten by @ref INS_RewriteMemoryOperand
*/
IARG_MEMORYREAD_PTR,
/*!
* Actual address of the memory access; same as @ref IARG_MEMORYREAD2_EA, unless the memory address is translated
* by @ref MEMORY_ADDR_TRANS_CALLBACK, or the memory operand is rewritten by @ref INS_RewriteMemoryOperand
*/
IARG_MEMORYREAD2_PTR,
/*!
* Actual address of the memory access. Same as @ref IARG_MEMORYWRITE_EA, unless the memory address is translated
* by @ref MEMORY_ADDR_TRANS_CALLBACK, or the memory operand is rewritten by @ref INS_RewriteMemoryOperand
*/
IARG_MEMORYWRITE_PTR,
/*!
* Actual address of the memory access. Same as @ref IARG_MEMORYOP_EA, unless the memory address is translated
* by @ref MEMORY_ADDR_TRANS_CALLBACK, or the memory operand is rewritten by @ref INS_RewriteMemoryOperand. \n
* When using @ref INS_RewriteMemoryOperand the value is valid only with IPOINT_AFTER.
*/
IARG_MEMORYOP_PTR,

/*!
* Type: PIN_MULTI_MEM_ACCESS_INFO* the addresses read by the various vgather* instructions,
* Also available for regular memory instructions
*/
IARG_MULTI_MEMORYACCESS_EA,

/*!
* Type: @ref IMULTI_ELEMENT_OPERAND * . Information about a multi element operand (implemented for IPOINT_BEFORE).
* The operand index is required as the next argument.
*/
IARG_MULTI_ELEMENT_OPERAND,

/*!
* Type: @ref ISCATTERED_MEMORY_REWRITE * . Interface that allows rewriting elemenets addresses for instructions
* with scattered memory access (implemented for IPOINT_BEFORE).
* Only valid for instructions where @ref INS_IsValidForIarg for this IARG returns TRUE.
*/
IARG_REWRITE_SCATTERED_MEMOP,

IARG_EXPLICIT_MEMORY_EA, ///< Type: ADDRINT. Effective address of the explicit memory operand. Useful for instrumenting LEA instructions

IARG_BRANCH_TAKEN, ///< Type: BOOL. Non zero if a branch is taken. Argument is invalid for XBEGIN and XEND instructions.
/*! Type: ADDRINT. Target address of branch instruction.\n
* In case of INS instrumentation, valid when @ref INS_IsControlFlow() is true.\n
* However, this argument is invalid if the instruction is XBEGIN or XEND.
* In Linux, for the case of branching into the vsyscall area (in kernel 5.3 or above), the target
* address that will be received in the analysis routine would be that of the vsyscall area. Note that
* this address is not readable (e.g. via PIN_SafeCopy() and PIN_FetchCode())
*/
IARG_BRANCH_TARGET_ADDR,
/*! Type: ADDRINT. Fall through address of the instrumented object.\n
* In case of INS instrumentation, valid only if @ref INS_HasFallThrough() is true.
*/
IARG_FALLTHROUGH_ADDR,

IARG_EXECUTING, ///< Type: BOOL. False if the instruction will not be executed because of predication, otherwise true.

/*! Type: BOOL. True if INS_HasRealRep(ins) and this the first iteration of the REP sequence, otherwise false.\n
* @note In case count register is 0 when calling analysis routine, the value of IARG_FIRST_REP_ITERATION is false.
*/
IARG_FIRST_REP_ITERATION,
IARG_PREDICATE, ///< Reserved. Do not use

// These are internal only
IARG_STACK_VALUE, ///< Reserved. Do not use
IARG_STACK_REFERENCE, ///< Reserved. Do not use
IARG_MEMORY_VALUE, ///< Reserved. IA32(e) only
IARG_MEMORY_REFERENCE, ///< Reserved. IA32(e) only

// Syscall stuff
IARG_SYSCALL_NUMBER, ///< Type: ADDRINT. System call number. Valid for IPOINT_BEFORE at the system call instruction
IARG_SYSARG_REFERENCE, ///< Type: "ADDRINT *". Pointer to system call argument n. Valid for IPOINT_BEFORE at the system call instruction. (First argument number is 0.)

IARG_SYSARG_VALUE, ///< Type: ADDRINT. System call argument n. Valid for IPOINT_BEFORE at the system call instruction. (First argument number is 0.)
IARG_SYSRET_VALUE, ///< Type: ADDRINT. System call return value. On Linux and macOS* the value is -1 if the system call failed. (IPOINT_AFTER only)
IARG_SYSRET_ERRNO, ///< Type: INT32. System call errno (IPOINT_AFTER_only).

// function arguments
/*!
* Type: "ADDRINT *". Pointer to integer argument n. Valid only at the call site. (First argument number is 0.)
*/
IARG_FUNCARG_CALLSITE_REFERENCE,
/*!
* Type: ADDRINT. Integer argument n. Valid only at the call site. (First argument number is 0.)
*/
IARG_FUNCARG_CALLSITE_VALUE,
/*!
* Type: "ADDRINT *". Pointer to integer argument n. Valid only at the entry point of a routine. (First argument number is 0.)
*/
IARG_FUNCARG_ENTRYPOINT_REFERENCE,
/*!
* Type: ADDRINT. Integer argument n. Valid only at the entry point of a routine. (First argument number is 0.)
*/
IARG_FUNCARG_ENTRYPOINT_VALUE,
IARG_FUNCRET_EXITPOINT_REFERENCE, ///< Type: "ADDRINT *". Pointer to function result. Valid only at return instruction.
IARG_FUNCRET_EXITPOINT_VALUE, ///< Type: ADDRINT. Function result. Valid only at return instruction.

IARG_RETURN_IP, ///< Type: ADDRINT. Return address for function call, valid only at the function entry point.

IARG_ORIG_FUNCPTR, ///< Type: AFUNPTR. Function pointer to the relocated entry of the original uninstrumented function.

IARG_PROTOTYPE, ///< Type: PROTO. The function prototype of the application function. See @ref PROTO

IARG_THREAD_ID, ///< Type: THREADID. Application thread id.

/*!
* Type: @ref CONTEXT *. Handle to access a context (architectural state). When passed at
* @ref IPOINT_AFTER or @ref IPOINT_TAKEN_BRANCH, PC points to the next instruction. Upon return from
* the analysis routine, Pin ignores any changes you've made to the @ref CONTEXT. If you want
* to change register values, use @ref IARG_REG_REFERENCE, @ref IARG_RETURN_REGS, or @ref PIN_ExecuteAt.
* See @ref PROBE_IARGS for probe mode restrictions. Not supported with the Buffering APIs.
*/
IARG_CONTEXT,
/*!
* Type: @ref CONTEXT *.
* Like @ref IARG_CONTEXT, but tool receives a read-only @ref CONTEXT*.
* @ref PIN_SetContextReg, @ref PIN_SetContextRegval and @ref PIN_SetContextFPState will not work with
* @ref IARG_CONST_CONTEXT.
* The overhead of @ref IARG_CONST_CONTEXT is considerably lower than that of @ref IARG_CONTEXT.
* Tools that need a @ref CONTEXT* and only read from it should use @ref IARG_CONST_CONTEXT.
* Tools that need a @ref CONTEXT* and only occasionally write into it should also use @ref IARG_CONST_CONTEXT (by
* using PIN_SaveContext() - see below).
* One example of a tool that needs a @ref CONTEXT * and only occasionally writes into it, would be an
* emulator that would need to write into the @ref CONTEXT * only when an exception occurs, and then
* raise an exception with the @ref CONTEXT *.
* @ref PIN_SaveContext can be used by the tool to get a writable copy of the @ref CONTEXT *.
* @ref IARG_CONST_CONTEXT is available only in Jit mode.
*/
IARG_CONST_CONTEXT,
/*!
* Type: @ref CONTEXT *. Additional two @ref REGSET * arguments are needed 'inSet' and 'outSet'.
* Like @ref IARG_CONTEXT, but tool needs to define also which registers set it needs to read (inSet)
* and which registers set it may write (outSet).
* @ref PIN_SetContextReg, @ref PIN_SetContextRegval and @ref PIN_SetContextFPState will work only on registers
* that are in the outSet. These functions will change the actual application registers so there is no need
* to perform @ref PIN_ExecuteAt as need to be done with @ref IARG_CONTEXT.
* @ref PIN_GetContextReg, @ref PIN_GetContextRegval and @ref PIN_GetContextFPState will return unexpected values
* for registers not defined in the 'inSet'.
* The overhead of @ref IARG_PARTIAL_CONTEXT may lower than that of @ref IARG_CONTEXT as there is no need to perform
* @ref PIN_ExecuteAt to update registers.
* Tools that know which application registers are needed to be read should use @ref IARG_PARTIAL_CONTEXT instead of
* @ref IARG_CONST_CONTEXT. Thus may reduce the overhead as not fully updated context for read need to be provided.
* Tools that know which application registers are needed to be written other than @ref REG_INST_PTR should use
* @ref IARG_PARTIAL_CONTEXT instead of combination of @ref IARG_CONTEXT and the use of @ref PIN_ExecuteAt
* for registers update.
* If @ref REG_INST_PTR needs to be updated @ref PIN_ExecuteAt still needs to be used.
* Any updates to a register from the outSet of an @ref IARG_PARTIAL_CONTEXT will be propagated to the application upon
* return from the analysis routine. Tools that want to update @ref CONTEXT registers without affecting application
* registers should use @ref IARG_CONTEXT.
* @ref IARG_PARTIAL_CONTEXT is available only in Jit mode.
*/
IARG_PARTIAL_CONTEXT,

/*!
* Type: @ref REGSET *.
* Used to specify registers whose values will be the same upon return from the analysis routine.
* Should be used to specify caller-saved registers which are preserved by the analysis routine.
* When a non-inlinable analysis routine preserves caller-saved registers, Pin can avoid generating
* code to preserve these registers, across the analysis call, if they are specified in the IARG_PRESERVE.
* e.g. if a non-inlinable analysis routine preserves the values in the x87 registers, then Pin
* can avoid generating code to preserve these registers, across the analysis call, if REG_X87 is specified in
* the @ref IARG_PRESERVE
* e.g. if a non-inlinable analysis routine preserves the values in XMM caller saved registers, then Pin
* can avoid generating code to preserve these registers, across the analysis call, if those preserved XMM
* registers are specified in the @ref IARG_PRESERVE
* @ref IARG_PRESERVE must be followed by a @ref REGSET* that points to the @ref REGSET containing the registers preserved.
* See the @ref REGSET_AddAll "(REGSET_*)" functions defined under @ref REG for @ref REGSET construction and manipulation.
*/
IARG_PRESERVE,

/*! Type: BOOL.
* Used to mark analysis routine that is expected to be inlined.
* If the analysis ruotine is not inlinable and -assert_check_inline has been passed then Pin will assert.
*/
IARG_CHECK_INLINE,

IARG_RETURN_REGS, ///< Register to write analysis function return value (additional register arg required). Not supported in Probe mode.

IARG_CALL_ORDER, ///< Type: CALL_ORDER. Determine order of analysis calls. See @ref CALL_ORDER.

IARG_IARGLIST, ///< Type: IARGLIST. List of IARGS which can be constructed one IARG at a time.

IARG_FAST_ANALYSIS_CALL, ///< No type: Use a fast linkage to call the analysis function. See @ref PIN_FAST_ANALYSIS_CALL

/*!
* Type: @ref REGSET *.
* The only supported register for now is REG_MXCSR. In case it is not included into the register set
* then an error is issued.
* . Upon entry to the analysis routine, the MXCSR physical register value is loaded with the application value.
* . Upon termination of the routine the physical MXCSR register including the changes are visible by
* the application.
* . As a side effect, the analysis routine will never be inlined.
* @ref IARG_EXPOSE must be followed by a @ref REGSET* that points to the @ref REGSET containing the registers
* exposed, REG_MXCSR must be included and be the only one.
*/
IARG_EXPOSE,

IARG_MEMORYOP_EA, ///< Type: ADDRINT. Effective address of a memory op (memory op index is next arg); only valid at IPOINT_BEFORE
IARG_MEMORYOP_SIZE, ///< Type: UINT32. Size of a memory op (memory op index is next arg)
IARG_MEMORYOP_MASKED_ON, ///< Type: BOOL. TRUE if the this memory op (memory op index is next arg) is masked on; only valid at IPOINT_BEFORE
IARG_TSC, ///< Type: UINT64. Time Stamp Counter value at the point of entering the analysis call.
IARG_FILE_NAME, ///< Reserved for internal use only.
IARG_LINE_NO, ///< Reserved for internal use only.
IARG_LAST ///< Reserved for internal use only.

} IARG_TYPE;

这里类型是IARG_INST_PTR,表示当前指令地址,IARG_END表示参数列表结尾

插桩函数

这里的插桩函数是由AddFunction函数设置的函数中的CALL函数

具体有很多,可以vs双击直接看定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
INS_InsertPredicatedCall
INS_InsertCall
INS_InsertIfCall
INS_InsertThenCall
INS_InsertIfPredicatedCall
INS_InsertThenPredicatedCall

BBL_InsertCall
BBL_InsertIfCall
BBL_InsertThenCall

TRACE_InsertCall
TRACE_InsertIfCall
TRACE_InsertThenCall

//IMAGE没有插桩函数,只有设置某个时机回调函数的AddFunction函数

注册函数

内容来自有毒的学Pin记录

  • INS_AddInstrumentFunction (INSCALLBACK fun, VOID *val):注册以指令粒度插桩的函数
  • TRACE_AddInstrumentFunction (TRACECALLBACK fun, VOID *val):注册以trace粒度插桩的函数
  • RTN_AddInstrumentFunction (RTNCALLBACK fun, VOID *val):注册以routine粒度插桩的函数
  • IMG_AddInstrumentFunction (IMGCALLBACK fun, VOID *val):注册以image粒度插桩的函数
  • PIN_AddFiniFunction (FINICALLBACK fun, VOID *val):注册在应用程序退出前执行的函数,该类函数不进行插桩,可以有多个。
  • PIN_AddDetachFunction (DETACHCALLBACK fun, VOID *val):注册在Pin通过PIN_Detach()函数放弃对应用程序的控制权限之前执行的函数,一个进程只调用一次,可以被任何线程调用,此时Pin的内存并没有释放。

修改指令的代码

1
2
3
4
INS_InsertDirectJump
INS_InsertIndirectJump

INS_RewriteMemoryOperand

注意

指令执行时,每条指令调用一次插桩函数,但是调用多次分析代码,所以尽量将大部分判断放到插桩函数中,然后再在插桩函数中执行精简后的分析代码,而不是把大部分操作放到分析代码中。

其它的优化性能内容见:有毒的学Pin记录

有关链接

pin使用小记-函数分析 | Anhkgg’Lab | Windows Kernel | Rootkit | Reverse Engineer | Expolit | 内核研究 | 逆向分析 | 漏洞分析挖掘

bbs.kanxue.com/article-16660.htm

使用 Pin 来实现对 Windows 函数及其参数的追踪