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