From 0549d95b5ddb8cdb5c27d80d9bb281c156a21a4c Mon Sep 17 00:00:00 2001 From: _xeroxz <_xeroxz@back.engineer> Date: Wed, 1 Dec 2021 01:45:31 -0800 Subject: [PATCH] porting project to support linux... std::vector module_data is not page aligned and so qemu shits itself. going to need to re-write some stuff... --- dependencies/vmprofiler | 2 +- include/unpacker.hpp | 96 +-- include/vmemu_t.hpp | 90 ++- src/main.cpp | 711 +++++++++++----------- src/unpacker.cpp | 942 +++++++++++++++-------------- src/vmemu_t.cpp | 1238 +++++++++++++++++++-------------------- 6 files changed, 1505 insertions(+), 1574 deletions(-) diff --git a/dependencies/vmprofiler b/dependencies/vmprofiler index 1b6875d..dd7d377 160000 --- a/dependencies/vmprofiler +++ b/dependencies/vmprofiler @@ -1 +1 @@ -Subproject commit 1b6875d18825529907289bc87990fed5d99e7f96 +Subproject commit dd7d3777ad10373d0eeb23c118e4bdcfc7464494 diff --git a/include/unpacker.hpp b/include/unpacker.hpp index eeb06e2..3f2739a 100644 --- a/include/unpacker.hpp +++ b/include/unpacker.hpp @@ -1,14 +1,14 @@ #pragma once -#include +#include #include -#include #include #include +#include #include #include #include -#include +#include #define PAGE_4KB 0x1000 #define STACK_SIZE PAGE_4KB * 512 @@ -27,46 +27,50 @@ #define MOV_RAX_0_SIG "\x48\xB8\x00\x00\x00\x00\x00\x00\x00\x00" #define MOV_RAX_0_MASK "xxxxxxxxxx" -static_assert( sizeof MOV_RAX_0_SIG == sizeof MOV_RAX_0_MASK, "signature and mask sizes are wrong..." ); - -namespace engine -{ - class unpack_t - { - public: - explicit unpack_t( const std::vector< std::uint8_t > &bin ); - ~unpack_t( void ); - - bool init( void ); - bool unpack( std::vector< std::uint8_t > &output ); - - private: - using iat_hook_t = std::function< void( uc_engine *, unpack_t * ) >; - - uc_engine *uc_ctx; - std::vector< uint8_t > bin, map_bin; - std::vector< uc_hook * > uc_hooks; - - std::uintptr_t img_base, img_size, heap_offset, pack_section_offset; - win::image_t<> *win_img; - - static void local_alloc_hook( uc_engine *, unpack_t * ); - static void local_free_hook( uc_engine *, unpack_t * ); - static void load_library_hook( uc_engine *, unpack_t * ); - static void uc_strcpy( uc_engine *, char *buff, std::uintptr_t addr ); - - static bool iat_dispatcher( uc_engine *uc, uint64_t address, uint32_t size, unpack_t *unpack ); - static bool unpack_section_callback( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, - unpack_t *unpack ); - - static bool code_exec_callback( uc_engine *uc, uint64_t address, uint32_t size, unpack_t *unpack ); - static void invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, - unpack_t *unpack ); - - std::vector< std::uintptr_t > loaded_modules; - std::map< std::string, std::pair< std::uint32_t, iat_hook_t > > iat_hooks = { - { "LocalAlloc", { LOCAL_ALLOC_VECTOR, &local_alloc_hook } }, - { "LocalFree", { LOCAL_FREE_VECTOR, &local_free_hook } }, - { "LoadLibraryA", { LOAD_LIBRARY_VECTOR, &load_library_hook } } }; - }; -} // namespace engine \ No newline at end of file +static_assert(sizeof MOV_RAX_0_SIG == sizeof MOV_RAX_0_MASK, + "signature and mask sizes are wrong..."); + +namespace engine { +class unpack_t { + public: + explicit unpack_t(const std::vector &bin); + ~unpack_t(void); + + bool init(void); + bool unpack(std::vector &output); + + private: + using iat_hook_t = std::function; + + uc_engine *uc_ctx; + std::vector bin, map_bin; + std::vector uc_hooks; + + std::uintptr_t img_base, img_size, heap_offset, pack_section_offset; + win::image_t<> *win_img; + + static void local_alloc_hook(uc_engine *, unpack_t *); + static void local_free_hook(uc_engine *, unpack_t *); + static void load_library_hook(uc_engine *, unpack_t *); + static void uc_strcpy(uc_engine *, char *buff, std::uintptr_t addr); + + static bool iat_dispatcher(uc_engine *uc, uint64_t address, uint32_t size, + unpack_t *unpack); + + static bool unpack_section_callback(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, int64_t value, + unpack_t *unpack); + + static bool code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, + unpack_t *unpack); + + static void invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, + int size, int64_t value, unpack_t *unpack); + + std::map loaded_modules; + std::map > iat_hooks = { + {"LocalAlloc", {LOCAL_ALLOC_VECTOR, &local_alloc_hook}}, + {"LocalFree", {LOCAL_FREE_VECTOR, &local_free_hook}}, + {"LoadLibraryA", {LOAD_LIBRARY_VECTOR, &load_library_hook}}}; +}; +} // namespace engine \ No newline at end of file diff --git a/include/vmemu_t.hpp b/include/vmemu_t.hpp index 0143222..cf7057f 100644 --- a/include/vmemu_t.hpp +++ b/include/vmemu_t.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -10,49 +11,46 @@ #define STACK_BASE 0xFFFF000000000000 #define IAT_VECTOR_TABLE 0xFFFFF00000000000 -namespace vm -{ - inline bool g_force_emu = false; - - class emu_t - { - struct cpu_ctx_t - { - std::uintptr_t rip; - uc_context *context; - std::uint8_t stack[ STACK_SIZE ]; - }; - - struct code_block_data_t - { - vm::instrs::code_block_t code_block; - std::shared_ptr< cpu_ctx_t > cpu_ctx; - std::shared_ptr< vm::ctx_t > g_vm_ctx; - }; - - public: - explicit emu_t( vm::ctx_t *vm_ctx ); - ~emu_t(); - - bool init(); - bool get_trace( std::vector< vm::instrs::code_block_t > &code_blocks ); - - private: - std::uintptr_t img_base, img_size; - uc_hook code_exec_hook, invalid_mem_hook, int_hook; - - uc_engine *uc_ctx; - vm::ctx_t *g_vm_ctx; - code_block_data_t *cc_block; - - std::vector< std::uintptr_t > vip_begins; - std::vector< code_block_data_t > code_blocks; - std::map< std::uintptr_t, std::shared_ptr< vm::ctx_t > > vm_ctxs; - - uc_err create_entry( vmp2::v2::entry_t *entry ); - static void int_callback( uc_engine *uc, std::uint32_t intno, emu_t *obj ); - static bool code_exec_callback( uc_engine *uc, uint64_t address, uint32_t size, emu_t *obj ); - static void invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, - emu_t *obj ); - }; -} // namespace vm \ No newline at end of file +namespace vm { +inline bool g_force_emu = false; + +class emu_t { + struct cpu_ctx_t { + std::uintptr_t rip; + uc_context *context; + std::uint8_t stack[STACK_SIZE]; + }; + + struct code_block_data_t { + vm::instrs::code_block_t code_block; + std::shared_ptr cpu_ctx; + std::shared_ptr g_vm_ctx; + }; + + public: + explicit emu_t(vm::ctx_t *vm_ctx); + ~emu_t(); + + bool init(); + bool get_trace(std::vector &code_blocks); + + private: + std::uintptr_t img_base, img_size; + uc_hook code_exec_hook, invalid_mem_hook, int_hook; + + uc_engine *uc_ctx; + vm::ctx_t *g_vm_ctx; + code_block_data_t *cc_block; + + std::vector vip_begins; + std::vector code_blocks; + std::map > vm_ctxs; + + uc_err create_entry(vmp2::v2::entry_t *entry); + static void int_callback(uc_engine *uc, std::uint32_t intno, emu_t *obj); + static bool code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, + emu_t *obj); + static void invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, + int size, int64_t value, emu_t *obj); +}; +} // namespace vm \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 7de4fcf..14a0ab2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,402 +1,365 @@ -#include "unpacker.hpp" -#include "vmemu_t.hpp" - #include #include #include -#include - -int __cdecl main( int argc, const char *argv[] ) -{ - argparse::argument_parser_t parser( "VMEmu", "VMProtect 2 VM Handler Emulator" ); - parser.add_argument().name( "--vmentry" ).description( "relative virtual address to a vm entry..." ); - parser.add_argument().name( "--bin" ).description( "path to unpacked virtualized binary..." ); - parser.add_argument().name( "--out" ).description( "output file name..." ); - parser.add_argument().name( "--unpack" ).description( "unpack a vmp2 binary..." ); - parser.add_argument().names( { "-f", "--force" } ).description( "force emulation of unknown vm handlers...\n" ); - parser.add_argument() - .name( "--emuall" ) - .description( "scan for all vm enters and trace all of them... this may take a few minutes..." ); - - parser.add_argument() - .name( "--locateconst" ) - .description( "scan all vm enters for a specific constant value...\n" ); - - parser.enable_help(); - auto result = parser.parse( argc, argv ); - - if ( result ) - { - std::printf( "[!] error parsing commandline arguments... reason = %s\n", result.what().c_str() ); - return -1; - } - - if ( parser.exists( "help" ) ) - { - parser.print_help(); - return 0; - } - - auto umtils = xtils::um_t::get_instance(); - vm::g_force_emu = parser.exists( "force" ); - - if ( !parser.exists( "unpack" ) && parser.exists( "vmentry" ) && parser.exists( "bin" ) && parser.exists( "out" ) ) - { - const auto module_base = reinterpret_cast< std::uintptr_t >( - LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) ); - if ( !module_base ) - { - std::printf( "[!] failed to open binary file...\n" ); - return -1; - } +#include "unpacker.hpp" +#include "vmemu_t.hpp" - const auto vm_entry_rva = std::strtoull( parser.get< std::string >( "vmentry" ).c_str(), nullptr, 16 ); - const auto image_base = umtils->image_base( parser.get< std::string >( "bin" ).c_str() ); - const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage; +int __cdecl main(int argc, const char *argv[]) { + argparse::argument_parser_t parser("VMEmu", + "VMProtect 2 VM Handler Emulator"); + parser.add_argument() + .name("--vmentry") + .description("relative virtual address to a vm entry..."); + parser.add_argument() + .name("--bin") + .description("path to unpacked virtualized binary...") + .required(true); + parser.add_argument() + .name("--out") + .description("output file name...") + .required(true); + parser.add_argument().name("--unpack").description("unpack a vmp2 binary..."); + parser.add_argument() + .names({"-f", "--force"}) + .description("force emulation of unknown vm handlers..."); + parser.add_argument() + .name("--emuall") + .description( + "scan for all vm enters and trace all of them... this may take a few " + "minutes..."); + + parser.enable_help(); + auto result = parser.parse(argc, argv); + + if (result) { + std::printf("[!] error parsing commandline arguments... reason = %s\n", + result.what().c_str()); + return -1; + } + + if (parser.exists("help")) { + parser.print_help(); + return 0; + } + + vm::util::init(); + vm::g_force_emu = parser.exists("force"); + + std::vector module_data, tmp, unpacked_bin; + if (!vm::util::open_binary_file(parser.get("bin"), + module_data)) { + std::printf("[!] failed to open binary file...\n"); + return -1; + } + + auto img = reinterpret_cast *>(module_data.data()); + auto image_size = img->get_nt_headers()->optional_header.size_image; + + tmp.resize(image_size); + std::memcpy(tmp.data(), module_data.data(), 0x1000); + std::for_each(img->get_nt_headers()->get_sections(), + img->get_nt_headers()->get_sections() + + img->get_nt_headers()->file_header.num_sections, + [&](const auto §ion_header) { + std::memcpy(tmp.data() + section_header.virtual_address, + module_data.data() + section_header.ptr_raw_data, + section_header.size_raw_data); + }); + + const auto module_base = reinterpret_cast(tmp.data()); + const auto image_base = img->get_nt_headers()->optional_header.image_base; + + std::printf("> image base = %p, image size = %p, module base = %p\n", + image_base, image_size, module_base); + + if (!image_base || !image_size || !module_base) { + std::printf("[!] failed to open binary on disk...\n"); + return -1; + } + + if (parser.exists("vmentry")) { + const auto vm_entry_rva = + std::strtoull(parser.get("vmentry").c_str(), nullptr, 16); + + std::vector code_blocks; + vm::ctx_t vmctx(module_base, image_base, image_size, vm_entry_rva); + + if (!vmctx.init()) { + std::printf( + "[!] failed to init vmctx... this can be for many reasons..." + " try validating your vm entry rva... make sure the binary is " + "unpacked and is" + "protected with VMProtect 2...\n"); + return -1; + } - std::printf( "> image base = %p, image size = %p, module base = %p\n", image_base, image_size, module_base ); + vm::emu_t emu(&vmctx); - if ( !image_base || !image_size || !module_base ) - { - std::printf( "[!] failed to open binary on disk...\n" ); - return -1; - } + if (!emu.init()) { + std::printf("[!] failed to init emulator...\n"); + return -1; + } - std::vector< vm::instrs::code_block_t > code_blocks; - vm::ctx_t vmctx( module_base, image_base, image_size, vm_entry_rva ); + if (!emu.get_trace(code_blocks)) { + std::printf( + "[!] something failed during tracing, review the console for more " + "information...\n"); + return -1; + } - if ( !vmctx.init() ) - { - std::printf( "[!] failed to init vmctx... this can be for many reasons..." - " try validating your vm entry rva... make sure the binary is unpacked and is" - "protected with VMProtect 2...\n" ); - return -1; + std::printf("> number of blocks = %d\n", code_blocks.size()); + + for (auto &code_block : code_blocks) { + std::printf("> code block starts at = %p\n", code_block.vip_begin); + std::printf("> number of virtual instructions = %d\n", + code_block.vinstrs.size()); + std::printf("> does this code block have a jcc? %s\n", + code_block.jcc.has_jcc ? "yes" : "no"); + + if (code_block.jcc.has_jcc) { + switch (code_block.jcc.type) { + case vm::instrs::jcc_type::branching: { + std::printf("> branch 1 = %p, branch 2 = %p\n", + code_block.jcc.block_addr[0], + code_block.jcc.block_addr[1]); + break; + } + case vm::instrs::jcc_type::absolute: { + std::printf("> branch 1 = %p\n", code_block.jcc.block_addr[0]); + break; + } + case vm::instrs::jcc_type::switch_case: { + std::printf("> switch case blocks:\n"); + for (auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx) + std::printf(" case block at = 0x%p\n", + code_block.jcc.block_addr[idx]); + break; + } } + } + } - vm::emu_t emu( &vmctx ); + std::printf("> serializing results....\n"); + vmp2::v4::file_header file_header; + file_header.magic = VMP_MAGIC; + file_header.epoch_time = std::time(nullptr); + file_header.version = vmp2::version_t::v4; + file_header.module_base = module_base; + file_header.image_base = image_base; + file_header.vm_entry_rva = vm_entry_rva; + file_header.module_offset = sizeof file_header; + file_header.module_size = image_size; + file_header.rtn_count = 1; + file_header.rtn_offset = image_size + sizeof file_header; + + vmp2::v4::rtn_t rtn; + std::ofstream output(parser.get("out"), std::ios::binary); + output.write(reinterpret_cast(&file_header), + sizeof file_header); + output.write(reinterpret_cast(module_base), image_size); + + std::vector vmp2_blocks; + for (const auto &code_block : code_blocks) { + const auto _code_block_size = + sizeof(vmp2::v4::code_block_t) + + (code_block.jcc.block_addr.size() * 8) + + code_block.vinstrs.size() * sizeof(vm::instrs::virt_instr_t); + + vmp2::v4::code_block_t *_code_block = + reinterpret_cast(malloc(_code_block_size)); + + // serialize block meta data... + _code_block->vip_begin = code_block.vip_begin; + _code_block->next_block_offset = _code_block_size; + _code_block->vinstr_count = code_block.vinstrs.size(); + _code_block->has_jcc = code_block.jcc.has_jcc; + _code_block->jcc_type = code_block.jcc.type; + _code_block->num_block_addrs = code_block.jcc.block_addr.size(); + + // serialize jcc branches... + for (auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx) + _code_block->branch_addr[idx] = code_block.jcc.block_addr[idx]; + + auto block_vinstrs = reinterpret_cast( + reinterpret_cast(_code_block) + + sizeof(vmp2::v4::code_block_t) + + (code_block.jcc.block_addr.size() * 8)); + + for (auto idx = 0u; idx < code_block.vinstrs.size(); ++idx) + block_vinstrs[idx] = code_block.vinstrs[idx]; + + vmp2_blocks.push_back(_code_block); + } - if ( !emu.init() ) - { - std::printf( "[!] failed to init emulator...\n" ); - return -1; - } + std::size_t code_blocks_size = sizeof(vmp2::v4::rtn_t::size) + + sizeof(vmp2::v4::rtn_t::code_block_count) + + sizeof(vmp2::v4::rtn_t::vm_enter_offset); + + std::for_each(vmp2_blocks.begin(), vmp2_blocks.end(), + [&](vmp2::v4::code_block_t *vmp2_block) -> void { + code_blocks_size += vmp2_block->next_block_offset; + }); + + rtn.size = code_blocks_size; + rtn.code_block_count = vmp2_blocks.size(); + rtn.vm_enter_offset = vm_entry_rva; + + output.write(reinterpret_cast(&rtn), + sizeof(vmp2::v4::rtn_t::size) + + sizeof(vmp2::v4::rtn_t::code_block_count) + + sizeof(vmp2::v4::rtn_t::vm_enter_offset)); + + std::for_each(vmp2_blocks.begin(), vmp2_blocks.end(), + [&](vmp2::v4::code_block_t *vmp2_block) -> void { + output.write(reinterpret_cast(vmp2_block), + vmp2_block->next_block_offset); + free(vmp2_block); + }); + output.close(); + } else if (parser.exists("unpack")) { + engine::unpack_t unpacker(module_data); + + if (!unpacker.init()) { + std::printf("> failed to init unpacker...\n"); + return -1; + } - if ( !emu.get_trace( code_blocks ) ) - { - std::printf( "[!] something failed during tracing, review the console for more information...\n" ); - return -1; - } + if (!unpacker.unpack(unpacked_bin)) { + std::printf("> failed to unpack binary... refer to log above...\n"); + return -1; + } - std::printf( "> number of blocks = %d\n", code_blocks.size() ); - - for ( auto &code_block : code_blocks ) - { - std::printf( "> code block starts at = %p\n", code_block.vip_begin ); - std::printf( "> number of virtual instructions = %d\n", code_block.vinstrs.size() ); - std::printf( "> does this code block have a jcc? %s\n", code_block.jcc.has_jcc ? "yes" : "no" ); - - if ( code_block.jcc.has_jcc ) - { - switch ( code_block.jcc.type ) - { - case vm::instrs::jcc_type::branching: - { - std::printf( "> branch 1 = %p, branch 2 = %p\n", code_block.jcc.block_addr[ 0 ], - code_block.jcc.block_addr[ 1 ] ); - break; - } - case vm::instrs::jcc_type::absolute: - { - std::printf( "> branch 1 = %p\n", code_block.jcc.block_addr[ 0 ] ); - break; - } - case vm::instrs::jcc_type::switch_case: - { - std::printf( "> switch case blocks:\n" ); - for ( auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx ) - std::printf( " case block at = 0x%p\n", code_block.jcc.block_addr[ idx ] ); - break; - } - } - } - } + std::printf("> writing result to = %s\n", + parser.get("out").c_str()); - std::printf( "> serializing results....\n" ); - vmp2::v4::file_header file_header; - file_header.magic = VMP_MAGIC; - file_header.epoch_time = std::time( nullptr ); - file_header.version = vmp2::version_t::v4; - file_header.module_base = module_base; - file_header.image_base = image_base; - file_header.vm_entry_rva = vm_entry_rva; - file_header.module_offset = sizeof file_header; - file_header.module_size = image_size; - file_header.rtn_count = 1; - file_header.rtn_offset = image_size + sizeof file_header; - - vmp2::v4::rtn_t rtn; - std::ofstream output( parser.get< std::string >( "out" ), std::ios::binary ); - output.write( reinterpret_cast< const char * >( &file_header ), sizeof file_header ); - output.write( reinterpret_cast< const char * >( module_base ), image_size ); - - std::vector< vmp2::v4::code_block_t * > vmp2_blocks; - for ( const auto &code_block : code_blocks ) - { - const auto _code_block_size = sizeof vmp2::v4::code_block_t + ( code_block.jcc.block_addr.size() * 8 ) + - code_block.vinstrs.size() * sizeof vm::instrs::virt_instr_t; - - vmp2::v4::code_block_t *_code_block = - reinterpret_cast< vmp2::v4::code_block_t * >( malloc( _code_block_size ) ); - - // serialize block meta data... - _code_block->vip_begin = code_block.vip_begin; - _code_block->next_block_offset = _code_block_size; - _code_block->vinstr_count = code_block.vinstrs.size(); - _code_block->has_jcc = code_block.jcc.has_jcc; - _code_block->jcc_type = code_block.jcc.type; - _code_block->num_block_addrs = code_block.jcc.block_addr.size(); - - // serialize jcc branches... - for ( auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx ) - _code_block->branch_addr[ idx ] = code_block.jcc.block_addr[ idx ]; - - auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >( - reinterpret_cast< std::uintptr_t >( _code_block ) + sizeof vmp2::v4::code_block_t + - ( code_block.jcc.block_addr.size() * 8 ) ); - - for ( auto idx = 0u; idx < code_block.vinstrs.size(); ++idx ) - block_vinstrs[ idx ] = code_block.vinstrs[ idx ]; - - vmp2_blocks.push_back( _code_block ); - } + std::ofstream output(parser.get("out"), std::ios::binary); + output.write(reinterpret_cast(unpacked_bin.data()), + unpacked_bin.size()); - std::size_t code_blocks_size = sizeof( vmp2::v4::rtn_t::size ) + sizeof( vmp2::v4::rtn_t::code_block_count ) + - sizeof( vmp2::v4::rtn_t::vm_enter_offset ); + output.close(); + } else if (parser.exists("emuall")) { + auto entries = vm::locate::get_vm_entries(module_base, image_size); - std::for_each( vmp2_blocks.begin(), vmp2_blocks.end(), [ & ]( vmp2::v4::code_block_t *vmp2_block ) -> void { - code_blocks_size += vmp2_block->next_block_offset; - } ); + std::vector< + std::pair > > + virt_rtns; - rtn.size = code_blocks_size; - rtn.code_block_count = vmp2_blocks.size(); - rtn.vm_enter_offset = vm_entry_rva; + for (const auto &[vm_enter_offset, encrypted_rva, hndlr_tble] : entries) { + std::printf("> emulating vm enter at rva = 0x%x\n", vm_enter_offset); + vm::ctx_t vm_ctx(module_base, image_base, image_size, vm_enter_offset); - output.write( reinterpret_cast< const char * >( &rtn ), sizeof( vmp2::v4::rtn_t::size ) + - sizeof( vmp2::v4::rtn_t::code_block_count ) + - sizeof( vmp2::v4::rtn_t::vm_enter_offset ) ); + if (!vm_ctx.init()) { + std::printf( + "[!] failed to init vmctx... this can be for many reasons..." + " try validating your vm entry rva... make sure the binary is " + "unpacked and is" + "protected with VMProtect 2...\n"); + return -1; + } - std::for_each( vmp2_blocks.begin(), vmp2_blocks.end(), [ & ]( vmp2::v4::code_block_t *vmp2_block ) -> void { - output.write( reinterpret_cast< const char * >( vmp2_block ), vmp2_block->next_block_offset ); - free( vmp2_block ); - } ); - output.close(); - } - else if ( parser.exists( "unpack" ) && parser.exists( "out" ) ) - { - std::vector< std::uint8_t > packed_bin, unpacked_bin; - if ( !umtils->open_binary_file( parser.get< std::string >( "unpack" ), packed_bin ) ) - { - std::printf( "> failed to read bin off disk...\n" ); - return -1; - } + vm::emu_t emu(&vm_ctx); - engine::unpack_t unpacker( packed_bin ); + if (!emu.init()) { + std::printf("[!] failed to init emulator...\n"); + return -1; + } - if ( !unpacker.init() ) - { - std::printf( "> failed to init unpacker...\n" ); - return -1; - } + std::vector code_blocks; - if ( !unpacker.unpack( unpacked_bin ) ) - { - std::printf( "> failed to unpack binary... refer to log above...\n" ); - return -1; - } + if (!emu.get_trace(code_blocks)) { + std::printf( + "[!] something failed during tracing, review the console for more " + "information...\n"); + continue; + } - std::printf( "> writing result to = %s\n", parser.get< std::string >( "out" ).c_str() ); - std::ofstream output( parser.get< std::string >( "out" ), std::ios::binary ); - output.write( reinterpret_cast< char * >( unpacked_bin.data() ), unpacked_bin.size() ); - output.close(); + std::printf("> number of blocks = %d\n", code_blocks.size()); + virt_rtns.push_back({vm_enter_offset, code_blocks}); } - else if ( parser.exists( "bin" ) && parser.exists( "emuall" ) && parser.exists( "out" ) ) - { - const auto module_base = reinterpret_cast< std::uintptr_t >( - LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) ); - - const auto image_base = umtils->image_base( parser.get< std::string >( "bin" ).c_str() ); - const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage; - - auto vm_handler_tables = vm::locate::all_handler_tables( module_base ); - auto vm_enters = vm::locate::all_vm_enters( module_base, vm_handler_tables ); - - std::vector< std::pair< std::uintptr_t, std::vector< vm::instrs::code_block_t > > > virt_rtns; - for ( const auto &[ vm_enter_offset, encrypted_rva ] : vm_enters ) - { - std::printf( "> emulating vm enter at rva = 0x%x\n", vm_enter_offset ); - vm::ctx_t vm_ctx( module_base, image_base, image_size, vm_enter_offset ); - - if ( !vm_ctx.init() ) - { - std::printf( "[!] failed to init vmctx... this can be for many reasons..." - " try validating your vm entry rva... make sure the binary is unpacked and is" - "protected with VMProtect 2...\n" ); - return -1; - } - - vm::emu_t emu( &vm_ctx ); - - if ( !emu.init() ) - { - std::printf( "[!] failed to init emulator...\n" ); - return -1; - } - - std::vector< vm::instrs::code_block_t > code_blocks; - - if ( !emu.get_trace( code_blocks ) ) - { - std::printf( "[!] something failed during tracing, review the console for more information...\n" ); - continue; - } - - std::printf( "> number of blocks = %d\n", code_blocks.size() ); - virt_rtns.push_back( { vm_enter_offset, code_blocks } ); - } - std::printf( "> traced %d virtual routines...\n", virt_rtns.size() ); - std::printf( "> serializing results....\n" ); - - vmp2::v4::file_header file_header; - file_header.magic = VMP_MAGIC; - file_header.epoch_time = std::time( nullptr ); - file_header.version = vmp2::version_t::v4; - file_header.module_base = module_base; - file_header.image_base = image_base; - file_header.vm_entry_rva = 0ull; - file_header.module_offset = sizeof file_header; - file_header.module_size = image_size; - file_header.rtn_count = virt_rtns.size(); - file_header.rtn_offset = image_size + sizeof file_header; - - std::ofstream output( parser.get< std::string >( "out" ), std::ios::binary ); - output.write( reinterpret_cast< const char * >( &file_header ), sizeof file_header ); - output.write( reinterpret_cast< const char * >( module_base ), image_size ); - - for ( auto &[ vm_enter_offset, virt_rtn ] : virt_rtns ) - { - vmp2::v4::rtn_t rtn{ virt_rtn.size() }; - std::vector< vmp2::v4::code_block_t * > vmp2_blocks; - - for ( const auto &code_block : virt_rtn ) - { - const auto _code_block_size = sizeof vmp2::v4::code_block_t + ( code_block.jcc.block_addr.size() * 8 ) + - code_block.vinstrs.size() * sizeof vm::instrs::virt_instr_t; - - vmp2::v4::code_block_t *_code_block = - reinterpret_cast< vmp2::v4::code_block_t * >( malloc( _code_block_size ) ); - - // serialize block meta data... - _code_block->vip_begin = code_block.vip_begin; - _code_block->next_block_offset = _code_block_size; - _code_block->vinstr_count = code_block.vinstrs.size(); - _code_block->has_jcc = code_block.jcc.has_jcc; - _code_block->jcc_type = code_block.jcc.type; - _code_block->num_block_addrs = code_block.jcc.block_addr.size(); - - // serialize jcc branches... - for ( auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx ) - _code_block->branch_addr[ idx ] = code_block.jcc.block_addr[ idx ]; - - auto block_vinstrs = reinterpret_cast< vm::instrs::virt_instr_t * >( - reinterpret_cast< std::uintptr_t >( _code_block ) + sizeof vmp2::v4::code_block_t + - ( code_block.jcc.block_addr.size() * 8 ) ); - - for ( auto idx = 0u; idx < code_block.vinstrs.size(); ++idx ) - block_vinstrs[ idx ] = code_block.vinstrs[ idx ]; - - vmp2_blocks.push_back( _code_block ); - } - - std::size_t code_blocks_size = sizeof( vmp2::v4::rtn_t::size ) + - sizeof( vmp2::v4::rtn_t::vm_enter_offset ) + - sizeof( vmp2::v4::rtn_t::code_block_count ); - - std::for_each( vmp2_blocks.begin(), vmp2_blocks.end(), [ & ]( vmp2::v4::code_block_t *vmp2_block ) -> void { - code_blocks_size += vmp2_block->next_block_offset; - } ); - - rtn.size = code_blocks_size; - rtn.code_block_count = vmp2_blocks.size(); - rtn.vm_enter_offset = vm_enter_offset; - - output.write( reinterpret_cast< const char * >( &rtn ), sizeof( vmp2::v4::rtn_t::size ) + - sizeof( vmp2::v4::rtn_t::code_block_count ) + - sizeof( vmp2::v4::rtn_t::vm_enter_offset ) ); - - std::for_each( vmp2_blocks.begin(), vmp2_blocks.end(), [ & ]( vmp2::v4::code_block_t *vmp2_block ) -> void { - output.write( reinterpret_cast< const char * >( vmp2_block ), vmp2_block->next_block_offset ); - free( vmp2_block ); - } ); - } - output.close(); - } - else if ( parser.exists( "bin" ) && parser.exists( "locateconst" ) ) - { - const auto module_base = reinterpret_cast< std::uintptr_t >( - LoadLibraryExA( parser.get< std::string >( "bin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) ); - - const auto const_val = std::strtoull( parser.get< std::string >( "locateconst" ).c_str(), nullptr, 16 ); - const auto image_base = umtils->image_base( parser.get< std::string >( "bin" ).c_str() ); - const auto image_size = NT_HEADER( module_base )->OptionalHeader.SizeOfImage; - - auto vm_handler_tables = vm::locate::all_handler_tables( module_base ); - auto vm_enters = vm::locate::all_vm_enters( module_base, vm_handler_tables ); - - std::printf( "> number of vm enters = %d\n", vm_enters.size() ); - for ( const auto &[ vm_enter_offset, encrypted_rva ] : vm_enters ) - { - std::printf( "> emulating vm enter at rva = 0x%x\n", vm_enter_offset ); - vm::ctx_t vm_ctx( module_base, image_base, image_size, vm_enter_offset ); - - if ( !vm_ctx.init() ) - { - std::printf( "[!] failed to init vmctx... this can be for many reasons..." - " try validating your vm entry rva... make sure the binary is unpacked and is" - "protected with VMProtect 2...\n" ); - return -1; - } - - vm::emu_t emu( &vm_ctx ); - - if ( !emu.init() ) - { - std::printf( "[!] failed to init emulator...\n" ); - return -1; - } - - std::vector< vm::instrs::code_block_t > code_blocks; - - if ( !emu.get_trace( code_blocks ) ) - { - std::printf( "[!] something failed during tracing, review the console for more information...\n" ); - return -1; - } - - std::printf( "> number of blocks = %d\n", code_blocks.size() ); - - for ( auto &code_block : code_blocks ) - { - for ( const auto &vinstr : code_block.vinstrs ) - { - if ( vinstr.operand.has_imm && vinstr.operand.imm.u == const_val ) - { - std::printf( "> found constant in vm enter at = 0x%x\n", vm_enter_offset ); - std::getchar(); - } - } - } - } + std::printf("> traced %d virtual routines...\n", virt_rtns.size()); + std::printf("> serializing results....\n"); + + vmp2::v4::file_header file_header; + file_header.magic = VMP_MAGIC; + file_header.epoch_time = std::time(nullptr); + file_header.version = vmp2::version_t::v4; + file_header.module_base = module_base; + file_header.image_base = image_base; + file_header.vm_entry_rva = 0ull; + file_header.module_offset = sizeof file_header; + file_header.module_size = image_size; + file_header.rtn_count = virt_rtns.size(); + file_header.rtn_offset = image_size + sizeof file_header; + + std::ofstream output(parser.get("out"), std::ios::binary); + output.write(reinterpret_cast(&file_header), + sizeof file_header); + output.write(reinterpret_cast(module_base), image_size); + + for (auto &[vm_enter_offset, virt_rtn] : virt_rtns) { + vmp2::v4::rtn_t rtn{(u32)virt_rtn.size()}; + std::vector vmp2_blocks; + + for (const auto &code_block : virt_rtn) { + const auto _code_block_size = + sizeof(vmp2::v4::code_block_t) + + (code_block.jcc.block_addr.size() * 8) + + code_block.vinstrs.size() * sizeof(vm::instrs::virt_instr_t); + + vmp2::v4::code_block_t *_code_block = + reinterpret_cast( + malloc(_code_block_size)); + + // serialize block meta data... + _code_block->vip_begin = code_block.vip_begin; + _code_block->next_block_offset = _code_block_size; + _code_block->vinstr_count = code_block.vinstrs.size(); + _code_block->has_jcc = code_block.jcc.has_jcc; + _code_block->jcc_type = code_block.jcc.type; + _code_block->num_block_addrs = code_block.jcc.block_addr.size(); + + // serialize jcc branches... + for (auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx) + _code_block->branch_addr[idx] = code_block.jcc.block_addr[idx]; + + auto block_vinstrs = reinterpret_cast( + reinterpret_cast(_code_block) + + sizeof(vmp2::v4::code_block_t) + + (code_block.jcc.block_addr.size() * 8)); + + for (auto idx = 0u; idx < code_block.vinstrs.size(); ++idx) + block_vinstrs[idx] = code_block.vinstrs[idx]; + + vmp2_blocks.push_back(_code_block); + } + + std::size_t code_blocks_size = sizeof(vmp2::v4::rtn_t::size) + + sizeof(vmp2::v4::rtn_t::vm_enter_offset) + + sizeof(vmp2::v4::rtn_t::code_block_count); + + std::for_each(vmp2_blocks.begin(), vmp2_blocks.end(), + [&](vmp2::v4::code_block_t *vmp2_block) -> void { + code_blocks_size += vmp2_block->next_block_offset; + }); + + rtn.size = code_blocks_size; + rtn.code_block_count = vmp2_blocks.size(); + rtn.vm_enter_offset = vm_enter_offset; + + output.write(reinterpret_cast(&rtn), + sizeof(vmp2::v4::rtn_t::size) + + sizeof(vmp2::v4::rtn_t::code_block_count) + + sizeof(vmp2::v4::rtn_t::vm_enter_offset)); + + std::for_each(vmp2_blocks.begin(), vmp2_blocks.end(), + [&](vmp2::v4::code_block_t *vmp2_block) -> void { + output.write(reinterpret_cast(vmp2_block), + vmp2_block->next_block_offset); + free(vmp2_block); + }); } + output.close(); + } } \ No newline at end of file diff --git a/src/unpacker.cpp b/src/unpacker.cpp index c96785f..605c084 100644 --- a/src/unpacker.cpp +++ b/src/unpacker.cpp @@ -1,507 +1,497 @@ #include -namespace engine -{ - unpack_t::unpack_t( const std::vector< std::uint8_t > &packed_bin ) - : bin( packed_bin ), uc_ctx( nullptr ), heap_offset( 0ull ), pack_section_offset( 0ull ) - { - win_img = reinterpret_cast< win::image_t<> * >( bin.data() ); - img_base = win_img->get_nt_headers()->optional_header.image_base; - img_size = win_img->get_nt_headers()->optional_header.size_image; - std::printf( "> image base = 0x%p, image size = 0x%x\n", img_base, img_size ); +namespace engine { +unpack_t::unpack_t(const std::vector &packed_bin) + : bin(packed_bin), + uc_ctx(nullptr), + heap_offset(0ull), + pack_section_offset(0ull) { + win_img = reinterpret_cast *>(bin.data()); + img_base = win_img->get_nt_headers()->optional_header.image_base; + img_size = win_img->get_nt_headers()->optional_header.size_image; + std::printf("> image base = 0x%p, image size = 0x%x\n", img_base, img_size); +} + +unpack_t::~unpack_t(void) { + if (uc_ctx) uc_close(uc_ctx); + + for (auto &ptr : uc_hooks) + if (ptr) delete ptr; +} + +bool unpack_t::init(void) { + uc_err err; + if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc_ctx))) { + std::printf("> uc_open err = %d\n", err); + return false; + } + + if ((err = uc_mem_map(uc_ctx, IAT_VECTOR_TABLE, PAGE_4KB, UC_PROT_ALL))) { + std::printf("> uc_mem_map iat vector table err = %d\n", err); + return false; + } + + if ((err = uc_mem_map(uc_ctx, STACK_BASE, STACK_SIZE, UC_PROT_ALL))) { + std::printf("> uc_mem_map stack err, reason = %d\n", err); + return false; + } + + if ((err = uc_mem_map(uc_ctx, img_base, img_size, UC_PROT_ALL))) { + std::printf("> map memory failed, reason = %d\n", err); + return false; + } + + // init iat vector table full of 'ret' instructions... + auto c3_page = malloc(PAGE_4KB); + { + memset(c3_page, 0xC3, PAGE_4KB); + + if ((err = uc_mem_write(uc_ctx, IAT_VECTOR_TABLE, c3_page, PAGE_4KB))) { + std::printf("> failed to init iat vector table...\n"); + free(c3_page); + return false; } - - unpack_t::~unpack_t( void ) - { - if ( uc_ctx ) - uc_close( uc_ctx ); - - for ( auto &ptr : uc_hooks ) - if ( ptr ) - delete ptr; - } - - bool unpack_t::init( void ) - { - uc_err err; - if ( ( err = uc_open( UC_ARCH_X86, UC_MODE_64, &uc_ctx ) ) ) - { - std::printf( "> uc_open err = %d\n", err ); - return false; - } - - if ( ( err = uc_mem_map( uc_ctx, IAT_VECTOR_TABLE, PAGE_4KB, UC_PROT_ALL ) ) ) - { - std::printf( "> uc_mem_map iat vector table err = %d\n", err ); - return false; - } - - if ( ( err = uc_mem_map( uc_ctx, STACK_BASE, STACK_SIZE, UC_PROT_ALL ) ) ) - { - std::printf( "> uc_mem_map stack err, reason = %d\n", err ); - return false; - } - - if ( ( err = uc_mem_map( uc_ctx, img_base, img_size, UC_PROT_ALL ) ) ) - { - std::printf( "> map memory failed, reason = %d\n", err ); - return false; - } - - // init iat vector table full of 'ret' instructions... - auto c3_page = malloc( PAGE_4KB ); - { - memset( c3_page, 0xC3, PAGE_4KB ); - - if ( ( err = uc_mem_write( uc_ctx, IAT_VECTOR_TABLE, c3_page, PAGE_4KB ) ) ) - { - std::printf( "> failed to init iat vector table...\n" ); - free( c3_page ); - return false; - } - } - free( c3_page ); - - map_bin.resize( img_size ); - memcpy( map_bin.data(), bin.data(), // copies pe headers (includes section headers) - win_img->get_nt_headers()->optional_header.size_headers ); - - win::section_header_t *sec_begin = win_img->get_nt_headers()->get_sections(), - *sec_end = sec_begin + win_img->get_nt_headers()->file_header.num_sections; - - std::for_each( sec_begin, sec_end, [ & ]( const win::section_header_t &sec_header ) { - memcpy( map_bin.data() + sec_header.virtual_address, bin.data() + sec_header.ptr_raw_data, - sec_header.size_raw_data ); - } ); - - auto basereloc_dir = win_img->get_directory( win::directory_id::directory_entry_basereloc ); - auto reloc_dir = reinterpret_cast< win::reloc_directory_t * >( basereloc_dir->rva + map_bin.data() ); - win::reloc_block_t *reloc_block = &reloc_dir->first_block; - - // apply relocations to all sections... - while ( reloc_block->base_rva && reloc_block->size_block ) - { - std::for_each( reloc_block->begin(), reloc_block->end(), [ & ]( win::reloc_entry_t &entry ) { - switch ( entry.type ) - { - case win::reloc_type_id::rel_based_dir64: - { - auto reloc_at = - reinterpret_cast< std::uintptr_t * >( entry.offset + reloc_block->base_rva + map_bin.data() ); - - *reloc_at = img_base + ( ( *reloc_at ) - img_base ); - break; - } - default: - break; - } - } ); - - reloc_block = reloc_block->next(); - } - - // iat hook specific function... - for ( auto import_dir = reinterpret_cast< win::import_directory_t * >( - win_img->get_directory( win::directory_id::directory_entry_import )->rva + map_bin.data() ); - import_dir->rva_name; ++import_dir ) - { - for ( auto iat_thunk = - reinterpret_cast< win::image_thunk_data_t<> * >( import_dir->rva_first_thunk + map_bin.data() ); - iat_thunk->address; ++iat_thunk ) - { - if ( iat_thunk->is_ordinal ) - continue; - - auto iat_name = reinterpret_cast< win::image_named_import_t * >( iat_thunk->address + map_bin.data() ); - - if ( iat_hooks.find( iat_name->name ) != iat_hooks.end() ) - iat_thunk->function = iat_hooks[ iat_name->name ].first + IAT_VECTOR_TABLE; - } - } - - // map the entire map buffer into unicorn-engine since we have set everything else up... - if ( ( err = uc_mem_write( uc_ctx, img_base, map_bin.data(), map_bin.size() ) ) ) - { - std::printf( "> failed to write memory... reason = %d\n", err ); - return false; - } - - // setup unicorn-engine hooks on IAT vector table, sections with 0 raw size/ptr, and an invalid memory - // handler... - - uc_hooks.push_back( new uc_hook ); - if ( ( err = uc_hook_add( uc_ctx, uc_hooks.back(), UC_HOOK_CODE, &engine::unpack_t::iat_dispatcher, this, - IAT_VECTOR_TABLE, IAT_VECTOR_TABLE + PAGE_4KB ) ) ) - { - std::printf( "> uc_hook_add error, reason = %d\n", err ); - return false; - } - - uc_hooks.push_back( new uc_hook ); - if ( ( err = uc_hook_add( uc_ctx, uc_hooks.back(), - UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED | - UC_HOOK_INSN_INVALID, - &engine::unpack_t::invalid_mem, this, true, false ) ) ) - { - std::printf( "> uc_hook_add error, reason = %d\n", err ); - return false; - } - - // execution break points on all sections that are executable but have no physical size on disk... - std::for_each( sec_begin, sec_end, [ & ]( win::section_header_t &header ) { - if ( !header.ptr_raw_data && !header.size_raw_data && header.characteristics.mem_execute && - header.characteristics.mem_write && !header.is_discardable() ) - { - uc_hooks.push_back( new uc_hook ); - if ( ( err = uc_hook_add( uc_ctx, uc_hooks.back(), UC_HOOK_CODE | UC_HOOK_MEM_WRITE, - &engine::unpack_t::unpack_section_callback, this, - header.virtual_address + img_base, - header.virtual_address + header.virtual_size + img_base ) ) ) - { - std::printf( "> failed to add hook... reason = %d\n", err ); - return false; - } - - pack_section_offset = header.virtual_address + header.virtual_size; - } - else if ( header.characteristics.mem_execute ) - { - uc_hooks.push_back( new uc_hook ); - if ( ( err = uc_hook_add( uc_ctx, uc_hooks.back(), UC_HOOK_CODE, &engine::unpack_t::code_exec_callback, - this, header.virtual_address + img_base, - header.virtual_address + header.virtual_size + img_base ) ) ) - { - std::printf( "> failed to add hook... reason = %d\n", err ); - return false; - } + } + free(c3_page); + + map_bin.resize(img_size); + memcpy(map_bin.data(), + bin.data(), // copies pe headers (includes section headers) + win_img->get_nt_headers()->optional_header.size_headers); + + win::section_header_t *sec_begin = win_img->get_nt_headers()->get_sections(), + *sec_end = + sec_begin + + win_img->get_nt_headers()->file_header.num_sections; + + std::for_each( + sec_begin, sec_end, [&](const win::section_header_t &sec_header) { + memcpy(map_bin.data() + sec_header.virtual_address, + bin.data() + sec_header.ptr_raw_data, sec_header.size_raw_data); + }); + + auto basereloc_dir = + win_img->get_directory(win::directory_id::directory_entry_basereloc); + auto reloc_dir = reinterpret_cast( + basereloc_dir->rva + map_bin.data()); + win::reloc_block_t *reloc_block = &reloc_dir->first_block; + + // apply relocations to all sections... + while (reloc_block->base_rva && reloc_block->size_block) { + std::for_each( + reloc_block->begin(), reloc_block->end(), + [&](win::reloc_entry_t &entry) { + switch (entry.type) { + case win::reloc_type_id::rel_based_dir64: { + auto reloc_at = reinterpret_cast( + entry.offset + reloc_block->base_rva + map_bin.data()); + + *reloc_at = img_base + ((*reloc_at) - img_base); + break; } - } ); - - return true; + default: + break; + } + }); + + reloc_block = reloc_block->next(); + } + + // iat hook specific function... + for (auto import_dir = reinterpret_cast( + win_img->get_directory(win::directory_id::directory_entry_import) + ->rva + + map_bin.data()); + import_dir->rva_name; ++import_dir) { + for (auto iat_thunk = reinterpret_cast *>( + import_dir->rva_first_thunk + map_bin.data()); + iat_thunk->address; ++iat_thunk) { + if (iat_thunk->is_ordinal) continue; + + auto iat_name = reinterpret_cast( + iat_thunk->address + map_bin.data()); + + if (iat_hooks.find(iat_name->name) != iat_hooks.end()) + iat_thunk->function = + iat_hooks[iat_name->name].first + IAT_VECTOR_TABLE; } - - bool unpack_t::unpack( std::vector< std::uint8_t > &output ) - { - uc_err err; - auto nt_headers = win_img->get_nt_headers(); - std::uintptr_t rip = nt_headers->optional_header.entry_point + img_base, rsp = STACK_BASE + STACK_SIZE; - - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RSP, &rsp ) ) ) - { - std::printf( "> uc_reg_write error, reason = %d\n", err ); - return false; - } - - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RIP, &rip ) ) ) - { - std::printf( "> uc_reg_write error, reason = %d\n", err ); - return false; - } - - std::printf( "> beginning execution at = 0x%p\n", rip ); - - if ( ( err = uc_emu_start( uc_ctx, rip, 0ull, 0ull, 0ull ) ) ) - { - std::printf( "> error starting emu... reason = %d\n", err ); - return false; - } - - output.resize( img_size ); - if ( ( err = uc_mem_read( uc_ctx, img_base, output.data(), output.size() ) ) ) - { - std::printf( "> uc_mem_read failed... err = %d\n", err ); - return false; - } - - auto output_img = reinterpret_cast< win::image_t<> * >( output.data() ); - auto sections = output_img->get_nt_headers()->get_sections(); - auto section_cnt = output_img->get_file_header()->num_sections; - - // { section virtual address -> vector of section offset to reloc } - std::map< std::uint32_t, std::vector< std::uint16_t > > new_relocs; - - // search executable sections for MOV RAX, 00 00 00 00 00 00 00 00... - std::for_each( sections, sections + section_cnt, [ & ]( win::section_header_t &header ) { - if ( header.characteristics.mem_execute ) - { - auto result = output.data() + header.virtual_address; - - do - { - result = reinterpret_cast< std::uint8_t * >( xtils::um_t::get_instance()->sigscan( - result, - header.virtual_size - - ( reinterpret_cast< std::uintptr_t >( result ) - - ( header.virtual_address + reinterpret_cast< std::uintptr_t >( output.data() ) ) ), - MOV_RAX_0_SIG, MOV_RAX_0_MASK ) ); - - if ( result ) - { - result += 2; // advance ahead of the 0x48 0xB8... - - // offset from section begin... - auto reloc_offset = - ( reinterpret_cast< std::uintptr_t >( result ) ) - - reinterpret_cast< std::uintptr_t >( output.data() + header.virtual_address ); - - new_relocs[ ( header.virtual_address + reloc_offset ) & ~0xFFFull ].push_back( reloc_offset ); - } - - } while ( result ); - } - - header.ptr_raw_data = header.virtual_address; - header.size_raw_data = header.virtual_size; - } ); - - // determines if a relocation block exists for a given page... - static const auto has_reloc_page = [ & ]( std::uint32_t page ) -> bool { - auto img = reinterpret_cast< win::image_t<> * >( output.data() ); - auto sections = img->get_nt_headers()->get_sections(); - auto section_cnt = img->get_file_header()->num_sections; - - auto basereloc_dir = img->get_directory( win::directory_id::directory_entry_basereloc ); - auto reloc_dir = reinterpret_cast< win::reloc_directory_t * >( basereloc_dir->rva + output.data() ); - win::reloc_block_t *reloc_block = &reloc_dir->first_block; - - while ( reloc_block->base_rva && reloc_block->size_block ) - { - if ( reloc_block->base_rva == page ) - return true; - - reloc_block = reloc_block->next(); + } + + // map the entire map buffer into unicorn-engine since we have set everything + // else up... + if ((err = uc_mem_write(uc_ctx, img_base, map_bin.data(), map_bin.size()))) { + std::printf("> failed to write memory... reason = %d\n", err); + return false; + } + + // setup unicorn-engine hooks on IAT vector table, sections with 0 raw + // size/ptr, and an invalid memory handler... + + uc_hooks.push_back(new uc_hook); + if ((err = uc_hook_add(uc_ctx, uc_hooks.back(), UC_HOOK_CODE, + (void *)&engine::unpack_t::iat_dispatcher, this, + IAT_VECTOR_TABLE, IAT_VECTOR_TABLE + PAGE_4KB))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + return false; + } + + uc_hooks.push_back(new uc_hook); + if ((err = uc_hook_add( + uc_ctx, uc_hooks.back(), + UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | + UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_INSN_INVALID, + (void *)&engine::unpack_t::invalid_mem, this, true, false))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + return false; + } + + // execution break points on all sections that are executable but have no + // physical size on disk... + std::for_each(sec_begin, sec_end, [&](win::section_header_t &header) { + if (!header.ptr_raw_data && !header.size_raw_data && + header.characteristics.mem_execute && + header.characteristics.mem_write && !header.is_discardable()) { + uc_hooks.push_back(new uc_hook); + if ((err = uc_hook_add( + uc_ctx, uc_hooks.back(), UC_HOOK_CODE | UC_HOOK_MEM_WRITE, + (void *)&engine::unpack_t::unpack_section_callback, this, + header.virtual_address + img_base, + header.virtual_address + header.virtual_size + img_base))) { + std::printf("> failed to add hook... reason = %d\n", err); + return false; + } + + pack_section_offset = header.virtual_address + header.virtual_size; + } else if (header.characteristics.mem_execute) { + uc_hooks.push_back(new uc_hook); + if ((err = uc_hook_add( + uc_ctx, uc_hooks.back(), UC_HOOK_CODE, + (void *)&engine::unpack_t::code_exec_callback, this, + header.virtual_address + img_base, + header.virtual_address + header.virtual_size + img_base))) { + std::printf("> failed to add hook... reason = %d\n", err); + return false; + } + } + }); + + return true; +} + +bool unpack_t::unpack(std::vector &output) { + uc_err err; + auto nt_headers = win_img->get_nt_headers(); + std::uintptr_t rip = nt_headers->optional_header.entry_point + img_base, + rsp = STACK_BASE + STACK_SIZE; + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RSP, &rsp))) { + std::printf("> uc_reg_write error, reason = %d\n", err); + return false; + } + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RIP, &rip))) { + std::printf("> uc_reg_write error, reason = %d\n", err); + return false; + } + + std::printf("> beginning execution at = 0x%p\n", rip); + + if ((err = uc_emu_start(uc_ctx, rip, 0ull, 0ull, 0ull))) { + std::printf("> error starting emu... reason = %d\n", err); + return false; + } + + output.resize(img_size); + if ((err = uc_mem_read(uc_ctx, img_base, output.data(), output.size()))) { + std::printf("> uc_mem_read failed... err = %d\n", err); + return false; + } + + auto output_img = reinterpret_cast *>(output.data()); + auto sections = output_img->get_nt_headers()->get_sections(); + auto section_cnt = output_img->get_file_header()->num_sections; + + // { section virtual address -> vector of section offset to reloc } + std::map > new_relocs; + + // search executable sections for MOV RAX, 00 00 00 00 00 00 00 00... + std::for_each( + sections, sections + section_cnt, [&](win::section_header_t &header) { + if (header.characteristics.mem_execute) { + auto result = output.data() + header.virtual_address; + + do { + result = reinterpret_cast(vm::locate::sigscan( + result, + header.virtual_size - + (reinterpret_cast(result) - + (header.virtual_address + + reinterpret_cast(output.data()))), + MOV_RAX_0_SIG, MOV_RAX_0_MASK)); + + if (result) { + result += 2; // advance ahead of the 0x48 0xB8... + + // offset from section begin... + auto reloc_offset = (reinterpret_cast(result)) - + reinterpret_cast( + output.data() + header.virtual_address); + + new_relocs[(header.virtual_address + reloc_offset) & ~0xFFFull] + .push_back(reloc_offset); } - return false; - }; - - // calc size to add new reloc info... - std::size_t resize_cnt = 0ull; - for ( const auto &[ reloc_rva, relocs ] : new_relocs ) - if ( !has_reloc_page( reloc_rva ) ) - resize_cnt += sizeof( win::reloc_block_t ) + ( relocs.size() * sizeof( win::reloc_entry_t ) ); - - // last block needs to contain 0 for block_rva and size_block... - if ( resize_cnt ) - resize_cnt += sizeof win::reloc_block_t; - - output.resize( output.size() + resize_cnt ); - output_img = reinterpret_cast< win::image_t<> * >( output.data() ); - - auto basereloc_dir = output_img->get_directory( win::directory_id::directory_entry_basereloc ); - auto reloc_dir = reinterpret_cast< win::reloc_directory_t * >( basereloc_dir->rva + output.data() ); - - basereloc_dir->size += resize_cnt; - for ( const auto &[ reloc_rva, relocs ] : new_relocs ) - { - if ( has_reloc_page( reloc_rva ) ) - continue; - - win::reloc_block_t *reloc_block = &reloc_dir->first_block; - while ( reloc_block->base_rva && reloc_block->size_block ) - reloc_block = reloc_block->next(); - - reloc_block->base_rva = reloc_rva; - reloc_block->size_block = relocs.size() * sizeof( win::reloc_entry_t ) + sizeof uint64_t; - - reloc_block->next()->base_rva = 0ull; - reloc_block->next()->size_block = 0ull; - - for ( auto idx = 0u; idx < relocs.size(); ++idx ) - { - reloc_block->entries[ idx ].type = win::reloc_type_id::rel_based_dir64; - reloc_block->entries[ idx ].offset = relocs[ idx ]; - } + } while (result); } - return true; - } - void unpack_t::local_alloc_hook( uc_engine *uc_ctx, unpack_t *obj ) - { - uc_err err; - std::uintptr_t rax, rdx; + header.ptr_raw_data = header.virtual_address; + header.size_raw_data = header.virtual_size; + }); - if ( ( err = uc_reg_read( uc_ctx, UC_X86_REG_RDX, &rdx ) ) ) - { - std::printf( "> failed to read RDX... reason = %d\n", rdx ); - return; - } + // determines if a relocation block exists for a given page... + static const auto has_reloc_page = [&](std::uint32_t page) -> bool { + auto img = reinterpret_cast *>(output.data()); + auto sections = img->get_nt_headers()->get_sections(); + auto section_cnt = img->get_file_header()->num_sections; - auto size = ( ( rdx + PAGE_4KB ) & ~0xFFFull ); - if ( ( err = uc_mem_map( uc_ctx, HEAP_BASE + obj->heap_offset, size, UC_PROT_ALL ) ) ) - { - std::printf( "> failed to allocate memory... reason = %d\n", err ); - return; - } + auto basereloc_dir = + img->get_directory(win::directory_id::directory_entry_basereloc); + auto reloc_dir = reinterpret_cast( + basereloc_dir->rva + output.data()); + win::reloc_block_t *reloc_block = &reloc_dir->first_block; - rax = HEAP_BASE + obj->heap_offset; - obj->heap_offset += size; + while (reloc_block->base_rva && reloc_block->size_block) { + if (reloc_block->base_rva == page) return true; - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RAX, &rax ) ) ) - { - std::printf( "> failed to write rax... reason = %d\n", err ); - return; - } + reloc_block = reloc_block->next(); } - void unpack_t::local_free_hook( uc_engine *uc_ctx, unpack_t *obj ) - { - uc_err err; - std::uintptr_t rax = 0ull; + return false; + }; - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RAX, &rax ) ) ) - { - std::printf( "> failed to write rax... reason = %d\n", err ); - return; - } - } - - void unpack_t::load_library_hook( uc_engine *uc_ctx, unpack_t *obj ) - { - uc_err err; - std::uintptr_t rcx = 0ull; + // calc size to add new reloc info... + std::size_t resize_cnt = 0ull; + for (const auto &[reloc_rva, relocs] : new_relocs) + if (!has_reloc_page(reloc_rva)) + resize_cnt += sizeof(win::reloc_block_t) + + (relocs.size() * sizeof(win::reloc_entry_t)); - if ( ( err = uc_reg_read( uc_ctx, UC_X86_REG_RCX, &rcx ) ) ) - { - std::printf( "> uc_reg_read error, reason = %d\n", err ); - return; - } + // last block needs to contain 0 for block_rva and size_block... + if (resize_cnt) resize_cnt += sizeof(win::reloc_block_t); - char buff[ 256 ]; - uc_strcpy( uc_ctx, buff, rcx ); - std::printf( "> LoadLibraryA(\"%s\")\n", buff ); + output.resize(output.size() + resize_cnt); + output_img = reinterpret_cast *>(output.data()); - auto module_base = reinterpret_cast< std::uintptr_t >( LoadLibraryA( buff ) ); + auto basereloc_dir = + output_img->get_directory(win::directory_id::directory_entry_basereloc); + auto reloc_dir = reinterpret_cast( + basereloc_dir->rva + output.data()); - auto module_size = - reinterpret_cast< win::image_t<> * >( module_base )->get_nt_headers()->optional_header.size_image; + basereloc_dir->size += resize_cnt; + for (const auto &[reloc_rva, relocs] : new_relocs) { + if (has_reloc_page(reloc_rva)) continue; - if ( std::find( obj->loaded_modules.begin(), obj->loaded_modules.end(), module_base ) != - obj->loaded_modules.end() ) - { - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RAX, &module_base ) ) ) - { - std::printf( "> failed to set rax... reason = %d\n", err ); - return; - } - } - else - { - if ( ( err = uc_mem_map( uc_ctx, module_base, module_size, UC_PROT_ALL ) ) ) - { - std::printf( "> failed to load library... reason = %d\n", err ); - return; - } + win::reloc_block_t *reloc_block = &reloc_dir->first_block; + while (reloc_block->base_rva && reloc_block->size_block) + reloc_block = reloc_block->next(); - if ( ( err = uc_mem_write( uc_ctx, module_base, reinterpret_cast< void * >( module_base ), module_size ) ) ) - { - std::printf( "> failed to copy module into emulator... reason = %d\n", err ); - return; - } + reloc_block->base_rva = reloc_rva; + reloc_block->size_block = + relocs.size() * sizeof(win::reloc_entry_t) + sizeof(uint64_t); - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RAX, &module_base ) ) ) - { - std::printf( "> failed to set rax... reason = %d\n", err ); - return; - } + reloc_block->next()->base_rva = 0ull; + reloc_block->next()->size_block = 0ull; - obj->loaded_modules.push_back( module_base ); - } + for (auto idx = 0u; idx < relocs.size(); ++idx) { + reloc_block->entries[idx].type = win::reloc_type_id::rel_based_dir64; + reloc_block->entries[idx].offset = relocs[idx]; } - - void unpack_t::uc_strcpy( uc_engine *uc_ctx, char *buff, std::uintptr_t addr ) - { - uc_err err; - char i = 0u; - auto idx = 0ul; - - do - { - if ( ( err = uc_mem_read( uc_ctx, addr + idx, &i, sizeof i ) ) ) - break; - - } while ( ( buff[ idx++ ] = i ) ); + } + return true; +} + +void unpack_t::local_alloc_hook(uc_engine *uc_ctx, unpack_t *obj) { + uc_err err; + std::uintptr_t rax, rdx; + + if ((err = uc_reg_read(uc_ctx, UC_X86_REG_RDX, &rdx))) { + std::printf("> failed to read RDX... reason = %d\n", rdx); + return; + } + + auto size = ((rdx + PAGE_4KB) & ~0xFFFull); + if ((err = uc_mem_map(uc_ctx, HEAP_BASE + obj->heap_offset, size, + UC_PROT_ALL))) { + std::printf("> failed to allocate memory... reason = %d\n", err); + return; + } + + rax = HEAP_BASE + obj->heap_offset; + obj->heap_offset += size; + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RAX, &rax))) { + std::printf("> failed to write rax... reason = %d\n", err); + return; + } +} + +void unpack_t::local_free_hook(uc_engine *uc_ctx, unpack_t *obj) { + uc_err err; + std::uintptr_t rax = 0ull; + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RAX, &rax))) { + std::printf("> failed to write rax... reason = %d\n", err); + return; + } +} + +void unpack_t::load_library_hook(uc_engine *uc_ctx, unpack_t *obj) { + uc_err err; + std::uintptr_t rcx = 0ull; + + if ((err = uc_reg_read(uc_ctx, UC_X86_REG_RCX, &rcx))) { + std::printf("> uc_reg_read error, reason = %d\n", err); + return; + } + + char buff[256]; + uc_strcpy(uc_ctx, buff, rcx); + std::printf("> LoadLibraryA(\"%s\")\n", buff); + + if (!obj->loaded_modules[buff]) { + std::vector module_data, tmp; + if (!vm::util::open_binary_file(buff, module_data)) { + std::printf( + "[!] failed to open a dependency... please put %s in the same folder " + "as vmprofiler-cli...\n", + buff); + exit(-1); } - bool unpack_t::iat_dispatcher( uc_engine *uc, uint64_t address, uint32_t size, unpack_t *unpack ) - { - auto vec = address - IAT_VECTOR_TABLE; - for ( auto &[ iat_name, iat_hook_data ] : unpack->iat_hooks ) - { - if ( iat_hook_data.first == vec ) - { - std::printf( "> hooking import = %s\n", iat_name.c_str() ); - iat_hook_data.second( uc, unpack ); - return true; - } - } - return false; + auto img = reinterpret_cast *>(module_data.data()); + auto image_size = img->get_nt_headers()->optional_header.size_image; + + tmp.resize(image_size); + std::memcpy(tmp.data(), module_data.data(), 0x1000); + std::for_each(img->get_nt_headers()->get_sections(), + img->get_nt_headers()->get_sections() + + img->get_nt_headers()->file_header.num_sections, + [&](const auto §ion_header) { + std::memcpy( + tmp.data() + section_header.virtual_address, + module_data.data() + section_header.ptr_raw_data, + section_header.size_raw_data); + }); + + const auto module_base = reinterpret_cast(tmp.data()); + const auto image_base = img->get_nt_headers()->optional_header.image_base; + const auto alloc_addr = module_base & ~0x1000ull; + obj->loaded_modules[buff] = alloc_addr; + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RAX, &alloc_addr))) { + std::printf("> failed to set rax... reason = %d\n", err); + return; } - - bool unpack_t::code_exec_callback( uc_engine *uc, uint64_t address, uint32_t size, unpack_t *unpack ) - { - static ZydisDecoder decoder; - static ZydisFormatter formatter; - static ZydisDecodedInstruction instr; - - if ( static std::atomic< bool > once{ false }; !once.exchange( true ) ) - { - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL ); - } - - auto instr_ptr = reinterpret_cast< void * >( unpack->map_bin.data() + ( address - unpack->img_base ) ); - if ( ZYAN_SUCCESS( ZydisDecoderDecodeBuffer( &decoder, instr_ptr, PAGE_4KB, &instr ) ) ) - { - if ( instr.mnemonic == ZYDIS_MNEMONIC_CALL && instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && - instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RAX ) - { - std::uintptr_t rax = 0u, rip = 0u; - uc_reg_read( uc, UC_X86_REG_RAX, &rax ); - uc_reg_read( uc, UC_X86_REG_RIP, &rip ); - - if ( rax > unpack->img_base + unpack->img_size ) // skip calls to kernel32.dll... - { - rip += instr.length; - uc_reg_write( uc, UC_X86_REG_RIP, &rip ); - } - } - } - - return true; + } else { + const auto alloc_addr = obj->loaded_modules[buff]; + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RAX, &alloc_addr))) { + std::printf("> failed to set rax... reason = %d\n", err); + return; } - - bool unpack_t::unpack_section_callback( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, - unpack_t *unpack ) - { - if ( address == unpack->pack_section_offset + unpack->img_base ) - { - std::printf( "> dumping...\n" ); - uc_emu_stop( uc ); - return false; - } - return true; + } +} // namespace engine + +void unpack_t::uc_strcpy(uc_engine *uc_ctx, char *buff, std::uintptr_t addr) { + uc_err err; + char i = 0u; + auto idx = 0ul; + + do { + if ((err = uc_mem_read(uc_ctx, addr + idx, &i, sizeof i))) break; + + } while ((buff[idx++] = i)); +} + +bool unpack_t::iat_dispatcher(uc_engine *uc, uint64_t address, uint32_t size, + unpack_t *unpack) { + auto vec = address - IAT_VECTOR_TABLE; + for (auto &[iat_name, iat_hook_data] : unpack->iat_hooks) { + if (iat_hook_data.first == vec) { + std::printf("> hooking import = %s\n", iat_name.c_str()); + iat_hook_data.second(uc, unpack); + return true; } - - void unpack_t::invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, - unpack_t *unpack ) - { - switch ( type ) - { - case UC_MEM_READ_UNMAPPED: - std::printf( ">>> reading invalid memory at address = 0x%p, size = 0x%x\n", address, size ); - break; - case UC_MEM_WRITE_UNMAPPED: - std::printf( ">>> writing invalid memory at address = 0x%p, size = 0x%x, val = 0x%x\n", address, size, - value ); - break; - case UC_MEM_FETCH_UNMAPPED: - { - std::printf( ">>> fetching invalid instructions at address = 0x%p\n", address ); - break; - } - default: - break; - } + } + return false; +} + +bool unpack_t::code_exec_callback(uc_engine *uc, uint64_t address, + uint32_t size, unpack_t *unpack) { + static ZydisDecoder decoder; + static ZydisFormatter formatter; + static ZydisDecodedInstruction instr; + + if (static std::atomic once{false}; !once.exchange(true)) { + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, + ZYDIS_ADDRESS_WIDTH_64); + ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + } + + auto instr_ptr = reinterpret_cast(unpack->map_bin.data() + + (address - unpack->img_base)); + + if (ZYAN_SUCCESS( + ZydisDecoderDecodeBuffer(&decoder, instr_ptr, PAGE_4KB, &instr))) { + if (instr.mnemonic == ZYDIS_MNEMONIC_CALL && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[0].reg.value == ZYDIS_REGISTER_RAX) { + std::uintptr_t rax = 0u, rip = 0u; + uc_reg_read(uc, UC_X86_REG_RAX, &rax); + uc_reg_read(uc, UC_X86_REG_RIP, &rip); + + if (rax > unpack->img_base + unpack->img_size || + rax < unpack->img_base) // skip calls to kernel32.dll... + { + rip += instr.length; + uc_reg_write(uc, UC_X86_REG_RIP, &rip); + } + } + } + + return true; +} + +bool unpack_t::unpack_section_callback(uc_engine *uc, uc_mem_type type, + uint64_t address, int size, + int64_t value, unpack_t *unpack) { + if (address == unpack->pack_section_offset + unpack->img_base) { + std::printf("> dumping...\n"); + uc_emu_stop(uc); + return false; + } + return true; +} + +void unpack_t::invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, + int size, int64_t value, unpack_t *unpack) { + switch (type) { + case UC_MEM_READ_UNMAPPED: + std::printf(">>> reading invalid memory at address = 0x%p, size = 0x%x\n", + address, size); + break; + case UC_MEM_WRITE_UNMAPPED: + std::printf( + ">>> writing invalid memory at address = 0x%p, size = 0x%x, val = " + "0x%x\n", + address, size, value); + break; + case UC_MEM_FETCH_UNMAPPED: { + std::printf(">>> fetching invalid instructions at address = 0x%p\n", + address); + break; } -} // namespace engine \ No newline at end of file + default: + break; + } +} +} // namespace engine \ No newline at end of file diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp index 27f7b0e..3297686 100644 --- a/src/vmemu_t.cpp +++ b/src/vmemu_t.cpp @@ -1,689 +1,665 @@ #include "vmemu_t.hpp" -namespace vm -{ - emu_t::emu_t( vm::ctx_t *vm_ctx ) - : g_vm_ctx( vm_ctx ), uc_ctx( nullptr ), img_base( vm_ctx->image_base ), img_size( vm_ctx->image_size ) - { - } - - emu_t::~emu_t() - { - if ( uc_ctx ) - uc_close( uc_ctx ); - } - - bool emu_t::init() - { - uc_err err; - if ( ( err = uc_open( UC_ARCH_X86, UC_MODE_64, &uc_ctx ) ) ) - { - std::printf( "> uc_open err = %d\n", err ); +namespace vm { +emu_t::emu_t(vm::ctx_t *vm_ctx) + : g_vm_ctx(vm_ctx), + uc_ctx(nullptr), + img_base(vm_ctx->image_base), + img_size(vm_ctx->image_size) {} + +emu_t::~emu_t() { + if (uc_ctx) uc_close(uc_ctx); +} + +bool emu_t::init() { + uc_err err; + if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc_ctx))) { + std::printf("> uc_open err = %d\n", err); + return false; + } + + if ((err = uc_mem_map(uc_ctx, STACK_BASE, STACK_SIZE, UC_PROT_ALL))) { + std::printf("> uc_mem_map stack err, reason = %d\n", err); + return false; + } + + if ((err = + uc_mem_map(uc_ctx, g_vm_ctx->module_base, img_size, UC_PROT_ALL))) { + std::printf("> map memory failed, reason = %d\n", err); + return false; + } + + if ((err = uc_mem_write(uc_ctx, g_vm_ctx->module_base, + reinterpret_cast(g_vm_ctx->module_base), + img_size))) { + std::printf("> failed to write memory... reason = %d\n", err); + return false; + } + + if ((err = uc_hook_add(uc_ctx, &code_exec_hook, UC_HOOK_CODE, + (void *)&vm::emu_t::code_exec_callback, this, + g_vm_ctx->module_base, + g_vm_ctx->module_base + img_size))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + return false; + } + + if ((err = uc_hook_add(uc_ctx, &int_hook, UC_HOOK_INTR, + (void *)&vm::emu_t::int_callback, this, 0ull, 0ull))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + return false; + } + + if ((err = + uc_hook_add(uc_ctx, &invalid_mem_hook, + UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | + UC_HOOK_MEM_FETCH_UNMAPPED | UC_HOOK_INSN_INVALID, + (void *)&vm::emu_t::invalid_mem, this, true, false))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + return false; + } + return true; +} + +bool emu_t::get_trace(std::vector &entries) { + uc_err err; + std::uintptr_t rip = g_vm_ctx->vm_entry_rva + g_vm_ctx->module_base, + rsp = STACK_BASE + STACK_SIZE - PAGE_4KB; + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RSP, &rsp))) { + std::printf("> uc_reg_write error, reason = %d\n", err); + return false; + } + + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RIP, &rip))) { + std::printf("> uc_reg_write error, reason = %d\n", err); + return false; + } + + // trace the first block given the vm enter... + code_block_data_t code_block{{}, nullptr, nullptr}; + cc_block = &code_block; + + std::printf("> beginning execution at = 0x%p\n", rip); + if ((err = uc_emu_start(uc_ctx, rip, 0ull, 0ull, 0ull))) { + std::printf("> error starting emu... reason = %d\n", err); + return false; + } + + if (cc_block) code_blocks.push_back(code_block); + + // code_blocks.size() will continue to grow as all branches are traced... + // when idx is > code_blocks.size() then we have traced all branches... + for (auto idx = 0u; idx < code_blocks.size(); ++idx) { + const auto _code_block = code_blocks[idx]; + if (!_code_block.code_block.jcc.has_jcc) continue; + + switch (_code_block.code_block.jcc.type) { + case vm::instrs::jcc_type::branching: { + if (std::find(vip_begins.begin(), vip_begins.end(), + _code_block.code_block.jcc.block_addr[1]) == + vip_begins.end()) { + std::uintptr_t rbp = 0ull; + std::uint32_t branch_rva = (_code_block.code_block.jcc.block_addr[1] - + g_vm_ctx->module_base) + + g_vm_ctx->image_base; + + // setup object globals so that the tracing will work... + code_block_data_t branch_block{{}, nullptr, nullptr}; + cc_block = &branch_block; + g_vm_ctx = _code_block.g_vm_ctx.get(); + + // restore register values... + if ((err = + uc_context_restore(uc_ctx, _code_block.cpu_ctx->context))) { + std::printf("> failed to restore emu context... reason = %d\n", + err); return false; - } + } - if ( ( err = uc_mem_map( uc_ctx, STACK_BASE, STACK_SIZE, UC_PROT_ALL ) ) ) - { - std::printf( "> uc_mem_map stack err, reason = %d\n", err ); + // restore stack values... + if ((err = uc_mem_write(uc_ctx, STACK_BASE, + _code_block.cpu_ctx->stack, STACK_SIZE))) { + std::printf("> failed to restore stack... reason = %d\n", err); return false; - } + } - if ( ( err = uc_mem_map( uc_ctx, g_vm_ctx->module_base, img_size, UC_PROT_ALL ) ) ) - { - std::printf( "> map memory failed, reason = %d\n", err ); + // get the address in rbp (top of vsp)... then patch the branch rva... + if ((err = uc_reg_read(uc_ctx, UC_X86_REG_RBP, &rbp))) { + std::printf("> failed to read rbp... reason = %d\n", err); return false; - } + } - if ( ( err = uc_mem_write( uc_ctx, g_vm_ctx->module_base, reinterpret_cast< void * >( g_vm_ctx->module_base ), - img_size ) ) ) - { - std::printf( "> failed to write memory... reason = %d\n", err ); + // patch the branch rva... + if ((err = + uc_mem_write(uc_ctx, rbp, &branch_rva, sizeof branch_rva))) { + std::printf("> failed to patch branch rva... reason = %d\n", err); return false; - } + } - if ( ( err = uc_hook_add( uc_ctx, &code_exec_hook, UC_HOOK_CODE, &vm::emu_t::code_exec_callback, this, - g_vm_ctx->module_base, g_vm_ctx->module_base + img_size ) ) ) - { - std::printf( "> uc_hook_add error, reason = %d\n", err ); + std::printf("> beginning execution at = 0x%p\n", + _code_block.cpu_ctx->rip); + if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, + 0ull))) { + std::printf("> error starting emu... reason = %d\n", err); return false; + } + + if (cc_block) + // push back new block that has been traced... + code_blocks.push_back(branch_block); } + // drop down and execute the absolute case as well since that + // will trace the first branch... + } + case vm::instrs::jcc_type::absolute: { + if (std::find(vip_begins.begin(), vip_begins.end(), + _code_block.code_block.jcc.block_addr[0]) == + vip_begins.end()) { + std::uintptr_t rbp = 0ull; + std::uint32_t branch_rva = (_code_block.code_block.jcc.block_addr[0] - + g_vm_ctx->module_base) + + g_vm_ctx->image_base; + + // setup object globals so that the tracing will work... + code_block_data_t branch_block{{}, nullptr, nullptr}; + cc_block = &branch_block; + g_vm_ctx = _code_block.g_vm_ctx.get(); + + // restore register values... + if ((err = + uc_context_restore(uc_ctx, _code_block.cpu_ctx->context))) { + std::printf("> failed to restore emu context... reason = %d\n", + err); + return false; + } - if ( ( err = uc_hook_add( uc_ctx, &int_hook, UC_HOOK_INTR, &vm::emu_t::int_callback, this, 0ull, 0ull ) ) ) - { - std::printf( "> uc_hook_add error, reason = %d\n", err ); + // restore stack values... + if ((err = uc_mem_write(uc_ctx, STACK_BASE, + _code_block.cpu_ctx->stack, STACK_SIZE))) { + std::printf("> failed to restore stack... reason = %d\n", err); return false; - } + } - if ( ( err = uc_hook_add( uc_ctx, &invalid_mem_hook, - UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | UC_HOOK_MEM_FETCH_UNMAPPED | - UC_HOOK_INSN_INVALID, - &vm::emu_t::invalid_mem, this, true, false ) ) ) - { - std::printf( "> uc_hook_add error, reason = %d\n", err ); + // get the address in rbp (top of vsp)... then patch the branch rva... + if ((err = uc_reg_read(uc_ctx, UC_X86_REG_RBP, &rbp))) { + std::printf("> failed to read rbp... reason = %d\n", err); return false; - } - return true; - } + } - bool emu_t::get_trace( std::vector< vm::instrs::code_block_t > &entries ) - { - uc_err err; - std::uintptr_t rip = g_vm_ctx->vm_entry_rva + g_vm_ctx->module_base, rsp = STACK_BASE + STACK_SIZE - PAGE_4KB; + // patch the branch rva... + if ((err = + uc_mem_write(uc_ctx, rbp, &branch_rva, sizeof branch_rva))) { + std::printf("> failed to patch branch rva... reason = %d\n", err); + return false; + } - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RSP, &rsp ) ) ) - { - std::printf( "> uc_reg_write error, reason = %d\n", err ); + std::printf("> beginning execution at = 0x%p\n", + _code_block.cpu_ctx->rip); + if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, + 0ull))) { + std::printf("> error starting emu... reason = %d\n", err); return false; + } + + if (cc_block) + // push back new block that has been traced... + code_blocks.push_back(branch_block); } + break; + } + case vm::instrs::jcc_type::switch_case: { + for (auto _idx = 0u; + _idx < _code_block.code_block.jcc.block_addr.size(); ++_idx) { + if (std::find(vip_begins.begin(), vip_begins.end(), + _code_block.code_block.jcc.block_addr[_idx]) != + vip_begins.end()) + continue; + + std::uintptr_t rbp = 0ull; + std::uint32_t branch_rva = + (_code_block.code_block.jcc.block_addr[_idx] - + g_vm_ctx->module_base) + + g_vm_ctx->image_base; + + // setup object globals so that the tracing will work... + code_block_data_t branch_block{{}, nullptr, nullptr}; + cc_block = &branch_block; + g_vm_ctx = _code_block.g_vm_ctx.get(); + + // restore register values... + if ((err = + uc_context_restore(uc_ctx, _code_block.cpu_ctx->context))) { + std::printf("> failed to restore emu context... reason = %d\n", + err); + return false; + } - if ( ( err = uc_reg_write( uc_ctx, UC_X86_REG_RIP, &rip ) ) ) - { - std::printf( "> uc_reg_write error, reason = %d\n", err ); + // restore stack values... + if ((err = uc_mem_write(uc_ctx, STACK_BASE, + _code_block.cpu_ctx->stack, STACK_SIZE))) { + std::printf("> failed to restore stack... reason = %d\n", err); return false; - } + } - // trace the first block given the vm enter... - code_block_data_t code_block{ {}, nullptr, nullptr }; - cc_block = &code_block; + // get the address in rbp (top of vsp)... then patch the branch rva... + if ((err = uc_reg_read(uc_ctx, UC_X86_REG_RBP, &rbp))) { + std::printf("> failed to read rbp... reason = %d\n", err); + return false; + } - std::printf( "> beginning execution at = 0x%p\n", rip ); - if ( ( err = uc_emu_start( uc_ctx, rip, 0ull, 0ull, 0ull ) ) ) - { - std::printf( "> error starting emu... reason = %d\n", err ); + // patch the branch rva... + if ((err = + uc_mem_write(uc_ctx, rbp, &branch_rva, sizeof branch_rva))) { + std::printf("> failed to patch branch rva... reason = %d\n", err); return false; - } + } - if ( cc_block ) - code_blocks.push_back( code_block ); - - // code_blocks.size() will continue to grow as all branches are traced... - // when idx is > code_blocks.size() then we have traced all branches... - for ( auto idx = 0u; idx < code_blocks.size(); ++idx ) - { - const auto _code_block = code_blocks[ idx ]; - if ( !_code_block.code_block.jcc.has_jcc ) - continue; - - switch ( _code_block.code_block.jcc.type ) - { - case vm::instrs::jcc_type::branching: - { - if ( std::find( vip_begins.begin(), vip_begins.end(), _code_block.code_block.jcc.block_addr[ 1 ] ) == - vip_begins.end() ) - { - std::uintptr_t rbp = 0ull; - std::uint32_t branch_rva = - ( _code_block.code_block.jcc.block_addr[ 1 ] - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - - // setup object globals so that the tracing will work... - code_block_data_t branch_block{ {}, nullptr, nullptr }; - cc_block = &branch_block; - g_vm_ctx = _code_block.g_vm_ctx.get(); - - // restore register values... - if ( ( err = uc_context_restore( uc_ctx, _code_block.cpu_ctx->context ) ) ) - { - std::printf( "> failed to restore emu context... reason = %d\n", err ); - return false; - } - - // restore stack values... - if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, _code_block.cpu_ctx->stack, STACK_SIZE ) ) ) - { - std::printf( "> failed to restore stack... reason = %d\n", err ); - return false; - } - - // get the address in rbp (top of vsp)... then patch the branch rva... - if ( ( err = uc_reg_read( uc_ctx, UC_X86_REG_RBP, &rbp ) ) ) - { - std::printf( "> failed to read rbp... reason = %d\n", err ); - return false; - } - - // patch the branch rva... - if ( ( err = uc_mem_write( uc_ctx, rbp, &branch_rva, sizeof branch_rva ) ) ) - { - std::printf( "> failed to patch branch rva... reason = %d\n", err ); - return false; - } - - std::printf( "> beginning execution at = 0x%p\n", _code_block.cpu_ctx->rip ); - if ( ( err = uc_emu_start( uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) ) - { - std::printf( "> error starting emu... reason = %d\n", err ); - return false; - } - - if ( cc_block ) - // push back new block that has been traced... - code_blocks.push_back( branch_block ); - } - // drop down and execute the absolute case as well since that - // will trace the first branch... - } - case vm::instrs::jcc_type::absolute: - { - if ( std::find( vip_begins.begin(), vip_begins.end(), _code_block.code_block.jcc.block_addr[ 0 ] ) == - vip_begins.end() ) - { - - std::uintptr_t rbp = 0ull; - std::uint32_t branch_rva = - ( _code_block.code_block.jcc.block_addr[ 0 ] - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - - // setup object globals so that the tracing will work... - code_block_data_t branch_block{ {}, nullptr, nullptr }; - cc_block = &branch_block; - g_vm_ctx = _code_block.g_vm_ctx.get(); - - // restore register values... - if ( ( err = uc_context_restore( uc_ctx, _code_block.cpu_ctx->context ) ) ) - { - std::printf( "> failed to restore emu context... reason = %d\n", err ); - return false; - } - - // restore stack values... - if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, _code_block.cpu_ctx->stack, STACK_SIZE ) ) ) - { - std::printf( "> failed to restore stack... reason = %d\n", err ); - return false; - } - - // get the address in rbp (top of vsp)... then patch the branch rva... - if ( ( err = uc_reg_read( uc_ctx, UC_X86_REG_RBP, &rbp ) ) ) - { - std::printf( "> failed to read rbp... reason = %d\n", err ); - return false; - } - - // patch the branch rva... - if ( ( err = uc_mem_write( uc_ctx, rbp, &branch_rva, sizeof branch_rva ) ) ) - { - std::printf( "> failed to patch branch rva... reason = %d\n", err ); - return false; - } - - std::printf( "> beginning execution at = 0x%p\n", _code_block.cpu_ctx->rip ); - if ( ( err = uc_emu_start( uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) ) - { - std::printf( "> error starting emu... reason = %d\n", err ); - return false; - } - - if ( cc_block ) - // push back new block that has been traced... - code_blocks.push_back( branch_block ); - } - break; - } - case vm::instrs::jcc_type::switch_case: - { - for ( auto _idx = 0u; _idx < _code_block.code_block.jcc.block_addr.size(); ++_idx ) - { - if ( std::find( vip_begins.begin(), vip_begins.end(), - _code_block.code_block.jcc.block_addr[ _idx ] ) != vip_begins.end() ) - continue; - - std::uintptr_t rbp = 0ull; - std::uint32_t branch_rva = - ( _code_block.code_block.jcc.block_addr[ _idx ] - g_vm_ctx->module_base ) + - g_vm_ctx->image_base; - - // setup object globals so that the tracing will work... - code_block_data_t branch_block{ {}, nullptr, nullptr }; - cc_block = &branch_block; - g_vm_ctx = _code_block.g_vm_ctx.get(); - - // restore register values... - if ( ( err = uc_context_restore( uc_ctx, _code_block.cpu_ctx->context ) ) ) - { - std::printf( "> failed to restore emu context... reason = %d\n", err ); - return false; - } - - // restore stack values... - if ( ( err = uc_mem_write( uc_ctx, STACK_BASE, _code_block.cpu_ctx->stack, STACK_SIZE ) ) ) - { - std::printf( "> failed to restore stack... reason = %d\n", err ); - return false; - } - - // get the address in rbp (top of vsp)... then patch the branch rva... - if ( ( err = uc_reg_read( uc_ctx, UC_X86_REG_RBP, &rbp ) ) ) - { - std::printf( "> failed to read rbp... reason = %d\n", err ); - return false; - } - - // patch the branch rva... - if ( ( err = uc_mem_write( uc_ctx, rbp, &branch_rva, sizeof branch_rva ) ) ) - { - std::printf( "> failed to patch branch rva... reason = %d\n", err ); - return false; - } - - std::printf( "> beginning execution at = 0x%p\n", _code_block.cpu_ctx->rip ); - if ( ( err = uc_emu_start( uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull ) ) ) - { - std::printf( "> error starting emu... reason = %d\n", err ); - return false; - } - - if ( cc_block ) - // push back new block that has been traced... - code_blocks.push_back( branch_block ); - } - break; - } - } - } + std::printf("> beginning execution at = 0x%p\n", + _code_block.cpu_ctx->rip); + if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, + 0ull))) { + std::printf("> error starting emu... reason = %d\n", err); + return false; + } - for ( auto &[ code_block, cpu_ctx, vm_ctx ] : code_blocks ) - { - // convert linear virtual addresses to image based addresses... - code_block.vip_begin = ( code_block.vip_begin - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - if ( code_block.jcc.has_jcc ) - { - switch ( code_block.jcc.type ) - { - case vm::instrs::jcc_type::branching: - { - code_block.jcc.block_addr[ 1 ] = - ( code_block.jcc.block_addr[ 1 ] - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - } - case vm::instrs::jcc_type::absolute: - { - code_block.jcc.block_addr[ 0 ] = - ( code_block.jcc.block_addr[ 0 ] - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - break; - } - case vm::instrs::jcc_type::switch_case: - { - for ( auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx ) - code_block.jcc.block_addr[ idx ] = - ( code_block.jcc.block_addr[ idx ] - g_vm_ctx->module_base ) + g_vm_ctx->image_base; - break; - } - } - } - entries.push_back( code_block ); + if (cc_block) + // push back new block that has been traced... + code_blocks.push_back(branch_block); } - - return true; + break; + } } - - uc_err emu_t::create_entry( vmp2::v2::entry_t *entry ) - { - uc_reg_read( uc_ctx, UC_X86_REG_R15, &entry->regs.r15 ); - uc_reg_read( uc_ctx, UC_X86_REG_R14, &entry->regs.r14 ); - uc_reg_read( uc_ctx, UC_X86_REG_R13, &entry->regs.r13 ); - uc_reg_read( uc_ctx, UC_X86_REG_R12, &entry->regs.r12 ); - uc_reg_read( uc_ctx, UC_X86_REG_R11, &entry->regs.r11 ); - uc_reg_read( uc_ctx, UC_X86_REG_R10, &entry->regs.r10 ); - uc_reg_read( uc_ctx, UC_X86_REG_R9, &entry->regs.r9 ); - uc_reg_read( uc_ctx, UC_X86_REG_R8, &entry->regs.r8 ); - uc_reg_read( uc_ctx, UC_X86_REG_RBP, &entry->regs.rbp ); - uc_reg_read( uc_ctx, UC_X86_REG_RDI, &entry->regs.rdi ); - uc_reg_read( uc_ctx, UC_X86_REG_RSI, &entry->regs.rsi ); - uc_reg_read( uc_ctx, UC_X86_REG_RDX, &entry->regs.rdx ); - uc_reg_read( uc_ctx, UC_X86_REG_RCX, &entry->regs.rcx ); - uc_reg_read( uc_ctx, UC_X86_REG_RBX, &entry->regs.rbx ); - uc_reg_read( uc_ctx, UC_X86_REG_RAX, &entry->regs.rax ); - uc_reg_read( uc_ctx, UC_X86_REG_EFLAGS, &entry->regs.rflags ); - - entry->vip = entry->regs.rsi; - entry->handler_idx = entry->regs.rax; - entry->decrypt_key = entry->regs.rbx; - - uc_err err; - if ( ( err = uc_mem_read( uc_ctx, entry->regs.rdi, entry->vregs.raw, sizeof entry->vregs.raw ) ) ) - return err; - - // copy virtual stack values... - for ( auto idx = 0u; idx < sizeof( entry->vsp ) / 8; ++idx ) - if ( ( err = uc_mem_read( uc_ctx, entry->regs.rbp + ( idx * 8 ), &entry->vsp.qword[ idx ], - sizeof entry->vsp.qword[ idx ] ) ) ) - return err; - - return UC_ERR_OK; - } - - bool emu_t::code_exec_callback( uc_engine *uc, uint64_t address, uint32_t size, emu_t *obj ) - { - uc_err err; - vmp2::v2::entry_t vinstr_entry; - std::uint8_t vm_handler_table_idx = 0u; - std::uintptr_t vm_handler_addr; - - static std::shared_ptr< vm::ctx_t > _jmp_ctx; - static zydis_routine_t _jmp_stream; - static auto inst_cnt = 0ull; - - static ZydisDecoder decoder; - static ZydisFormatter formatter; - static ZydisDecodedInstruction instr; - - if ( static std::atomic< bool > once{ false }; !once.exchange( true ) ) - { - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - ZydisFormatterInit( &formatter, ZYDIS_FORMATTER_STYLE_INTEL ); + } + + for (auto &[code_block, cpu_ctx, vm_ctx] : code_blocks) { + // convert linear virtual addresses to image based addresses... + code_block.vip_begin = + (code_block.vip_begin - g_vm_ctx->module_base) + g_vm_ctx->image_base; + if (code_block.jcc.has_jcc) { + switch (code_block.jcc.type) { + case vm::instrs::jcc_type::branching: { + code_block.jcc.block_addr[1] = + (code_block.jcc.block_addr[1] - g_vm_ctx->module_base) + + g_vm_ctx->image_base; } - - if ( !ZYAN_SUCCESS( - ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( address ), PAGE_4KB, &instr ) ) ) - { - std::printf( "> failed to decode instruction at = 0x%p\n", address ); - - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; + case vm::instrs::jcc_type::absolute: { + code_block.jcc.block_addr[0] = + (code_block.jcc.block_addr[0] - g_vm_ctx->module_base) + + g_vm_ctx->image_base; + break; } - - if ( instr.mnemonic == ZYDIS_MNEMONIC_INVALID ) - { - obj->cc_block = nullptr; - uc_emu_stop( uc ); - return false; + case vm::instrs::jcc_type::switch_case: { + for (auto idx = 0u; idx < code_block.jcc.block_addr.size(); ++idx) + code_block.jcc.block_addr[idx] = + (code_block.jcc.block_addr[idx] - g_vm_ctx->module_base) + + g_vm_ctx->image_base; + break; } - - // if there are over 4k instructions executed before a JMP is found then we are gunna stop emulation - // this is a sanity check to prevent inf loops... - if ( ++inst_cnt > 0x1000 ) - { - obj->cc_block = nullptr, inst_cnt = 0ull; - uc_emu_stop( uc ); - return false; + } + } + entries.push_back(code_block); + } + + return true; +} + +uc_err emu_t::create_entry(vmp2::v2::entry_t *entry) { + uc_reg_read(uc_ctx, UC_X86_REG_R15, &entry->regs.r15); + uc_reg_read(uc_ctx, UC_X86_REG_R14, &entry->regs.r14); + uc_reg_read(uc_ctx, UC_X86_REG_R13, &entry->regs.r13); + uc_reg_read(uc_ctx, UC_X86_REG_R12, &entry->regs.r12); + uc_reg_read(uc_ctx, UC_X86_REG_R11, &entry->regs.r11); + uc_reg_read(uc_ctx, UC_X86_REG_R10, &entry->regs.r10); + uc_reg_read(uc_ctx, UC_X86_REG_R9, &entry->regs.r9); + uc_reg_read(uc_ctx, UC_X86_REG_R8, &entry->regs.r8); + uc_reg_read(uc_ctx, UC_X86_REG_RBP, &entry->regs.rbp); + uc_reg_read(uc_ctx, UC_X86_REG_RDI, &entry->regs.rdi); + uc_reg_read(uc_ctx, UC_X86_REG_RSI, &entry->regs.rsi); + uc_reg_read(uc_ctx, UC_X86_REG_RDX, &entry->regs.rdx); + uc_reg_read(uc_ctx, UC_X86_REG_RCX, &entry->regs.rcx); + uc_reg_read(uc_ctx, UC_X86_REG_RBX, &entry->regs.rbx); + uc_reg_read(uc_ctx, UC_X86_REG_RAX, &entry->regs.rax); + uc_reg_read(uc_ctx, UC_X86_REG_EFLAGS, &entry->regs.rflags); + + entry->vip = entry->regs.rsi; + entry->handler_idx = entry->regs.rax; + entry->decrypt_key = entry->regs.rbx; + + uc_err err; + if ((err = uc_mem_read(uc_ctx, entry->regs.rdi, entry->vregs.raw, + sizeof entry->vregs.raw))) + return err; + + // copy virtual stack values... + for (auto idx = 0u; idx < sizeof(entry->vsp) / 8; ++idx) + if ((err = + uc_mem_read(uc_ctx, entry->regs.rbp + (idx * 8), + &entry->vsp.qword[idx], sizeof entry->vsp.qword[idx]))) + return err; + + return UC_ERR_OK; +} + +bool emu_t::code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, + emu_t *obj) { + uc_err err; + vmp2::v2::entry_t vinstr_entry; + std::uint8_t vm_handler_table_idx = 0u; + std::uintptr_t vm_handler_addr; + + static std::shared_ptr _jmp_ctx; + static zydis_routine_t _jmp_stream; + static auto inst_cnt = 0ull; + + static ZydisDecoder decoder; + static ZydisFormatter formatter; + static ZydisDecodedInstruction instr; + + if (static std::atomic once{false}; !once.exchange(true)) { + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, + ZYDIS_ADDRESS_WIDTH_64); + ZydisFormatterInit(&formatter, ZYDIS_FORMATTER_STYLE_INTEL); + } + + if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer( + &decoder, reinterpret_cast(address), PAGE_4KB, &instr))) { + std::printf("> failed to decode instruction at = 0x%p\n", address); + + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } + return false; + } + + if (instr.mnemonic == ZYDIS_MNEMONIC_INVALID) { + obj->cc_block = nullptr; + uc_emu_stop(uc); + return false; + } + + // if there are over 4k instructions executed before a JMP is found then we + // are gunna stop emulation this is a sanity check to prevent inf loops... + if (++inst_cnt > 0x1000) { + obj->cc_block = nullptr, inst_cnt = 0ull; + uc_emu_stop(uc); + return false; + } + + // if the native instruction is a jmp rcx/rdx... then AL will contain the vm + // handler table index of the vm handler that the emulator is about to jmp + // too... + if (!(instr.mnemonic == ZYDIS_MNEMONIC_JMP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + (instr.operands[0].reg.value == ZYDIS_REGISTER_RCX || + instr.operands[0].reg.value == ZYDIS_REGISTER_RDX))) + return true; + + // reset sanity check... + inst_cnt = 0ull; + + // extract address of vm handler table... + switch (instr.operands[0].reg.value) { + case ZYDIS_REGISTER_RCX: + if ((err = uc_reg_read(uc, UC_X86_REG_RCX, &vm_handler_addr))) { + std::printf("> failed to read rcx... reason = %d\n", err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); } - - // if the native instruction is a jmp rcx/rdx... then AL will contain the vm handler - // table index of the vm handler that the emulator is about to jmp too... - if ( !( instr.mnemonic == ZYDIS_MNEMONIC_JMP && instr.operands[ 0 ].type == ZYDIS_OPERAND_TYPE_REGISTER && - ( instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RCX || - instr.operands[ 0 ].reg.value == ZYDIS_REGISTER_RDX ) ) ) - return true; - - // reset sanity check... - inst_cnt = 0ull; - - // extract address of vm handler table... - switch ( instr.operands[ 0 ].reg.value ) - { - case ZYDIS_REGISTER_RCX: - if ( ( err = uc_reg_read( uc, UC_X86_REG_RCX, &vm_handler_addr ) ) ) - { - std::printf( "> failed to read rcx... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - break; - case ZYDIS_REGISTER_RDX: - if ( ( err = uc_reg_read( uc, UC_X86_REG_RDX, &vm_handler_addr ) ) ) - { - std::printf( "> failed to read rdx... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - break; + return false; + } + break; + case ZYDIS_REGISTER_RDX: + if ((err = uc_reg_read(uc, UC_X86_REG_RDX, &vm_handler_addr))) { + std::printf("> failed to read rdx... reason = %d\n", err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); } + return false; + } + break; + } + + if ((err = uc_reg_read(obj->uc_ctx, UC_X86_REG_AL, &vm_handler_table_idx))) { + std::printf("> failed to read register... reason = %d\n", err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } + return false; + } - if ( ( err = uc_reg_read( obj->uc_ctx, UC_X86_REG_AL, &vm_handler_table_idx ) ) ) - { - std::printf( "> failed to read register... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } + auto &vm_handler = obj->g_vm_ctx->vm_handlers[vm_handler_table_idx]; - auto &vm_handler = obj->g_vm_ctx->vm_handlers[ vm_handler_table_idx ]; + if ((err = obj->create_entry(&vinstr_entry))) { + std::printf("> failed to create vinstr entry... reason = %d\n", err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } + return false; + } + + // quick check to ensure sanity... things can get crazy so this is good to + // check... + if (vm_handler.address != vm_handler_addr || + vinstr_entry.vip >= + obj->g_vm_ctx->module_base + obj->g_vm_ctx->image_size || + vinstr_entry.vip < obj->g_vm_ctx->module_base) { + std::printf( + "> vm handler index (%d) does not match vm handler address (%p)...\n", + vm_handler_table_idx, vm_handler_addr); + + obj->cc_block = nullptr; + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } - if ( ( err = obj->create_entry( &vinstr_entry ) ) ) - { - std::printf( "> failed to create vinstr entry... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } + return false; + } - // quick check to ensure sanity... things can get crazy so this is good to check... - if ( vm_handler.address != vm_handler_addr || - vinstr_entry.vip >= obj->g_vm_ctx->module_base + obj->g_vm_ctx->image_size || - vinstr_entry.vip < obj->g_vm_ctx->module_base ) - { - std::printf( "> vm handler index (%d) does not match vm handler address (%p)...\n", vm_handler_table_idx, - vm_handler_addr ); - - obj->cc_block = nullptr; - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } + if (!vm_handler.profile) { + if (!g_force_emu) obj->cc_block = nullptr; - return false; - } + std::printf("> please define virtual machine handler (0x%p): \n\n", + (vm_handler_addr - obj->g_vm_ctx->module_base) + + obj->g_vm_ctx->image_base); - if ( !vm_handler.profile ) - { - if ( !g_force_emu ) - obj->cc_block = nullptr; + vm::util::print(vm_handler.instrs); + std::printf("\n\n"); - std::printf( "> please define virtual machine handler (0x%p): \n\n", - ( vm_handler_addr - obj->g_vm_ctx->module_base ) + obj->g_vm_ctx->image_base ); + if (!g_force_emu) exit(0); + } - vm::util::print( vm_handler.instrs ); - std::printf( "\n\n" ); + auto vinstr = vm::instrs::get(*obj->g_vm_ctx, vinstr_entry); - if ( !g_force_emu ) - exit( 0 ); + if (!vinstr.has_value()) { + std::printf("> failed to decode virtual instruction...\n"); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } + return false; + } + + // log this virtual blocks vip_begin... + if (obj->cc_block->code_block.vinstrs.empty()) { + obj->cc_block->code_block.vip_begin = + obj->g_vm_ctx->exec_type == vmp2::exec_type_t::forward + ? vinstr_entry.vip - 1 + : vinstr_entry.vip + 1; + + obj->vip_begins.push_back(obj->cc_block->code_block.vip_begin); + } + + vinstr.value().trace_data.vm_handler_rva = + (vm_handler_addr - obj->g_vm_ctx->module_base); + obj->cc_block->code_block.vinstrs.push_back(vinstr.value()); + + if (vm_handler.profile) { + switch (vm_handler.profile->mnemonic) { + case vm::handler::VMEXIT: { + obj->cc_block->code_block.jcc.has_jcc = false; + obj->cc_block->code_block.jcc.type = vm::instrs::jcc_type::none; + + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); } - - auto vinstr = vm::instrs::get( *obj->g_vm_ctx, vinstr_entry ); - - if ( !vinstr.has_value() ) - { - std::printf( "> failed to decode virtual instruction...\n" ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); + break; + } + case vm::handler::JMP: { + // get jcc data about the virtual instruction code block that was just + // emulated... + auto jcc_data = + vm::instrs::get_jcc_data(*obj->g_vm_ctx, obj->cc_block->code_block); + obj->cc_block->code_block.jcc = jcc_data.value(); + + // allocate space for the cpu context and stack... + auto new_cpu_ctx = std::make_shared(); + + // optimize so that we dont need to create a new vm::ctx_t every single + // virtual JMP... + if (obj->vm_ctxs.find(vm_handler_addr) == obj->vm_ctxs.end()) { + obj->vm_ctxs[vm_handler_addr] = std::make_shared( + obj->g_vm_ctx->module_base, obj->img_base, obj->img_size, + vm_handler_addr - obj->g_vm_ctx->module_base); + + if (!obj->vm_ctxs[vm_handler_addr]->init()) { + std::printf( + "> failed to init vm::ctx_t for virtual jmp... vip = 0x%p, jmp " + "handler = 0x%p\n", + vinstr_entry.vip, vm_handler_addr); + + if ((err = uc_emu_stop(uc))) { + std::printf( + "> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); } return false; + } } - // log this virtual blocks vip_begin... - if ( obj->cc_block->code_block.vinstrs.empty() ) - { - obj->cc_block->code_block.vip_begin = - obj->g_vm_ctx->exec_type == vmp2::exec_type_t::forward ? vinstr_entry.vip - 1 : vinstr_entry.vip + 1; - - obj->vip_begins.push_back( obj->cc_block->code_block.vip_begin ); + _jmp_ctx = obj->vm_ctxs[vm_handler_addr]; + if ((err = uc_context_alloc(uc, &new_cpu_ctx->context))) { + std::printf("> failed to allocate a unicorn context... reason = %d\n", + err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); + } + return false; } - vinstr.value().trace_data.vm_handler_rva = ( vm_handler_addr - obj->g_vm_ctx->module_base ); - obj->cc_block->code_block.vinstrs.push_back( vinstr.value() ); - - if ( vm_handler.profile ) - { - switch ( vm_handler.profile->mnemonic ) - { - case vm::handler::VMEXIT: - { - obj->cc_block->code_block.jcc.has_jcc = false; - obj->cc_block->code_block.jcc.type = vm::instrs::jcc_type::none; - - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - break; - } - case vm::handler::JMP: - { - // get jcc data about the virtual instruction code block that was just emulated... - auto jcc_data = vm::instrs::get_jcc_data( *obj->g_vm_ctx, obj->cc_block->code_block ); - obj->cc_block->code_block.jcc = jcc_data.value(); - - // allocate space for the cpu context and stack... - auto new_cpu_ctx = std::make_shared< vm::emu_t::cpu_ctx_t >(); - - // optimize so that we dont need to create a new vm::ctx_t every single virtual JMP... - if ( obj->vm_ctxs.find( vm_handler_addr ) == obj->vm_ctxs.end() ) - { - obj->vm_ctxs[ vm_handler_addr ] = - std::make_shared< vm::ctx_t >( obj->g_vm_ctx->module_base, obj->img_base, obj->img_size, - vm_handler_addr - obj->g_vm_ctx->module_base ); - - if ( !obj->vm_ctxs[ vm_handler_addr ]->init() ) - { - std::printf( "> failed to init vm::ctx_t for virtual jmp... vip = 0x%p, jmp handler = 0x%p\n", - vinstr_entry.vip, vm_handler_addr ); - - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - } - - _jmp_ctx = obj->vm_ctxs[ vm_handler_addr ]; - if ( ( err = uc_context_alloc( uc, &new_cpu_ctx->context ) ) ) - { - std::printf( "> failed to allocate a unicorn context... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - - // save the cpu's registers... - new_cpu_ctx->rip = vm_handler_addr; - if ( ( err = uc_context_save( uc, new_cpu_ctx->context ) ) ) - { - std::printf( "> failed to save emulator context... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - - // save the entire stack... - if ( ( err = uc_mem_read( uc, STACK_BASE, new_cpu_ctx->stack, STACK_SIZE ) ) ) - { - std::printf( "> failed to read stack... reason = %d\n", err ); - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return false; - } - - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - - obj->cc_block->cpu_ctx = new_cpu_ctx; - obj->cc_block->g_vm_ctx = _jmp_ctx; - break; - } - default: - break; - } + // save the cpu's registers... + new_cpu_ctx->rip = vm_handler_addr; + if ((err = uc_context_save(uc, new_cpu_ctx->context))) { + std::printf("> failed to save emulator context... reason = %d\n", + err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); + } + return false; } - return true; - } - void emu_t::int_callback( uc_engine *uc, std::uint32_t intno, emu_t *obj ) - { - uc_err err; - std::uintptr_t rip = 0ull; - static ZydisDecoder decoder; - static ZydisDecodedInstruction instr; - - if ( static std::atomic< bool > once{ false }; !once.exchange( true ) ) - ZydisDecoderInit( &decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64 ); - - if ( ( err = uc_reg_read( uc, UC_X86_REG_RIP, &rip ) ) ) - { - std::printf( "> failed to read rip... reason = %d\n", err ); - return; + // save the entire stack... + if ((err = + uc_mem_read(uc, STACK_BASE, new_cpu_ctx->stack, STACK_SIZE))) { + std::printf("> failed to read stack... reason = %d\n", err); + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); + } + return false; } - if ( !ZYAN_SUCCESS( - ZydisDecoderDecodeBuffer( &decoder, reinterpret_cast< void * >( rip ), PAGE_4KB, &instr ) ) ) - { - std::printf( "> failed to decode instruction at = 0x%p\n", rip ); - - if ( ( err = uc_emu_stop( uc ) ) ) - { - std::printf( "> failed to stop emulation, exiting... reason = %d\n", err ); - exit( 0 ); - } - return; + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", + err); + exit(0); } - // advance rip over the instruction that caused the exception... - rip += instr.length; - - if ( ( err = uc_reg_write( uc, UC_X86_REG_RIP, &rip ) ) ) - { - std::printf( "> failed to write rip... reason = %d\n", err ); - return; - } + obj->cc_block->cpu_ctx = new_cpu_ctx; + obj->cc_block->g_vm_ctx = _jmp_ctx; + break; + } + default: + break; } - - void emu_t::invalid_mem( uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, emu_t *obj ) - { - switch ( type ) - { - case UC_MEM_READ_UNMAPPED: - { - uc_mem_map( uc, address & ~0xFFFull, PAGE_4KB, UC_PROT_ALL ); - std::printf( ">>> reading invalid memory at address = 0x%p, size = 0x%x\n", address, size ); - break; - } - case UC_MEM_WRITE_UNMAPPED: - { - uc_mem_map( uc, address & ~0xFFFull, PAGE_4KB, UC_PROT_ALL ); - std::printf( ">>> writing invalid memory at address = 0x%p, size = 0x%x, val = 0x%x\n", address, size, - value ); - break; - } - case UC_MEM_FETCH_UNMAPPED: - { - std::printf( ">>> fetching invalid instructions at address = 0x%p\n", address ); - break; - } - default: - break; - } + } + return true; +} + +void emu_t::int_callback(uc_engine *uc, std::uint32_t intno, emu_t *obj) { + uc_err err; + std::uintptr_t rip = 0ull; + static ZydisDecoder decoder; + static ZydisDecodedInstruction instr; + + if (static std::atomic once{false}; !once.exchange(true)) + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, + ZYDIS_ADDRESS_WIDTH_64); + + if ((err = uc_reg_read(uc, UC_X86_REG_RIP, &rip))) { + std::printf("> failed to read rip... reason = %d\n", err); + return; + } + + if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer( + &decoder, reinterpret_cast(rip), PAGE_4KB, &instr))) { + std::printf("> failed to decode instruction at = 0x%p\n", rip); + + if ((err = uc_emu_stop(uc))) { + std::printf("> failed to stop emulation, exiting... reason = %d\n", err); + exit(0); + } + return; + } + + // advance rip over the instruction that caused the exception... + rip += instr.length; + + if ((err = uc_reg_write(uc, UC_X86_REG_RIP, &rip))) { + std::printf("> failed to write rip... reason = %d\n", err); + return; + } +} + +void emu_t::invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, + int size, int64_t value, emu_t *obj) { + switch (type) { + case UC_MEM_READ_UNMAPPED: { + uc_mem_map(uc, address & ~0xFFFull, PAGE_4KB, UC_PROT_ALL); + std::printf(">>> reading invalid memory at address = 0x%p, size = 0x%x\n", + address, size); + break; + } + case UC_MEM_WRITE_UNMAPPED: { + uc_mem_map(uc, address & ~0xFFFull, PAGE_4KB, UC_PROT_ALL); + std::printf( + ">>> writing invalid memory at address = 0x%p, size = 0x%x, val = " + "0x%x\n", + address, size, value); + break; + } + case UC_MEM_FETCH_UNMAPPED: { + std::printf(">>> fetching invalid instructions at address = 0x%p\n", + address); + break; } -} // namespace vm \ No newline at end of file + default: + break; + } +} +} // namespace vm \ No newline at end of file