You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vmprofiler/vmutils.cpp

334 lines
12 KiB

#include "vmutils.h"
#include <Windows.h>
namespace vm
{
namespace util
{
namespace reg
{
ZydisRegister to64(ZydisRegister reg)
{
switch (reg)
{
case ZYDIS_REGISTER_AL:
return ZYDIS_REGISTER_RAX;
case ZYDIS_REGISTER_CL:
return ZYDIS_REGISTER_RCX;
case ZYDIS_REGISTER_DL:
return ZYDIS_REGISTER_RDX;
case ZYDIS_REGISTER_BL:
return ZYDIS_REGISTER_RBX;
case ZYDIS_REGISTER_AH:
return ZYDIS_REGISTER_RAX;
case ZYDIS_REGISTER_CH:
return ZYDIS_REGISTER_RCX;
case ZYDIS_REGISTER_DH:
return ZYDIS_REGISTER_RDX;
case ZYDIS_REGISTER_BH:
return ZYDIS_REGISTER_RBX;
case ZYDIS_REGISTER_SPL:
return ZYDIS_REGISTER_RSP;
case ZYDIS_REGISTER_BPL:
return ZYDIS_REGISTER_RBP;
case ZYDIS_REGISTER_SIL:
return ZYDIS_REGISTER_RSI;
case ZYDIS_REGISTER_DIL:
return ZYDIS_REGISTER_RDI;
case ZYDIS_REGISTER_R8B:
return ZYDIS_REGISTER_R8;
case ZYDIS_REGISTER_R9B:
return ZYDIS_REGISTER_R9;
case ZYDIS_REGISTER_R10B:
return ZYDIS_REGISTER_R10;
case ZYDIS_REGISTER_R11B:
return ZYDIS_REGISTER_R11;
case ZYDIS_REGISTER_R12B:
return ZYDIS_REGISTER_R12;
case ZYDIS_REGISTER_R13B:
return ZYDIS_REGISTER_R13;
case ZYDIS_REGISTER_R14B:
return ZYDIS_REGISTER_R14;
case ZYDIS_REGISTER_R15B:
return ZYDIS_REGISTER_R15;
case ZYDIS_REGISTER_AX:
return ZYDIS_REGISTER_RAX;
case ZYDIS_REGISTER_CX:
return ZYDIS_REGISTER_RCX;
case ZYDIS_REGISTER_DX:
return ZYDIS_REGISTER_RDX;
case ZYDIS_REGISTER_BX:
return ZYDIS_REGISTER_RBX;
case ZYDIS_REGISTER_SP:
return ZYDIS_REGISTER_RSP;
case ZYDIS_REGISTER_BP:
return ZYDIS_REGISTER_RBP;
case ZYDIS_REGISTER_SI:
return ZYDIS_REGISTER_RSI;
case ZYDIS_REGISTER_DI:
return ZYDIS_REGISTER_RDI;
case ZYDIS_REGISTER_R8W:
return ZYDIS_REGISTER_R8;
case ZYDIS_REGISTER_R9W:
return ZYDIS_REGISTER_R9;
case ZYDIS_REGISTER_R10W:
return ZYDIS_REGISTER_R10;
case ZYDIS_REGISTER_R11W:
return ZYDIS_REGISTER_R11;
case ZYDIS_REGISTER_R12W:
return ZYDIS_REGISTER_R12;
case ZYDIS_REGISTER_R13W:
return ZYDIS_REGISTER_R13;
case ZYDIS_REGISTER_R14W:
return ZYDIS_REGISTER_R14;
case ZYDIS_REGISTER_R15W:
return ZYDIS_REGISTER_R15;
case ZYDIS_REGISTER_EAX:
return ZYDIS_REGISTER_RAX;
case ZYDIS_REGISTER_ECX:
return ZYDIS_REGISTER_RCX;
case ZYDIS_REGISTER_EDX:
return ZYDIS_REGISTER_RDX;
case ZYDIS_REGISTER_EBX:
return ZYDIS_REGISTER_RBX;
case ZYDIS_REGISTER_ESP:
return ZYDIS_REGISTER_RSP;
case ZYDIS_REGISTER_EBP:
return ZYDIS_REGISTER_RBP;
case ZYDIS_REGISTER_ESI:
return ZYDIS_REGISTER_RSI;
case ZYDIS_REGISTER_EDI:
return ZYDIS_REGISTER_RDI;
case ZYDIS_REGISTER_R8D:
return ZYDIS_REGISTER_R8;
case ZYDIS_REGISTER_R9D:
return ZYDIS_REGISTER_R9;
case ZYDIS_REGISTER_R10D:
return ZYDIS_REGISTER_R10;
case ZYDIS_REGISTER_R11D:
return ZYDIS_REGISTER_R11;
case ZYDIS_REGISTER_R12D:
return ZYDIS_REGISTER_R12;
case ZYDIS_REGISTER_R13D:
return ZYDIS_REGISTER_R13;
case ZYDIS_REGISTER_R14D:
return ZYDIS_REGISTER_R14;
case ZYDIS_REGISTER_R15D:
return ZYDIS_REGISTER_R15;
}
return reg;
}
bool compare(ZydisRegister a, ZydisRegister b)
{
return to64(a) == to64(b);
}
}
void print(const ZydisDecodedInstruction& instr)
{
char buffer[256];
ZydisFormatter formatter;
ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
ZydisFormatterFormatInstruction(&formatter, &instr,
buffer, sizeof(buffer), 0u);
puts(buffer);
}
void print(zydis_routine_t& routine)
{
char buffer[256];
ZydisFormatter formatter;
ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL);
for (auto [instr, raw, addr] : routine)
{
std::printf("> 0x%p ", addr);
ZydisFormatterFormatInstruction(&formatter, &instr,
buffer, sizeof(buffer), addr);
puts(buffer);
}
}
bool is_jmp(const ZydisDecodedInstruction& instr)
{
switch (instr.mnemonic)
{
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JMP:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
return true;
default:
break;
}
return false;
}
bool flatten(zydis_routine_t& routine, std::uintptr_t routine_addr, bool keep_jmps)
{
ZydisDecoder decoder;
ZydisDecodedInstruction instr;
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64);
while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, reinterpret_cast<void*>(
routine_addr), 0x1000, &instr)))
{
std::vector<u8> raw_instr;
raw_instr.insert(raw_instr.begin(),
(u8*)routine_addr,
(u8*)routine_addr + instr.length);
if (is_jmp(instr))
{
if (instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER)
{
routine.push_back({ instr, raw_instr, routine_addr });
return true;
}
if (keep_jmps)
routine.push_back({ instr, raw_instr, routine_addr });
ZydisCalcAbsoluteAddress(&instr, &instr.operands[0], routine_addr, &routine_addr);
}
else if (instr.mnemonic == ZYDIS_MNEMONIC_RET)
{
routine.push_back({ instr, raw_instr, routine_addr });
return true;
}
else
{
routine.push_back({ instr, raw_instr, routine_addr });
routine_addr += instr.length;
}
}
return false;
}
void deobfuscate(zydis_routine_t& routine)
{
static const auto _uses =
[](ZydisDecodedOperand& op, ZydisRegister reg) -> bool
{
switch (op.type)
{
case ZYDIS_OPERAND_TYPE_MEMORY:
{
return reg::compare(op.mem.base, reg) || reg::compare(op.mem.index, reg);
}
case ZYDIS_OPERAND_TYPE_REGISTER:
{
return reg::compare(op.reg.value, reg);
}
}
return false;
};
static const auto _writes =
[](ZydisDecodedInstruction& inst) -> bool
{
for (auto idx = 0; idx < inst.operand_count; ++idx)
if (inst.operands[idx].actions & ZYDIS_OPERAND_ACTION_MASK_WRITE)
return true;
return false;
};
static const auto _remove =
[](zydis_routine_t& routine, zydis_routine_t::iterator itr,
ZydisRegister reg, u32 opcode_size) -> void
{
for (; itr >= routine.begin(); --itr)
{
const auto instruction = &itr->instr;
bool stop = false;
if (instruction->mnemonic == ZYDIS_MNEMONIC_JMP)
continue;
for (auto op_idx = 0u; op_idx < instruction->operand_count; ++op_idx)
{
const auto op = &instruction->operands[op_idx];
if (!_uses(*op, reg))
continue;
if (op->type == ZYDIS_OPERAND_TYPE_MEMORY)
{
stop = true;
continue;
}
if (opcode_size < 32 && op->size > opcode_size)
continue;
if (op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE)
op->actions &= ~ZYDIS_OPERAND_ACTION_MASK_WRITE;
else stop = true;
}
if (!_writes(*instruction))
routine.erase(itr);
else if (stop) break;
}
};
for (const auto& instr_data : routine)
{
if (routine.empty() || routine.size() == 1 ||
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_JMP)
continue;
for (auto itr = routine.begin() + 1; itr != routine.end(); itr++)
{
if (itr->instr.mnemonic == ZYDIS_MNEMONIC_JMP ||
itr->instr.mnemonic == ZYDIS_MNEMONIC_RET)
break;
// find the write operations that happen...
for (auto idx = 0u; idx < itr->instr.operand_count; ++idx)
{
const auto op = &itr->instr.operands[idx];
// if its a read, continue to next opcode...
if (op->actions & ZYDIS_OPERAND_ACTION_MASK_READ)
continue;
// if its not a write then continue to next opcode...
if (!(op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE))
continue;
// if this operand is not a register then we continue...
if (op->type != ZYDIS_OPERAND_TYPE_REGISTER)
continue;
// else we see if we can remove dead writes to this register...
_remove(routine, itr - 1, op->reg.value, op->size);
}
}
}
}
}
}