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