parent
b9dc2520fe
commit
47a46d1c58
@ -1,51 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <transform.hpp>
|
|
||||||
#include <vmutils.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);
|
|
||||||
|
|
||||||
std::pair<std::uint64_t, std::uint64_t> encrypt_operand(transform::map_t& transforms,
|
|
||||||
std::uint64_t operand, std::uint64_t rolling_key);
|
|
||||||
|
|
||||||
void inverse_transforms(transform::map_t& transforms, transform::map_t& inverse);
|
|
||||||
bool get_calc_jmp(const zydis_routine_t& vm_entry, zydis_routine_t& calc_jmp);
|
|
||||||
bool get_vinstr_rva_transform(
|
|
||||||
const zydis_routine_t& vm_entry, zydis_decoded_instr_t* transform_instr);
|
|
||||||
|
|
||||||
struct handler_t
|
|
||||||
{
|
|
||||||
u8 imm_size; // size in bits...
|
|
||||||
vm::transform::map_t transforms;
|
|
||||||
vm::handler::profile_t* profile;
|
|
||||||
zydis_routine_t instrs;
|
|
||||||
std::uintptr_t address;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace handler
|
|
||||||
{
|
|
||||||
bool has_imm(const zydis_routine_t& vm_handler);
|
|
||||||
std::uint8_t imm_size(const zydis_routine_t& vm_handler);
|
|
||||||
bool get(zydis_routine_t& vm_entry, zydis_routine_t& vm_handler, std::uintptr_t handler_addr);
|
|
||||||
|
|
||||||
// may throw an exception...
|
|
||||||
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);
|
|
||||||
|
|
||||||
// can be used on calc_jmp...
|
|
||||||
bool get_operand_transforms(const zydis_routine_t& vm_handler, transform::map_t& transforms);
|
|
||||||
vm::handler::profile_t* get_profile(vm::handler_t& vm_handler);
|
|
||||||
|
|
||||||
namespace table
|
|
||||||
{
|
|
||||||
std::uintptr_t* get(const zydis_routine_t& vm_entry);
|
|
||||||
bool get_transform(const zydis_routine_t& vm_entry, zydis_decoded_instr_t* transform_instr);
|
|
||||||
|
|
||||||
std::uint64_t encrypt(zydis_decoded_instr_t& transform_instr, std::uint64_t val);
|
|
||||||
std::uint64_t decrypt(zydis_decoded_instr_t& transform_instr, std::uint64_t val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <vm.h>
|
|
||||||
#include <vmp2.hpp>
|
|
||||||
|
|
||||||
namespace vm
|
|
||||||
{
|
|
||||||
class vmctx_t
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit vmctx_t(
|
|
||||||
vmp2::file_header* file_header,
|
|
||||||
vmp2::entry_t* entry_list,
|
|
||||||
std::vector<vm::handler_t>& vm_handlers,
|
|
||||||
std::uintptr_t module_base,
|
|
||||||
std::uintptr_t image_base
|
|
||||||
);
|
|
||||||
|
|
||||||
std::pair<std::string, const vmp2::entry_t*> step() const;
|
|
||||||
private:
|
|
||||||
std::uintptr_t get_imm(vmp2::exec_type_t exec_type_t,
|
|
||||||
std::uint32_t vip_offset, std::uint8_t imm_size) const;
|
|
||||||
|
|
||||||
mutable std::uint32_t idx;
|
|
||||||
const std::uintptr_t image_base, module_base;
|
|
||||||
const vmp2::entry_t* entry_list;
|
|
||||||
const vmp2::file_header* file_header;
|
|
||||||
std::vector<vm::handler_t> vm_handlers;
|
|
||||||
};
|
|
||||||
}
|
|
@ -0,0 +1,33 @@
|
|||||||
|
#include <vmprofiler.hpp>
|
||||||
|
|
||||||
|
namespace vm
|
||||||
|
{
|
||||||
|
namespace calc_jmp
|
||||||
|
{
|
||||||
|
bool get(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,115 +0,0 @@
|
|||||||
#include "vmctx.h"
|
|
||||||
|
|
||||||
namespace vm
|
|
||||||
{
|
|
||||||
vmctx_t::vmctx_t(
|
|
||||||
vmp2::file_header* file_header,
|
|
||||||
vmp2::entry_t* entry_list,
|
|
||||||
std::vector<vm::handler_t>& vm_handlers,
|
|
||||||
std::uintptr_t module_base,
|
|
||||||
std::uintptr_t image_base
|
|
||||||
)
|
|
||||||
: module_base(module_base),
|
|
||||||
image_base(image_base),
|
|
||||||
entry_list(entry_list),
|
|
||||||
file_header(file_header),
|
|
||||||
vm_handlers(vm_handlers),
|
|
||||||
idx(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::pair<std::string, const vmp2::entry_t*> vmctx_t::step() const
|
|
||||||
{
|
|
||||||
if (idx >= file_header->entry_count)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
const auto vm_handler = &vm_handlers[entry_list[idx].handler_idx];
|
|
||||||
|
|
||||||
if (vm_handler->imm_size)
|
|
||||||
{
|
|
||||||
const auto operand = get_imm(file_header->advancement,
|
|
||||||
entry_list[idx].vip, vm_handler->imm_size / 8);
|
|
||||||
|
|
||||||
auto transforms = vm_handler->transforms;
|
|
||||||
auto [decrypted_operand, rolling_key] =
|
|
||||||
vm::decrypt_operand(transforms,
|
|
||||||
operand, entry_list[idx].decrypt_key);
|
|
||||||
|
|
||||||
if (vm_handler->profile)
|
|
||||||
{
|
|
||||||
if (vm_handler->profile->extention ==
|
|
||||||
vm::handler::extention_t::sign_extend)
|
|
||||||
{
|
|
||||||
switch (vm_handler->imm_size)
|
|
||||||
{
|
|
||||||
case 8:
|
|
||||||
{
|
|
||||||
if ((u8)(decrypted_operand >> 7))
|
|
||||||
decrypted_operand += ~0xFFull;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 16:
|
|
||||||
{
|
|
||||||
if ((u16)(decrypted_operand >> 15))
|
|
||||||
decrypted_operand += ~0xFFFFull;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 32:
|
|
||||||
{
|
|
||||||
if ((u32)(decrypted_operand >> 31))
|
|
||||||
decrypted_operand += ~0xFFFFFFFFull;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw std::invalid_argument(
|
|
||||||
"invalid imm size for sign extention...\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char buff[256];
|
|
||||||
if (vm_handler->profile)
|
|
||||||
{
|
|
||||||
snprintf(buff, sizeof buff, "%s 0x%p",
|
|
||||||
vm_handler->profile->name, decrypted_operand);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
snprintf(buff, sizeof buff, "UNK(%d) 0x%p",
|
|
||||||
entry_list[idx].handler_idx, decrypted_operand);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { buff, &entry_list[idx++] };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vm_handler->profile)
|
|
||||||
return { vm_handler->profile->name, &entry_list[idx++] };
|
|
||||||
|
|
||||||
char buff[256];
|
|
||||||
snprintf(buff, sizeof buff, "UNK(%d)", entry_list[idx++].handler_idx);
|
|
||||||
return { buff, &entry_list[idx++] };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::uintptr_t vmctx_t::get_imm(vmp2::exec_type_t exec_type_t,
|
|
||||||
std::uint32_t vip_offset, std::uint8_t imm_size) const
|
|
||||||
{
|
|
||||||
std::uintptr_t operand = 0u;
|
|
||||||
if (file_header->advancement == vmp2::exec_type_t::forward)
|
|
||||||
{
|
|
||||||
const auto operand_ptr =
|
|
||||||
reinterpret_cast<void*>((entry_list[idx].vip -
|
|
||||||
file_header->module_base) + module_base);
|
|
||||||
|
|
||||||
memcpy(&operand, operand_ptr, imm_size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const auto operand_ptr =
|
|
||||||
reinterpret_cast<void*>(((entry_list[idx].vip -
|
|
||||||
file_header->module_base) + module_base) - imm_size);
|
|
||||||
|
|
||||||
memcpy(&operand, operand_ptr, imm_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return operand;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,191 @@
|
|||||||
|
#include <vmprofiler.hpp>
|
||||||
|
|
||||||
|
namespace vm
|
||||||
|
{
|
||||||
|
namespace instrs
|
||||||
|
{
|
||||||
|
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 };
|
||||||
|
}
|
||||||
|
|
||||||
|
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_rva_decrypt(
|
||||||
|
const zydis_routine_t& vm_entry, std::vector<zydis_decoded_instr_t>& transform_instrs)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// 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 three instruction with ESI as the dest...
|
||||||
|
//
|
||||||
|
|
||||||
|
for (auto idx = 0u; idx < 3; ++idx)
|
||||||
|
{
|
||||||
|
result = std::find_if(++result, vm_entry.end(),
|
||||||
|
[](const zydis_instr_t& instr_data) -> bool
|
||||||
|
{
|
||||||
|
return instr_data.instr.operands[0].reg.value == ZYDIS_REGISTER_ESI;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == vm_entry.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
transform_instrs.push_back(result->instr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue