everything works... still need to add JCC/JMP support...

2.0
_xeroxz 4 years ago
parent 2106099c68
commit e98a543f14

@ -91,6 +91,10 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="DriverEntry.c" />
<ClCompile Include="ObfuscateDemo.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Theodosius.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

@ -5,13 +5,21 @@
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header">
<UniqueIdentifier>{6ad835cd-061a-454e-b69a-e064070a3bc2}</UniqueIdentifier>
<Filter Include="Header Files">
<UniqueIdentifier>{92e25b44-aaeb-40a2-b8c9-7eab6c210e8d}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="DriverEntry.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="ObfuscateDemo.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Theodosius.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

@ -1,8 +1,7 @@
#include <intrin.h>
#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());
}

@ -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;
}

@ -0,0 +1,21 @@
#pragma once
#include <intrin.h>
#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;

@ -10,40 +10,72 @@ namespace drv
auto hmdm_ctx::map_objs(std::vector<lnk::obj_buffer_t>& objs) -> image_entry_t
{
std::map<std::string, std::uintptr_t> 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<std::uintptr_t>(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<lnk::obj_buffer_t>& 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<void*>(
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<lnk::obj_buffer_t>& 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<std::uintptr_t*>(
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<lnk::obj_buffer_t>& objs)
{
for (auto& obj : objs)
{
for (auto symbol : lnk::sym::get_all(obj))
{
const auto symbol_mapped =
reinterpret_cast<void*>(
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<std::uintptr_t*>(
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<std::uint8_t> 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<void*>(
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<std::uint8_t> 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<std::uintptr_t*>(
&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<std::uintptr_t*>(
&final_instruction[instruction.length + JMP_RIP_ADDR_IDX]));
const auto instruc_alloc =
reinterpret_cast<void*>(
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<lnk::obj_buffer_t>& 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<std::uintptr_t>(
kalloc(instruction.length + (JMP_RIP_SIZE * 2)));
break;
}
case ZYDIS_MNEMONIC_JMP:
{
mapped_symbols[symbol_name] =
reinterpret_cast<std::uintptr_t>(
kalloc(JMP_RIP_SIZE));
break;
}
case ZYDIS_MNEMONIC_RET:
{
mapped_symbols[symbol_name] =
reinterpret_cast<std::uintptr_t>(
kalloc(instruction.length));
break;
}
default: // not a JCC, JMP, or RET...
{
mapped_symbols[symbol_name] =
reinterpret_cast<std::uintptr_t>(
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<lnk::obj_buffer_t>& 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<std::uintptr_t>(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);
}
}
}
}

@ -9,6 +9,13 @@
#include <winternl.h>
#include <type_traits>
#include <dbghelp.h>
#include <string>
#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<kalloc_t, kmemcpy_t>;
class hmdm_ctx
{
public:
explicit hmdm_ctx(const mapper_routines_t& routines);
auto map_objs(std::vector<lnk::obj_buffer_t>& objs) -> image_entry_t;
const kalloc_t kalloc;
const kmemcpy_t kmemcpy;
kalloc_t kalloc;
kmemcpy_t kmemcpy;
private:
bool map_symbols(std::vector<lnk::obj_buffer_t>& objs);
bool map_obfuscated_symbols(std::vector<lnk::obj_buffer_t>& objs);
bool resolve_relocs(std::vector<lnk::obj_buffer_t>& objs);
bool alloc_obfuscated_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
bool alloc_symbol_space(std::vector<lnk::obj_buffer_t>& objs);
std::map<std::string, std::uintptr_t> mapped_symbols;
};
}

@ -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;

@ -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"

@ -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<decltype(&memcpy)>(
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();
}

@ -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<std::uintptr_t>(
GetProcAddress(module_base, export_name)) -
reinterpret_cast<std::uintptr_t>(module_base);
GetProcAddress(module_base, export_name));
return image_base + rva;
if (!export_um_addr)
return NULL;
return (export_um_addr - reinterpret_cast<std::uintptr_t>(module_base)) + image_base;
}
}

Loading…
Cancel
Save