blew the tires off vmctx for now... fixed a bug in vmlocate... working now...

main
xerox 3 years ago
parent 3061b00f08
commit cbac337a60

@ -2,3 +2,4 @@
BasedOnStyle: Chromium BasedOnStyle: Chromium
... ...

@ -78,7 +78,6 @@ list(APPEND vmprofiler_SOURCES
"dependencies/vmprofiler/include/vmprofiles.hpp" "dependencies/vmprofiler/include/vmprofiles.hpp"
"dependencies/vmprofiler/include/vmutils.hpp" "dependencies/vmprofiler/include/vmutils.hpp"
"dependencies/vmprofiler/include/scn.hpp" "dependencies/vmprofiler/include/scn.hpp"
"src/vmctx.cpp"
"src/vmlocate.cpp" "src/vmlocate.cpp"
"include/vmctx.hpp" "include/vmctx.hpp"
"include/vmlocate.hpp" "include/vmlocate.hpp"

@ -3,40 +3,7 @@
#include <vmp2.hpp> #include <vmp2.hpp>
namespace vm { namespace vm {
/// <summary> struct ctx_t {
/// vm::ctx_t class is used to auto generate vm_entry, calc_jmp, and other
/// per-vm entry information... creating a vm::ctx_t object can make it easier
/// to pass around information pertaining to a given vm entry...
/// </summary>
class ctx_t {
public:
/// <summary>
/// default constructor for vm::ctx_t... all information for a given vm entry
/// must be provided...
/// </summary>
/// <param name="module_base">the linear virtual address of the module
/// base...</param> <param name="image_base">image base from optional nt
/// header... <a
/// href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64">IMAGE_OPTIONAL_HEADER64</a>...</param>
/// <param name="image_size">image size from optional nt header... <a
/// href="https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_optional_header64">IMAGE_OPTIONAL_HEADER64</a>...</param>
/// <param name="vm_entry_rva">relative virtual address from the module base
/// address to the first push prior to a vm entry...</param>
explicit ctx_t(std::uintptr_t module_base, std::uintptr_t image_base,
std::uintptr_t image_size, std::uintptr_t vm_entry_rva);
/// <summary>
/// init all per-vm entry data such as vm_entry, calc_jmp, and vm handlers...
/// </summary>
/// <returns>returns true if no errors...</returns>
bool init();
const std::uintptr_t module_base, image_base, vm_entry_rva, image_size; const std::uintptr_t module_base, image_base, vm_entry_rva, image_size;
/// <summary>
/// the order in which VIP advances...
/// </summary>
vmp2::exec_type_t exec_type;
zydis_routine_t vm_entry;
}; };
} // namespace vm } // namespace vm

@ -5,20 +5,23 @@
#define PUSH_4B_IMM "\x68\x00\x00\x00\x00" #define PUSH_4B_IMM "\x68\x00\x00\x00\x00"
#define PUSH_4B_MASK "x????" #define PUSH_4B_MASK "x????"
namespace vm::locate namespace vm::locate {
{ inline bool find(const zydis_routine_t& rtn,
inline bool find( const zydis_routine_t &rtn, std::function< bool( const zydis_instr_t & ) > callback ) std::function<bool(const zydis_instr_t&)> callback) {
{ auto res = std::find_if(rtn.begin(), rtn.end(), callback);
auto res = std::find_if( rtn.begin(), rtn.end(), callback ); return res != rtn.end();
return res != rtn.end(); }
}
struct vm_enter_t struct vm_enter_t {
{ std::uint32_t rva;
std::uint32_t rva; std::uint32_t encrypted_rva;
std::uint32_t encrypted_rva; };
};
std::uintptr_t sigscan( void *base, std::uint32_t size, const char *pattern, const char *mask ); std::uintptr_t sigscan(void* base,
std::vector< vm_enter_t > get_vm_entries( std::uintptr_t module_base, std::uint32_t module_size ); std::uint32_t size,
} // namespace vm::locate const char* pattern,
const char* mask);
std::vector<vm_enter_t> get_vm_entries(std::uintptr_t module_base,
std::uint32_t module_size);
} // namespace vm::locate

@ -1,19 +0,0 @@
#include <vmctx.hpp>
namespace vm
{
ctx_t::ctx_t( std::uintptr_t module_base, std::uintptr_t image_base, std::uintptr_t image_size,
std::uintptr_t vm_entry_rva )
: module_base( module_base ), image_base( image_base ), image_size( image_size ), vm_entry_rva( vm_entry_rva )
{
}
bool ctx_t::init()
{
if ( !vm::util::flatten( vm_entry, vm_entry_rva + module_base ) )
return false;
vm::util::deobfuscate( vm_entry );
return true;
}
} // namespace vm

@ -1,170 +1,164 @@
#include <vmlocate.hpp> #include <vmlocate.hpp>
namespace vm::locate namespace vm::locate {
{ std::uintptr_t sigscan(void* base,
std::uintptr_t sigscan( void *base, std::uint32_t size, const char *pattern, const char *mask ) std::uint32_t size,
{ const char* pattern,
static const auto check_mask = [ & ]( const char *base, const char *pattern, const char *mask ) -> bool { const char* mask) {
for ( ; *mask; ++base, ++pattern, ++mask ) static const auto check_mask = [&](const char* base, const char* pattern,
if ( *mask == 'x' && *base != *pattern ) const char* mask) -> bool {
return false; for (; *mask; ++base, ++pattern, ++mask)
return true; if (*mask == 'x' && *base != *pattern)
}; return false;
return true;
size -= std::strlen( mask ); };
for ( auto i = 0; i <= size; ++i )
{ size -= std::strlen(mask);
void *addr = ( void * )&( ( ( char * )base )[ i ] ); for (auto i = 0; i <= size; ++i) {
if ( check_mask( ( char * )addr, pattern, mask ) ) void* addr = (void*)&(((char*)base)[i]);
return reinterpret_cast< std::uintptr_t >( addr ); if (check_mask((char*)addr, pattern, mask))
} return reinterpret_cast<std::uintptr_t>(addr);
}
return {};
return {};
}
std::vector<vm_enter_t> get_vm_entries(std::uintptr_t module_base,
std::uint32_t module_size) {
std::uintptr_t result = module_base;
std::vector<vm_enter_t> entries;
static const auto push_regs = [&](const zydis_routine_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 {
return instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH &&
instr.instr.operands[0].type ==
ZYDIS_OPERAND_TYPE_REGISTER &&
instr.instr.operands[0].reg.value == reg;
});
// skip RSP push...
if (res == rtn.end() && reg != ZYDIS_REGISTER_RSP)
return false;
} }
return true;
std::vector< vm_enter_t > get_vm_entries( std::uintptr_t module_base, std::uint32_t module_size ) };
{
std::uintptr_t result = module_base; do {
std::vector< vm_enter_t > entries; result = sigscan((void*)++result, module_size - (result - module_base),
PUSH_4B_IMM, PUSH_4B_MASK);
static const auto push_regs = [ & ]( const zydis_routine_t &rtn ) -> bool {
for ( unsigned reg = ZYDIS_REGISTER_RAX; reg < ZYDIS_REGISTER_R15; ++reg ) zydis_routine_t rtn;
{ if (!scn::executable(module_base, result))
auto res = std::find_if( rtn.begin(), rtn.end(), [ & ]( const zydis_instr_t &instr ) -> bool { continue;
return instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH &&
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && if (!vm::util::flatten(rtn, result, false, 500, module_base))
instr.instr.operands[ 0 ].reg.value == reg; continue;
} );
// the last instruction in the stream should be a JMP to a register or a
// skip RSP push... // return instruction...
if ( res == rtn.end() && reg != ZYDIS_REGISTER_RSP ) const auto& last_instr = rtn[rtn.size() - 1];
return false; if (!((last_instr.instr.mnemonic == ZYDIS_MNEMONIC_JMP &&
} last_instr.instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER) ||
return true; last_instr.instr.mnemonic == ZYDIS_MNEMONIC_RET))
}; continue;
do std::uint8_t num_pushs = 0u;
{ std::for_each(rtn.begin(), rtn.end(), [&](const zydis_instr_t& instr) {
result = sigscan( ( void * )++result, module_size - ( result - module_base ), PUSH_4B_IMM, PUSH_4B_MASK ); if (instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH &&
instr.instr.operands[0].type == ZYDIS_OPERAND_TYPE_IMMEDIATE)
zydis_routine_t rtn; ++num_pushs;
if ( !scn::executable( module_base, result ) ) });
continue;
/*
if ( !vm::util::flatten( rtn, result, false, 500, module_base ) ) only one legit imm pushes for every vm entry...
continue; > 0x822c : push 0xFFFFFFFF890001FA <---
> 0x7fc9 : call xxxxx
// the last instruction in the stream should be a JMP to a register or a return instruction... > 0x48e4 : push r13
const auto &last_instr = rtn[ rtn.size() - 1 ]; > 0x4690 : push rsi
if ( !( ( last_instr.instr.mnemonic == ZYDIS_MNEMONIC_JMP && > 0x4e53 : push r14
last_instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER ) || > 0x74fb : push rcx
last_instr.instr.mnemonic == ZYDIS_MNEMONIC_RET ) ) > 0x607c : push rsp
continue; > 0x4926 : pushfq
> 0x4dc2 : push rbp
std::uint8_t num_pushs = 0u; > 0x5c8c : push r12
std::for_each( rtn.begin(), rtn.end(), [ & ]( const zydis_instr_t &instr ) { > 0x52ac : push r10
if ( instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSH && > 0x51a5 : push r9
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_IMMEDIATE ) > 0x5189 : push rdx
++num_pushs; > 0x7d5f : push r8
} ); > 0x4505 : push rdi
> 0x4745 : push r11
/* > 0x478b : push rax
only one legit imm pushes for every vm entry... > 0x7a53 : push rbx
> 0x822c : push 0xFFFFFFFF890001FA <--- > 0x500d : push r15
> 0x7fc9 : call xxxxx */
> 0x48e4 : push r13 if (num_pushs != 1)
> 0x4690 : push rsi continue;
> 0x4e53 : push r14
> 0x74fb : push rcx // check for a pushfq...
> 0x607c : push rsp // > 0x4926 : pushfq <---
> 0x4926 : pushfq if (!vm::locate::find(rtn, [&](const zydis_instr_t& instr) -> bool {
> 0x4dc2 : push rbp return instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ;
> 0x5c8c : push r12 }))
> 0x52ac : push r10 continue;
> 0x51a5 : push r9
> 0x5189 : push rdx /*
> 0x7d5f : push r8 check to see if we push all of these registers...
> 0x4505 : push rdi > 0x48e4 : push r13
> 0x4745 : push r11 > 0x4690 : push rsi
> 0x478b : push rax > 0x4e53 : push r14
> 0x7a53 : push rbx > 0x74fb : push rcx
> 0x500d : push r15 > 0x607c : push rsp
*/ > 0x4926 : pushfq
if ( num_pushs != 1 ) > 0x4dc2 : push rbp
continue; > 0x5c8c : push r12
> 0x52ac : push r10
// check for a pushfq... > 0x51a5 : push r9
// > 0x4926 : pushfq <--- > 0x5189 : push rdx
if ( !vm::locate::find( rtn, [ & ]( const zydis_instr_t &instr ) -> bool { > 0x7d5f : push r8
return instr.instr.mnemonic == ZYDIS_MNEMONIC_PUSHFQ; > 0x4505 : push rdi
} ) ) > 0x4745 : push r11
continue; > 0x478b : push rax
> 0x7a53 : push rbx
/* > 0x500d : push r15
check to see if we push all of these registers... */
> 0x48e4 : push r13 if (!push_regs(rtn))
> 0x4690 : push rsi continue;
> 0x4e53 : push r14
> 0x74fb : push rcx // check for a mov reg, rsp
> 0x607c : push rsp if (!vm::locate::find(rtn, [&](const zydis_instr_t& instr) -> bool {
> 0x4926 : pushfq return instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
> 0x4dc2 : push rbp instr.instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
> 0x5c8c : push r12 instr.instr.operands[1].type == ZYDIS_OPERAND_TYPE_REGISTER &&
> 0x52ac : push r10 instr.instr.operands[1].reg.value == ZYDIS_REGISTER_RSP;
> 0x51a5 : push r9 }))
> 0x5189 : push rdx continue;
> 0x7d5f : push r8
> 0x4505 : push rdi // check for a mov reg, [rsp+0x90]
> 0x4745 : push r11 if (!vm::locate::find(rtn, [&](const zydis_instr_t& instr) -> bool {
> 0x478b : push rax return instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
> 0x7a53 : push rbx instr.instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER &&
> 0x500d : push r15 instr.instr.operands[1].type == ZYDIS_OPERAND_TYPE_MEMORY &&
*/ instr.instr.operands[1].mem.base == ZYDIS_REGISTER_RSP &&
if ( !push_regs( rtn ) ) instr.instr.operands[1].mem.disp.value == 0x90;
continue; }))
continue;
// check for a mov r13, rax...
if ( !vm::locate::find( rtn, [ & ]( const zydis_instr_t &instr ) -> bool { // if code execution gets to here then we can assume this is a legit vm
return instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV && // entry... its time to build a vm_enter_t... first we check to see if an
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && // existing entry already exits...
instr.instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_R13 &&
instr.instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_REGISTER && auto push_val = (std::uint32_t)rtn[0].instr.operands[0].imm.value.u;
instr.instr.operands[ 1 ].reg.value == ZYDIS_REGISTER_RAX; if (std::find_if(entries.begin(), entries.end(),
} ) ) [&](const vm_enter_t& vm_enter) -> bool {
continue; return vm_enter.encrypted_rva == push_val;
}) != entries.end())
// check for a mov reg, rsp continue;
if ( !vm::locate::find( rtn, [ & ]( const zydis_instr_t &instr ) -> bool {
return instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV && vm_enter_t entry{(std::uint32_t)(result - module_base), push_val};
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && entries.push_back(entry);
instr.instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_REGISTER && } while (result);
instr.instr.operands[ 1 ].reg.value == ZYDIS_REGISTER_RSP; return entries;
} ) ) }
continue; } // namespace vm::locate
// check for a mov reg, [rsp+0x90]
if ( !vm::locate::find( rtn, [ & ]( const zydis_instr_t &instr ) -> bool {
return instr.instr.mnemonic == ZYDIS_MNEMONIC_MOV &&
instr.instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER &&
instr.instr.operands[ 1 ].type == ZYDIS_OPERAND_TYPE_MEMORY &&
instr.instr.operands[ 1 ].mem.base == ZYDIS_REGISTER_RSP &&
instr.instr.operands[ 1 ].mem.disp.value == 0x90;
} ) )
continue;
// if code execution gets to here then we can assume this is a legit vm
// entry... its time to build a vm_enter_t... first we check to see if an
// existing entry already exits...
auto push_val = ( std::uint32_t )rtn[ 0 ].instr.operands[ 0 ].imm.value.u;
if ( std::find_if( entries.begin(), entries.end(), [ & ]( const vm_enter_t &vm_enter ) -> bool {
return vm_enter.encrypted_rva == push_val;
} ) != entries.end() )
continue;
vm_enter_t entry{ ( std::uint32_t )( result - module_base ), push_val };
entries.push_back( entry );
} while ( result );
return entries;
}
} // namespace vm::locate
Loading…
Cancel
Save