c++ - PIN 从指令地址获取汇编操作码

标签 c++ c assembly x86 intel-pin

我正在使用 PIN 来分析 C 程序的指令并执行必要的操作。我在 Ubuntu 上使用 GCC 编译了我的 C 程序,然后将生成的可执行文件作为输入传递给 pintool。我有一个 pintool,它调用一个指令检测例程,然后每次都调用一个分析例程。这是我用 C++ 编写的 Pintool -

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

UINT64 icount = 0;

using namespace std;

KNOB<string> KnobOutputFile(KNOB_MODE_WRITEONCE, "pintool", "o", "test.out","A pin tool");

FILE * trace;

//====================================================================
// Analysis Routines
//====================================================================

VOID dump(VOID *ip, UINT32 size) { 
    unsigned int i;
    UINT8 opcodeBytes[15];

    UINT32 fetched = PIN_SafeCopy(&opcodeBytes[0], ip, size);

    if (fetched != size) {
        fprintf(trace, "*** error fetching instruction at address 0x%lx",(unsigned long)ip);
        return;
    }

    fprintf(trace, "\n");
    fprintf(trace, "\n%d\n",size);

    for (i=0; i<size; i++)
        fprintf(trace, " %02x", opcodeBytes[i]); //print the opcode bytes
    fflush(trace);
}

//====================================================================
// Instrumentation Routines
//====================================================================

VOID Instruction(INS ins, void *v) {
      INS_InsertCall( ins, IPOINT_BEFORE, (AFUNPTR)dump, IARG_INST_PTR, IARG_UINT32, INS_Size(ins) , IARG_END);
}

VOID Fini(INT32 code, VOID *v) {
    printf("count = %ld\n",(long)icount);
}

INT32 Usage(VOID) {
    PIN_ERROR("This Pintool failed\n"
          + KNOB_BASE::StringKnobSummary() + "\n");
    return -1;
}

int main(int argc, char *argv[])
{
    trace = fopen("test.out", "w");

    if (PIN_Init(argc, argv)) return Usage();

    PIN_InitSymbols();
    PIN_AddInternalExceptionHandler(ExceptionHandler,NULL);
    INS_AddInstrumentFunction(Instruction, 0);
    PIN_AddFiniFunction(Fini, 0);

    // Never returns
    PIN_StartProgram();

    return 0;
}

当我检查我的输出跟踪时,我看到我得到了这样的输出-

3
 48 89 e7

5
 e8 78 0d 00 00

1
 55

第一行是指令的字节大小,第二行是每个字节中存储的操作码。

我看到了这个特别的论坛- https://groups.yahoo.com/neo/groups/pinheads/conversations/topics/4405#

他们提到 Linux 输出不一致,这是由于 64 位指令的 32 位反汇编程序造成的。我得到的输出与此处提到的 Linux 输出相同,而 Windows 输出是我期望的正确 x86_64 操作码。

知道如何获得正确的操作码,如果反汇编有误,我该如何纠正。我使用的是 64 位 PC,所以不知道我是否在进行 32 位反汇编。

最佳答案

在 32 位模式下,48 是一个 1 字节的 incdec(我忘了是哪个)。

在 64 位模式下,它是一个 REX 前缀(W=1,其他位未设置,选择 64 位操作数大小)。 (AMD 64 将整个 0x40-f 范围的 inc/dec 短编码重新用作 REX 前缀。)

48 89 e7 解码为 3 字节指令而不是 4889 e7 绝对证明它在 64- 中反汇编位模式。

So how am I supposed to interpret the instruction here?

很明显,作为 x86-64 指令。

对于您的情况,我将这些十六进制字节提供给反汇编程序:

db 0x48, 0x89, 0xe7
db 0xe8, 0x78, 0x0d, 0x00, 0x00
db 0x55

nasm -f elf64 foo.asm && objdump -drwC -Mintel foo.o

  400080:       48 89 e7                mov    rdi,rsp
  400083:       e8 78 0d 00 00          call rel32
  400088:       55                      push   rbp

objdump -d 发现相同的指令中断,因为 PIN 已正确解码。

push 大概在被调用函数的开头。将它们粘在一起有点扁平化跟踪,并且不是制作可运行版本的方法,只是为了拆解字节。

I should simple ignore the first byte and then use the remaining?

不,当然不是。 REX 前缀是指令的一部分。如果没有 0x48,第一条指令将解码为 mov edi,esp,这是一条不同的指令。

尝试查看一些现有代码的一些反汇编输出,以习惯 x86-64 指令的样子。有关具体的编码细节,请参阅 Intel 的 vol.2 手册。它有一些关于指令编码细节的介绍和附录部分。 (手册的主体是指令集引用,详细介绍了每条指令的工作原理及其操作码。)参见 https://software.intel.com/en-us/articles/intel-sdm#three-volume ,以及 中的其他链接标记维基。

关于c++ - PIN 从指令地址获取汇编操作码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46615099/

相关文章:

c++ - 所有 .txt 文件的列表

c - printf 长度修饰符 %L 是标准的(或 future 的标准)吗?

c - 使用内联汇编将 int 保存在来自结构指针的函数变量中

C++ 从文本文件数据类型中读取结构并将数据存储在列表 vector 中

c++ - 矩阵堆栈、glLoadMatrix 和 glMultMatrix 与场景图和相机一起使用?

c# - 在 pinvoke 类/结构构造中使用 MarshalAs 与不使用 Marshal

assembly - linux g++ x64 通过 FS 段寄存器访问内存

macos - 汇编语言中的数组访问

c++ - 我可以包含全局静态成员吗​​?

c++ - 如何使用 NCurses 阻止 C++ 中的某些键