diff --git a/DemoDrv/DemoDrv.vcxproj b/DemoDrv/DemoDrv.vcxproj index 2a8fca4..3b4d2e6 100644 --- a/DemoDrv/DemoDrv.vcxproj +++ b/DemoDrv/DemoDrv.vcxproj @@ -91,6 +91,10 @@ + + + + diff --git a/DemoDrv/DemoDrv.vcxproj.filters b/DemoDrv/DemoDrv.vcxproj.filters index 309d408..c7f29fe 100644 --- a/DemoDrv/DemoDrv.vcxproj.filters +++ b/DemoDrv/DemoDrv.vcxproj.filters @@ -5,13 +5,21 @@ {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - {6ad835cd-061a-454e-b69a-e064070a3bc2} + + {92e25b44-aaeb-40a2-b8c9-7eab6c210e8d} Source Files + + Source Files + + + + + Header Files + \ No newline at end of file diff --git a/DemoDrv/DriverEntry.c b/DemoDrv/DriverEntry.c index ea41864..514798d 100644 --- a/DemoDrv/DriverEntry.c +++ b/DemoDrv/DriverEntry.c @@ -1,8 +1,7 @@ -#include -#define ObfiscateRoutine __declspec(code_seg(".theo")) -unsigned long DbgPrint(const char* format, ...); +#include "Theodosius.h" int drv_entry() { DbgPrint("> hello world! this is a demo!\n"); + DbgPrint("> current pml4 = 0x%p\n", get_dirbase()); } \ No newline at end of file diff --git a/DemoDrv/ObfuscateDemo.c b/DemoDrv/ObfuscateDemo.c new file mode 100644 index 0000000..6961b07 --- /dev/null +++ b/DemoDrv/ObfuscateDemo.c @@ -0,0 +1,14 @@ +#include "Theodosius.h" + +ObfuscateRoutine +unsigned long long get_dirbase() +{ + cr3 result; + result.flags = + *(unsigned long long*)(IoGetCurrentProcess() + 0x28); + + if (!result.address_of_page_directory) + return -1; + + return result.address_of_page_directory << 12; +} \ No newline at end of file diff --git a/DemoDrv/Theodosius.h b/DemoDrv/Theodosius.h new file mode 100644 index 0000000..44c8acd --- /dev/null +++ b/DemoDrv/Theodosius.h @@ -0,0 +1,21 @@ +#pragma once +#include +#define ObfuscateRoutine __declspec(code_seg(".theo")) + +unsigned long DbgPrint(const char* format, ...); +unsigned long long IoGetCurrentProcess(); +unsigned long long get_dirbase(); + +typedef union +{ + struct + { + unsigned long long reserved1 : 3; + unsigned long long page_level_write_through : 1; + unsigned long long page_level_cache_disable : 1; + unsigned long long reserved2 : 7; + unsigned long long address_of_page_directory : 36; + unsigned long long reserved3 : 16; + }; + unsigned long long flags; +} cr3; \ No newline at end of file diff --git a/Theodosius/hmdm_ctx.cpp b/Theodosius/hmdm_ctx.cpp index 557073b..88374a5 100644 --- a/Theodosius/hmdm_ctx.cpp +++ b/Theodosius/hmdm_ctx.cpp @@ -10,40 +10,72 @@ namespace drv auto hmdm_ctx::map_objs(std::vector& objs) -> image_entry_t { - std::map mapped_symbols; - // for each obj, allocate space for each symbol and create a map of where - // these symbols will be in memory... - for (auto obj : objs) + if (!alloc_symbol_space(objs)) { - for (auto symbol : lnk::sym::get_all(obj)) - { - mapped_symbols[symbol.symbol_name] = - reinterpret_cast(kalloc(symbol.size)); + std::printf("> failed to allocate symbol space...\n"); + return {}; + } - std::printf("> %s allocated at = 0x%p, size = %d\n", - symbol.symbol_name.c_str(), mapped_symbols[symbol.symbol_name], symbol.size); - } + if (!alloc_obfuscated_symbol_space(objs)) + { + std::printf("> failed to allocate space for obfuscated functions...\n"); + return {}; } - try + if (!map_obfuscated_symbols(objs)) { - if (!mapped_symbols["drv_entry"]) - { - std::printf("> no symbol named drv_entry... (entry point must be named drv_entry)...\n"); - return {}; - } + std::printf("> failed to resolve obfuscated relocs...\n"); + return {}; + } + + if (!resolve_relocs(objs)) + { + std::printf("> failed to resolve relocations...\n"); + return {}; } - catch (std::exception& e) + + if (!map_symbols(objs)) { - std::printf("> no symbol named drv_entry... (entry point must be named drv_entry)...\n"); + std::printf("> failed to map symbols into memory...\n"); return {}; } - // resolve relocations and "imports"... - // if a relocation to a symbol is not found inside of the hashmap of symbols - // created in the last for loop (above ^) then its going to try and - // resolve the symbol as an ntoskrnl import... - for (auto obj : objs) + return mapped_symbols["drv_entry"]; + } + + bool hmdm_ctx::map_symbols(std::vector& objs) + { + for (auto& obj : objs) + { + for (auto symbol : lnk::sym::get_all(obj)) + { + // dont map obfuscated routines into memory as they + // get mapped differently... + if (symbol.obfuscate_routine) + continue; + + const auto symbol_mapped = + reinterpret_cast( + mapped_symbols[symbol.symbol_name]); + + if (!symbol_mapped) + { + std::printf("> failed to resolve symbol allocation = %s\n", + symbol.symbol_name.c_str()); + + return false; + } + + kmemcpy(symbol_mapped, + obj.data() + symbol.file_offset, symbol.size); + } + } + return true; + } + + bool hmdm_ctx::resolve_relocs(std::vector& objs) + { + for (auto& obj : objs) { for (auto reloc : lnk::sym::get_relocs(obj)) { @@ -52,22 +84,26 @@ namespace drv std::printf("> error... unsupported relocation at file offset = 0x%x\n\t> symbol = %s\n", reloc.file_offset, reloc.resolve_symbol_name.c_str()); - return {}; + return false; } - const auto reloc_addr = + const auto reloc_addr = reinterpret_cast( obj.data() + reloc.file_offset); if (mapped_symbols[reloc.resolve_symbol_name]) { + std::printf("> resolved relocation %s = 0x%p\n", + reloc.resolve_symbol_name.c_str(), + mapped_symbols[reloc.resolve_symbol_name]); + // patch kernel address into relocation... *reloc_addr = mapped_symbols[reloc.resolve_symbol_name]; } else { // TODO: parse PDB for kernel driver symbols... - const auto ntoskrnl_symbol = + const auto ntoskrnl_symbol = utils::kmodule::get_export( "ntoskrnl.exe", reloc.resolve_symbol_name.c_str()); @@ -76,9 +112,10 @@ namespace drv if (!ntoskrnl_symbol) { - std::printf("> brutal! unresolved external symbol = %s\n", + std::printf("> brutal! unresolved external symbol = %s\n", reloc.resolve_symbol_name.c_str()); - return {}; + + return false; } // resolve ntoskrnl exports for now... @@ -86,21 +123,292 @@ namespace drv } } } + return true; + } - // copy each symbol into memory now... - for (auto obj : objs) + bool hmdm_ctx::map_obfuscated_symbols(std::vector& objs) + { + for (auto& obj : objs) { for (auto symbol : lnk::sym::get_all(obj)) { - const auto symbol_mapped = - reinterpret_cast( - mapped_symbols[symbol.symbol_name]); + if (!symbol.obfuscate_routine) + continue; - kmemcpy(symbol_mapped, - obj.data() + symbol.file_offset, symbol.size); + std::printf("> resolving obfuscated relocations for routine = %s\n", symbol.symbol_name.c_str()); + + // fix relocations inside of this obfuscated routine... + for (auto reloc : lnk::sym::get_relocs(obj)) + { + // if the relocation lands inside of this symbol then we resolve it right now... + if (reloc.file_offset >= symbol.file_offset && + reloc.file_offset < symbol.file_offset + symbol.size) + { + if (reloc.type != IMAGE_REL_AMD64_ADDR64) + { + std::printf("> error... unsupported relocation at file offset = 0x%x\n\t> symbol = %s\n", + reloc.file_offset, reloc.resolve_symbol_name.c_str()); + + return false; + } + + const auto reloc_addr = + reinterpret_cast( + obj.data() + reloc.file_offset); + + if (mapped_symbols[reloc.resolve_symbol_name]) + { + std::printf(" > resolved obfuscated relocation %s = 0x%p\n", + reloc.resolve_symbol_name.c_str(), + mapped_symbols[reloc.resolve_symbol_name]); + + // patch kernel address into relocation... + *reloc_addr = mapped_symbols[reloc.resolve_symbol_name]; + } + else + { + // TODO: parse PDB for kernel driver symbols... + const auto ntoskrnl_symbol = + utils::kmodule::get_export( + "ntoskrnl.exe", reloc.resolve_symbol_name.c_str()); + + std::printf(" > resolved obfuscated external symbol %s = 0x%p\n", + reloc.resolve_symbol_name.c_str(), ntoskrnl_symbol); + + if (!ntoskrnl_symbol) + { + std::printf("> brutal! unresolved external symbol = %s\n", + reloc.resolve_symbol_name.c_str()); + + return false; + } + + // resolve ntoskrnl exports for now... + *reloc_addr = ntoskrnl_symbol; + } + } + } + + ZydisDecoder decoder; + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); + + ZyanUSize offset = 0; + ZyanUSize length = symbol.size; + ZydisDecodedInstruction instruction; + + const auto routine_begin = symbol.file_offset + obj.data(); + bool first_instruction = true; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, routine_begin + offset, + length - offset, &instruction))) + { + auto symbol_name = symbol.symbol_name; + auto next_instruction_symbol = symbol.symbol_name; + + next_instruction_symbol.append("@").append( + std::to_string(offset + instruction.length)); + + if (first_instruction) + first_instruction = false; + else + symbol_name.append("@") + .append(std::to_string(offset)); + + switch (instruction.mnemonic) + { + case ZYDIS_MNEMONIC_JB: + case ZYDIS_MNEMONIC_JBE: + case ZYDIS_MNEMONIC_JCXZ: + case ZYDIS_MNEMONIC_JECXZ: + case ZYDIS_MNEMONIC_JKNZD: + case ZYDIS_MNEMONIC_JKZD: + case ZYDIS_MNEMONIC_JL: + case ZYDIS_MNEMONIC_JLE: + case ZYDIS_MNEMONIC_JNB: + case ZYDIS_MNEMONIC_JNBE: + case ZYDIS_MNEMONIC_JNL: + case ZYDIS_MNEMONIC_JNLE: + case ZYDIS_MNEMONIC_JNO: + case ZYDIS_MNEMONIC_JNP: + case ZYDIS_MNEMONIC_JNS: + case ZYDIS_MNEMONIC_JNZ: + case ZYDIS_MNEMONIC_JO: + case ZYDIS_MNEMONIC_JP: + case ZYDIS_MNEMONIC_JRCXZ: + case ZYDIS_MNEMONIC_JS: + case ZYDIS_MNEMONIC_JZ: + { + + break; + } + case ZYDIS_MNEMONIC_JMP: + { + + break; + } + case ZYDIS_MNEMONIC_RET: + { + std::vector final_instruction; + final_instruction.resize(instruction.length); + + // copy instruction into buffer... + memcpy(final_instruction.data(), + obj.data() + symbol.file_offset + offset, instruction.length); + + const auto instruc_alloc = + reinterpret_cast( + mapped_symbols[symbol_name]); + + // copy the instruction into memory... + kmemcpy(instruc_alloc, final_instruction.data(), final_instruction.size()); + break; + } + default: // not a JCC, JMP, or RET... + { + std::vector final_instruction; + // resize buffer so that jmp [rip] can fit... + final_instruction.resize(instruction.length + JMP_RIP_SIZE); + + // copy instruction into buffer... + memcpy(final_instruction.data(), + obj.data() + symbol.file_offset + offset, instruction.length); + + // copy jmp [rip] after instruction... + memcpy(&final_instruction[instruction.length], jmp_rip, sizeof jmp_rip); + + // copy address to jmp to (next instruction)... + *reinterpret_cast( + &final_instruction[instruction.length + JMP_RIP_ADDR_IDX]) = + mapped_symbols[next_instruction_symbol]; + + std::printf(" > next instruction symbol = %s, address = 0x%p\n", + next_instruction_symbol, *reinterpret_cast( + &final_instruction[instruction.length + JMP_RIP_ADDR_IDX])); + + const auto instruc_alloc = + reinterpret_cast( + mapped_symbols[symbol_name]); + + // copy the instruction and jmp into memory... + kmemcpy(instruc_alloc, final_instruction.data(), final_instruction.size()); + break; + } + } + offset += instruction.length; + } } } + return true; + } - return mapped_symbols["drv_entry"]; + bool hmdm_ctx::alloc_obfuscated_symbol_space(std::vector& objs) + { + for (auto& obj : objs) + { + for (auto symbol : lnk::sym::get_all(obj)) + { + // skip obfuscated routines for now... those get scattered... + if (!symbol.obfuscate_routine) + continue; + + ZydisDecoder decoder; + ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64); + + ZyanUSize offset = 0; + ZyanUSize length = symbol.size; + ZydisDecodedInstruction instruction; + + const auto routine_begin = symbol.file_offset + obj.data(); + bool first_instruction = true; + + while (ZYAN_SUCCESS(ZydisDecoderDecodeBuffer(&decoder, routine_begin + offset, length - offset, + &instruction))) + { + auto symbol_name = symbol.symbol_name; + if (first_instruction) + first_instruction = false; + else + symbol_name.append("@") + .append(std::to_string(offset)); + + switch (instruction.mnemonic) + { + case ZYDIS_MNEMONIC_JB: + case ZYDIS_MNEMONIC_JBE: + case ZYDIS_MNEMONIC_JCXZ: + case ZYDIS_MNEMONIC_JECXZ: + case ZYDIS_MNEMONIC_JKNZD: + case ZYDIS_MNEMONIC_JKZD: + case ZYDIS_MNEMONIC_JL: + case ZYDIS_MNEMONIC_JLE: + case ZYDIS_MNEMONIC_JNB: + case ZYDIS_MNEMONIC_JNBE: + case ZYDIS_MNEMONIC_JNL: + case ZYDIS_MNEMONIC_JNLE: + case ZYDIS_MNEMONIC_JNO: + case ZYDIS_MNEMONIC_JNP: + case ZYDIS_MNEMONIC_JNS: + case ZYDIS_MNEMONIC_JNZ: + case ZYDIS_MNEMONIC_JO: + case ZYDIS_MNEMONIC_JP: + case ZYDIS_MNEMONIC_JRCXZ: + case ZYDIS_MNEMONIC_JS: + case ZYDIS_MNEMONIC_JZ: + { + mapped_symbols[symbol_name] = + reinterpret_cast( + kalloc(instruction.length + (JMP_RIP_SIZE * 2))); + break; + } + case ZYDIS_MNEMONIC_JMP: + { + mapped_symbols[symbol_name] = + reinterpret_cast( + kalloc(JMP_RIP_SIZE)); + break; + } + case ZYDIS_MNEMONIC_RET: + { + mapped_symbols[symbol_name] = + reinterpret_cast( + kalloc(instruction.length)); + break; + } + default: // not a JCC, JMP, or RET... + { + mapped_symbols[symbol_name] = + reinterpret_cast( + kalloc(instruction.length + JMP_RIP_SIZE)); + break; + } + } + + std::printf(" > %s allocated = 0x%p\n", + symbol_name.c_str(), mapped_symbols[symbol_name]); + + offset += instruction.length; + } + } + } + return true; + } + + bool hmdm_ctx::alloc_symbol_space(std::vector& objs) + { + for (auto& obj : objs) + { + for (auto symbol : lnk::sym::get_all(obj)) + { + // skip obfuscated routines for now... those get scattered... + if (symbol.obfuscate_routine) + continue; + + mapped_symbols[symbol.symbol_name] = + reinterpret_cast(kalloc(symbol.size)); + + std::printf("> %s allocated at = 0x%p, size = %d\n", + symbol.symbol_name.c_str(), mapped_symbols[symbol.symbol_name], symbol.size); + } + } } } \ No newline at end of file diff --git a/Theodosius/hmdm_ctx.h b/Theodosius/hmdm_ctx.h index 8a21f99..49f2ff2 100644 --- a/Theodosius/hmdm_ctx.h +++ b/Theodosius/hmdm_ctx.h @@ -9,6 +9,13 @@ #include #include #include +#include + +#define JMP_RIP_SIZE 14 +#define JMP_RIP_ADDR_IDX 6 + +inline const std::uint8_t jmp_rip[] = + { 0xFF, 0x25, 0x00, 0x00, 0x00, 0x00 }; #pragma comment(lib, "Dbghelp.lib") namespace drv @@ -19,15 +26,22 @@ namespace drv using image_entry_t = std::uintptr_t; using mapper_routines_t = std::pair; - + class hmdm_ctx { public: explicit hmdm_ctx(const mapper_routines_t& routines); auto map_objs(std::vector& objs) -> image_entry_t; - const kalloc_t kalloc; - const kmemcpy_t kmemcpy; + kalloc_t kalloc; + kmemcpy_t kmemcpy; private: + bool map_symbols(std::vector& objs); + bool map_obfuscated_symbols(std::vector& objs); + + bool resolve_relocs(std::vector& objs); + bool alloc_obfuscated_symbol_space(std::vector& objs); + bool alloc_symbol_space(std::vector& objs); + std::map mapped_symbols; }; } \ No newline at end of file diff --git a/Theodosius/linker/linker.cpp b/Theodosius/linker/linker.cpp index fc3f285..d33a6ec 100644 --- a/Theodosius/linker/linker.cpp +++ b/Theodosius/linker/linker.cpp @@ -175,6 +175,7 @@ namespace lnk std::string(string_table + symbol_table[idx].N.Name.Long); + // skip over aux symbols... if (symbol_table[idx].NumberOfAuxSymbols) ++idx; @@ -186,6 +187,13 @@ namespace lnk symbol.type = symbol_table[idx].Type; symbol.size = get_symbol_size(symbol, obj); + if (!strncmp((char*)section_headers[ + symbol_table[idx].SectionNumber - 1].Name, ".theo", strlen(".theo") - 1)) + symbol.obfuscate_routine = true; + else + symbol.obfuscate_routine = false; + + // there can be more then one aux symbols... if (symbol_table[idx].NumberOfAuxSymbols) idx += symbol_table[idx].NumberOfAuxSymbols; diff --git a/Theodosius/linker/linker.hpp b/Theodosius/linker/linker.hpp index 2b13564..5963f4e 100644 --- a/Theodosius/linker/linker.hpp +++ b/Theodosius/linker/linker.hpp @@ -28,6 +28,9 @@ namespace lnk // only used by functions... size in bytes of routine... std::uint32_t size; + + // if this symbol is a function and is inside of a .theo section... + bool obfuscate_routine; }; // redef of IMAGE_RELOCATION so that "VirtualAddress" diff --git a/Theodosius/main.cpp b/Theodosius/main.cpp index 901f7d9..002190a 100644 --- a/Theodosius/main.cpp +++ b/Theodosius/main.cpp @@ -27,7 +27,8 @@ int main(int argc, char** argv) } std::printf("> number of objs = %d\n", image_objs.size()); - const auto [drv_handle, drv_key, drv_status] = vdm::load_drv(); + /*const auto [drv_handle, drv_key, drv_status] = vdm::load_drv(); + if (drv_status != STATUS_SUCCESS || drv_handle == INVALID_HANDLE_VALUE) { std::printf("> failed to load driver... reason -> 0x%x\n", drv_status); @@ -44,7 +45,9 @@ int main(int argc, char** argv) drv::kalloc_t _kalloc = [&](std::size_t size) -> void* { void* alloc_base; - msrexec.exec([&](void* krnl_base, get_system_routine_t get_kroutine) -> void + msrexec.exec + ( + [&](void* krnl_base, get_system_routine_t get_kroutine) -> void { using ex_alloc_pool_t = void* (*)(std::uint32_t, std::size_t); @@ -54,7 +57,8 @@ int main(int argc, char** argv) get_kroutine(krnl_base, "ExAllocatePool")); alloc_base = ex_alloc_pool(NULL, size); - }); + } + ); return alloc_base; }; @@ -62,15 +66,29 @@ int main(int argc, char** argv) [&](void* dest, const void* src, std::size_t size) -> void* { void* result = nullptr; - msrexec.exec([&](void* krnl_base, get_system_routine_t get_kroutine) -> void + msrexec.exec + ( + [&](void* krnl_base, get_system_routine_t get_kroutine) -> void { const auto kmemcpy = reinterpret_cast( get_kroutine(krnl_base, "memcpy")); result = kmemcpy(dest, src, size); - }); + } + ); return result; + };*/ + + drv::kmemcpy_t _kmemcpy = + [&](void* dest, const void* src, std::size_t size) -> void* + { + return memcpy(dest, src, size); + }; + + drv::kalloc_t _kalloc = [&](std::size_t size) -> void* + { + return malloc(size); }; drv::hmdm_ctx drv_mapper({ _kalloc, _kmemcpy }); @@ -79,7 +97,7 @@ int main(int argc, char** argv) std::printf("\n\n> driver entry -> 0x%p\n", drv_entry); std::getchar(); - int result; + /*int result; msrexec.exec([&result, drv_entry = drv_entry] (void* krnl_base, get_system_routine_t get_kroutine) -> void { @@ -92,7 +110,8 @@ int main(int argc, char** argv) { std::printf("> failed to unload driver... reason -> 0x%x\n", unload_status); return -1; - } + }*/ + std::printf("> press enter to close...\n"); std::getchar(); } \ No newline at end of file diff --git a/Theodosius/utils.hpp b/Theodosius/utils.hpp index 9566443..f7dbcfc 100644 --- a/Theodosius/utils.hpp +++ b/Theodosius/utils.hpp @@ -291,12 +291,14 @@ namespace utils // free the RTL_PROCESS_MODULES buffer... VirtualFree(buffer, NULL, MEM_RELEASE); - const auto rva = + const auto export_um_addr = reinterpret_cast( - GetProcAddress(module_base, export_name)) - - reinterpret_cast(module_base); + GetProcAddress(module_base, export_name)); - return image_base + rva; + if (!export_um_addr) + return NULL; + + return (export_um_addr - reinterpret_cast(module_base)) + image_base; } }