英特尔引脚:分析例程检测到 ah 寄存器而不是 rsp (REG_STACK_PTR)

Posted

技术标签:

【中文标题】英特尔引脚:分析例程检测到 ah 寄存器而不是 rsp (REG_STACK_PTR)【英文标题】:intel Pin: analysis routine detects ah register instead of rsp (REG_STACK_PTR) 【发布时间】:2019-08-18 08:12:49 【问题描述】:

几天前我问了this的问题。

我想获取堆栈分配大小(在函数创建之后)。答案建议这样做:

if((INS_Opcode(ins) == XED_ICLASS_ADD || INS_Opcode(ins) == XED_ICLASS_SUB) && 
   REG(INS_OperandReg(ins, 0)) == REG_STACK_PTR && INS_OperandIsImmediate(ins, 1)

这在理论上是正确的并且是有道理的。但是,它在实践中不起作用(如果我在这里错了,请纠正我)。如果我删除REG(INS_OperandReg(ins, 0)) == REG_STACK_PTR 检查,它工作得很好。为什么?因为当使用REG(INS_OperandReg(ins, 0))检测时,pin检测不到REG_STACK_PTR寄存器。相反,它会检测ah(我相信是RAX),当我检查add rsp, 0xffffffffffffff80指令时(所以,每次它给出:register: ah),如下面的输出所示:

in
register: rbp
40051e  push rbp
register: *invalid*
value: -128
40051f  mov rbp, rsp
register: ah
400522  add rsp, 0xffffffffffffff80
register: *invalid*
400526  mov dword ptr [rbp-0x28], 0x7
register: *invalid*
40052d  mov dword ptr [rbp-0x64], 0x9
register: eax
400534  mov eax, 0x0
register: *invalid*
400539  call 0x4004e6
register: rbp
4004e6  push rbp
register: *invalid*
value: 64
4004e7  mov rbp, rsp
register: ah
4004ea  sub rsp, 0x40
register: *invalid*
4004ee  mov dword ptr [rbp-0xc], 0x4
register: rax
4004f5  lea rax, ptr [rbp-0xc]
register: *invalid*
4004f9  mov qword ptr [rbp-0x8], rax
register: rax
4004fd  mov rax, qword ptr [rbp-0x8]
register: eax
400501  mov eax, dword ptr [rax]
register: *invalid*
400503  mov esi, eax
register: edi
400505  mov edi, 0x4005d0
register: eax
40050a  mov eax, 0x0
register: rdi
40050f  call 0x4003f0
register: rdi
4003f0  jmp qword ptr [rip+0x200c22]
register: *invalid*
4003f6  push 0x0
register: *invalid*
4003fb  jmp 0x4003e0
register: *invalid*
4003e0  push qword ptr [rip+0x200c22]
register: rdi
4003e6  jmp qword ptr [rip+0x200c24]
4
register: *invalid*
400514  mov dword ptr [rbp-0x3c], 0x3
40051b  nop
register: *invalid*
40051c  leave 
register: *invalid*
40051d  ret 
register: eax
40053e  mov eax, 0x0
register: *invalid*
400543  leave 
out

好吧,有趣的是,它对每次出现 rsp 都执行此操作(即,它检测到 ah 而不是 rsp)。另外,它总是打印指令400522 add rsp, 0xffffffffffffff80,包括rsp(那么,为什么这里不打印ah?)

如果ah 以某种方式代表rsp,那么我总是可以使用REG(INS_OperandReg(ins, 0)) == REG_AH 检测ah。但是,我想了解这里发生了什么。

我的代码:

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

// key to open the main Routine
static uint32_t key = 0;

// Ins object mapping
class Insr

private:
  // Disassembled instruction
    string insDis;
  INS ins;

public:
    Insr(string insDis, INS ins)  this->insDis = insDis; this->ins = ins;
    string get_insDis()  return insDis;
  INS get_ins()  return ins;
;

// Stack for the Insr structure
static std::unordered_map<ADDRINT, Insr*> insstack;

// This function is called before every instruction is executed
VOID protect(uint64_t addr)

  if (addr > 0x700000000000)
        return;
    if (!key)
        return;
  // Initialize the diassembled instruction
  string insdis = insstack[addr]->get_insDis();
  INS ins = insstack[addr]->get_ins();
    if (INS_OperandCount(ins) > 0)
    
        if (REG(INS_OperandReg(ins, 0)) == REG_AH)
            std::cout << "register: " << REG_StringShort(REG(INS_OperandReg(ins, 0))) << '\n';
    

  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: " << dec<<value << '\n';
    
  std::cout << hex <<addr << "\t" << insdis << std::endl;


// Pin calls this function every time a new instruction is encountered
VOID Instruction(INS ins, VOID *v)

        if (INS_Address(ins) > 0x700000000000)
        return;

    insstack.insert(std::make_pair(INS_Address(ins), new Insr(string(INS_Disassemble(ins)),
    ins)));
    // if (REG_valid_for_iarg_reg_value(INS_MemoryIndexReg(ins)))
    //   std::cout << "true" << '\n';
    // Insert a call to docount before every instruction, no arguments are passed
    INS_InsertCall(ins, IPOINT_BEFORE, (AFUNPTR)protect, IARG_ADDRINT, INS_Address(ins),
  IARG_END);


// Lock Routine
void mutex_lock()

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

void mutex_unlock()

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


void Routine(RTN rtn, VOID *V)

    if (RTN_Name(rtn) == "main")
    
        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);
    


INT32 Usage()

    cerr << "This tool counts the number of dynamic instructions executed" << endl;
    cerr << endl << KNOB_BASE::StringKnobSummary() << endl;
    return -1;


int main(int argc, char * argv[])

    // Initialize the symbol table
    PIN_InitSymbols();

    // Initialize pin
    if (PIN_Init(argc, argv)) return Usage();

    PIN_SetSyntaxIntel();

    // Routine instrumentation
    RTN_AddInstrumentFunction(Routine, 0);

    // Register Instruction to be called to instrument instructions
    INS_AddInstrumentFunction(Instruction, 0);

    // Start the program, never returns
    PIN_StartProgram();

    return 0;

我对此有几个问题。

我如何理解这样的行为?如果我愿意,我该如何检测 rsp?最后,指令怎么打印rsp,但是REG(INS_OperandReg(ins, 0)) == REG_STACK_PTR检测不到?

【问题讨论】:

【参考方案1】:

INS 对象仅在检测例程内部有效,例如您的Instruction 例程。 INS 类型只不过是一个标识指令的 32 位整数。 Pin 运行时在内部维护一个将这些 32 位整数映射到特定静态指令的表。每当它要调用检测例程时,它都会创建这样一个表。当检测例程返回时,不能保证这些标识符中的任何一个都映射到相同的静态指令,它们甚至可能无效。因此,当您在以下代码行中保存INS 对象的副本时:

insstack.insert(std::make_pair(INS_Address(ins), new Insr(string(INS_Disassemble(ins)),
    ins)));

该副本仅在Instruction 例程的同一实例中有用。下次调用 Instruction 例程(或任何其他检测例程)时,指令标识符可能会被其他指令重用。

如果您真的想将指令传递给分析例程,您有两种选择:

将指令的实际字节复制到缓冲区并传递缓冲区的地址,然后使用 XED API 对其进行解码。 传递指令的地址,然后使用 XED API 对其进行解码。如果保证以后可以在同一位置使用该指令,则此方法有效。

【讨论】:

谢谢@Hadi。但是,有没有办法在分析例程中检测 rsp 指令。你有什么建议?如果我在检测例程中添加一个条件来检测这样的指令,这可以吗?或者我应该在分析例程中检测ah。哪个更可靠? @Ruturaj 是的,但这取决于你想做什么。如果分析例程只关心第一个操作数为RSP 的指令,则在检测例程中执行检查(其中INS 有效)。否则,您必须复制指令的字节或传递指令的地址,以便在分析例程中对其进行检查。

以上是关于英特尔引脚:分析例程检测到 ah 寄存器而不是 rsp (REG_STACK_PTR)的主要内容,如果未能解决你的问题,请参考以下文章

rc522为啥我ic卡离天线远一些读写就会出错

为啥此代码段错误(在分配期间)与 pgi 而不是英特尔?

8089汇编 movadd 指令

AX、AH、AL 如何映射到 EAX?

ZigBee案例笔记 -- LED控制与按键检测(输入/输出)

ZigBee案例笔记 -- LED控制与按键检测(输入/输出)