英特尔 Pin 内存操作跟踪

逆向工程 拆卸 二元分析 记忆 小工具
2021-06-12 13:52:15

我正在使用 Intel Pin 来跟踪 Windows 上可执行文件的内存活动。我发现,大多数内存操作数(读取或写入)以 2 或 4 个字节进行操作。所以我决定修改原始 Pin 的pinatrace示例,以查看哪些程序集操作码产生哪些内存活动。

VOID Instruction(INS ins, VOID *v)
{

        UINT32 memOperands = INS_MemoryOperandCount(ins);
        fprintf(trace,"\n[%s]\n",(INS_Disassemble(ins)).c_str()); 
        for (UINT32 memOp = 0; memOp < memOperands; memOp++)
        { 
             .....

它基本上所做的(我希望)只是在它产生的内存操作数之前编写反汇编的操作码。但是后来我查看了文件(W 用于写入,R 用于读取):

[测试edx,0x800000]

[jnz 0x77708557]

[mov dword ptr [ebp-0x4], edi]

[测试dl,0x1]

[jnz 0x77703136] RWWRWW

[lea edi, ptr [ebx+0xcc]]

[push dword ptr [edi]]

[呼叫 0x77702520] RWW

[mov edi, edi]

[推ebp]

[mov ebp, esp]

[mov eax, dword ptr [ebp+0x8]]

[mov ecx, dword ptr fs:[0x18]]

[lea edx, ptr [eax+0x4]]

[锁定 btr dword ptr [edx], 0x0]

[jnb 0x777041dc]

[mov ecx, dword ptr [ecx+0x24]]

[mov dword ptr [eax+0xc], ecx]

[mov dword ptr [eax+0x8], 0x1]

[移动 eax,0x1]

[流行音乐]

[ret 0x4] WRRRWRWWRR

正如我们所见,应该与内存一起工作的操作码(例如mov)不会产生内存操作数。虽然内存跟踪在ret/call/jnz之后作为块连接

问题:Intel Pin 跟踪什么样的内存操作数?是关于调用虚拟内存/RAM/CPU 寄存器吗?由于 CPU 的管道,内存活动是否有可能成块?

2个回答

所以,最后我想出了按照我想要的方式工作的解决方案,根据这个指令表的参考结果似乎是有效的

fprintf(trace,"\n[%s]\n",(INS_Disassemble(ins)).c_str()); //(INS_Disassemble(ins)).c_str()
fflush(trace);
   
for (UINT32 memOp = 0; memOp < memOperands; memOp++)
{
    if (INS_MemoryOperandIsRead(ins, memOp))
    {
        fprintf(trace,"R");
        icount++;
    }

    if (INS_MemoryOperandIsWritten(ins, memOp))
    {
        fprintf(trace,"W");
        icount++;
    }
}

它产生以下输出:

[mov eax, dword ptr [ebp+0x10]]
R
[mov byte ptr [ebx+0x2], 0x0]
W
[mov byte ptr [ebx+0x7], 0x0]
W

我不能确定它是正在分析的可执行文件的真实序列,因为我在检测阶段进行输出,但是代码可能可以修改为在另一个 INS_InsertPredicatedCall 中写入操作码的方式,因此它会在执行时被记录.

如果您想将打印输出限制为仅内存读/写指令,您fprintf()需要在if (INS_MemoryOperandIsRead(ins, memOp)) { ... }块内和if (INS_MemoryOperandIsWritten(ins, memOp)) { ... }块内(并且您需要包含一些逻辑以不多次打印相同的指令)。

例如:

// Is called for every instruction and instruments reads and writes
VOID Instruction(INS ins, VOID *v)
{
    // Instruments memory accesses using a predicated call, i.e.
    // the instrumentation is called iff the instruction will actually be executed.
    //
    // On the IA-32 and Intel(R) 64 architectures conditional moves and REP 
    // prefixed instructions appear as predicated instructions in Pin.
    UINT32 memOperands = INS_MemoryOperandCount(ins);

    BOOL printed = FALSE;

    // Iterate over each memory operand of the instruction.
    for (UINT32 memOp = 0; memOp < memOperands; memOp++)
    {
        if (INS_MemoryOperandIsRead(ins, memOp))
        {
            if (!printed)
            {
                fprintf(trace,"\n[%s]\n",(INS_Disassemble(ins)).c_str()); 
                printed = TRUE;
            }

            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordMemRead,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
        // Note that in some architectures a single memory operand can be 
        // both read and written (for instance incl (%eax) on IA-32)
        // In that case we instrument it once for read and once for write.
        if (INS_MemoryOperandIsWritten(ins, memOp))
        {
            if (!printed)
            {
                fprintf(trace,"\n[%s]\n",(INS_Disassemble(ins)).c_str()); 
                printed = TRUE;
            }

            INS_InsertPredicatedCall(
                ins, IPOINT_BEFORE, (AFUNPTR)RecordMemWrite,
                IARG_INST_PTR,
                IARG_MEMORYOP_EA, memOp,
                IARG_END);
        }
    }
}