Intel Pin仅插装子进程expr问题:全局变量trg_pid未更新
问题根源
Intel Pin 工具中,全局变量是每个被插装进程独立拥有一份副本的。你在FollowChild回调中设置的trg_pid,仅会修改 Pin 控制进程或父进程(run.sh)的工具副本值,而expr进程的工具副本里的trg_pid仍保持初始值0,导致print_ins无法正确识别目标进程。
解决方法
以下是三种可行的解决方案,按实现复杂度从低到高排序:
方法一:通过进程名称直接过滤(最简单)
在插装回调中直接获取当前进程的可执行文件名称,判断是否为expr后再执行插装逻辑:
#include <cstring> #include "pin.H" VOID YourCallback(ADDRINT ins_ptr) { // 你的指令处理逻辑,比如打印地址 printf("Indirect call at: 0x%lx\n", ins_ptr); } VOID print_ins(INS ins, VOID *v) { // 获取当前进程的可执行文件路径 const char* app_path = PIN_GetApplicationName(); // 匹配进程名(兼容绝对路径和相对路径) if (strstr(app_path, "expr") != nullptr) { // 仅处理间接调用指令 if (INS_IsIndirectCall(ins)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)YourCallback, IARG_INST_PTR, IARG_END); } } } int main(int argc, char *argv[]) { if (PIN_Init(argc, argv)) return 1; INS_AddInstrumentFunction(print_ins, nullptr); PIN_StartProgram(); return 0; }
注意:如果需要精确匹配进程名(避免误匹配含expr字符串的其他进程),可以用basename函数提取路径的最后部分再对比。
方法二:利用 Pin 跨进程共享数据存储目标 PID
使用 Pin 提供的PIN_SetDataInPinTool和PIN_GetDataFromPinTool接口,在FollowChild回调中存储expr的 PID,再在插装回调中读取并对比:
#include <cstring> #include "pin.H" // 共享数据的唯一标识Key static const int TRG_PID_KEY = 0; VOID YourCallback(ADDRINT ins_ptr) { printf("Indirect call at: 0x%lx\n", ins_ptr); } VOID FollowChild(CHILD_PROCESS childProcess, VOID *userData) { PID child_pid = CHILD_PROCESS_GetId(childProcess); const char* child_path = CHILD_PROCESS_GetExecutablePath(childProcess); if (strstr(child_path, "expr") != nullptr) { // 将目标PID存入Pin共享存储 PIN_SetDataInPinTool(TRG_PID_KEY, &child_pid, sizeof(PID)); // 允许插装该子进程 CHILD_PROCESS_SetPinFlags(childProcess, PIN_FLAG_DEFAULT); } else { // 跳过其他子进程的插装 CHILD_PROCESS_SetPinFlags(childProcess, PIN_FLAG_NEVER_PIN); } } VOID print_ins(INS ins, VOID *v) { PID trg_pid = 0; // 从共享存储读取目标PID if (PIN_GetDataFromPinTool(TRG_PID_KEY, &trg_pid, sizeof(PID)) == PIN_DATA_SUCCESS) { PID current_pid = PIN_GetApplicationPID(); if (current_pid == trg_pid && INS_IsIndirectCall(ins)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)YourCallback, IARG_INST_PTR, IARG_END); } } } int main(int argc, char *argv[]) { if (PIN_Init(argc, argv)) return 1; PIN_AddFollowChildProcessFunction(FollowChild, nullptr); INS_AddInstrumentFunction(print_ins, nullptr); PIN_StartProgram(); return 0; }
方法三:通过环境变量标记目标进程
在FollowChild回调中给expr进程添加专属环境变量,插装回调通过检查环境变量判断是否为目标进程:
#include <cstring> #include <cstdlib> #include "pin.H" VOID YourCallback(ADDRINT ins_ptr) { printf("Indirect call at: 0x%lx\n", ins_ptr); } VOID FollowChild(CHILD_PROCESS childProcess, VOID *userData) { const char* child_path = CHILD_PROCESS_GetExecutablePath(childProcess); if (strstr(child_path, "expr") != nullptr) { // 添加环境变量标记目标进程 CHILD_PROCESS_AddEnvironmentVariable(childProcess, "PIN_TRACK_EXPR=1"); CHILD_PROCESS_SetPinFlags(childProcess, PIN_FLAG_DEFAULT); } else { CHILD_PROCESS_SetPinFlags(childProcess, PIN_FLAG_NEVER_PIN); } } VOID print_ins(INS ins, VOID *v) { // 检查环境变量是否存在并匹配 char* track_flag = getenv("PIN_TRACK_EXPR"); if (track_flag != nullptr && strcmp(track_flag, "1") == 0 && INS_IsIndirectCall(ins)) { INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)YourCallback, IARG_INST_PTR, IARG_END); } } int main(int argc, char *argv[]) { if (PIN_Init(argc, argv)) return 1; PIN_AddFollowChildProcessFunction(FollowChild, nullptr); INS_AddInstrumentFunction(print_ins, nullptr); PIN_StartProgram(); return 0; }
注意事项
- 如果不需要插装
run.sh本身,可以在main函数中提前判断当前进程名,直接跳过插装逻辑。 - 进程名匹配时,根据实际场景选择精确匹配或模糊匹配,避免误判。
- 确保使用的 Pin API 与你的 Pin 版本兼容,部分旧版本可能存在接口差异。
内容的提问来源于stack exchange,提问作者CowHorse
相关产品推荐
相关产品推荐

