|
|
|
@ -1,244 +1,7 @@
|
|
|
|
|
#include "vm.h"
|
|
|
|
|
#include <vmprofiler.hpp>
|
|
|
|
|
|
|
|
|
|
namespace vm
|
|
|
|
|
{
|
|
|
|
|
std::pair<std::uint64_t, std::uint64_t> decrypt_operand(transform::map_t& transforms,
|
|
|
|
|
std::uint64_t operand, std::uint64_t rolling_key)
|
|
|
|
|
{
|
|
|
|
|
const auto generic_decrypt_0 = &transforms[transform::type::generic0];
|
|
|
|
|
const auto key_decrypt = &transforms[transform::type::rolling_key];
|
|
|
|
|
const auto generic_decrypt_1 = &transforms[transform::type::generic1];
|
|
|
|
|
const auto generic_decrypt_2 = &transforms[transform::type::generic2];
|
|
|
|
|
const auto generic_decrypt_3 = &transforms[transform::type::generic3];
|
|
|
|
|
const auto update_key = &transforms[transform::type::update_key];
|
|
|
|
|
|
|
|
|
|
if (generic_decrypt_0->mnemonic != ZYDIS_MNEMONIC_INVALID)
|
|
|
|
|
{
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_0->operands[0].size,
|
|
|
|
|
generic_decrypt_0->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_0) ?
|
|
|
|
|
generic_decrypt_0->operands[1].imm.value.u : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// apply transformation with rolling decrypt key...
|
|
|
|
|
operand = transform::apply(key_decrypt->operands[0].size,
|
|
|
|
|
key_decrypt->mnemonic, operand, rolling_key);
|
|
|
|
|
|
|
|
|
|
// apply three generic transformations...
|
|
|
|
|
{
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_1->operands[0].size,
|
|
|
|
|
generic_decrypt_1->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_1) ?
|
|
|
|
|
generic_decrypt_1->operands[1].imm.value.u : 0);
|
|
|
|
|
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_2->operands[0].size,
|
|
|
|
|
generic_decrypt_2->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_2) ?
|
|
|
|
|
generic_decrypt_2->operands[1].imm.value.u : 0);
|
|
|
|
|
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_3->operands[0].size,
|
|
|
|
|
generic_decrypt_3->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_3) ?
|
|
|
|
|
generic_decrypt_3->operands[1].imm.value.u : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update rolling key...
|
|
|
|
|
auto result = transform::apply(update_key->operands[0].size,
|
|
|
|
|
update_key->mnemonic, rolling_key, operand);
|
|
|
|
|
|
|
|
|
|
// update decryption key correctly...
|
|
|
|
|
switch (update_key->operands[0].size)
|
|
|
|
|
{
|
|
|
|
|
case 8:
|
|
|
|
|
rolling_key = (rolling_key & ~0xFFull) + result;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
rolling_key = (rolling_key & ~0xFFFFull) + result;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
rolling_key = result;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { operand, rolling_key };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void inverse_transforms(transform::map_t& transforms, transform::map_t& inverse)
|
|
|
|
|
{
|
|
|
|
|
inverse[transform::type::generic0] = transforms[transform::type::generic0];
|
|
|
|
|
inverse[transform::type::generic0].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::generic0].mnemonic];
|
|
|
|
|
|
|
|
|
|
inverse[transform::type::rolling_key] = transforms[transform::type::rolling_key];
|
|
|
|
|
inverse[transform::type::rolling_key].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::rolling_key].mnemonic];
|
|
|
|
|
|
|
|
|
|
inverse[transform::type::generic1] = transforms[transform::type::generic1];
|
|
|
|
|
inverse[transform::type::generic1].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::generic1].mnemonic];
|
|
|
|
|
|
|
|
|
|
inverse[transform::type::generic2] = transforms[transform::type::generic2];
|
|
|
|
|
inverse[transform::type::generic2].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::generic2].mnemonic];
|
|
|
|
|
|
|
|
|
|
inverse[transform::type::generic3] = transforms[transform::type::generic3];
|
|
|
|
|
inverse[transform::type::generic3].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::generic3].mnemonic];
|
|
|
|
|
|
|
|
|
|
inverse[transform::type::update_key] = transforms[transform::type::update_key];
|
|
|
|
|
inverse[transform::type::update_key].mnemonic =
|
|
|
|
|
transform::inverse[transforms[transform::type::update_key].mnemonic];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::pair<std::uint64_t, std::uint64_t> encrypt_operand(transform::map_t& transforms,
|
|
|
|
|
std::uint64_t operand, std::uint64_t rolling_key)
|
|
|
|
|
{
|
|
|
|
|
transform::map_t inverse;
|
|
|
|
|
inverse_transforms(transforms, inverse);
|
|
|
|
|
|
|
|
|
|
const auto generic_decrypt_0 = &inverse[transform::type::generic0];
|
|
|
|
|
const auto key_decrypt = &inverse[transform::type::rolling_key];
|
|
|
|
|
const auto generic_decrypt_1 = &inverse[transform::type::generic1];
|
|
|
|
|
const auto generic_decrypt_2 = &inverse[transform::type::generic2];
|
|
|
|
|
const auto generic_decrypt_3 = &inverse[transform::type::generic3];
|
|
|
|
|
const auto update_key = &inverse[transform::type::update_key];
|
|
|
|
|
|
|
|
|
|
auto result = transform::apply(update_key->operands[0].size,
|
|
|
|
|
update_key->mnemonic, rolling_key, operand);
|
|
|
|
|
|
|
|
|
|
// make sure we update the rolling decryption key correctly...
|
|
|
|
|
switch (update_key->operands[0].size)
|
|
|
|
|
{
|
|
|
|
|
case 8:
|
|
|
|
|
rolling_key = (rolling_key & ~0xFFull) + result;
|
|
|
|
|
break;
|
|
|
|
|
case 16:
|
|
|
|
|
rolling_key = (rolling_key & ~0xFFFFull) + result;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
rolling_key = result;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_3->operands[0].size,
|
|
|
|
|
generic_decrypt_3->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_3) ?
|
|
|
|
|
generic_decrypt_3->operands[1].imm.value.u : 0);
|
|
|
|
|
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_2->operands[0].size,
|
|
|
|
|
generic_decrypt_2->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_2) ?
|
|
|
|
|
generic_decrypt_2->operands[1].imm.value.u : 0);
|
|
|
|
|
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_1->operands[0].size,
|
|
|
|
|
generic_decrypt_1->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_1) ?
|
|
|
|
|
generic_decrypt_1->operands[1].imm.value.u : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operand = transform::apply(key_decrypt->operands[0].size,
|
|
|
|
|
key_decrypt->mnemonic, operand, rolling_key);
|
|
|
|
|
|
|
|
|
|
if (generic_decrypt_0->mnemonic != ZYDIS_MNEMONIC_INVALID)
|
|
|
|
|
{
|
|
|
|
|
operand = transform::apply(
|
|
|
|
|
generic_decrypt_0->operands[0].size,
|
|
|
|
|
generic_decrypt_0->mnemonic, operand,
|
|
|
|
|
// check to see if this instruction has an IMM...
|
|
|
|
|
transform::has_imm(generic_decrypt_0) ?
|
|
|
|
|
generic_decrypt_0->operands[1].imm.value.u : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { operand, rolling_key };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool get_calc_jmp(const zydis_routine_t& vm_entry, zydis_routine_t& calc_jmp)
|
|
|
|
|
{
|
|
|
|
|
auto result = std::find_if(vm_entry.begin(), vm_entry.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
{
|
|
|
|
|
// mov/movsx/movzx rax/eax/ax/al, [rsi]
|
|
|
|
|
if (instr_data.instr.operand_count > 1 &&
|
|
|
|
|
(instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOV ||
|
|
|
|
|
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOVSX ||
|
|
|
|
|
instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOVZX) &&
|
|
|
|
|
instr_data.instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
|
|
|
|
|
util::reg::to64(instr_data.instr.operands[0].reg.value) == ZYDIS_REGISTER_RAX &&
|
|
|
|
|
instr_data.instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY &&
|
|
|
|
|
instr_data.instr.operands[1].mem.base == ZYDIS_REGISTER_RSI)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (result == vm_entry.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
calc_jmp.insert(calc_jmp.end(), result, vm_entry.end());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool get_vinstr_rva_transform(
|
|
|
|
|
const zydis_routine_t& vm_entry, zydis_decoded_instr_t* transform_instr)
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// find mov esi, [rsp+0xA0]
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
auto result = std::find_if(vm_entry.begin(), vm_entry.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
{
|
|
|
|
|
if (instr_data.instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
|
|
|
|
|
instr_data.instr.operand_count == 2 &&
|
|
|
|
|
instr_data.instr.operands[0].reg.value == ZYDIS_REGISTER_ESI &&
|
|
|
|
|
instr_data.instr.operands[1].mem.base == ZYDIS_REGISTER_RSP &&
|
|
|
|
|
instr_data.instr.operands[1].mem.disp.has_displacement &&
|
|
|
|
|
instr_data.instr.operands[1].mem.disp.value == 0xA0)
|
|
|
|
|
return true;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (result == vm_entry.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// find the next instruction with ESI as the dest...
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
result = std::find_if(++result, vm_entry.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
{
|
|
|
|
|
if (instr_data.instr.operands[0].reg.value == ZYDIS_REGISTER_ESI)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (result == vm_entry.end())
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
*transform_instr = result->instr;
|
|
|
|
|
transform_instr->mnemonic = transform::inverse[result->instr.mnemonic];
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
namespace handler
|
|
|
|
|
{
|
|
|
|
|
bool get(zydis_routine_t& calc_jmp, zydis_routine_t& vm_handler, std::uintptr_t handler_addr)
|
|
|
|
@ -248,7 +11,7 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
vm::util::deobfuscate(vm_handler);
|
|
|
|
|
|
|
|
|
|
static const auto calc_jmp_check =
|
|
|
|
|
static const auto calc_jmp_check =
|
|
|
|
|
[&](std::uintptr_t addr) -> bool
|
|
|
|
|
{
|
|
|
|
|
for (const auto& [instr, instr_raw, instr_addr] : calc_jmp)
|
|
|
|
@ -259,8 +22,8 @@ namespace vm
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
auto result = std::find_if(
|
|
|
|
|
vm_handler.begin(), vm_handler.end(),
|
|
|
|
|
[](const zydis_instr_t& instr) -> bool
|
|
|
|
|
vm_handler.begin(), vm_handler.end(),
|
|
|
|
|
[](const zydis_instr_t& instr) -> bool
|
|
|
|
|
{
|
|
|
|
|
if (instr.instr.mnemonic == ZYDIS_MNEMONIC_LEA &&
|
|
|
|
|
instr.instr.operands[0].reg.value == ZYDIS_REGISTER_RAX &&
|
|
|
|
@ -311,15 +74,15 @@ namespace vm
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool get_all(std::uintptr_t module_base, std::uintptr_t image_base,
|
|
|
|
|
zydis_routine_t& vm_entry, std::uintptr_t* vm_handler_table, std::vector<vm::handler_t>& vm_handlers)
|
|
|
|
|
bool get_all(std::uintptr_t module_base, std::uintptr_t image_base,
|
|
|
|
|
zydis_routine_t& vm_entry, std::uintptr_t* vm_handler_table, std::vector<vm::handler::handler_t>& vm_handlers)
|
|
|
|
|
{
|
|
|
|
|
zydis_decoded_instr_t instr;
|
|
|
|
|
if (!vm::handler::table::get_transform(vm_entry, &instr))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
zydis_routine_t calc_jmp;
|
|
|
|
|
if (!vm::get_calc_jmp(vm_entry, calc_jmp))
|
|
|
|
|
if (!vm::calc_jmp::get(vm_entry, calc_jmp))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (auto idx = 0u; idx < 256; ++idx)
|
|
|
|
@ -328,7 +91,7 @@ namespace vm
|
|
|
|
|
vm::handler::table::decrypt(
|
|
|
|
|
instr, vm_handler_table[idx]);
|
|
|
|
|
|
|
|
|
|
vm::handler_t vm_handler;
|
|
|
|
|
handler_t vm_handler;
|
|
|
|
|
vm::transform::map_t transforms;
|
|
|
|
|
zydis_routine_t vm_handler_instrs;
|
|
|
|
|
|
|
|
|
@ -358,7 +121,7 @@ namespace vm
|
|
|
|
|
bool has_imm(const zydis_routine_t& vm_handler)
|
|
|
|
|
{
|
|
|
|
|
const auto result = std::find_if(
|
|
|
|
|
vm_handler.begin(), vm_handler.end(),
|
|
|
|
|
vm_handler.begin(), vm_handler.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
{
|
|
|
|
|
// mov/movsx/movzx rax/eax/ax/al, [rsi]
|
|
|
|
@ -429,8 +192,8 @@ namespace vm
|
|
|
|
|
|
|
|
|
|
// this finds the first transformation which looks like:
|
|
|
|
|
// transform rax, rbx <--- note these registers can be smaller so we to64 them...
|
|
|
|
|
auto key_transform = std::find_if(imm_fetch, vm_handler.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
auto key_transform = std::find_if(imm_fetch, vm_handler.end(),
|
|
|
|
|
[](const zydis_instr_t& instr_data) -> bool
|
|
|
|
|
{
|
|
|
|
|
if (util::reg::compare(instr_data.instr.operands[0].reg.value, ZYDIS_REGISTER_RAX) &&
|
|
|
|
|
util::reg::compare(instr_data.instr.operands[1].reg.value, ZYDIS_REGISTER_RBX))
|
|
|
|
@ -489,10 +252,10 @@ namespace vm
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vm::handler::profile_t* get_profile(vm::handler_t& vm_handler)
|
|
|
|
|
vm::handler::profile_t* get_profile(handler_t& vm_handler)
|
|
|
|
|
{
|
|
|
|
|
static const auto vcontains =
|
|
|
|
|
[](vm::handler::profile_t* vprofile, vm::handler_t* vm_handler) -> bool
|
|
|
|
|
[](vm::handler::profile_t* vprofile, handler_t* vm_handler) -> bool
|
|
|
|
|
{
|
|
|
|
|
if (vprofile->imm_size != vm_handler->imm_size)
|
|
|
|
|
return false;
|