добавьте свои собственные инструкции, используя пин-код - PullRequest
2 голосов
/ 05 марта 2019

Можно ли добавить собственный код в код, сгенерированный 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]

инструкции в моем первом коде (код без функции печати), используя пин-код в процедуре инструментария / или в процедуре анализа, чтобы я мог имитировать мой второй код (путем динамического добавления этих инструкций)? (Я не хочу вызывать printf напрямую, но хочу имитировать поведение) (в будущем я думал об имитации проверки работоспособности или intel mpx с использованием pin, если бы я мог каким-то образом динамически добавить эти инструкции проверки)

Я посмотрел документацию по выводам , в нем есть модификация инструкции api , но его можно использовать только для добавления прямых / косвенных ветвей или удаления инструкций (но мы не можем добавить добавить новые).

Не могли бы вы мне помочь? Также я хочу заранее поблагодарить за изучение этого вопроса.

1 Ответ

2 голосов
/ 28 марта 2019

Процедура анализа (или процедура замены) - это просто код, вставленный в профилируемое приложение. Но мне кажется, что вы хотите изменить один или несколько регистров контекста приложения. По умолчанию при выполнении подпрограммы анализа среда выполнения Pin сохраняет контекст приложения при входе в подпрограмму анализа, а затем восстанавливает его при возврате. Это в основном позволяет выполнять процедуру анализа без каких-либо непреднамеренных изменений в приложении. Однако Pin предоставляет три способа изменения контекста приложения в процедуре анализа или замены:

  • Передайте аргумент IARG_RETURN_REGS в процедуру. Возвращаемое из подпрограммы значение сохраняется в указанном регистре контекста приложения. Это позволяет вам изменять любой отдельный регистр, размер которого не превышает размер ADDRINT, который является типом возвращаемого значения подпрограммы. Это не поддерживается в режиме Probe или с API буферизации 1 . Однако это самый эффективный способ изменить один регистр.
  • Передайте аргумент IARG_REG_REFERENCE для каждого регистра, который вы хотите изменить в подпрограмме. Для каждого такого аргумента вам необходимо добавить параметр в объявление подпрограммы типа PIN_REGISTER*. Это не поддерживается в режиме Probe или с API буферизации, но это наиболее эффективный способ изменить пару регистров и поддерживает все регистры.
  • Передайте аргумент IARG_CONTEXT в процедуру. Вам необходимо добавить параметр в объявление подпрограммы типа CONTEXT*. Используйте API для управления контекстом, чтобы изменить один или несколько регистров контекста приложения. Например, вы можете изменить регистр RIP контекста приложения, используя PIN_SetContextReg(ctxt, REG_INST_PTR, NewRipValue). Чтобы изменения контекста вступили в силу, необходимо вызвать PIN_ExecuteAt, что возобновит выполнение приложения с потенциально измененным RIP с указанным контекстом. Это не поддерживается API буферизации и существуют ограничения в режиме Probe.

Например, если вы хотите выполнить 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) Похоже, вы не используете режим Probe или API буферизации.

...