使用图钉添加您自己的说明

时间:2019-03-05 17:16:16

标签: c++ c x86 profiling intel-pin

是否可以在intel-pin生成的代码中添加您自己的代码?

我想知道一会儿,我创建了一个简单的工具:

#include <fstream>
#include <iostream>
#include "pin.H"

// Additional library calls go here

/*********************/

// Output file object
ofstream OutFile;

//static uint64_t counter = 0;

uint32_t lock = 0;
uint32_t unlock = 1;
std::string rtin = "";
// Make this lock if you want to print from _start
uint32_t key = unlock;

void printmaindisas(uint64_t addr, std::string disassins)
{
    std::stringstream tempstream;
    tempstream << std::hex << addr;
    std::string address = tempstream.str();
    if (key)
        return;
    if (addr > 0x700000000000)
        return;
    std::cout<<address<<"\t"<<disassins<<std::endl;
}

void mutex_lock()
{

key = !lock;
std::cout<<"out\n";

}
void mutex_unlock()
{

    key = lock;
    std::cout<<"in\n";

}

void Instruction(INS ins, VOID *v)
{
    //if
  // Insert a call to docount before every instruction, no arguments are passed
  INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printmaindisas, IARG_ADDRINT, INS_Address(ins),
  IARG_PTR, new string(INS_Disassemble(ins)), IARG_END);
    //std::cout<<INS_Disassemble(ins)<<std::endl;
}

void Routine(RTN rtn, VOID *V)
{
    if (RTN_Name(rtn) == "main")
    {
        //std::cout<<"Loading: "<<RTN_Name(rtn) << endl;
        RTN_Open(rtn);
        RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)mutex_unlock, IARG_END);
        RTN_InsertCall(rtn, IPOINT_AFTER, (AFUNPTR)mutex_lock, IARG_END);
        RTN_Close(rtn);
    }
}

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "mytool.out", "specify output file name");
/*
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 " << count << endl;
    OutFile.close();
}
*/

int32_t Usage()
{
  cerr << "This is my custom tool" << endl;
  cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
  return -1;
}

int main(int argc, char * argv[])
{
  // It must be called for image instrumentation
  // Initialize the symbol table
  PIN_InitSymbols();

  // Initialize pin
  if (PIN_Init(argc, argv)) return Usage();
  // Open the output file to write
  OutFile.open(KnobOutputFile.Value().c_str());

  // Set instruction format as intel
    // Not needed because my machine is intel
  //PIN_SetSyntaxIntel();

  RTN_AddInstrumentFunction(Routine, 0);
  //IMG_AddInstrumentFunction(Image, 0);

  // Add an isntruction instrumentation
  INS_AddInstrumentFunction(Instruction, 0);

  //PIN_AddFiniFunction(Fini, 0);

  // Start the program here
  PIN_StartProgram();

  return 0;

}

如果我打印以下c代码(实际上什么也不做):

int main(void)
{}

给我这个输出:

in
400496  push rbp
400497  mov rbp, rsp
40049a  mov eax, 0x0
40049f  pop rbp
out

并使用以下代码:

#include <stdio.h>
int main(void)
{
  printf("%s\n", "Hello");
}

打印:

in
4004e6  push rbp
4004e7  mov rbp, rsp
4004ea  mov edi, 0x400580
4004ef  call 0x4003f0
4003f0  jmp qword ptr [rip+0x200c22]
4003f6  push 0x0
4003fb  jmp 0x4003e0
4003e0  push qword ptr [rip+0x200c22]
4003e6  jmp qword ptr [rip+0x200c24]
Hello
4004f4  mov eax, 0x0
4004f9  pop rbp
out

所以,我的问题是,是否可以添加:

4004ea  mov edi, 0x400580
4004ef  call 0x4003f0
4003f0  jmp qword ptr [rip+0x200c22]
4003f6  push 0x0
4003fb  jmp 0x4003e0
4003e0  push qword ptr [rip+0x200c22]
4003e6  jmp qword ptr [rip+0x200c24]
第一个代码中的

指令(没有打印功能的代码),使用检测例程/分析例程中的pin,以便可以模仿第二个代码(通过动态添加这些指令)? (我不想直接调用printf,而是想模仿行为)(将来,我想考虑使用pin模仿健全性检查器或intel mpx,如果我可以通过某种方式动态添加这些检查指令的话)

我看着pin documentation,它有instruction modification api,但是它只能用于添加直接/间接分支或删除指令(但我们不能添加添加新的分支)。

能帮我吗?我还要感谢您对此问题的调查。

1 个答案:

答案 0 :(得分:2)

分析例程(或替换例程)实际上只是将代码插入到正在分析的应用程序中。但是在我看来,您想要修改一个或多个应用程序上下文的寄存器。默认情况下,执行分析例程时,Pin运行时会在进入分析例程时保存应用程序上下文,然后在例程返回时将其还原。基本上,这使分析例程得以执行,而无需对应用程序进行任何意外更改。但是,Pin提供了三种在分析或替换例程中修改应用程序上下文的方法:

  • IARG_RETURN_REGS参数传递给例程。从例程返回的值存储在应用程序上下文的指定寄存器中。这使您能够更改其大小不超过例程的返回值类型ADDRINT的大小的任何单个寄存器。探针模式或缓冲API 1 不支持此功能。但是,这是更改单个寄存器的最有效方法。
  • 为例程中要修改的每个寄存器传递一个IARG_REG_REFERENCE参数。对于每个这样的参数,您需要在类型PIN_REGISTER*的例程的声明中添加一个参数。探针模式或缓冲API不支持此功能,但这是更改几个寄存器并支持所有寄存器的最有效方法。
  • IARG_CONTEXT参数传递给例程。您需要在类型CONTEXT*的例程的声明中添加一个参数。使用上下文操作API更改应用程序上下文的一个或多个寄存器。例如,您可以使用RIP更改应用程序上下文的PIN_SetContextReg(ctxt, REG_INST_PTR, NewRipValue)寄存器。为了使上下文更改生效,必须调用PIN_ExecuteAt,它会在具有指定上下文的可能更改的RIP处恢复应用程序的执行。缓冲API不支持此功能,并且探针模式存在一些限制。

例如,如果您想在应用程序上下文中执行mov edi, 0x400580,则只需在分析例程中将值0x400580存储在应用程序上下文的EDI寄存器中:

r->dword[0] = 0x400580;
r->dword[1] = 0x0;      // See: https://stackoverflow.com/questions/11177137/why-do-x86-64-instructions-on-32-bit-registers-zero-the-upper-part-of-the-full-6

其中r的类型为PIN_REGISTER*。或者:

PIN_SetContextReg(ctxt, REG_EDI, 0x400580); // https://stackoverflow.com/questions/38782709/what-is-the-default-type-of-integral-literals-represented-in-hex-or-octal-in-c

稍后应用恢复执行时,RDI将包含0x400580。

请注意,您可以在分析例程中更改任何有效的内存位置,无论它属于应用程序还是Pin工具。例如,如果应用程序上下文的RAX寄存器包含一个指针,则您可以像访问其他任何指针一样直接访问该指针处的内存位置。


脚注:

(1)似乎您没有使用探测模式或缓冲API。