TransWikia.com

Intel Pin memory operations tracking

Reverse Engineering Asked by aGGeRReS on June 25, 2021

I am using Intel Pin in order trace memory activity of an executable on Windows. What I have found, that most of the memory operands (Read or Write) operates with 2 or 4 bytes. So I decided to modify original Pin’s pinatrace example, in order to see which Assembly opcodes produces which memory activity.

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++)
        { 
             .....

What it basically does (I hope), is just writes disassembled opcode BEFORE the memory operands it produces. But then I looked in the file (W is for write, R is for read):

[test edx, 0x800000]

[jnz 0x77708557]

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

[test dl, 0x1]

[jnz 0x77703136] RWWRWW

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

[push dword ptr [edi]]

[call 0x77702520] RWW

[mov edi, edi]

[push ebp]

[mov ebp, esp]

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

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

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

[lock 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]

[mov eax, 0x1]

[pop ebp]

[ret 0x4] WRRRWRWWRR

As we can see, opcodes that are supposed to work with memory (e.g. mov) do not produce memory operands. While memory traces are connected as blocks after ret/call/jnz etc.

Question: What kind of memory operands does Intel Pin trace? Is it about calls to virtual memory/RAM/CPU registers? Could it be possible, that memory activity goes in blocks due to CPU’s pipeline?

2 Answers

So, finally I came up with the solution that works how I want and results seem to be valid according to this reference of instruction tables

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++;
    }
}

And it produces the following output:

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

I cannot be sure that it is the true sequence of executable under analysis because I do output in the instrumentation phase, but the code can probably be modified it the way to write opcode inside another INS_InsertPredicatedCall, so it will be recorded when it will be executed.

Correct answer by aGGeRReS on June 25, 2021

If you want to limit your print-out to just memory read/write instructions, your fprintf() needs to be inside the if (INS_MemoryOperandIsRead(ins, memOp)) { ... } block and inside the if (INS_MemoryOperandIsWritten(ins, memOp)) { ... } block (and you'll need to include some logic to not print the same instruction more than once).

For example:

// 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);
        }
    }
}

Answered by Jason Geffner on June 25, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP