PIN从指令地址获取程序集操作码

PIN get assembly opcodes from instruction address

本文关键字:程序集 操作码 获取 地址 指令 PIN      更新时间:2023-10-16

我使用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%dn",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 = %ldn",(long)icount);
}
INT32 Usage(VOID) {
PIN_ERROR("This Pintool failedn"
+ 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操作码。

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

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

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

48 89 e7解码为3字节指令,而不是4889 e7,这绝对证明了它是在64位模式下进行反汇编的。

那么我应该如何解释这里的指令呢?

显然是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可能位于被调用函数的开头。将它们粘在一起会使跟踪变得平坦,并且不是制作可运行版本的方法,只是为了分解字节。

我应该简单地忽略第一个字节,然后使用剩余的?

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

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

Pin有一个API用于反汇编,你应该使用它。关于如何完成它,请参阅这个问题:

https://reverseengineering.stackexchange.com/questions/12404/intel-pin-how-to-access-the-ins-object-from-inside-an-analysis-function