diff --git a/R3nzSkin_Injector/Injector.cpp b/R3nzSkin_Injector/Injector.cpp index 7f619822..9bf48fe4 100644 --- a/R3nzSkin_Injector/Injector.cpp +++ b/R3nzSkin_Injector/Injector.cpp @@ -8,10 +8,11 @@ #include "Injector.hpp" #include "R3nzUI.hpp" +#include "lazy_importer.hpp" proclist_t WINAPI Injector::findProcesses(const std::wstring name) noexcept { - auto process_snap{ ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }; + auto process_snap{ LI_FN(CreateToolhelp32Snapshot)(TH32CS_SNAPPROCESS, 0) }; proclist_t list; if (process_snap == INVALID_HANDLE_VALUE) @@ -20,23 +21,23 @@ proclist_t WINAPI Injector::findProcesses(const std::wstring name) noexcept PROCESSENTRY32W pe32; pe32.dwSize = sizeof(PROCESSENTRY32W); - if (::Process32First(process_snap, &pe32)) { + if (LI_FN(Process32FirstW).get()(process_snap, &pe32)) { if (pe32.szExeFile == name) list.push_back(pe32.th32ProcessID); - while (::Process32Next(process_snap, &pe32)) { + while (LI_FN(Process32NextW).get()(process_snap, &pe32)) { if (pe32.szExeFile == name) list.push_back(pe32.th32ProcessID); } } - ::CloseHandle(process_snap); + LI_FN(CloseHandle)(process_snap); return list; } bool WINAPI Injector::isInjected(const std::uint32_t pid) noexcept { - auto hProcess{ ::OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid) }; + auto hProcess{ LI_FN(OpenProcess)(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid) }; if (NULL == hProcess) return false; @@ -44,18 +45,18 @@ bool WINAPI Injector::isInjected(const std::uint32_t pid) noexcept HMODULE hMods[1024]; DWORD cbNeeded; - if (::K32EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded)) { + if (LI_FN(K32EnumProcessModules)(hProcess, hMods, sizeof(hMods), &cbNeeded)) { for (auto i{ 0u }; i < (cbNeeded / sizeof(HMODULE)); ++i) { TCHAR szModName[MAX_PATH]; - if (::K32GetModuleBaseNameW(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { - if (::wcscmp(szModName, L"R3nzSkin.dll") == 0) { - ::CloseHandle(hProcess); + if (LI_FN(K32GetModuleBaseNameW)(hProcess, hMods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) { + if (std::wcscmp(szModName, L"R3nzSkin.dll") == 0) { + LI_FN(CloseHandle)(hProcess); return true; } } } } - ::CloseHandle(hProcess); + LI_FN(CloseHandle)(hProcess); return false; } @@ -63,7 +64,7 @@ bool WINAPI Injector::inject(const std::uint32_t pid) noexcept { NtCreateThreadExBuffer ntbuffer; - ::memset(&ntbuffer, 0, sizeof(NtCreateThreadExBuffer)); + std::memset(&ntbuffer, 0, sizeof(NtCreateThreadExBuffer)); DWORD temp1{ 0 }; DWORD temp2{ 0 }; @@ -78,18 +79,18 @@ bool WINAPI Injector::inject(const std::uint32_t pid) noexcept ntbuffer.Unknown8 = 0; TCHAR current_dir[MAX_PATH]; - ::GetCurrentDirectoryW(MAX_PATH, current_dir); - const auto handle{ ::OpenProcess(PROCESS_ALL_ACCESS, false, pid) }; + LI_FN(GetCurrentDirectoryW)(MAX_PATH, current_dir); + const auto handle{ LI_FN(OpenProcess)(PROCESS_ALL_ACCESS, false, pid) }; if (!handle || handle == INVALID_HANDLE_VALUE) return false; FILETIME ft; SYSTEMTIME st; - ::GetSystemTime(&st); - ::SystemTimeToFileTime(&st, &ft); + LI_FN(GetSystemTime)(&st); + LI_FN(SystemTimeToFileTime)(&st, &ft); FILETIME create, exit, kernel, user; - ::GetProcessTimes(handle, &create, &exit, &kernel, &user); + LI_FN(GetProcessTimes)(handle, &create, &exit, &kernel, &user); const auto delta{ 10 - static_cast((*reinterpret_cast(&ft) - *reinterpret_cast(&create.dwLowDateTime)) / 10000000U) }; @@ -99,37 +100,37 @@ bool WINAPI Injector::inject(const std::uint32_t pid) noexcept const auto dll_path{ std::wstring(current_dir) + L"\\R3nzSkin.dll" }; if (auto f{ std::ifstream(dll_path) }; !f.is_open()) { - MessageBox(nullptr, L"R3nzSkin.dll file could not be found.\nTry reinstalling the cheat.", L"R3nzSkin", MB_ICONERROR | MB_OK); - ::CloseHandle(handle); + LI_FN(MessageBox)(nullptr, L"R3nzSkin.dll file could not be found.\nTry reinstalling the cheat.", L"R3nzSkin", MB_ICONERROR | MB_OK); + LI_FN(CloseHandle)(handle); return false; } - const auto dll_path_remote{ ::VirtualAllocEx(handle, nullptr, (dll_path.size() + 1) * sizeof(wchar_t), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE) }; + const auto dll_path_remote{ LI_FN(VirtualAllocEx).get()(handle, nullptr, (dll_path.size() + 1) * sizeof(wchar_t), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE) }; if (!dll_path_remote) { - ::CloseHandle(handle); + LI_FN(CloseHandle)(handle); return false; } - if (!::WriteProcessMemory(handle, dll_path_remote, dll_path.data(), (dll_path.size() + 1) * sizeof(wchar_t), nullptr)) { - ::VirtualFreeEx(handle, dll_path_remote, 0u, MEM_RELEASE); - ::CloseHandle(handle); + if (!LI_FN(WriteProcessMemory).get()(handle, dll_path_remote, dll_path.data(), (dll_path.size() + 1) * sizeof(wchar_t), nullptr)) { + LI_FN(VirtualFreeEx).get()(handle, dll_path_remote, 0u, MEM_RELEASE); + LI_FN(CloseHandle)(handle); return false; } HANDLE thread; - NtCreateThreadEx(&thread, GENERIC_ALL, NULL, handle, reinterpret_cast(::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")), dll_path_remote, FALSE, NULL, NULL, NULL, &ntbuffer); + LI_FN(NtCreateThreadEx).nt_cached()(&thread, GENERIC_ALL, NULL, handle, reinterpret_cast(::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW")), dll_path_remote, FALSE, NULL, NULL, NULL, &ntbuffer); if (!thread || thread == INVALID_HANDLE_VALUE) { - ::VirtualFreeEx(handle, dll_path_remote, 0u, MEM_RELEASE); - ::CloseHandle(handle); + LI_FN(VirtualFreeEx).get()(handle, dll_path_remote, 0u, MEM_RELEASE); + LI_FN(CloseHandle)(handle); return false; } - ::WaitForSingleObject(thread, INFINITE); - ::CloseHandle(thread); - ::VirtualFreeEx(handle, dll_path_remote, 0u, MEM_RELEASE); - ::CloseHandle(handle); + LI_FN(WaitForSingleObject)(thread, INFINITE); + LI_FN(CloseHandle)(thread); + LI_FN(VirtualFreeEx).get()(handle, dll_path_remote, 0u, MEM_RELEASE); + LI_FN(CloseHandle)(handle); return true; } @@ -137,15 +138,15 @@ void WINAPI Injector::enableDebugPrivilege() noexcept { HANDLE token; - if (::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { + if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)) { LUID value; - if (::LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &value)) { + if (LookupPrivilegeValueW(NULL, SE_DEBUG_NAME, &value)) { TOKEN_PRIVILEGES tp{}; tp.PrivilegeCount = 1; tp.Privileges[0].Luid = value; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; - if (::AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL)) - ::CloseHandle(token); + if (AdjustTokenPrivileges(token, FALSE, &tp, sizeof(tp), NULL, NULL)) + LI_FN(CloseHandle)(token); } } } @@ -165,13 +166,13 @@ std::string Injector::randomString(std::uint32_t size) noexcept void Injector::renameExe() noexcept { char szExeFileName[MAX_PATH]; - GetModuleFileNameA(nullptr, szExeFileName, MAX_PATH); + LI_FN(GetModuleFileNameA)(nullptr, szExeFileName, MAX_PATH); const auto path{ std::string(szExeFileName) }; const auto exe{ path.substr(path.find_last_of("\\") + 1, path.size()) }; const auto newName{ randomString(std::rand() % (10 - 7 + 1) + 7) + ".exe" }; - rename(exe.c_str(), newName.c_str()); + std::rename(exe.c_str(), newName.c_str()); } void Injector::run() noexcept diff --git a/R3nzSkin_Injector/Injector.hpp b/R3nzSkin_Injector/Injector.hpp index d61cf962..e4a9861b 100644 --- a/R3nzSkin_Injector/Injector.hpp +++ b/R3nzSkin_Injector/Injector.hpp @@ -21,8 +21,7 @@ struct NtCreateThreadExBuffer { SIZE_T Unknown8; }; -#pragma comment(lib, "ntdll.lib") -EXTERN_C NTSYSAPI NTSTATUS NTAPI NtCreateThreadEx(PHANDLE, ACCESS_MASK, LPVOID, HANDLE, LPTHREAD_START_ROUTINE, LPVOID, BOOL, SIZE_T, SIZE_T, SIZE_T, LPVOID); +NTSTATUS NTAPI NtCreateThreadEx(PHANDLE, ACCESS_MASK, LPVOID, HANDLE, LPTHREAD_START_ROUTINE, LPVOID, BOOL, SIZE_T, SIZE_T, SIZE_T, LPVOID); class Injector { public: diff --git a/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj b/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj index 49eb0560..92f8b316 100644 --- a/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj +++ b/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj @@ -110,6 +110,7 @@ + CppForm diff --git a/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj.filters b/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj.filters index e95d95c8..177d2a19 100644 --- a/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj.filters +++ b/R3nzSkin_Injector/R3nzSkin_Injector.vcxproj.filters @@ -32,6 +32,9 @@ Source Files + + Source Files + diff --git a/R3nzSkin_Injector/lazy_importer.hpp b/R3nzSkin_Injector/lazy_importer.hpp new file mode 100644 index 00000000..29b8fff9 --- /dev/null +++ b/R3nzSkin_Injector/lazy_importer.hpp @@ -0,0 +1,715 @@ +/* + * Copyright 2018-2022 Justas Masiulis + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// === FAQ === documentation is available at https://github.com/JustasMasiulis/lazy_importer +// * Code doesn't compile with errors about pointer conversion: +// - Try using `nullptr` instead of `NULL` or call `get()` instead of using the overloaded operator() +// * Lazy importer can't find the function I want: +// - Double check that the module in which it's located in is actually loaded +// - Try #define LAZY_IMPORTER_CASE_INSENSITIVE +// This will start using case insensitive comparison globally +// - Try #define LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS +// This will enable forwarded export resolution globally instead of needing explicit `forwarded()` calls + +#ifndef LAZY_IMPORTER_HPP +#define LAZY_IMPORTER_HPP + + +#define LI_FN(name) ::li::detail::lazy_function() + +#define LI_FN_DEF(name) ::li::detail::lazy_function() + +#define LI_MODULE(name) ::li::detail::lazy_module() + +#ifndef LAZY_IMPORTER_CPP_FORWARD +#ifdef LAZY_IMPORTER_NO_CPP_FORWARD +#define LAZY_IMPORTER_CPP_FORWARD(t, v) v +#else +#include +#define LAZY_IMPORTER_CPP_FORWARD(t, v) std::forward( v ) +#endif +#endif + +#include + +#ifndef LAZY_IMPORTER_NO_FORCEINLINE +#if defined(_MSC_VER) +#define LAZY_IMPORTER_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ > 3 +#define LAZY_IMPORTER_FORCEINLINE inline __attribute__((__always_inline__)) +#else +#define LAZY_IMPORTER_FORCEINLINE inline +#endif +#else +#define LAZY_IMPORTER_FORCEINLINE inline +#endif + + +#ifdef LAZY_IMPORTER_CASE_INSENSITIVE +#define LAZY_IMPORTER_CASE_SENSITIVITY false +#else +#define LAZY_IMPORTER_CASE_SENSITIVITY true +#endif + +#define LAZY_IMPORTER_STRINGIZE(x) #x +#define LAZY_IMPORTER_STRINGIZE_EXPAND(x) LAZY_IMPORTER_STRINGIZE(x) + +#define LAZY_IMPORTER_KHASH(str) ::li::detail::khash(str, \ + ::li::detail::khash_impl( __TIME__ __DATE__ LAZY_IMPORTER_STRINGIZE_EXPAND(__LINE__) LAZY_IMPORTER_STRINGIZE_EXPAND(__COUNTER__), 2166136261 )) + +namespace li { namespace detail { + + namespace win { + + struct LIST_ENTRY_T { + const char* Flink; + const char* Blink; + }; + + struct UNICODE_STRING_T { + unsigned short Length; + unsigned short MaximumLength; + wchar_t* Buffer; + }; + + struct PEB_LDR_DATA_T { + unsigned long Length; + unsigned long Initialized; + const char* SsHandle; + LIST_ENTRY_T InLoadOrderModuleList; + }; + + struct PEB_T { + unsigned char Reserved1[2]; + unsigned char BeingDebugged; + unsigned char Reserved2[1]; + const char* Reserved3[2]; + PEB_LDR_DATA_T* Ldr; + }; + + struct LDR_DATA_TABLE_ENTRY_T { + LIST_ENTRY_T InLoadOrderLinks; + LIST_ENTRY_T InMemoryOrderLinks; + LIST_ENTRY_T InInitializationOrderLinks; + const char* DllBase; + const char* EntryPoint; + union { + unsigned long SizeOfImage; + const char* _dummy; + }; + UNICODE_STRING_T FullDllName; + UNICODE_STRING_T BaseDllName; + + LAZY_IMPORTER_FORCEINLINE const LDR_DATA_TABLE_ENTRY_T* + load_order_next() const noexcept + { + return reinterpret_cast( + InLoadOrderLinks.Flink); + } + }; + + struct IMAGE_DOS_HEADER { // DOS .EXE header + unsigned short e_magic; // Magic number + unsigned short e_cblp; // Bytes on last page of file + unsigned short e_cp; // Pages in file + unsigned short e_crlc; // Relocations + unsigned short e_cparhdr; // Size of header in paragraphs + unsigned short e_minalloc; // Minimum extra paragraphs needed + unsigned short e_maxalloc; // Maximum extra paragraphs needed + unsigned short e_ss; // Initial (relative) SS value + unsigned short e_sp; // Initial SP value + unsigned short e_csum; // Checksum + unsigned short e_ip; // Initial IP value + unsigned short e_cs; // Initial (relative) CS value + unsigned short e_lfarlc; // File address of relocation table + unsigned short e_ovno; // Overlay number + unsigned short e_res[4]; // Reserved words + unsigned short e_oemid; // OEM identifier (for e_oeminfo) + unsigned short e_oeminfo; // OEM information; e_oemid specific + unsigned short e_res2[10]; // Reserved words + long e_lfanew; // File address of new exe header + }; + + struct IMAGE_FILE_HEADER { + unsigned short Machine; + unsigned short NumberOfSections; + unsigned long TimeDateStamp; + unsigned long PointerToSymbolTable; + unsigned long NumberOfSymbols; + unsigned short SizeOfOptionalHeader; + unsigned short Characteristics; + }; + + struct IMAGE_EXPORT_DIRECTORY { + unsigned long Characteristics; + unsigned long TimeDateStamp; + unsigned short MajorVersion; + unsigned short MinorVersion; + unsigned long Name; + unsigned long Base; + unsigned long NumberOfFunctions; + unsigned long NumberOfNames; + unsigned long AddressOfFunctions; // RVA from base of image + unsigned long AddressOfNames; // RVA from base of image + unsigned long AddressOfNameOrdinals; // RVA from base of image + }; + + struct IMAGE_DATA_DIRECTORY { + unsigned long VirtualAddress; + unsigned long Size; + }; + + struct IMAGE_OPTIONAL_HEADER64 { + unsigned short Magic; + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + unsigned long SizeOfCode; + unsigned long SizeOfInitializedData; + unsigned long SizeOfUninitializedData; + unsigned long AddressOfEntryPoint; + unsigned long BaseOfCode; + unsigned long long ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + unsigned long Win32VersionValue; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + unsigned long long SizeOfStackReserve; + unsigned long long SizeOfStackCommit; + unsigned long long SizeOfHeapReserve; + unsigned long long SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; + }; + + struct IMAGE_OPTIONAL_HEADER32 { + unsigned short Magic; + unsigned char MajorLinkerVersion; + unsigned char MinorLinkerVersion; + unsigned long SizeOfCode; + unsigned long SizeOfInitializedData; + unsigned long SizeOfUninitializedData; + unsigned long AddressOfEntryPoint; + unsigned long BaseOfCode; + unsigned long BaseOfData; + unsigned long ImageBase; + unsigned long SectionAlignment; + unsigned long FileAlignment; + unsigned short MajorOperatingSystemVersion; + unsigned short MinorOperatingSystemVersion; + unsigned short MajorImageVersion; + unsigned short MinorImageVersion; + unsigned short MajorSubsystemVersion; + unsigned short MinorSubsystemVersion; + unsigned long Win32VersionValue; + unsigned long SizeOfImage; + unsigned long SizeOfHeaders; + unsigned long CheckSum; + unsigned short Subsystem; + unsigned short DllCharacteristics; + unsigned long SizeOfStackReserve; + unsigned long SizeOfStackCommit; + unsigned long SizeOfHeapReserve; + unsigned long SizeOfHeapCommit; + unsigned long LoaderFlags; + unsigned long NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; + }; + + struct IMAGE_NT_HEADERS { + unsigned long Signature; + IMAGE_FILE_HEADER FileHeader; +#ifdef _WIN64 + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +#else + IMAGE_OPTIONAL_HEADER32 OptionalHeader; +#endif + }; + + } // namespace win + + struct forwarded_hashes { + unsigned module_hash; + unsigned function_hash; + }; + + // 64 bit integer where 32 bits are used for the hash offset + // and remaining 32 bits are used for the hash computed using it + using offset_hash_pair = unsigned long long; + + LAZY_IMPORTER_FORCEINLINE constexpr unsigned get_hash(offset_hash_pair pair) noexcept { return ( pair & 0xFFFFFFFF ); } + + LAZY_IMPORTER_FORCEINLINE constexpr unsigned get_offset(offset_hash_pair pair) noexcept { return ( pair >> 32 ); } + + template + LAZY_IMPORTER_FORCEINLINE constexpr unsigned hash_single(unsigned value, char c) noexcept + { + return static_cast( + (value ^ ((!CaseSensitive && c >= 'A' && c <= 'Z') ? (c | (1 << 5)) : c)) * + static_cast(16777619)); + } + + LAZY_IMPORTER_FORCEINLINE constexpr unsigned + khash_impl(const char* str, unsigned value) noexcept + { + return (*str ? khash_impl(str + 1, hash_single(value, *str)) : value); + } + + LAZY_IMPORTER_FORCEINLINE constexpr offset_hash_pair khash( + const char* str, unsigned offset) noexcept + { + return ((offset_hash_pair{ offset } << 32) | khash_impl(str, offset)); + } + + template + LAZY_IMPORTER_FORCEINLINE unsigned hash(const CharT* str, unsigned offset) noexcept + { + unsigned value = offset; + + for(;;) { + char c = *str++; + if(!c) + return value; + value = hash_single(value, c); + } + } + + LAZY_IMPORTER_FORCEINLINE unsigned hash( + const win::UNICODE_STRING_T& str, unsigned offset) noexcept + { + auto first = str.Buffer; + const auto last = first + (str.Length / sizeof(wchar_t)); + auto value = offset; + for(; first != last; ++first) + value = hash_single(value, static_cast(*first)); + + return value; + } + + LAZY_IMPORTER_FORCEINLINE forwarded_hashes hash_forwarded( + const char* str, unsigned offset) noexcept + { + forwarded_hashes res{ offset, offset }; + + for(; *str != '.'; ++str) + res.module_hash = hash_single(res.module_hash, *str); + + ++str; + + for(; *str; ++str) + res.function_hash = hash_single(res.function_hash, *str); + + return res; + } + + // some helper functions + LAZY_IMPORTER_FORCEINLINE const win::PEB_T* peb() noexcept + { +#if defined(_M_X64) || defined(__amd64__) + return reinterpret_cast(__readgsqword(0x60)); +#elif defined(_M_IX86) || defined(__i386__) + return reinterpret_cast(__readfsdword(0x30)); +#elif defined(_M_ARM) || defined(__arm__) + return *reinterpret_cast(_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0x30); +#elif defined(_M_ARM64) || defined(__aarch64__) + return *reinterpret_cast(__getReg(18) + 0x60); +#elif defined(_M_IA64) || defined(__ia64__) + return *reinterpret_cast(static_cast(_rdteb()) + 0x60); +#else +#error Unsupported platform. Open an issue and I'll probably add support. +#endif + } + + LAZY_IMPORTER_FORCEINLINE const win::PEB_LDR_DATA_T* ldr() + { + return reinterpret_cast(peb()->Ldr); + } + + LAZY_IMPORTER_FORCEINLINE const win::IMAGE_NT_HEADERS* nt_headers( + const char* base) noexcept + { + return reinterpret_cast( + base + reinterpret_cast(base)->e_lfanew); + } + + LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* image_export_dir( + const char* base) noexcept + { + return reinterpret_cast( + base + nt_headers(base)->OptionalHeader.DataDirectory->VirtualAddress); + } + + LAZY_IMPORTER_FORCEINLINE const win::LDR_DATA_TABLE_ENTRY_T* ldr_data_entry() noexcept + { + return reinterpret_cast( + ldr()->InLoadOrderModuleList.Flink); + } + + struct exports_directory { + const char* _base; + const win::IMAGE_EXPORT_DIRECTORY* _ied; + unsigned long _ied_size; + + public: + using size_type = unsigned long; + + LAZY_IMPORTER_FORCEINLINE + exports_directory(const char* base) noexcept : _base(base) + { + const auto ied_data_dir = nt_headers(base)->OptionalHeader.DataDirectory[0]; + _ied = reinterpret_cast( + base + ied_data_dir.VirtualAddress); + _ied_size = ied_data_dir.Size; + } + + LAZY_IMPORTER_FORCEINLINE explicit operator bool() const noexcept + { + return reinterpret_cast(_ied) != _base; + } + + LAZY_IMPORTER_FORCEINLINE size_type size() const noexcept + { + return _ied->NumberOfNames; + } + + LAZY_IMPORTER_FORCEINLINE const char* base() const noexcept { return _base; } + LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* ied() const noexcept + { + return _ied; + } + + LAZY_IMPORTER_FORCEINLINE const char* name(size_type index) const noexcept + { + return reinterpret_cast( + _base + reinterpret_cast( + _base + _ied->AddressOfNames)[index]); + } + + LAZY_IMPORTER_FORCEINLINE const char* address(size_type index) const noexcept + { + const auto* const rva_table = + reinterpret_cast(_base + _ied->AddressOfFunctions); + + const auto* const ord_table = reinterpret_cast( + _base + _ied->AddressOfNameOrdinals); + + return _base + rva_table[ord_table[index]]; + } + + LAZY_IMPORTER_FORCEINLINE bool is_forwarded( + const char* export_address) const noexcept + { + const auto ui_ied = reinterpret_cast(_ied); + return (export_address > ui_ied && export_address < ui_ied + _ied_size); + } + }; + + struct safe_module_enumerator { + using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T; + value_type* value; + value_type* head; + + LAZY_IMPORTER_FORCEINLINE safe_module_enumerator() noexcept + : safe_module_enumerator(ldr_data_entry()) + {} + + LAZY_IMPORTER_FORCEINLINE + safe_module_enumerator(const detail::win::LDR_DATA_TABLE_ENTRY_T* ldr) noexcept + : value(ldr->load_order_next()), head(value) + {} + + LAZY_IMPORTER_FORCEINLINE void reset() noexcept + { + value = head->load_order_next(); + } + + LAZY_IMPORTER_FORCEINLINE bool next() noexcept + { + value = value->load_order_next(); + + return value != head && value->DllBase; + } + }; + + struct unsafe_module_enumerator { + using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T*; + value_type value; + + LAZY_IMPORTER_FORCEINLINE unsafe_module_enumerator() noexcept + : value(ldr_data_entry()) + {} + + LAZY_IMPORTER_FORCEINLINE void reset() noexcept { value = ldr_data_entry(); } + + LAZY_IMPORTER_FORCEINLINE bool next() noexcept + { + value = value->load_order_next(); + return true; + } + }; + + // provides the cached functions which use Derive classes methods + template + class lazy_base { + protected: + // This function is needed because every templated function + // with different args has its own static buffer + LAZY_IMPORTER_FORCEINLINE static void*& _cache() noexcept + { + static void* value = nullptr; + return value; + } + + public: + template + LAZY_IMPORTER_FORCEINLINE static T safe() noexcept + { + return Derived::template get(); + } + + template + LAZY_IMPORTER_FORCEINLINE static T cached() noexcept + { + auto& cached = _cache(); + if(!cached) + cached = Derived::template get(); + + return (T)(cached); + } + + template + LAZY_IMPORTER_FORCEINLINE static T safe_cached() noexcept + { + return cached(); + } + }; + + template + struct lazy_module : lazy_base> { + template + LAZY_IMPORTER_FORCEINLINE static T get() noexcept + { + Enum e; + do { + if(hash(e.value->BaseDllName, get_offset(OHP)) == get_hash(OHP)) + return (T)(e.value->DllBase); + } while(e.next()); + return {}; + } + + template + LAZY_IMPORTER_FORCEINLINE static T in(Ldr ldr) noexcept + { + safe_module_enumerator e((const detail::win::LDR_DATA_TABLE_ENTRY_T*)(ldr)); + do { + if(hash(e.value->BaseDllName, get_offset(OHP)) == get_hash(OHP)) + return (T)(e.value->DllBase); + } while(e.next()); + return {}; + } + + template + LAZY_IMPORTER_FORCEINLINE static T in_cached(Ldr ldr) noexcept + { + auto& cached = lazy_base>::_cache(); + if(!cached) + cached = in(ldr); + + return (T)(cached); + } + }; + + template + struct lazy_function : lazy_base, T> { + using base_type = lazy_base, T>; + + template + LAZY_IMPORTER_FORCEINLINE decltype(auto) operator()(Args&&... args) const + { +#ifndef LAZY_IMPORTER_CACHE_OPERATOR_PARENS + return get()(LAZY_IMPORTER_CPP_FORWARD(Args, args)...); +#else + return this->cached()(LAZY_IMPORTER_CPP_FORWARD(Args, args)...); +#endif + } + + template + LAZY_IMPORTER_FORCEINLINE static F get() noexcept + { + // for backwards compatability. + // Before 2.0 it was only possible to resolve forwarded exports when + // this macro was enabled +#ifdef LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS + return forwarded(); +#else + + Enum e; + + do { +#ifdef LAZY_IMPORTER_HARDENED_MODULE_CHECKS + if(!e.value->DllBase || !e.value->FullDllName.Length) + continue; +#endif + + const exports_directory exports(e.value->DllBase); + + if(exports) { + auto export_index = exports.size(); + while(export_index--) + if(hash(exports.name(export_index), get_offset(OHP)) == get_hash(OHP)) + return (F)(exports.address(export_index)); + } + } while(e.next()); + return {}; +#endif + } + + template + LAZY_IMPORTER_FORCEINLINE static F forwarded() noexcept + { + detail::win::UNICODE_STRING_T name; + forwarded_hashes hashes{ 0, get_hash(OHP) }; + + Enum e; + do { + name = e.value->BaseDllName; + name.Length -= 8; // get rid of .dll extension + + if(!hashes.module_hash || hash(name, get_offset(OHP)) == hashes.module_hash) { + const exports_directory exports(e.value->DllBase); + + if(exports) { + auto export_index = exports.size(); + while(export_index--) + if(hash(exports.name(export_index), get_offset(OHP)) == hashes.function_hash) { + const auto addr = exports.address(export_index); + + if(exports.is_forwarded(addr)) { + hashes = hash_forwarded( + reinterpret_cast(addr), + get_offset(OHP)); + + e.reset(); + break; + } + return (F)(addr); + } + } + } + } while(e.next()); + return {}; + } + + template + LAZY_IMPORTER_FORCEINLINE static F forwarded_safe() noexcept + { + return forwarded(); + } + + template + LAZY_IMPORTER_FORCEINLINE static F forwarded_cached() noexcept + { + auto& value = base_type::_cache(); + if(!value) + value = forwarded(); + return (F)(value); + } + + template + LAZY_IMPORTER_FORCEINLINE static F forwarded_safe_cached() noexcept + { + return forwarded_cached(); + } + + template + LAZY_IMPORTER_FORCEINLINE static F in(Module m) noexcept + { + if(IsSafe && !m) + return {}; + + const exports_directory exports((const char*)(m)); + if(IsSafe && !exports) + return {}; + + for(unsigned long i{};; ++i) { + if(IsSafe && i == exports.size()) + break; + + if(hash(exports.name(i), get_offset(OHP)) == get_hash(OHP)) + return (F)(exports.address(i)); + } + return {}; + } + + template + LAZY_IMPORTER_FORCEINLINE static F in_safe(Module m) noexcept + { + return in(m); + } + + template + LAZY_IMPORTER_FORCEINLINE static F in_cached(Module m) noexcept + { + auto& value = base_type::_cache(); + if(!value) + value = in(m); + return (F)(value); + } + + template + LAZY_IMPORTER_FORCEINLINE static F in_safe_cached(Module m) noexcept + { + return in_cached(m); + } + + template + LAZY_IMPORTER_FORCEINLINE static F nt() noexcept + { + return in(ldr_data_entry()->load_order_next()->DllBase); + } + + template + LAZY_IMPORTER_FORCEINLINE static F nt_safe() noexcept + { + return in_safe(ldr_data_entry()->load_order_next()->DllBase); + } + + template + LAZY_IMPORTER_FORCEINLINE static F nt_cached() noexcept + { + return in_cached(ldr_data_entry()->load_order_next()->DllBase); + } + + template + LAZY_IMPORTER_FORCEINLINE static F nt_safe_cached() noexcept + { + return in_safe_cached(ldr_data_entry()->load_order_next()->DllBase); + } + }; + +}} // namespace li::detail + +#endif // include guard