From 9e65de6100db9c77628953a4835b7e77de6a84f3 Mon Sep 17 00:00:00 2001 From: _xeroxz Date: Fri, 21 May 2021 20:07:28 -0700 Subject: [PATCH] i think its working >:) --- src/vmemu.vcxproj | 9 +- src/vmemu_t.cpp | 213 ++++++++++++++++++++++++++++++++++++-------- src/vmemu_t.hpp | 68 +++----------- src/vmptest.vmp.exe | Bin 0 -> 24064 bytes vmemu.sln | 20 ++++- 5 files changed, 206 insertions(+), 104 deletions(-) create mode 100644 src/vmptest.vmp.exe diff --git a/src/vmemu.vcxproj b/src/vmemu.vcxproj index 7282200..d3e66c7 100644 --- a/src/vmemu.vcxproj +++ b/src/vmemu.vcxproj @@ -119,6 +119,7 @@ true + $(ProjectDir)..\dependencies\unicorn\include\;$(ProjectDir)..\dependencies\xtils\;$(ProjectDir)..\dependencies\vmprofiler\include\;$(ProjectDir)..\dependencies\cli-parser\;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\include;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\dependencies\zycore\include;$(ProjectDir)..\dependencies\vmprofiler\dependencies\zydis\msvc;$(IncludePath) false @@ -128,12 +129,14 @@ Level3 true - _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS true + stdcpp17 Console true + $(ProjectDir)..\dependencies\unicorn\msvc\x64\Debug\*.lib @@ -142,7 +145,7 @@ true true true - NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS true Disabled stdcpp17 @@ -152,7 +155,7 @@ true true true - $(ProjectDir)..\dependencies\unicorn\msvc\x64\Release\*.lib;%(AdditionalDependencies) + $(ProjectDir)..\dependencies\unicorn\msvc\x64\Debug\*.lib diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp index 20dc5a1..9a2b663 100644 --- a/src/vmemu_t.cpp +++ b/src/vmemu_t.cpp @@ -9,7 +9,8 @@ namespace vm image_base(image_base), vm_entry_rva(vm_entry_rva), vm_handler_table(nullptr), - uc(nullptr) + uc(nullptr), + trace_entries(nullptr) {} bool emu_t::init() @@ -24,7 +25,7 @@ namespace vm return false; } - //vm::util::deobfuscate(vm_entry); + vm::util::deobfuscate(vm_entry); vm::util::print(vm_entry); if (!(vm_handler_table = vm::handler::table::get(vm_entry))) @@ -39,14 +40,20 @@ namespace vm std::printf("[!] failed to get all vm handlers...\n"); return false; } + std::printf("> got all vm handlers...\n"); // // unicorn init stuff... // - auto err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc); + const auto image_size = + NT_HEADER(module_base)->OptionalHeader.SizeOfImage; - if (err) + std::uintptr_t stack_base = 0x1000000; + std::uintptr_t stack_addr = stack_base + (0x1000 * 20); + + uc_err err; + if ((err = uc_open(UC_ARCH_X86, UC_MODE_64, &uc))) { std::printf("failed on uc_mem_map() with error returned %u: %s\n", err, uc_strerror(err)); @@ -54,16 +61,7 @@ namespace vm return false; } - const auto image_size = - NT_HEADER(module_base)->OptionalHeader.SizeOfImage; - - const auto vm_entry = vm_entry_rva + module_base; - constexpr auto stack_addr = 0x1000000 + (0x1000 * 6); - - // allocate space for module... - err = uc_mem_map(uc, module_base, image_size, UC_PROT_ALL); - - if (err) + if ((err = uc_mem_map(uc, module_base, image_size, UC_PROT_ALL))) { std::printf("failed on uc_mem_map() with error returned %u: %s\n", err, uc_strerror(err)); @@ -71,10 +69,7 @@ namespace vm return false; } - // allocate 6 pages for stack... - err = uc_mem_map(uc, stack_addr, 0x1000 * 6, UC_PROT_ALL); - - if (err) + if ((err = uc_mem_map(uc, 0x1000000, 0x1000 * 20, UC_PROT_ALL))) { std::printf("failed on uc_mem_map() with error returned %u: %s\n", err, uc_strerror(err)); @@ -82,16 +77,15 @@ namespace vm return false; } - // write the module into memory... - err = uc_mem_write(uc, module_base, reinterpret_cast(module_base), image_size); - - if (err) std::printf("failed on uc_mem_write() with error returned %u: %s\n", - err, uc_strerror(err)); + if ((err = uc_mem_write(uc, module_base, reinterpret_cast(module_base), image_size))) + { + std::printf("failed on uc_mem_write() with error returned %u: %s\n", + err, uc_strerror(err)); - // set vm_entry into RIP... - err = uc_reg_write(uc, UC_X86_REG_RIP, &vm_entry); + return false; + } - if (err) + if ((err = uc_reg_write(uc, UC_X86_REG_RIP, &vm_entry))) { std::printf("failed on uc_reg_write() with error returned %u: %s\n", err, uc_strerror(err)); @@ -99,10 +93,7 @@ namespace vm return false; } - // set stack address up... - err = uc_reg_write(uc, UC_X86_REG_RSP, &stack_addr); - - if (err) + if ((err = uc_reg_write(uc, UC_X86_REG_RSP, &stack_addr))) { std::printf("failed on uc_reg_write() with error returned %u: %s\n", err, uc_strerror(err)); @@ -110,10 +101,8 @@ namespace vm return false; } - err = uc_hook_add(uc, &trace, UC_HOOK_CODE, &jmp_hook, - nullptr, module_base, module_base + image_size); - - if (err) + if ((err = uc_hook_add(uc, &trace, UC_HOOK_CODE, &vm::emu_t::hook_code, + this, module_base, module_base + image_size))) { std::printf("failed on uc_hook_add() with error returned %u: %s\n", err, uc_strerror(err)); @@ -121,16 +110,14 @@ namespace vm return false; } - // emulate machine code in infinite time - err = uc_emu_start(uc, vm_entry, NULL, NULL, NULL); - if (err) + if ((err = uc_hook_add(uc, &trace1, UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED, + vm::emu_t::hook_mem_invalid, this, 1, 0))) { - std::printf("Failed on uc_emu_start() with error returned %u: %s\n", + std::printf("failed on uc_hook_add() with error returned %u: %s\n", err, uc_strerror(err)); return false; } - return true; } @@ -138,4 +125,152 @@ namespace vm { if (uc) uc_close(uc); } + + bool emu_t::get_trace(std::vector& entries) + { + // hook_code will fill this vector up with values... + trace_entries = &entries; + uc_err err; + + if ((err = uc_emu_start(uc, vm_entry_rva + module_base, NULL, NULL, NULL))) + { + std::printf("failed on uc_emu_start() with error returned %u: %s\n", + err, uc_strerror(err)); + + return false; + } + return true; + } + + uc_err emu_t::create_entry(vmp2::entry_t* entry) + { + uc_reg_read(uc, UC_X86_REG_R15, &entry->regs.r15); + uc_reg_read(uc, UC_X86_REG_R14, &entry->regs.r14); + uc_reg_read(uc, UC_X86_REG_R13, &entry->regs.r13); + uc_reg_read(uc, UC_X86_REG_R12, &entry->regs.r12); + uc_reg_read(uc, UC_X86_REG_R11, &entry->regs.r11); + uc_reg_read(uc, UC_X86_REG_R10, &entry->regs.r10); + uc_reg_read(uc, UC_X86_REG_R9, &entry->regs.r9); + uc_reg_read(uc, UC_X86_REG_R8, &entry->regs.r8); + uc_reg_read(uc, UC_X86_REG_RBP, &entry->regs.rbp); + uc_reg_read(uc, UC_X86_REG_RDI, &entry->regs.rdi); + uc_reg_read(uc, UC_X86_REG_RSI, &entry->regs.rsi); + uc_reg_read(uc, UC_X86_REG_RDX, &entry->regs.rdx); + uc_reg_read(uc, UC_X86_REG_RCX, &entry->regs.rcx); + uc_reg_read(uc, UC_X86_REG_RBX, &entry->regs.rbx); + uc_reg_read(uc, UC_X86_REG_RAX, &entry->regs.rax); + uc_reg_read(uc, 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, 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, entry->regs.rbp + (idx * 8), + &entry->vsp.qword[idx], sizeof entry->vsp.qword[idx]))) + return err; + + return UC_ERR_OK; + } + + void emu_t::hook_code(uc_engine* uc, uint64_t address, uint32_t size, vm::emu_t* obj) + { + std::printf(">>> Tracing instruction at 0x%p, instruction size = 0x%x\n", address, size); + + // grab JMP RDX/RCX <-- this register... + static const auto jmp_reg = obj->vm_entry[obj->vm_entry.size()] + .instr.operands[0] + .reg + .value; + + static ZydisDecoder decoder; + static std::once_flag once; + static ZydisDecodedInstruction instr; + static std::uintptr_t reg_val = 0u; + + // init zydis decoder just a single time... + std::call_once(once, [&]() -> void { + ZydisDecoderInit(&decoder, + ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); }); + + if (address == obj->vm_entry[obj->vm_entry.size()].addr) + { + std::printf("stopped at jmp... addr = 0x%p\n", address); + std::getchar(); + + vmp2::entry_t new_entry; + if (!obj->create_entry(&new_entry)) + { + std::printf("[!] failed to create new entry... exiting...\n"); + exit(0); + } + obj->trace_entries->push_back(new_entry); + } + // if we are getting a callback for a JMP RCX/RDX instruction... + else if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer( + &decoder, reinterpret_cast(address), size, &instr)) && + instr.mnemonic == ZYDIS_MNEMONIC_JMP && + instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && + instr.operands[0].reg.value == jmp_reg) + { + switch (jmp_reg) + { + ZYDIS_REGISTER_RDX: + uc_reg_read(uc, UC_X86_REG_RDX, ®_val); + break; + ZYDIS_REGISTER_RCX: + uc_reg_read(uc, UC_X86_REG_RCX, ®_val); + break; + default: + std::printf("[!] invalid jump register...\n"); + exit(0); + } + + // checks to see if the address + // in JMP RDX/RCX is a vm handler address... + static const auto vm_handler_check = + [&](const vm::handler_t& vm_handler) -> bool + { return vm_handler.address == reg_val; }; + + if (std::find_if(obj->vm_handlers.begin(), obj->vm_handlers.end(), + vm_handler_check) == obj->vm_handlers.end()) + return; + + std::printf("stopped at jmp... addr = 0x%p\n", address); + std::getchar(); + + vmp2::entry_t new_entry; + if (!obj->create_entry(&new_entry)) + { + std::printf("[!] failed to create new entry... exiting...\n"); + exit(0); + } + obj->trace_entries->push_back(new_entry); + } + } + + bool emu_t::hook_mem_invalid(uc_engine* uc, uc_mem_type type, + uint64_t address, int size, int64_t value, vm::emu_t* obj) + { + switch (type) + { + default: + // return false to indicate we want to stop emulation + return false; + case UC_MEM_WRITE_UNMAPPED: + printf(">>> Missing memory is being WRITE at 0x%p, data size = %u, data value = 0x%p\n", + address, size, value); + return false; + case UC_MEM_READ_UNMAPPED: + printf(">>> Missing memory is being READ at 0x%p, data size = %u, data value = 0x%p\n", + address, size, value); + return false; + } + } } \ No newline at end of file diff --git a/src/vmemu_t.hpp b/src/vmemu_t.hpp index 5d26600..5a6de31 100644 --- a/src/vmemu_t.hpp +++ b/src/vmemu_t.hpp @@ -6,6 +6,7 @@ #include #include #include +#include namespace vm { @@ -15,12 +16,18 @@ namespace vm public: explicit emu_t(std::uint32_t vm_entry_rva, std::uintptr_t image_base, std::uintptr_t module_base); + ~emu_t(); bool init(); - ~emu_t(); + bool get_trace(std::vector& entries); private: + uc_err create_entry(vmp2::entry_t* entry); + static void hook_code(uc_engine* uc, uint64_t address, uint32_t size, vm::emu_t* obj); + static bool hook_mem_invalid(uc_engine* uc, uc_mem_type type, + uint64_t address, int size, int64_t value, vm::emu_t* obj); + uc_engine* uc; - uc_hook trace; + uc_hook trace, trace1; std::uintptr_t image_base, module_base; std::uint32_t vm_entry_rva; @@ -28,61 +35,6 @@ namespace vm zydis_routine_t vm_entry; std::uintptr_t* vm_handler_table; std::vector vm_handlers; - - // very janky work around to use classes & callbacks with unicorn... it is what it is... - callback_t jmp_hook = - [&, this](uc_engine* uc, uint64_t address, uint32_t size, void* user_data) -> void - { - // grab JMP RDX/RCX <-- this register... - static auto jmp_reg = vm_entry[ - vm_entry.size() - 1].instr.operands[0].reg.value; - - static ZydisDecoder decoder; - static std::once_flag once; - static ZydisDecodedInstruction instr; - static std::uintptr_t reg_val = 0u; - - // init zydis decoder just a single time... - std::call_once(once, []() -> void { - ZydisDecoderInit(&decoder, - ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); }); - - if (address == vm_entry[vm_entry.size() - 1].addr) - { - std::printf("stopped at jmp... addr = 0x%p\n", address); - std::getchar(); - } - // if we are getting a callback for a JMP RCX/RDX instruction... - else if (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer( - &decoder, reinterpret_cast(address), size, &instr)) && - instr.operands[0].type == ZYDIS_OPERAND_TYPE_REGISTER && - instr.operands[0].reg.value == jmp_reg) - { - switch (jmp_reg) - { - ZYDIS_REGISTER_RDX: - uc_reg_read(uc, UC_X86_REG_RDX, ®_val); - break; - ZYDIS_REGISTER_RCX: - uc_reg_read(uc, UC_X86_REG_RCX, ®_val); - break; - default: - throw std::exception("invalid register to jump from...\n"); - } - - // checks to see if the address - // in JMP RDX/RCX is a vm handler address... - static const auto vm_handler_check = - [&](vm::handler_t& vm_handler) -> bool - { return vm_handler.address == reg_val; }; - - if (std::find_if(vm_handlers.begin(), vm_handlers.end(), - vm_handler_check) == vm_handlers.end()) - return; - - std::printf("stopped at jmp... addr = 0x%p\n", address); - std::getchar(); - } - }; + std::vector* trace_entries; }; } \ No newline at end of file diff --git a/src/vmptest.vmp.exe b/src/vmptest.vmp.exe new file mode 100644 index 0000000000000000000000000000000000000000..60c276e74f089de6a42cd355f0b8885e353efbe5 GIT binary patch literal 24064 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