使用 Intel PIN 在寻址模式下检测具有特定寄存器的 mov dword ptr [rbp - ...] 指令

Posted

技术标签:

【中文标题】使用 Intel PIN 在寻址模式下检测具有特定寄存器的 mov dword ptr [rbp - ...] 指令【英文标题】:Detecting mov dword ptr [rbp - ...] instructions with a specific register in the addressing mode, using Intel PIN 【发布时间】:2019-08-15 05:49:23 【问题描述】:

我想使用 Intel PIN 检测像 mov dword ptr [rbp-0x28], 0x7 这样的指令(所以,所有 mov dword ptr [rbp-0xxx], xxx 格式的指令)(主要是为了获取数组写入信息)。在未优化的代码中,这应该使大多数存储到局部变量。

我能做到:

if (INS_Opcode(ins) == XED_ICLASS_MOV)
   instruction detection;

检测mov 指令。但是,除此之外,它还会检测其他指令,例如mov eax, 0x0。我想用dword ptr size 指令检测指令。

我检查了引脚指令检查API 和引脚xed-iclass-enum。使用该文档,我尝试了类似的方法:

if ((INS_Opcode(ins) == XED_ICLASS_MOV) && INS_OperandIsMemory(ins, 0))
    instruction detection;

这给了我想要的结果。但也给了我像mov esi, eax这样的指令(我不想要)。

我的代码:

#include <fstream>
#include <iostream>
#include "pin.H"
#include <stack>
#include <unordered_map>
// Additional library calls go here

// Stack allocation

struct Node

    int value;
;

std::stack<Node> mainStack;

// Ins object mapping

class Insr

private:
    INS insobject;

public:
    Insr(INS insob)
    
        insobject = insob;
    
    INS get_insobject()
    
        return insobject;
    
;

static std::unordered_map<ADDRINT, Insr*> insstack;

// Output file object
ofstream OutFile;

//static uint64_t counter = 0;

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

void printmaindisas(uint64_t addr, std::string disassins)

    std::stringstream tempstream;
    tempstream << std::hex << addr;
    std::string address = tempstream.str();
    // if (addr > 0x700000000000)
    //  return;
    if (addr > 0x700000000000)
        return;
    if (!key)
        return;
    // if (insstack[addr]->get_opcode() == XED_ICLASS_ADD || insstack[addr]->get_opcode()
    //      == XED_ICLASS_SUB)
    INS ins = insstack[addr]->get_insobject();
    if((INS_Opcode(ins) == XED_ICLASS_ADD || INS_Opcode(ins) == XED_ICLASS_SUB)
            &&(INS_OperandIsImmediate(ins, 1)))
    
      int value = INS_OperandImmediate(ins, 1);
        std::cout << "value: " << value << '\n';
        Node nodevalue;
        mainStack.push(node);
        std::cout << "stack top: " << mainStack.top().value << '\n';
    
    if ((INS_Opcode(ins) == XED_ICLASS_MOV) && INS_OperandIsMemory(ins, 0))
    
            std::cout << "yes!" << '\n';
    


    std::cout<<address<<"\t"<<disassins<<std::endl;


void mutex_lock()

key = 0;
std::cout<<"out\n";

void mutex_unlock()

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


void Instruction(INS ins, VOID *v)


    insstack.insert(std::make_pair(INS_Address(ins), new Insr(ins)));
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)printmaindisas, IARG_ADDRINT, INS_Address(ins),
    IARG_PTR, new string(INS_Disassemble(ins)), IARG_END);


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
    // PIN_Init must be called before PIN_StartProgram
    // as mentioned in the documentation
  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);

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


  //PIN_AddFiniFunction(Fini, 0);

  // Start the program here
  PIN_StartProgram();

  return 0;


我得到的输出:

in
40051e  push rbp
value: -128
stack top: -128
40051f  mov rbp, rsp
400522  add rsp, 0xffffffffffffff80
yes!
400526  mov dword ptr [rbp-0x28], 0x7
yes!
40052d  mov dword ptr [rbp-0x64], 0x9
400534  mov eax, 0x0
400539  call 0x4004e6
4004e6  push rbp
value: 64
stack top: 64
4004e7  mov rbp, rsp
4004ea  sub rsp, 0x40
yes!
4004ee  mov dword ptr [rbp-0xc], 0x4
4004f5  lea rax, ptr [rbp-0xc]
yes!
4004f9  mov qword ptr [rbp-0x8], rax
4004fd  mov rax, qword ptr [rbp-0x8]
400501  mov eax, dword ptr [rax]
yes!
400503  mov esi, eax
400505  mov edi, 0x4005d0
40050a  mov eax, 0x0
40050f  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]
4
yes!
400514  mov dword ptr [rbp-0x3c], 0x3
40051b  nop
40051c  leave 
40051d  ret 
40053e  mov eax, 0x0
400543  leave 
out

这是正确的方法吗(没有任何误报)?

【问题讨论】:

INS_Opcode(ins) != REG_ESI 部分没有意义。你的意思是别的吗?您可以使用INS_OperandWidth 检查内存操作数的大小。如果我正确理解你的问题,你想检查INS_OperandWidth(ins, 0) == 32 感谢您的回复。如果没有INS_Opcode(ins) != REG_ESI,它会检测到像mov esi, eax 这样的指令。此外,我通过执行if ((INS_Opcode(ins) == XED_ICLASS_MOV) &amp;&amp; (INS_OperandWidth(ins, 0)==32)) 之类的操作尝试了您的方法,但它也检测到其他指令,例如“mov eax,0x0”(我相信这是有道理的)。我只想检测像 mov dword ptr [rbp-xxx], xxx 这样的指令 但是INS_OperandIsMemory 在这些情况下应该会失败,因为第一个操作数不是内存引用。 我认为 Hadi 的观点是 ESI 不是操作码,而是操作数。因此INS_Opcode(ins) != REG_ESI 没有意义。 你是对的。 INS_Opcode(ins) != REG_ESI 没有任何意义(我的错误)。我已经相应地编辑了这个问题。我明白您对INS_OperandIsMemory(ins, 0) 的看法,但我不确定我如何获得预期的结果。我还添加了我的代码和我得到的输出。 【参考方案1】:

如果您想接受以下所有说明:

mov [rbp + disp], reg/imm
mov [rbp*scale + disp], reg/imm
mov [reg + rbp*scale], reg/imm
mov [rbp + reg*scale + disp], reg/imm

那么您需要执行以下检查:

if (INS_Opcode(ins) == XED_ICLASS_MOV &&              // Check that the instruction is MOV.
    INS_OperandIsMemory(ins, 0) &&                    // Check that the destination operand is a memory operand.
    INS_OperandWidth(ins, 0) == 32 &&                 // Check that the size of the operand is 32 bits.
    (INS_OperandMemoryBaseReg(ins, 0) == REG_EBP || 
     INS_OperandMemoryIndexReg(ins, 0) == REG_EBP))  // Check that the base or index register is RBP.

    ...

请注意,这些检查接受带有位移(包括零位移)的 MOV 指令和不带位移的 MOV 指令(在语义上等效于零位移,但编码不同)。

我假设您希望同时接受 RBP 作为基址寄存器或索引寄存器(可能具有大于 1 的比例)。请注意,如果 RBP 用作基址寄存器,则指令的编码将始终包含位移。请参阅:Why are rbp and rsp called general purpose registers?。

如果您想接受以下所有使用RBP 作为基址寄存器的指令:

mov [rbp + disp], reg/imm
mov [rbp + reg*scale + disp], reg/imm

那么您需要执行以下检查:

if (INS_Opcode(ins) == XED_ICLASS_MOV &&              // Check that the instruction is MOV.
    INS_OperandIsMemory(ins, 0) &&                    // Check that the destination operand is a memory operand.
    INS_OperandWidth(ins, 0) == 32 &&                 // Check that the size of the operand is 32 bits.
    INS_OperandMemoryBaseReg(ins, 0) == REG_EBP)      // Check that the base is RBP.

    ...

如果您只想接受以下指令:

mov [rbp + disp], reg/imm

那么您需要执行以下检查:

if (INS_Opcode(ins) == XED_ICLASS_MOV &&                 // Check that the instruction is MOV.
    INS_OperandIsMemory(ins, 0) &&                       // Check that the destination operand is a memory operand.
    INS_OperandWidth(ins, 0) == 32 &&                    // Check that the size of the operand is 32 bits.
    INS_OperandMemoryBaseReg(ins, 0) == REG_EBP &&       // Check that the base is RBP.
    INS_OperandMemoryIndexReg(ins, 0) == REG_INVALID())  // Check that there is no index register.

    ...

如果要检查位移是否为负数,使用如下检查:

INS_OperandMemoryDisplacement(ins, 0) < 0

请注意,INS_OperandMemoryDisplacement 不区分没有位移的内存操作数和位移为零的内存操作数。如果没有位移,它只是返回零。如果要判断指令编码是否真的包含位移字段,则应使用 XED API。

【讨论】:

这太完美了!谢谢。 @Ruturaj 您的问题实际上有点模棱两可。我已经扩展了答案以涵盖我能想到的所有可能的解释。 [rbp*scale] 是不可能的(使用任何寄存器),只有 [reg*scale + disp32]。唯一具有索引但没有基址寄存器的编码使用 disp32:mod=00(通常没有位移)SIB.Base=101=RBP wiki.osdev.org/…。因此,这种特殊情况实际上意味着使用 disp32 as 基数而不是寄存器。你不能只是没有基础,只有 reg 或 disp32。 (对于 LEA 来说,复制和缩放是不幸的;它需要 4 个字节的零) @Hadi 是的,很抱歉给您带来不便。但是,你的问题很好地回答了它。是的,我确实做到了这一点:INS_OperandMemoryDisplacement(ins, 0) &lt; 0 将其保持在界限内。 @Peter 感谢分享这一观察结果。

以上是关于使用 Intel PIN 在寻址模式下检测具有特定寄存器的 mov dword ptr [rbp - ...] 指令的主要内容,如果未能解决你的问题,请参考以下文章

intel pin工具中图片的含义

Intel pin:检测运行过程

如何使用 Intel Pin 工具生成分支列表?

Intel Pin 获取函数参数编号

目标代码重定位和 Intel Pin 交互

Pin