essentially added a bunch of shit...

main
John Doe 3 years ago
parent 11650b6d8a
commit 20ad0b5950

@ -52,8 +52,11 @@ set(vmprofiler_SOURCES "")
list(APPEND vmprofiler_SOURCES
"src/vmctx.cpp"
"src/vmlocate.cpp"
"src/vmprofiles/jmp.cpp"
"src/vmprofiles/sreg.cpp"
"src/vmutils.cpp"
"include/vmctx.hpp"
"include/vminstrs.hpp"
"include/vmlocate.hpp"
"include/vmprofiler.hpp"
"include/vmutils.hpp"

@ -12,9 +12,9 @@ class vmctx_t {
const std::uintptr_t m_module_base, m_image_base, m_vm_entry_rva,
m_image_size;
zydis_register_t get_vip() const { return m_vip; }
zydis_register_t get_vsp() const { return m_vsp; }
zydis_routine_t get_vm_enter() const { return m_vm_entry; }
zydis_reg_t get_vip() const { return m_vip; }
zydis_reg_t get_vsp() const { return m_vsp; }
zydis_rtn_t get_vm_enter() const { return m_vm_entry; }
private:
/// <summary>
@ -22,11 +22,11 @@ class vmctx_t {
/// enter... these will change during the execution inside of the vm but these
/// values stay the same as the ones used by vm enter...
/// </summary>
zydis_register_t m_vip, m_vsp;
zydis_reg_t m_vip, m_vsp;
/// <summary>
/// the virtual machine enter flattened and deobfuscated...
/// </summary>
zydis_routine_t m_vm_entry;
zydis_rtn_t m_vm_entry;
};
} // namespace vm

@ -0,0 +1,140 @@
#pragma once
#include <vmutils.hpp>
namespace vm::instrs {
/// <summary>
/// mnemonic representation of supported virtual instructions...
/// </summary>
enum class mnemonic_t {
unknown,
sreg,
lreg,
lconst,
add,
div,
idiv,
mul,
imul,
nand,
read,
write,
shl,
shld,
shr,
shrd,
lvsp,
svsp,
writecr3,
readcr3,
writecr8,
readcr8,
cpuid,
rdtsc,
call,
jmp,
vmexit
};
/// <summary>
/// the main virtual instruction structure which is returned by profilers...
/// </summary>
struct vinstr_t {
/// <summary>
/// mnemonic of the virtual instruction...
/// </summary>
mnemonic_t mnemonic;
/// <summary>
/// size varient of the virtual instruction... I.E SREGQ would have a value of
/// "64" here...where the SREGDW varient would have a "32" here...
/// </summary>
u8 size;
struct {
/// <summary>
/// true if the virtual instruction has an imm false if not...
/// </summary>
bool has_imm;
/// <summary>
/// size in bits of the imm... 8, 16, 32, 64...
/// </summary>
u8 size;
/// <summary>
/// imm value...
/// </summary>
u64 val;
} imm;
};
/// <summary>
/// matcher function which returns true if an instruction matches a desired
/// one...
/// </summary>
using matcher_t = std::function<bool(const zydis_reg_t vip,
const zydis_reg_t vsp,
const zydis_decoded_instr_t& instr)>;
/// <summary>
/// virtual instruction structure generator... this can update the vip and vsp
/// argument... it cannot update the instruction stream (hndlr)...
/// </summary>
using vinstr_gen_t = std::function<std::optional<vinstr_t>(zydis_reg_t& vip,
zydis_reg_t& vsp,
zydis_rtn_t& hndlr)>;
/// <summary>
/// each virtual instruction has its own profiler_t structure which can generate
/// all varients of the virtual instruction for each size...
/// </summary>
struct profiler_t {
/// <summary>
/// string name of the virtual instruction that this profile generates for...
/// </summary>
std::string name;
/// <summary>
/// mnemonic representation of the virtual instruction...
/// </summary>
mnemonic_t mnemonic;
/// <summary>
/// vector of matcher lambda's which return true if a given instruction
/// matches...
/// </summary>
std::vector<matcher_t> matchers;
/// <summary>
/// generates a virtual instruction structure...
/// </summary>
vinstr_gen_t generate;
};
extern profiler_t jmp;
inline std::vector<profiler_t*> profiles = {&jmp};
inline vinstr_t determine(zydis_reg_t& vip,
zydis_reg_t& vsp,
zydis_rtn_t& hndlr) {
const auto profile = std::find_if(
profiles.begin(), profiles.end(), [&](profiler_t* profile) -> bool {
for (auto& matcher : profile->matchers) {
const auto matched = std::find_if(hndlr.begin(), hndlr.end(),
[&](zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return matcher(vip, vsp, i);
});
if (matched == hndlr.end())
return false;
}
return true;
});
if (profile == profiles.end())
return vinstr_t{mnemonic_t::unknown};
auto result = (*profile)->generate(vip, vsp, hndlr);
return result.has_value() ? result.value() : vinstr_t{mnemonic_t::unknown};
}
} // namespace vm::instrs

@ -6,7 +6,7 @@
#define PUSH_4B_MASK "x????"
namespace vm::locate {
inline bool find(const zydis_routine_t& rtn,
inline bool find(const zydis_rtn_t& rtn,
std::function<bool(const zydis_instr_t&)> callback) {
auto res = std::find_if(rtn.begin(), rtn.end(), callback);
return res != rtn.end();

@ -1,4 +1,5 @@
#pragma once
#include <vmctx.hpp>
#include <vmlocate.hpp>
#include <vmutils.hpp>
#include <vmutils.hpp>
#include <vminstrs.hpp>

@ -5,6 +5,7 @@
#include <fstream>
#include <functional>
#include <nt/image.hpp>
#include <optional>
#include <vector>
using u8 = unsigned char;
@ -13,7 +14,7 @@ using u32 = unsigned int;
using u64 = unsigned long long;
using zydis_decoded_instr_t = ZydisDecodedInstruction;
using zydis_register_t = ZydisRegister;
using zydis_reg_t = ZydisRegister;
using zydis_mnemonic_t = ZydisMnemonic;
using zydis_decoded_operand_t = ZydisDecodedOperand;
@ -23,7 +24,7 @@ struct zydis_instr_t {
std::uintptr_t addr;
};
using zydis_routine_t = std::vector<zydis_instr_t>;
using zydis_rtn_t = std::vector<zydis_instr_t>;
namespace vm::utils {
inline thread_local std::shared_ptr<ZydisDecoder> g_decoder = nullptr;
@ -72,7 +73,7 @@ bool is_jmp(const zydis_decoded_instr_t& instr);
/// </summary>
/// <param name="routine">reference to a zydis_routine_t to be
/// printed...</param>
void print(zydis_routine_t& routine);
void print(zydis_rtn_t& routine);
/// <summary>
/// prints a single disassembly view of an instruction...
@ -90,7 +91,7 @@ namespace reg {
/// <param name="reg">a zydis decoded register value...</param>
/// <returns>returns the largest width register of the given register... AL
/// gives RAX...</returns>
zydis_register_t to64(zydis_register_t reg);
zydis_reg_t to64(zydis_reg_t reg);
/// <summary>
/// compares to registers with each other... calls to64 and compares...
@ -98,7 +99,7 @@ zydis_register_t to64(zydis_register_t reg);
/// <param name="a">register a...</param>
/// <param name="b">register b...</param>
/// <returns>returns true if register to64(a) == to64(b)...</returns>
bool compare(zydis_register_t a, zydis_register_t b);
bool compare(zydis_reg_t a, zydis_reg_t b);
} // namespace reg
/// <summary>
@ -109,7 +110,7 @@ bool compare(zydis_register_t a, zydis_register_t b);
/// from...</param> <param name="keep_jmps">keep JCC's in the flattened
/// instruction stream...</param> <returns>returns true if flattened was
/// successful...</returns>
bool flatten(zydis_routine_t& routine,
bool flatten(zydis_rtn_t& routine,
std::uintptr_t routine_addr,
bool keep_jmps = false,
std::uint32_t max_instrs = 500,
@ -119,7 +120,7 @@ bool flatten(zydis_routine_t& routine,
/// deadstore deobfuscation of a flattened routine...
/// </summary>
/// <param name="routine">reference to a flattened instruction vector...</param>
void deobfuscate(zydis_routine_t& routine);
void deobfuscate(zydis_rtn_t& routine);
/// <summary>
/// small namespace that contains function wrappers to determine the validity of

@ -28,7 +28,7 @@ std::vector<vm_enter_t> get_vm_entries(std::uintptr_t module_base,
std::uintptr_t result = module_base;
std::vector<vm_enter_t> entries;
static const auto push_regs = [&](const zydis_routine_t& rtn) -> bool {
static const auto push_regs = [&](const zydis_rtn_t& rtn) -> bool {
for (unsigned reg = ZYDIS_REGISTER_RAX; reg < ZYDIS_REGISTER_R15; ++reg) {
auto res = std::find_if(
rtn.begin(), rtn.end(), [&](const zydis_instr_t& instr) -> bool {
@ -49,7 +49,7 @@ std::vector<vm_enter_t> get_vm_entries(std::uintptr_t module_base,
result = sigscan((void*)++result, module_size - (result - module_base),
PUSH_4B_IMM, PUSH_4B_MASK);
zydis_routine_t rtn;
zydis_rtn_t rtn;
if (!vm::utils::scn::executable(module_base, result))
continue;

@ -0,0 +1,127 @@
#include <vminstrs.hpp>
namespace vm::instrs {
profiler_t jmp = {
"JMP",
mnemonic_t::jmp,
// MOV REG, [VSP]
{{[&](const zydis_reg_t vip,
const zydis_reg_t vsp,
const zydis_decoded_instr_t& instr) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.operands[1].mem.base == vsp;
},
// ADD VSP, 8
[&](const zydis_reg_t vip,
const zydis_reg_t vsp,
const zydis_decoded_instr_t& instr) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_ADD &&
instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[0].reg.value == vsp &&
instr.operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
instr.operands[1].imm.value.u == 8;
},
// MOV VIP, REG
[&](const zydis_reg_t vip,
const zydis_reg_t vsp,
const zydis_decoded_instr_t& instr) -> bool {
return instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.operands[0].reg.value == vip &&
instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER;
}}},
[&](zydis_reg_t& vip,
zydis_reg_t& vsp,
zydis_rtn_t& hndlr) -> std::optional<vinstr_t> {
const auto xchg = std::find_if(
hndlr.begin(), hndlr.end(), [&](const zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return i.mnemonic == ZYDIS_MNEMONIC_XCHG &&
i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
// exclusive or... operand 1 or operand 2 can be VSP but they
// both cannot be...
((i.operands[1].reg.value == vsp ||
i.operands[0].reg.value == vsp) &&
!((i.operands[1].reg.value == vsp) &&
(i.operands[0].reg.value == vsp)));
});
// this JMP virtual instruction changes VSP as well as VIP...
if (xchg != hndlr.end()) {
// grab the register that isnt VSP in the XCHG...
// xchg reg, vsp or xchg vsp, reg...
zydis_reg_t write_dep = xchg->instr.operands[0].reg.value != vsp
? xchg->instr.operands[0].reg.value
: xchg->instr.operands[1].reg.value;
// update VIP... VSP becomes VIP... with the XCHG...
vip = xchg->instr.operands[0].reg.value != vsp
? xchg->instr.operands[1].reg.value
: xchg->instr.operands[0].reg.value;
// find the next MOV REG, write_dep... this REG will be VSP...
const auto mov_reg_write_dep = std::find_if(
xchg, hndlr.end(), [&](const zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return i.mnemonic == ZYDIS_MNEMONIC_MOV &&
i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].reg.value == write_dep;
});
if (mov_reg_write_dep == hndlr.end())
return {};
vsp = mov_reg_write_dep->instr.operands[0].reg.value;
} else {
// find the MOV REG, [VSP] instruction...
const auto mov_reg_deref_vsp = std::find_if(
hndlr.begin(), hndlr.end(),
[&](const zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return i.mnemonic == ZYDIS_MNEMONIC_MOV &&
i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY &&
i.operands[1].mem.base == vsp;
});
if (mov_reg_deref_vsp == hndlr.end())
return {};
// find the MOV REG, mov_reg_deref_vsp->operands[0].reg.value
const auto mov_vip_reg = std::find_if(
mov_reg_deref_vsp, hndlr.end(),
[&](const zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return i.mnemonic == ZYDIS_MNEMONIC_MOV &&
i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].reg.value ==
mov_reg_deref_vsp->instr.operands[0].reg.value;
});
if (mov_vip_reg == hndlr.end())
return {};
vip = mov_vip_reg->instr.operands[0].reg.value;
// see if VSP gets updated as well...
const auto mov_reg_vsp = std::find_if(
mov_reg_deref_vsp, hndlr.end(),
[&](const zydis_instr_t& instr) -> bool {
const auto& i = instr.instr;
return i.mnemonic == ZYDIS_MNEMONIC_MOV &&
i.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
i.operands[1].reg.value == vsp;
});
if (mov_reg_vsp != hndlr.end())
vsp = mov_reg_vsp->instr.operands[0].reg.value;
}
return vinstr_t{mnemonic_t::jmp};
}};
}

@ -8,7 +8,7 @@ void print(const zydis_decoded_instr_t& instr) {
std::puts(buffer);
}
void print(zydis_routine_t& routine) {
void print(zydis_rtn_t& routine) {
char buffer[256];
for (auto [instr, raw, addr] : routine) {
ZydisFormatterFormatInstruction(vm::utils::g_formatter.get(), &instr,
@ -22,7 +22,7 @@ bool is_jmp(const zydis_decoded_instr_t& instr) {
instr.mnemonic <= ZYDIS_MNEMONIC_JZ;
}
bool flatten(zydis_routine_t& routine,
bool flatten(zydis_rtn_t& routine,
std::uintptr_t routine_addr,
bool keep_jmps,
std::uint32_t max_instrs,
@ -74,9 +74,9 @@ bool flatten(zydis_routine_t& routine,
return false;
}
void deobfuscate(zydis_routine_t& routine) {
void deobfuscate(zydis_rtn_t& routine) {
static const auto _uses_reg = [](zydis_decoded_operand_t& op,
zydis_register_t reg) -> bool {
zydis_reg_t reg) -> bool {
switch (op.type) {
case ZYDIS_OPERAND_TYPE_MEMORY: {
return vm::utils::reg::compare(op.mem.base, reg) ||
@ -92,7 +92,7 @@ void deobfuscate(zydis_routine_t& routine) {
};
static const auto _reads = [](zydis_decoded_instr_t& instr,
zydis_register_t reg) -> bool {
zydis_reg_t reg) -> bool {
if (instr.operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY &&
vm::utils::reg::compare(instr.operands[0].mem.base, reg))
return true;
@ -105,7 +105,7 @@ void deobfuscate(zydis_routine_t& routine) {
};
static const auto _writes = [](zydis_decoded_instr_t& instr,
zydis_register_t reg) -> bool {
zydis_reg_t reg) -> bool {
for (auto op_idx = 0u; op_idx < instr.operand_count; ++op_idx)
// if instruction writes to the specific register...
if (instr.operands[op_idx].type == ZYDIS_OPERAND_TYPE_REGISTER &&
@ -119,7 +119,8 @@ void deobfuscate(zydis_routine_t& routine) {
std::uint32_t last_size = 0u;
static const std::vector<ZydisMnemonic> blacklist = {
ZYDIS_MNEMONIC_CLC, ZYDIS_MNEMONIC_BT, ZYDIS_MNEMONIC_TEST,
ZYDIS_MNEMONIC_CMP, ZYDIS_MNEMONIC_CMC, ZYDIS_MNEMONIC_STC};
ZYDIS_MNEMONIC_CMP, ZYDIS_MNEMONIC_CMC, ZYDIS_MNEMONIC_STC,
ZYDIS_MNEMONIC_JMP};
static const std::vector<ZydisMnemonic> whitelist = {
ZYDIS_MNEMONIC_PUSH, ZYDIS_MNEMONIC_POP, ZYDIS_MNEMONIC_CALL,
@ -138,7 +139,7 @@ void deobfuscate(zydis_routine_t& routine) {
break;
}
zydis_register_t reg = ZYDIS_REGISTER_NONE;
zydis_reg_t reg = ZYDIS_REGISTER_NONE;
// look for operands with writes to a register...
for (auto op_idx = 0u; op_idx < itr->instr.operand_count; ++op_idx)
if (itr->instr.operands[op_idx].type == ZYDIS_OPERAND_TYPE_REGISTER &&
@ -183,11 +184,11 @@ void deobfuscate(zydis_routine_t& routine) {
}
namespace reg {
zydis_register_t to64(zydis_register_t reg) {
zydis_reg_t to64(zydis_reg_t reg) {
return ZydisRegisterGetLargestEnclosing(ZYDIS_MACHINE_MODE_LONG_64, reg);
}
bool compare(zydis_register_t a, zydis_register_t b) {
bool compare(zydis_reg_t a, zydis_reg_t b) {
return to64(a) == to64(b);
}
} // namespace reg

Loading…
Cancel
Save