You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
376 lines
10 KiB
376 lines
10 KiB
#include "theo.h"
|
|
|
|
namespace theo
|
|
{
|
|
hmm_ctx::hmm_ctx(const mapper_routines_t& routines)
|
|
:
|
|
kalloc(std::get<0>(routines)),
|
|
kmemcpy(std::get<1>(routines)),
|
|
resolve_symbol(std::get<2>(routines))
|
|
{}
|
|
|
|
auto hmm_ctx::map_objs(std::vector<lnk::obj_buffer_t>& objs) -> image_entry_t
|
|
{
|
|
std::printf("[+] allocating space for symbols...\n");
|
|
if (!alloc_symbol_space(objs))
|
|
{
|
|
std::printf("[!] failed to allocate symbol space...\n");
|
|
return {};
|
|
}
|
|
|
|
std::printf("[+] allocating space for obfuscated symbols...\n");
|
|
if (!alloc_obfuscated_symbol_space(objs))
|
|
{
|
|
std::printf("[!] failed to allocate space for obfuscated functions...\n");
|
|
return {};
|
|
}
|
|
|
|
std::printf("[+] mapping obfuscated symbols...\n");
|
|
if (!map_obfuscated_symbols(objs))
|
|
{
|
|
std::printf("[!] failed to resolve obfuscated relocs...\n");
|
|
return {};
|
|
}
|
|
|
|
std::printf("[+] resolving non-obfuscated relocations...\n");
|
|
if (!resolve_relocs(objs))
|
|
{
|
|
std::printf("[!] failed to resolve relocations...\n");
|
|
return {};
|
|
}
|
|
|
|
std::printf("[+] mapping non-obfuscated symbols...\n");
|
|
if (!map_symbols(objs))
|
|
{
|
|
std::printf("> failed to map symbols into memory...\n");
|
|
return {};
|
|
}
|
|
|
|
std::printf("[+] linking complete...\n");
|
|
return mapped_symbols["ModuleEntry"];
|
|
}
|
|
|
|
bool hmm_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_type)
|
|
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;
|
|
}
|
|
|
|
std::printf(" > mapping symbol = %s, at = 0x%p\n",
|
|
symbol.symbol_name.c_str(), symbol_mapped);
|
|
|
|
kmemcpy(symbol_mapped, obj.data() +
|
|
symbol.file_offset, symbol.size);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hmm_ctx::resolve_relocs(std::vector<lnk::obj_buffer_t>& objs)
|
|
{
|
|
for (auto& obj : objs)
|
|
{
|
|
for (auto& reloc : lnk::sym::get_relocs(obj))
|
|
{
|
|
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);
|
|
|
|
// check obj symbol table for this relocation...
|
|
if (mapped_symbols[reloc.resolve_symbol_name])
|
|
{
|
|
std::printf(" > resolving internal symbol...\n");
|
|
std::printf(" > address = 0x%p\n", mapped_symbols[reloc.resolve_symbol_name]);
|
|
std::printf(" > symbol = %s\n", reloc.resolve_symbol_name.c_str());
|
|
*reloc_addr = mapped_symbols[reloc.resolve_symbol_name];
|
|
}
|
|
else // else check external symbol table...
|
|
{
|
|
const auto extern_symbol =
|
|
resolve_symbol(reloc.resolve_symbol_name.c_str());
|
|
|
|
if (!extern_symbol)
|
|
{
|
|
std::printf("[!] unresolved external symbol = %s...\n",
|
|
reloc.resolve_symbol_name.c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
*reloc_addr = extern_symbol;
|
|
}
|
|
|
|
std::printf(" > resolving external symbol...\n");
|
|
std::printf(" > address = 0x%p\n", *reloc_addr);
|
|
std::printf(" > symbol = %s\n", reloc.resolve_symbol_name.c_str());
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hmm_ctx::map_obfuscated_symbols(std::vector<lnk::obj_buffer_t>& objs)
|
|
{
|
|
for (auto& obj : objs)
|
|
{
|
|
for (auto& symbol : lnk::sym::get_all(obj))
|
|
{
|
|
if (!symbol.obfuscate_type)
|
|
continue;
|
|
|
|
std::printf(" > mapping obfuscated routine %s into memory...\n", symbol.symbol_name.c_str());
|
|
std::int32_t instruc_offset = 0u;
|
|
|
|
while (true) // TODO: this is bad code... dont do this!
|
|
{
|
|
auto symbol_name = symbol.symbol_name;
|
|
|
|
if (instruc_offset)
|
|
symbol_name.append("@")
|
|
.append(std::to_string(instruc_offset));
|
|
|
|
// if there is no allocation for this symbol then we are done...
|
|
if (!mapped_symbols[symbol_name])
|
|
break;
|
|
|
|
const auto instruc_len = obfuscated_gadgets
|
|
[mapped_symbols[symbol_name]]->get_instruc().length;
|
|
|
|
auto gadget_stack = obfuscated_gadgets
|
|
[mapped_symbols[symbol_name]]->get_gadget();
|
|
|
|
const auto gadget_size = obfuscated_gadgets
|
|
[mapped_symbols[symbol_name]]->get_size();
|
|
|
|
unsigned gadget_offset = 0u;
|
|
std::vector<std::uint8_t> gadget_raw;
|
|
|
|
for (auto& [gadget, reloc] : gadget_stack)
|
|
{
|
|
const auto fix_reloc_addr =
|
|
gadget.data() + reloc.offset;
|
|
|
|
switch (reloc.type)
|
|
{
|
|
case obfuscation::reloc_type::jcc:
|
|
{
|
|
const auto next_instruc_symbol =
|
|
std::string(symbol.symbol_name).append("@")
|
|
.append(std::to_string(instruc_offset + instruc_len + reloc.rva));
|
|
|
|
*reinterpret_cast<std::uintptr_t*>(fix_reloc_addr) =
|
|
mapped_symbols[next_instruc_symbol];
|
|
break;
|
|
}
|
|
case obfuscation::reloc_type::next_instruction_addr:
|
|
{
|
|
const auto next_instruc_symbol =
|
|
std::string(symbol.symbol_name).append("@")
|
|
.append(std::to_string(instruc_offset + instruc_len));
|
|
|
|
*reinterpret_cast<std::uintptr_t*>(fix_reloc_addr) =
|
|
mapped_symbols[next_instruc_symbol];
|
|
|
|
break; // we resolved our own relocation...
|
|
}
|
|
case obfuscation::reloc_type::none:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// check this instruction to see if it needs any relocs...
|
|
for (auto& reloc : lnk::sym::get_relocs(obj))
|
|
{
|
|
if (reloc.file_offset >= symbol.file_offset + instruc_offset &&
|
|
reloc.file_offset < symbol.file_offset + instruc_offset + instruc_len)
|
|
{
|
|
std::printf(" > resolving relocation for instruction...\n");
|
|
if (reloc.type != IMAGE_REL_AMD64_ADDR64)
|
|
{
|
|
std::printf("[!] error, cannot resolve reloc = %s, type = 0x%x\n",
|
|
reloc.resolve_symbol_name.c_str(), reloc.type);
|
|
|
|
// cant relocate anything but IMAGE_REL_AMD64_ADDR64...
|
|
// this is fine since the compiler shouldnt ever make any rip relative code
|
|
// besides JCC's...
|
|
return false;
|
|
}
|
|
|
|
const auto reloc_instruc_offset =
|
|
reloc.file_offset - (symbol.file_offset + instruc_offset);
|
|
|
|
const auto reloc_addr =
|
|
reinterpret_cast<std::uintptr_t*>(
|
|
gadget.data() + reloc_instruc_offset);
|
|
|
|
// check obj symbol table for this relocation...
|
|
if (mapped_symbols[reloc.resolve_symbol_name])
|
|
{
|
|
*reloc_addr = mapped_symbols[reloc.resolve_symbol_name];
|
|
}
|
|
else // else check external symbol table...
|
|
{
|
|
const auto extern_symbol =
|
|
resolve_symbol(reloc.resolve_symbol_name.c_str());
|
|
|
|
if (!extern_symbol)
|
|
{
|
|
std::printf("[!] unresolved external symbol = %s...\n",
|
|
reloc.resolve_symbol_name.c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
*reloc_addr = extern_symbol;
|
|
}
|
|
|
|
std::printf(" > address = 0x%p\n", *reloc_addr);
|
|
std::printf(" > symbol = %s\n", reloc.resolve_symbol_name.c_str());
|
|
break; // break out of for loop... we resolve the symbol...
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gadget_raw.insert(gadget_raw.end(), gadget.begin(), gadget.end());
|
|
gadget_offset += gadget.size();
|
|
}
|
|
|
|
const auto gadget_addr =
|
|
reinterpret_cast<void*>(
|
|
mapped_symbols[symbol_name]);
|
|
|
|
std::printf(" > copying gadget at = 0x%p\n", gadget_addr);
|
|
kmemcpy(gadget_addr, gadget_raw.data(), gadget_raw.size());
|
|
// used to calc symbol for next instruction...
|
|
instruc_offset += instruc_len;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hmm_ctx::alloc_obfuscated_symbol_space(std::vector<lnk::obj_buffer_t>& objs)
|
|
{
|
|
ZydisDecoder decoder;
|
|
ZydisDecoderInit(&decoder, ZYDIS_MACHINE_MODE_LONG_64, ZYDIS_ADDRESS_WIDTH_64);
|
|
|
|
for (auto& obj : objs)
|
|
{
|
|
for (auto& symbol : lnk::sym::get_all(obj))
|
|
{
|
|
// skip normal routines for now... those get scattered...
|
|
if (!symbol.obfuscate_type)
|
|
continue;
|
|
|
|
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)))
|
|
{
|
|
// dont append @offset for the first instruction...
|
|
auto new_symbol = symbol.symbol_name;
|
|
|
|
if (first_instruction)
|
|
first_instruction = false;
|
|
else
|
|
new_symbol.append("@")
|
|
.append(std::to_string(offset));
|
|
|
|
std::vector<std::uint8_t> instruc_bytes{};
|
|
instruc_bytes.resize(instruction.length);
|
|
|
|
memcpy(instruc_bytes.data(), obj.data() +
|
|
symbol.file_offset + offset, instruction.length);
|
|
|
|
std::shared_ptr<obfuscation::obfuscate> new_gadget{};
|
|
switch (symbol.obfuscate_type)
|
|
{
|
|
case lnk::theo_type::obfuscate:
|
|
{
|
|
new_gadget.reset(
|
|
new obfuscation::obfuscate(
|
|
{ instruction, instruc_bytes }));
|
|
break;
|
|
}
|
|
case lnk::theo_type::mutate:
|
|
{
|
|
new_gadget.reset(
|
|
new obfuscation::mutation(
|
|
{ instruction, instruc_bytes }));
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
std::printf("[!] unsupported obfuscation type on routine = %s, type = %d\n",
|
|
symbol.symbol_name.c_str(), symbol.obfuscate_type);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
mapped_symbols[new_symbol] =
|
|
reinterpret_cast<std::uintptr_t>(
|
|
kalloc(new_gadget->get_size()));
|
|
|
|
obfuscated_gadgets[mapped_symbols[new_symbol]] = new_gadget;
|
|
std::printf(" > %s allocated = 0x%p, size = %d\n", new_symbol.c_str(),
|
|
mapped_symbols[new_symbol], new_gadget->get_size());
|
|
|
|
offset += instruction.length;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool hmm_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_type)
|
|
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);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
} |