From 1e62befcf06506e19382014cabe24d7cde0448cc Mon Sep 17 00:00:00 2001 From: _xeroxz <_xeroxz@back.engineer> Date: Thu, 2 Dec 2021 01:30:59 -0800 Subject: [PATCH] porting the project to linux... LoadLibraryA hook is fucked up and kernel32.dll causes the unpacker to freak out for some reason... --- include/unpacker.hpp | 3 ++ src/main.cpp | 48 ++++++++++++++++++++++++++----- src/unpacker.cpp | 68 +++++++++++++++++++++++++++++--------------- src/vmemu_t.cpp | 47 +++++++++++++++++------------- 4 files changed, 117 insertions(+), 49 deletions(-) diff --git a/include/unpacker.hpp b/include/unpacker.hpp index 3f2739a..45a64ca 100644 --- a/include/unpacker.hpp +++ b/include/unpacker.hpp @@ -67,6 +67,9 @@ class unpack_t { static void invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, int size, int64_t value, unpack_t *unpack); + static void dep_read_watch(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}}, diff --git a/src/main.cpp b/src/main.cpp index 14a0ab2..6c0815b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -55,20 +55,54 @@ int __cdecl main(int argc, const char *argv[]) { auto img = reinterpret_cast *>(module_data.data()); auto image_size = img->get_nt_headers()->optional_header.size_image; + const auto image_base = img->get_nt_headers()->optional_header.image_base; + + // page align the vector allocation so that unicorn-engine is happy girl... + tmp.resize(image_size + PAGE_4KB); + const std::uintptr_t module_base = + reinterpret_cast(tmp.data()) + + (PAGE_4KB - (reinterpret_cast(tmp.data()) & 0xFFFull)); - tmp.resize(image_size); - std::memcpy(tmp.data(), module_data.data(), 0x1000); + std::memcpy((void *)module_base, 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); + std::memcpy( + (void *)(module_base + 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; + auto win_img = reinterpret_cast *>(module_base); + + auto basereloc_dir = + win_img->get_directory(win::directory_id::directory_entry_basereloc); + + auto reloc_dir = reinterpret_cast( + basereloc_dir->rva + module_base); + + 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 + module_base); + + *reloc_at = module_base + ((*reloc_at) - image_base); + break; + } + default: + break; + } + }); + + reloc_block = reloc_block->next(); + } std::printf("> image base = %p, image size = %p, module base = %p\n", image_base, image_size, module_base); diff --git a/src/unpacker.cpp b/src/unpacker.cpp index 605c084..0f58067 100644 --- a/src/unpacker.cpp +++ b/src/unpacker.cpp @@ -72,8 +72,10 @@ bool unpack_t::init(void) { 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... @@ -147,7 +149,7 @@ bool unpack_t::init(void) { // 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) { + std::for_each(sec_begin, sec_end, [&](const 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()) { @@ -158,7 +160,7 @@ bool unpack_t::init(void) { 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; } pack_section_offset = header.virtual_address + header.virtual_size; @@ -170,7 +172,7 @@ bool unpack_t::init(void) { 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; } } }); @@ -194,7 +196,7 @@ bool unpack_t::unpack(std::vector &output) { return false; } - std::printf("> beginning execution at = 0x%p\n", rip); + std::printf("> beginning execution at = %p\n", rip); if ((err = uc_emu_start(uc_ctx, rip, 0ull, 0ull, 0ull))) { std::printf("> error starting emu... reason = %d\n", err); @@ -345,6 +347,11 @@ void unpack_t::local_free_hook(uc_engine *uc_ctx, unpack_t *obj) { } } +void unpack_t::dep_read_watch(uc_engine *uc, uc_mem_type type, uint64_t address, + int size, int64_t value, unpack_t *unpack) { + std::printf("> reading address = %p, size = %d\n", address, size); +} + void unpack_t::load_library_hook(uc_engine *uc_ctx, unpack_t *obj) { uc_err err; std::uintptr_t rcx = 0ull; @@ -359,11 +366,12 @@ void unpack_t::load_library_hook(uc_engine *uc_ctx, unpack_t *obj) { std::printf("> LoadLibraryA(\"%s\")\n", buff); if (!obj->loaded_modules[buff]) { + std::printf("> loading library from disk...\n"); 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", + "as vmemu...\n", buff); exit(-1); } @@ -372,7 +380,7 @@ void unpack_t::load_library_hook(uc_engine *uc_ctx, unpack_t *obj) { auto image_size = img->get_nt_headers()->optional_header.size_image; tmp.resize(image_size); - std::memcpy(tmp.data(), module_data.data(), 0x1000); + std::memcpy(tmp.data(), module_data.data(), PAGE_4KB); std::for_each(img->get_nt_headers()->get_sections(), img->get_nt_headers()->get_sections() + img->get_nt_headers()->file_header.num_sections, @@ -385,15 +393,34 @@ void unpack_t::load_library_hook(uc_engine *uc_ctx, unpack_t *obj) { 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; + const auto alloc_addr = module_base & ~0xFFFull; obj->loaded_modules[buff] = alloc_addr; + if ((err = uc_mem_map(uc_ctx, alloc_addr, image_size, UC_PROT_ALL))) { + std::printf("> failed to load library... reason = %d\n", err); + return; + } + + if ((err = uc_mem_write(uc_ctx, alloc_addr, tmp.data(), image_size))) { + std::printf("> failed to copy module into emulator... reason = %d\n", + err); + return; + } + if ((err = uc_reg_write(uc_ctx, UC_X86_REG_RAX, &alloc_addr))) { std::printf("> failed to set rax... reason = %d\n", err); return; } + + obj->uc_hooks.push_back(new uc_hook); + uc_hook_add(uc_ctx, obj->uc_hooks.back(), UC_HOOK_MEM_READ, + (void *)&engine::unpack_t::dep_read_watch, obj, alloc_addr, + alloc_addr + image_size); + + std::printf("> mapped %s to base address %p\n", buff, alloc_addr); } else { const auto alloc_addr = obj->loaded_modules[buff]; + std::printf("> library already loaded... returning %p...\n", 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; @@ -427,21 +454,12 @@ bool unpack_t::iat_dispatcher(uc_engine *uc, uint64_t address, uint32_t size, 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 (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(vm::util::g_decoder.get(), + 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) { @@ -475,18 +493,22 @@ bool unpack_t::unpack_section_callback(uc_engine *uc, uc_mem_type type, 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", + 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: + } + 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 = " + ">>> 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 = 0x%p\n", + std::printf(">>> fetching invalid instructions at address = %p\n", address); break; } diff --git a/src/vmemu_t.cpp b/src/vmemu_t.cpp index 3297686..f696f55 100644 --- a/src/vmemu_t.cpp +++ b/src/vmemu_t.cpp @@ -80,7 +80,7 @@ bool emu_t::get_trace(std::vector &entries) { code_block_data_t code_block{{}, nullptr, nullptr}; cc_block = &code_block; - std::printf("> beginning execution at = 0x%p\n", rip); + std::printf("> beginning execution at = %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; @@ -137,7 +137,7 @@ bool emu_t::get_trace(std::vector &entries) { return false; } - std::printf("> beginning execution at = 0x%p\n", + std::printf("> beginning execution at = %p\n", _code_block.cpu_ctx->rip); if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull))) { @@ -194,7 +194,7 @@ bool emu_t::get_trace(std::vector &entries) { return false; } - std::printf("> beginning execution at = 0x%p\n", + std::printf("> beginning execution at = %p\n", _code_block.cpu_ctx->rip); if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull))) { @@ -255,7 +255,7 @@ bool emu_t::get_trace(std::vector &entries) { return false; } - std::printf("> beginning execution at = 0x%p\n", + std::printf("> beginning execution at = %p\n", _code_block.cpu_ctx->rip); if ((err = uc_emu_start(uc_ctx, _code_block.cpu_ctx->rip, 0ull, 0ull, 0ull))) { @@ -351,19 +351,11 @@ bool emu_t::code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, 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))) { + if (!ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(vm::util::g_decoder.get(), + reinterpret_cast(address), + PAGE_4KB, &instr))) { std::printf("> failed to decode instruction at = 0x%p\n", address); if ((err = uc_emu_stop(uc))) { @@ -387,6 +379,23 @@ bool emu_t::code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, return false; } + // skip calls entirely since the virtual machine will execute to make calls... + // only time calls legit happen are with the CALL handler used only by the + // packer... + 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 > obj->g_vm_ctx->module_base + obj->img_size || + rax < obj->g_vm_ctx->module_base) { + rip += instr.length; + uc_reg_write(uc, UC_X86_REG_RIP, &rip); + } + } + // 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... @@ -467,7 +476,7 @@ bool emu_t::code_exec_callback(uc_engine *uc, uint64_t address, uint32_t size, if (!vm_handler.profile) { if (!g_force_emu) obj->cc_block = nullptr; - std::printf("> please define virtual machine handler (0x%p): \n\n", + std::printf("> please define virtual machine handler (%p): \n\n", (vm_handler_addr - obj->g_vm_ctx->module_base) + obj->g_vm_ctx->image_base); @@ -641,20 +650,20 @@ void emu_t::invalid_mem(uc_engine *uc, uc_mem_type type, uint64_t address, 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", + 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 = 0x%p, size = 0x%x, val = " + ">>> 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 = 0x%p\n", + std::printf(">>> fetching invalid instructions at address = %p\n", address); break; }