diff --git a/.gitmodules b/.gitmodules index 598137b..b7495c3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "deps/unicorn"] path = deps/unicorn url = https://github.com/unicorn-engine/unicorn.git -[submodule "deps/cli-parser"] - path = deps/cli-parser - url = https://githacks.org/_xeroxz/cli-parser.git diff --git a/CMakeLists.txt b/CMakeLists.txt index af161ce..a8bf4af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ set(vmemu_SOURCES "") list(APPEND vmemu_SOURCES "src/main.cpp" + "src/vmemu_t.cpp" "include/vmemu_t.hpp" ) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 0586732..18060f9 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -26,21 +26,3 @@ endif() add_subdirectory(unicorn) set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER}) -# Target cli-parser -set(CMKR_TARGET cli-parser) -set(cli-parser_SOURCES "") - -set(CMKR_SOURCES ${cli-parser_SOURCES}) -add_library(cli-parser INTERFACE) - -if(cli-parser_SOURCES) - target_sources(cli-parser INTERFACE ${cli-parser_SOURCES}) -endif() - -target_include_directories(cli-parser INTERFACE - cli-parser -) - -unset(CMKR_TARGET) -unset(CMKR_SOURCES) - diff --git a/deps/cli-parser b/deps/cli-parser deleted file mode 160000 index 1aedaf8..0000000 --- a/deps/cli-parser +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1aedaf8bb7f383f54b7cd498767611535526da85 diff --git a/deps/cmake.toml b/deps/cmake.toml index b32a172..8a1e231 100644 --- a/deps/cmake.toml +++ b/deps/cmake.toml @@ -1,6 +1,2 @@ [subdir.vmprofiler] -[subdir.unicorn] - -[target.cli-parser] -type = "interface" -include-directories = ["cli-parser"] +[subdir.unicorn] \ No newline at end of file diff --git a/deps/vmprofiler b/deps/vmprofiler index f252173..11650b6 160000 --- a/deps/vmprofiler +++ b/deps/vmprofiler @@ -1 +1 @@ -Subproject commit f252173d7c3d8b237f9055033a9392f594ee5915 +Subproject commit 11650b6d8aa319940a298f707645334898f8de5d diff --git a/include/vmemu_t.hpp b/include/vmemu_t.hpp index d4523e4..0d72ec6 100644 --- a/include/vmemu_t.hpp +++ b/include/vmemu_t.hpp @@ -1,54 +1,27 @@ #pragma once #include - #include #include -#include #include #include #define PAGE_4KB 0x1000 #define STACK_SIZE PAGE_4KB * 512 - #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; - std::shared_ptr g_vm_ctx; - }; - public: - explicit emu_t(vm::ctx_t* vm_ctx); + explicit emu_t(vm::vmctx_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; + const vm::vmctx_t* m_vm_ctx; + uc_hook code_exec_hook, invalid_mem_hook, int_hook; - 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, diff --git a/src/main.cpp b/src/main.cpp index 1d333d8..23c4c11 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -46,11 +46,11 @@ int __cdecl main(int argc, const char* argv[]) { return 0; } - vm::util::init(); - + vm::utils::init(); std::vector module_data, tmp, unpacked_bin; - if (!vm::util::open_binary_file(parser.get("bin"), - module_data)) { + + if (!vm::utils::open_binary_file(parser.get("bin"), + module_data)) { std::printf("[!] failed to open binary file...\n"); return -1; } @@ -117,17 +117,22 @@ int __cdecl main(int argc, const char* argv[]) { 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); - - // testing flatten and deobfuscate on vmp3 vm enters... - zydis_routine_t vm_entry; - vm::util::flatten(vm_entry, module_base + vm_entry_rva); - vm::util::deobfuscate(vm_entry); - vm::util::print(vm_entry); - - // testing vmlocate port for vmp3... - const auto vm_entries = vm::locate::get_vm_entries(module_base, image_size); - std::printf("> number of vm entries = %d\n", vm_entries.size()); + vm::vmctx_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 3...\n"); + return -1; + } + + vm::emu_t emu(&vmctx); + if (!emu.init()) { + std::printf( + "[!] failed to init vm::emu_t... read above in the console for the " + "reason...\n"); + return -1; + } } } \ No newline at end of file diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp new file mode 100644 index 0000000..cab1df7 --- /dev/null +++ b/src/vmemu_t.cpp @@ -0,0 +1,137 @@ +#include + +namespace vm { +emu_t::emu_t(vm::vmctx_t* vm_ctx) : m_vm_ctx(vm_ctx) {} + +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, m_vm_ctx->m_module_base, m_vm_ctx->m_image_size, + UC_PROT_ALL))) { + std::printf("> map memory failed, reason = %d\n", err); + return false; + } + + if ((err = uc_mem_write(uc_ctx, m_vm_ctx->m_module_base, + reinterpret_cast(m_vm_ctx->m_module_base), + m_vm_ctx->m_image_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, + m_vm_ctx->m_module_base, + m_vm_ctx->m_module_base + m_vm_ctx->m_image_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, + (void*)&vm::emu_t::invalid_mem, this, true, false))) { + std::printf("> uc_hook_add error, reason = %d\n", err); + 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 thread_local zydis_decoded_instr_t instr; + + 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(vm::utils::g_decoder.get(), + 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... this is + // usually a division by 0... + 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; + } +} + +bool emu_t::code_exec_callback(uc_engine* uc, + uint64_t address, + uint32_t size, + emu_t* obj) { + return true; +} + +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 = %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 = %p, size = 0x%x, val = " + "0x%x\n", + address, size, value); + break; + } + case UC_MEM_FETCH_UNMAPPED: { + std::printf(">>> fetching invalid instructions at address = %p\n", + address); + + std::uintptr_t rip, rsp; + uc_reg_read(uc, UC_X86_REG_RSP, &rsp); + uc_mem_read(uc, rsp, &rip, sizeof rip); + rsp += 8; + uc_reg_write(uc, UC_X86_REG_RSP, &rsp); + uc_reg_write(uc, UC_X86_REG_RIP, &rip); + std::printf(">>> injecting return to try and recover... rip = %p\n", rip); + break; + } + default: + break; + } +} +} // namespace vm \ No newline at end of file