Skip to content

Instantly share code, notes, and snippets.

@archercreat
Created February 28, 2023 16:18
Show Gist options
  • Save archercreat/4a4803a8d4f6d3c5a07818790a37e484 to your computer and use it in GitHub Desktop.
Save archercreat/4a4803a8d4f6d3c5a07818790a37e484 to your computer and use it in GitHub Desktop.
VMProtect virtual machine tracer using Intel pin.
#include "pin.h"
#include <iostream>
static uint64_t last_address = 0ull;
static uint32_t last_opcode = 0ul;
static constexpr auto vm_section = ".vmp1";
std::string to_hex(uint64_t value)
{
std::stringstream ss;
ss << "0x" << std::hex << value;
return ss.str();
}
bool in_main_module(uint64_t address)
{
PIN_LockClient();
bool exe = false;
if (auto img = IMG_FindByAddress(address); IMG_Valid(img))
{
exe = IMG_IsMainExecutable(img);
}
PIN_UnlockClient();
return exe;
}
bool in_vm_section(uint64_t address)
{
PIN_LockClient();
bool vm = false;
if (auto rtn = RTN_FindByAddress(address); RTN_Valid(rtn))
{
if (auto sec = RTN_Sec(rtn); SEC_Valid(sec))
{
vm = SEC_Name(sec).find(vm_section) != std::string::npos;
}
}
PIN_UnlockClient();
return vm;
}
void handle_call(uint64_t rip, uint32_t opcode)
{
// If we found external call.
//
PIN_LockClient();
if (in_vm_section(last_address) && !in_main_module(rip))
{
if (auto rtn = RTN_FindByAddress(rip); RTN_Valid(rtn))
{
std::printf("\t0x%llx %s\n", last_address, RTN_Name(rtn).c_str());
}
else
{
std::printf("\t0x%llx 0x%llx\n", last_address, rip);
}
}
else if (in_vm_section(last_address) && last_opcode == XED_ICLASS_CALL_NEAR)
{
std::printf("\t0x%llx 0x%llx\n", last_address, rip);
}
last_address = rip;
last_opcode = opcode;
PIN_UnlockClient();
}
void handle_popfq(void* rsp)
{
uint64_t top{};
if (PIN_SafeCopy(&top, rsp, sizeof(top)) == sizeof(top))
{
// Discard trap flag bit.
//
top &= ~0x100;
PIN_SafeCopy(rsp, &top, sizeof(top));
}
}
void instruction_cb(INS ins, void*)
{
const auto rip = INS_Address(ins);
const auto op = INS_Opcode(ins);
if (op == XED_ICLASS_PUSH && INS_OperandIsImmediate(ins, 0) && in_vm_section(rip))
{
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)+[](uint64_t rip) { std::printf("vm:\n\tentry: 0x%llx\n", rip); },
IARG_INST_PTR,
IARG_END
);
}
if (op == XED_ICLASS_POPFQ)
{
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)handle_popfq,
IARG_REG_VALUE, REG_RSP,
IARG_END
);
}
else if (op == XED_ICLASS_CPUID)
{
INS_InsertCall(
ins, IPOINT_BEFORE,
(AFUNPTR)+[](uint64_t rip) { if (in_vm_section(rip)) std::printf("\t0x%llx cpuid\n", rip); },
IARG_INST_PTR,
IARG_END
);
}
else if (op == XED_ICLASS_RDTSC)
{
INS_InsertCall(
ins, IPOINT_BEFORE,
(AFUNPTR)+[](uint64_t rip) { if (in_vm_section(rip)) std::printf("\t0x%llx rdtsc\n", rip); },
IARG_INST_PTR,
IARG_END
);
}
INS_InsertCall(
ins, IPOINT_BEFORE, (AFUNPTR)handle_call,
IARG_INST_PTR,
IARG_UINT32, op,
IARG_END
);
}
void image_load_cb(IMG img, void*)
{
std::printf("Loaded: 0x%llx %s\n", IMG_LowAddress(img), IMG_Name(img).c_str());
}
void syscall_cb(uint32_t tid, CONTEXT* context, SYSCALL_STANDARD std, void*)
{
auto rip = PIN_GetContextReg(context, REG_INST_PTR);
auto eax = PIN_GetSyscallNumber(context, std);
auto arg0 = PIN_GetSyscallArgument(context, std, 0);
auto arg1 = PIN_GetSyscallArgument(context, std, 1);
auto arg2 = PIN_GetSyscallArgument(context, std, 2);
auto arg3 = PIN_GetSyscallArgument(context, std, 3);
if (in_main_module(rip))
{
std::printf("0x%llx %lld(0x%llx, 0x%llx, 0x%llx, 0x%llx)\n", rip, eax, arg0, arg1, arg2, arg3);
}
}
void fini(int32_t code, void*)
{
}
int main(int argc, char* argv[])
{
// Initialize symbol processing.
//
PIN_InitSymbols();
// Initialize pin.
//
if (PIN_Init(argc, argv))
{
return -1;
}
PIN_AddSyscallEntryFunction(syscall_cb, nullptr);
PIN_AddFiniFunction(fini, nullptr);
INS_AddInstrumentFunction(instruction_cb, nullptr);
IMG_AddInstrumentFunction(image_load_cb, nullptr);
PIN_StartProgram();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment