├── Injector.vcxproj ├── Injector.vcxproj.filters ├── Injector.vcxproj.user ├── README.md ├── driver ├── Driver.cpp └── Driver.h ├── includes.h ├── inject ├── Inject.cpp └── Inject.h ├── main.cpp ├── showcase.gif └── utils ├── lazy_importer.h ├── utils.h └── xor.h /Injector.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {b2d29888-2215-4b6c-b623-da991e6914ce} 25 | Injector 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 118 | true 119 | 120 | 121 | Console 122 | true 123 | 124 | 125 | 126 | 127 | Level3 128 | true 129 | true 130 | 131 | 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS 133 | true 134 | MaxSpeed 135 | true 136 | stdcpp17 137 | false 138 | 139 | 140 | Console 141 | true 142 | true 143 | true 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /Injector.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {9a87fbbb-36a5-4d6d-8994-1b05f651b25c} 14 | 15 | 16 | {5676a617-28f7-4add-9dea-0056231826cc} 17 | 18 | 19 | {fc6c7489-c664-4f1c-83cd-f611b732931b} 20 | 21 | 22 | 23 | 24 | Driver 25 | 26 | 27 | Inject 28 | 29 | 30 | Source Files 31 | 32 | 33 | 34 | 35 | Utils 36 | 37 | 38 | Utils 39 | 40 | 41 | Utils 42 | 43 | 44 | Driver 45 | 46 | 47 | Inject 48 | 49 | 50 | Header Files 51 | 52 | 53 | -------------------------------------------------------------------------------- /Injector.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kernel-Manual-Map-Injector 2 | 3 | # What is this 4 | > This is a kenrel DLL injector using SetWinEventHook to call the Entry Point of the DLL 5 | 6 | # Will this work on EAC & BE protected games? 7 | > Yes it will. But dont think that will this be undetected without editing the code 8 | 9 | # I need help what do I do ? 10 | > You can add me on discord kidra#6986 11 | # Showcase 12 | ![](showcase.gif) 13 | # Credits 14 | 15 | [Kernel Driver used](https://github.com/mactec0/Kernelmode-manual-mapping-through-IAT) 16 | 17 | [Shellcode (edited)](https://github.com/TheCruZ/Simple-Manual-Map-Injector) 18 | -------------------------------------------------------------------------------- /driver/Driver.cpp: -------------------------------------------------------------------------------- 1 | #include "Driver.h" 2 | 3 | Driver::Driver(const wchar_t* driver_name, int target_process_id) 4 | { 5 | this->driver_handle = CreateFileW(driver_name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); 6 | this->target_process_id = target_process_id; // yes i am lazy 7 | } 8 | Driver::~Driver() 9 | { 10 | CloseHandle(driver_handle); 11 | } 12 | uintptr_t Driver::get_base_address(const wchar_t* process_name) 13 | { 14 | if (!driver_handle) return 0; 15 | 16 | k_get_base_module_request request; 17 | request.pid = target_process_id; 18 | 19 | memset(request.name, 0, sizeof(WCHAR) * 260); 20 | wcscpy(request.name, process_name); 21 | request.handle = 0; // Make Sure that it is 0 so it doesnt return another one 22 | 23 | DeviceIoControl(driver_handle, ioctl_get_module_base, &request, sizeof(k_get_base_module_request), &request, sizeof(k_get_base_module_request), 0, 0); 24 | return request.handle; 25 | } 26 | 27 | uintptr_t Driver::allocate_virtual_memory(int size, int allocation_type, int protect_type) 28 | { 29 | if (!driver_handle) return 0; 30 | k_alloc_mem_request request; 31 | request.pid = this->target_process_id; 32 | request.addr = (PVOID)(0); 33 | request.size = size; 34 | request.allocation_type = allocation_type; 35 | request.protect = protect_type; 36 | DeviceIoControl(driver_handle, ioctl_allocate_virtual_memory, &request, sizeof(k_alloc_mem_request), &request, sizeof(k_alloc_mem_request), 0, 0); 37 | return (uintptr_t)request.addr; 38 | 39 | } 40 | bool Driver::write_memory(uintptr_t destination, uintptr_t source, int size) 41 | { 42 | if (driver_handle == INVALID_HANDLE_VALUE) return 0; 43 | k_rw_request request; 44 | request.pid = this->target_process_id; 45 | request.dst = destination; 46 | request.src = source; 47 | request.size = size; 48 | return DeviceIoControl(driver_handle, ioctl_write_memory, &request, sizeof(k_rw_request), 0, 0, 0, 0); 49 | } 50 | bool Driver::read_memory(uintptr_t source, uintptr_t destination, int size) 51 | { 52 | if (driver_handle == INVALID_HANDLE_VALUE) return 0; 53 | k_rw_request request; 54 | request.pid = this->target_process_id; 55 | request.dst = destination; 56 | request.src = source; 57 | request.size = size; 58 | return DeviceIoControl(driver_handle, ioctl_read_memory, &request, sizeof(k_rw_request), 0, 0, 0, 0); 59 | } 60 | 61 | bool Driver::protect_virtual_memory(uintptr_t address, int size, int protect_type) 62 | { 63 | if (!driver_handle) return 0; 64 | k_protect_mem_request request; 65 | request.pid = this->target_process_id; 66 | request.addr = address; 67 | request.size = size; 68 | request.protect = protect_type; 69 | return DeviceIoControl(driver_handle, ioctl_protect_virutal_memory, &request, sizeof(k_protect_mem_request), &request, sizeof(k_protect_mem_request), 0, 0); 70 | 71 | } -------------------------------------------------------------------------------- /driver/Driver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../includes.h" 3 | 4 | class Driver 5 | { 6 | public: 7 | Driver(const wchar_t* driver_name, int target_process_id); 8 | ~Driver(); 9 | uintptr_t get_base_address(const wchar_t* process_name); 10 | uintptr_t allocate_virtual_memory(int size, int allocation_type, int protect_type); 11 | bool protect_virtual_memory(uintptr_t address, int size, int protect_type); 12 | bool write_memory(uintptr_t destination, uintptr_t source, int size); 13 | bool read_memory(uintptr_t source, uintptr_t destination, int size); 14 | HANDLE driver_handle; 15 | int target_process_id; 16 | private: 17 | 18 | /* 19 | Driver Structs 20 | */ 21 | typedef struct _k_get_base_module_request { 22 | ULONG pid; 23 | ULONGLONG handle; 24 | WCHAR name[260]; 25 | } k_get_base_module_request, * pk_get_base_module_request; 26 | 27 | typedef struct _k_rw_request { 28 | ULONG pid; 29 | ULONGLONG src; 30 | ULONGLONG dst; 31 | ULONGLONG size; 32 | } k_rw_request, * pk_rw_request; 33 | 34 | typedef struct _k_alloc_mem_request { 35 | ULONG pid, allocation_type, protect; 36 | PVOID addr; 37 | SIZE_T size; 38 | } k_alloc_mem_request, * pk_alloc_mem_request; 39 | 40 | typedef struct _k_protect_mem_request { 41 | ULONG pid, protect; 42 | ULONGLONG addr; 43 | SIZE_T size; 44 | } k_protect_mem_request, * pk_protect_mem_request; 45 | 46 | /* 47 | Driver IOCTL codes 48 | */ 49 | #define ioctl_read_memory CTL_CODE(FILE_DEVICE_UNKNOWN, 0x093286, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 50 | #define ioctl_write_memory CTL_CODE(FILE_DEVICE_UNKNOWN, 0x729823, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 51 | #define ioctl_get_module_base CTL_CODE(FILE_DEVICE_UNKNOWN, 0x461419, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 52 | #define ioctl_protect_virutal_memory CTL_CODE(FILE_DEVICE_UNKNOWN, 0x433146, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 53 | #define ioctl_allocate_virtual_memory CTL_CODE(FILE_DEVICE_UNKNOWN, 0x523794, METHOD_BUFFERED, FILE_SPECIAL_ACCESS) 54 | 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "utils/lazy_importer.h" 8 | #include "utils/utils.h" 9 | #include "utils/xor.h" 10 | #include "Driver/Driver.h" 11 | #include "Inject/inject.h" -------------------------------------------------------------------------------- /inject/Inject.cpp: -------------------------------------------------------------------------------- 1 | #include "Inject.h" 2 | /* 3 | !!!IMPORTANT!!! for this to work correctly please disable Security Check (/GS-) 4 | */ 5 | #define RELOC_FLAG64(RelInfo) ((RelInfo >> 0x0C) == IMAGE_REL_BASED_DIR64) 6 | 7 | //keep this 8 | #pragma runtime_checks( "", off ) 9 | #pragma optimize( "", off ) 10 | void __stdcall shellcode() 11 | { 12 | uintptr_t base = 0x15846254168; // random 13 | uintptr_t pointer_address = 0x24856841253; // random 14 | 15 | memset((void*)pointer_address, 0x69, 1); 16 | 17 | BYTE* pBase = (BYTE*)base; 18 | auto* pOpt = &reinterpret_cast(pBase + reinterpret_cast((uintptr_t)pBase)->e_lfanew)->OptionalHeader; 19 | 20 | auto _LoadLibraryA = LI_FN(LoadLibraryA).get(); 21 | auto _GetProcAddress = LI_FN(GetProcAddress).get(); 22 | auto _RtlAddFunctionTable = LI_FN(RtlAddFunctionTable).get(); 23 | 24 | auto _DllMain = reinterpret_cast(pBase + pOpt->AddressOfEntryPoint); 25 | 26 | BYTE* LocationDelta = pBase - pOpt->ImageBase; 27 | if (LocationDelta) { 28 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size) { 29 | auto* pRelocData = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress); 30 | const auto* pRelocEnd = reinterpret_cast(reinterpret_cast(pRelocData) + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size); 31 | while (pRelocData < pRelocEnd && pRelocData->SizeOfBlock) { 32 | UINT AmountOfEntries = (pRelocData->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD); 33 | WORD* pRelativeInfo = reinterpret_cast(pRelocData + 1); 34 | 35 | for (UINT i = 0; i != AmountOfEntries; ++i, ++pRelativeInfo) { 36 | if (RELOC_FLAG64(*pRelativeInfo)) { 37 | UINT_PTR* pPatch = reinterpret_cast(pBase + pRelocData->VirtualAddress + ((*pRelativeInfo) & 0xFFF)); 38 | *pPatch += reinterpret_cast(LocationDelta); 39 | } 40 | } 41 | pRelocData = reinterpret_cast(reinterpret_cast(pRelocData) + pRelocData->SizeOfBlock); 42 | } 43 | } 44 | } 45 | 46 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size) { 47 | auto* pImportDescr = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); 48 | while (pImportDescr->Name) { 49 | char* szMod = reinterpret_cast(pBase + pImportDescr->Name); 50 | HINSTANCE hDll = _LoadLibraryA(szMod); 51 | 52 | ULONG_PTR* pThunkRef = reinterpret_cast(pBase + pImportDescr->OriginalFirstThunk); 53 | ULONG_PTR* pFuncRef = reinterpret_cast(pBase + pImportDescr->FirstThunk); 54 | 55 | if (!pThunkRef) 56 | pThunkRef = pFuncRef; 57 | 58 | for (; *pThunkRef; ++pThunkRef, ++pFuncRef) { 59 | if (IMAGE_SNAP_BY_ORDINAL(*pThunkRef)) { 60 | *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, reinterpret_cast(*pThunkRef & 0xFFFF)); 61 | } 62 | else { 63 | auto* pImport = reinterpret_cast(pBase + (*pThunkRef)); 64 | *pFuncRef = (ULONG_PTR)_GetProcAddress(hDll, pImport->Name); 65 | } 66 | } 67 | ++pImportDescr; 68 | } 69 | } 70 | 71 | if (pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].Size) { 72 | auto* pTLS = reinterpret_cast(pBase + pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS].VirtualAddress); 73 | auto* pCallback = reinterpret_cast(pTLS->AddressOfCallBacks); 74 | for (; pCallback && *pCallback; ++pCallback) 75 | (*pCallback)(pBase, DLL_PROCESS_ATTACH, nullptr); 76 | } 77 | 78 | 79 | //SEH SUPPORT 80 | auto excep = pOpt->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]; 81 | if (excep.Size) { 82 | if (!_RtlAddFunctionTable( 83 | reinterpret_cast(pBase + excep.VirtualAddress), 84 | excep.Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY), (DWORD64)pBase)) { 85 | } 86 | } 87 | 88 | _DllMain(pBase, DLL_PROCESS_ATTACH, 0); 89 | } 90 | 91 | 92 | bool Inject::inject_module_from_path_to_process_by_name(const wchar_t* module_path, const wchar_t* process_name) 93 | { 94 | int target_process_id = utils::get_pid_from_name(process_name); 95 | 96 | if (!target_process_id) 97 | { 98 | printf(xor_a("Target Process not found!\n")); 99 | return false; 100 | } 101 | 102 | printf(xor_a("Target Process Id is : %i\n"), target_process_id); 103 | 104 | auto target_process_hwnd = utils::get_hwnd_of_process_id(target_process_id); // HWND needed for hook 105 | auto nt_dll = LoadLibraryA(xor_a("ntdll.dll")); 106 | auto thread_id = GetWindowThreadProcessId(target_process_hwnd, 0); // also needed for hook 107 | 108 | 109 | uintptr_t target_file = utils::read_file_by_name(module_path); 110 | 111 | if (!target_file) 112 | { 113 | printf(xor_a("Target File not found!\n")); 114 | return false; 115 | } 116 | 117 | printf(xor_a("Target File is : %p\n"), target_file); 118 | 119 | 120 | PIMAGE_NT_HEADERS nt_header = utils::get_nt_header(target_file); 121 | 122 | 123 | 124 | Driver* driver = new Driver(xor_w(L"\\\\.\\drivernamehere"), target_process_id); 125 | 126 | uintptr_t target_process_base_address = driver->get_base_address(process_name); 127 | 128 | uintptr_t allocated_base = driver->allocate_virtual_memory(nt_header->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ); 129 | 130 | driver->protect_virtual_memory((uintptr_t)allocated_base, nt_header->OptionalHeader.SizeOfImage, PAGE_EXECUTE_READWRITE); 131 | 132 | printf(xor_a("Allocated 0x%p at %p\n"), nt_header->OptionalHeader.SizeOfImage, allocated_base); 133 | 134 | if (!driver->write_memory((uintptr_t)allocated_base, (uintptr_t)target_file, 0x1000)) 135 | { 136 | printf(xor_a("Failed writing memory at %p\n"), allocated_base); 137 | } 138 | printf(xor_a("Successfully wrote at %p\n"), allocated_base); 139 | 140 | IMAGE_SECTION_HEADER* section_header = IMAGE_FIRST_SECTION(nt_header); 141 | for (int i = 0; i != nt_header->FileHeader.NumberOfSections; i++, ++section_header) 142 | { 143 | if (section_header->SizeOfRawData) 144 | { 145 | if (!driver->write_memory((uintptr_t)allocated_base + section_header->VirtualAddress, (uintptr_t)target_file + section_header->PointerToRawData, section_header->SizeOfRawData)) { 146 | printf(xor_a("Failed writing memory at %p\n"), allocated_base + section_header->VirtualAddress); 147 | 148 | return false; 149 | } 150 | } 151 | } 152 | printf(xor_a("Successfully wrote sections!\n")); 153 | 154 | uintptr_t allocated_shellcode = driver->allocate_virtual_memory(0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 155 | uintptr_t shellcode_value = driver->allocate_virtual_memory(sizeof(int), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); // there we set the value to stop the hook once the shellcode is called! 156 | printf(xor_a("Allocated 0x%p at %p\n"), 0x1000, allocated_shellcode); 157 | 158 | 159 | uintptr_t allocatedbase_offset = uintptr_t((uintptr_t)utils::find_pattern(xor_a("\x68\x41\x25\x46\x58\x01\x00\x00"), xor_a("xxxxxx??")) - (uintptr_t)&shellcode); //scans the value 0x15846254168 in shellcode 160 | uintptr_t allocatedvalue_offset = uintptr_t((uintptr_t)utils::find_pattern(xor_a("\x53\x12\x84\x56\x48\x02\x00\x00"), xor_a("xxxxxx??")) - (uintptr_t)&shellcode); // scans the value 0x24856841253 in shellcode 161 | if (!allocatedbase_offset || !allocatedvalue_offset) 162 | { 163 | printf(xor_a("Check signatures !\n")); 164 | return false; 165 | } 166 | 167 | auto shellcodefunction_length = utils::get_function_length(&shellcode); 168 | uintptr_t localshellcodealloc = (uintptr_t)VirtualAlloc(0, shellcodefunction_length, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); 169 | 170 | memcpy((PVOID)localshellcodealloc, &shellcode, shellcodefunction_length); 171 | 172 | *(uintptr_t*)(localshellcodealloc + allocatedbase_offset) = allocated_base; 173 | *(uintptr_t*)(localshellcodealloc + allocatedvalue_offset) = shellcode_value; 174 | 175 | driver->write_memory(allocated_shellcode, localshellcodealloc, 0x1000); 176 | 177 | auto win_event_hook = SetWinEventHook(EVENT_MIN, EVENT_MAX, nt_dll, (WINEVENTPROC)allocated_shellcode, target_process_id, thread_id, WINEVENT_INCONTEXT); 178 | printf(xor_a("Hook created at : %p\nWaiting..."), win_event_hook); 179 | while (true) 180 | { 181 | 182 | int buffer; 183 | driver->read_memory(shellcode_value, (uintptr_t)&buffer, sizeof(int)); 184 | if (buffer == 0x69) { // if shellcode called 185 | UnhookWinEvent(win_event_hook); 186 | return true; 187 | } 188 | } 189 | return false; 190 | } 191 | -------------------------------------------------------------------------------- /inject/Inject.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "../includes.h" 3 | 4 | class Inject 5 | { 6 | public: 7 | Inject() {}; 8 | ~Inject() {}; 9 | bool inject_module_from_path_to_process_by_name(const wchar_t* module_path, const wchar_t* process_name); 10 | 11 | private: 12 | 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | 3 | int main() 4 | { 5 | Inject* inject = new Inject(); 6 | inject->inject_module_from_path_to_process_by_name(xor_w(L"C:\\module_to_inject.dll"), xor_w(L"notepad.exe")); 7 | } -------------------------------------------------------------------------------- /showcase.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TTKKO/Kernel-Manual-Map-Injector/ebb45196f1e26685fb919bbb2c844611c37ecb75/showcase.gif -------------------------------------------------------------------------------- /utils/lazy_importer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 Justas Masiulis 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // === FAQ === documentation is available at https://github.com/JustasMasiulis/lazy_importer 18 | // * Code doesn't compile with errors about pointer conversion: 19 | // - Try using `nullptr` instead of `NULL` or call `get()` instead of using the overloaded operator() 20 | // * Lazy importer can't find the function I want: 21 | // - Double check that the module in which it's located in is actually loaded 22 | // - Try #define LAZY_IMPORTER_CASE_INSENSITIVE 23 | // This will start using case insensitive comparison globally 24 | // - Try #define LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS 25 | // This will enable forwarded export resolution globally instead of needing explicit `forwarded()` calls 26 | 27 | #ifndef LAZY_IMPORTER_HPP 28 | #define LAZY_IMPORTER_HPP 29 | 30 | 31 | #define LI_FN(name) ::li::detail::lazy_function() 32 | 33 | #define LI_FN_DEF(name) ::li::detail::lazy_function() 34 | 35 | #define LI_MODULE(name) ::li::detail::lazy_module() 36 | 37 | #ifndef LAZY_IMPORTER_CPP_FORWARD 38 | #ifdef LAZY_IMPORTER_NO_CPP_FORWARD 39 | #define LAZY_IMPORTER_CPP_FORWARD(t, v) v 40 | #else 41 | #include 42 | #define LAZY_IMPORTER_CPP_FORWARD(t, v) std::forward( v ) 43 | #endif 44 | #endif 45 | 46 | #include 47 | 48 | #ifndef LAZY_IMPORTER_NO_FORCEINLINE 49 | #if defined(_MSC_VER) 50 | #define LAZY_IMPORTER_FORCEINLINE __forceinline 51 | #elif defined(__GNUC__) && __GNUC__ > 3 52 | #define LAZY_IMPORTER_FORCEINLINE inline __attribute__((__always_inline__)) 53 | #else 54 | #define LAZY_IMPORTER_FORCEINLINE inline 55 | #endif 56 | #else 57 | #define LAZY_IMPORTER_FORCEINLINE inline 58 | #endif 59 | 60 | 61 | #ifdef LAZY_IMPORTER_CASE_INSENSITIVE 62 | #define LAZY_IMPORTER_CASE_SENSITIVITY false 63 | #else 64 | #define LAZY_IMPORTER_CASE_SENSITIVITY true 65 | #endif 66 | 67 | #define LAZY_IMPORTER_STRINGIZE(x) #x 68 | #define LAZY_IMPORTER_STRINGIZE_EXPAND(x) LAZY_IMPORTER_STRINGIZE(x) 69 | 70 | #define LAZY_IMPORTER_KHASH(str) ::li::detail::khash(str, \ 71 | ::li::detail::khash_impl( __TIME__ __DATE__ LAZY_IMPORTER_STRINGIZE_EXPAND(__LINE__) LAZY_IMPORTER_STRINGIZE_EXPAND(__COUNTER__), 2166136261 )) 72 | 73 | namespace li { 74 | namespace detail { 75 | 76 | namespace win { 77 | 78 | struct LIST_ENTRY_T { 79 | const char* Flink; 80 | const char* Blink; 81 | }; 82 | 83 | struct UNICODE_STRING_T { 84 | unsigned short Length; 85 | unsigned short MaximumLength; 86 | wchar_t* Buffer; 87 | }; 88 | 89 | struct PEB_LDR_DATA_T { 90 | unsigned long Length; 91 | unsigned long Initialized; 92 | const char* SsHandle; 93 | LIST_ENTRY_T InLoadOrderModuleList; 94 | }; 95 | 96 | struct PEB_T { 97 | unsigned char Reserved1[2]; 98 | unsigned char BeingDebugged; 99 | unsigned char Reserved2[1]; 100 | const char* Reserved3[2]; 101 | PEB_LDR_DATA_T* Ldr; 102 | }; 103 | 104 | struct LDR_DATA_TABLE_ENTRY_T { 105 | LIST_ENTRY_T InLoadOrderLinks; 106 | LIST_ENTRY_T InMemoryOrderLinks; 107 | LIST_ENTRY_T InInitializationOrderLinks; 108 | const char* DllBase; 109 | const char* EntryPoint; 110 | union { 111 | unsigned long SizeOfImage; 112 | const char* _dummy; 113 | }; 114 | UNICODE_STRING_T FullDllName; 115 | UNICODE_STRING_T BaseDllName; 116 | 117 | LAZY_IMPORTER_FORCEINLINE const LDR_DATA_TABLE_ENTRY_T* 118 | load_order_next() const noexcept 119 | { 120 | return reinterpret_cast( 121 | InLoadOrderLinks.Flink); 122 | } 123 | }; 124 | 125 | struct IMAGE_DOS_HEADER { // DOS .EXE header 126 | unsigned short e_magic; // Magic number 127 | unsigned short e_cblp; // Bytes on last page of file 128 | unsigned short e_cp; // Pages in file 129 | unsigned short e_crlc; // Relocations 130 | unsigned short e_cparhdr; // Size of header in paragraphs 131 | unsigned short e_minalloc; // Minimum extra paragraphs needed 132 | unsigned short e_maxalloc; // Maximum extra paragraphs needed 133 | unsigned short e_ss; // Initial (relative) SS value 134 | unsigned short e_sp; // Initial SP value 135 | unsigned short e_csum; // Checksum 136 | unsigned short e_ip; // Initial IP value 137 | unsigned short e_cs; // Initial (relative) CS value 138 | unsigned short e_lfarlc; // File address of relocation table 139 | unsigned short e_ovno; // Overlay number 140 | unsigned short e_res[4]; // Reserved words 141 | unsigned short e_oemid; // OEM identifier (for e_oeminfo) 142 | unsigned short e_oeminfo; // OEM information; e_oemid specific 143 | unsigned short e_res2[10]; // Reserved words 144 | long e_lfanew; // File address of new exe header 145 | }; 146 | 147 | struct IMAGE_FILE_HEADER { 148 | unsigned short Machine; 149 | unsigned short NumberOfSections; 150 | unsigned long TimeDateStamp; 151 | unsigned long PointerToSymbolTable; 152 | unsigned long NumberOfSymbols; 153 | unsigned short SizeOfOptionalHeader; 154 | unsigned short Characteristics; 155 | }; 156 | 157 | struct IMAGE_EXPORT_DIRECTORY { 158 | unsigned long Characteristics; 159 | unsigned long TimeDateStamp; 160 | unsigned short MajorVersion; 161 | unsigned short MinorVersion; 162 | unsigned long Name; 163 | unsigned long Base; 164 | unsigned long NumberOfFunctions; 165 | unsigned long NumberOfNames; 166 | unsigned long AddressOfFunctions; // RVA from base of image 167 | unsigned long AddressOfNames; // RVA from base of image 168 | unsigned long AddressOfNameOrdinals; // RVA from base of image 169 | }; 170 | 171 | struct IMAGE_DATA_DIRECTORY { 172 | unsigned long VirtualAddress; 173 | unsigned long Size; 174 | }; 175 | 176 | struct IMAGE_OPTIONAL_HEADER64 { 177 | unsigned short Magic; 178 | unsigned char MajorLinkerVersion; 179 | unsigned char MinorLinkerVersion; 180 | unsigned long SizeOfCode; 181 | unsigned long SizeOfInitializedData; 182 | unsigned long SizeOfUninitializedData; 183 | unsigned long AddressOfEntryPoint; 184 | unsigned long BaseOfCode; 185 | unsigned long long ImageBase; 186 | unsigned long SectionAlignment; 187 | unsigned long FileAlignment; 188 | unsigned short MajorOperatingSystemVersion; 189 | unsigned short MinorOperatingSystemVersion; 190 | unsigned short MajorImageVersion; 191 | unsigned short MinorImageVersion; 192 | unsigned short MajorSubsystemVersion; 193 | unsigned short MinorSubsystemVersion; 194 | unsigned long Win32VersionValue; 195 | unsigned long SizeOfImage; 196 | unsigned long SizeOfHeaders; 197 | unsigned long CheckSum; 198 | unsigned short Subsystem; 199 | unsigned short DllCharacteristics; 200 | unsigned long long SizeOfStackReserve; 201 | unsigned long long SizeOfStackCommit; 202 | unsigned long long SizeOfHeapReserve; 203 | unsigned long long SizeOfHeapCommit; 204 | unsigned long LoaderFlags; 205 | unsigned long NumberOfRvaAndSizes; 206 | IMAGE_DATA_DIRECTORY DataDirectory[16]; 207 | }; 208 | 209 | struct IMAGE_OPTIONAL_HEADER32 { 210 | unsigned short Magic; 211 | unsigned char MajorLinkerVersion; 212 | unsigned char MinorLinkerVersion; 213 | unsigned long SizeOfCode; 214 | unsigned long SizeOfInitializedData; 215 | unsigned long SizeOfUninitializedData; 216 | unsigned long AddressOfEntryPoint; 217 | unsigned long BaseOfCode; 218 | unsigned long BaseOfData; 219 | unsigned long ImageBase; 220 | unsigned long SectionAlignment; 221 | unsigned long FileAlignment; 222 | unsigned short MajorOperatingSystemVersion; 223 | unsigned short MinorOperatingSystemVersion; 224 | unsigned short MajorImageVersion; 225 | unsigned short MinorImageVersion; 226 | unsigned short MajorSubsystemVersion; 227 | unsigned short MinorSubsystemVersion; 228 | unsigned long Win32VersionValue; 229 | unsigned long SizeOfImage; 230 | unsigned long SizeOfHeaders; 231 | unsigned long CheckSum; 232 | unsigned short Subsystem; 233 | unsigned short DllCharacteristics; 234 | unsigned long SizeOfStackReserve; 235 | unsigned long SizeOfStackCommit; 236 | unsigned long SizeOfHeapReserve; 237 | unsigned long SizeOfHeapCommit; 238 | unsigned long LoaderFlags; 239 | unsigned long NumberOfRvaAndSizes; 240 | IMAGE_DATA_DIRECTORY DataDirectory[16]; 241 | }; 242 | 243 | struct IMAGE_NT_HEADERS { 244 | unsigned long Signature; 245 | IMAGE_FILE_HEADER FileHeader; 246 | #ifdef _WIN64 247 | IMAGE_OPTIONAL_HEADER64 OptionalHeader; 248 | #else 249 | IMAGE_OPTIONAL_HEADER32 OptionalHeader; 250 | #endif 251 | }; 252 | 253 | } // namespace win 254 | 255 | struct forwarded_hashes { 256 | unsigned module_hash; 257 | unsigned function_hash; 258 | }; 259 | 260 | // 64 bit integer where 32 bits are used for the hash offset 261 | // and remaining 32 bits are used for the hash computed using it 262 | using offset_hash_pair = unsigned long long; 263 | 264 | LAZY_IMPORTER_FORCEINLINE constexpr unsigned get_hash(offset_hash_pair pair) noexcept { return (pair & 0xFFFFFFFF); } 265 | 266 | LAZY_IMPORTER_FORCEINLINE constexpr unsigned get_offset(offset_hash_pair pair) noexcept { return (pair >> 32); } 267 | 268 | template 269 | LAZY_IMPORTER_FORCEINLINE constexpr unsigned hash_single(unsigned value, char c) noexcept 270 | { 271 | return static_cast( 272 | (value ^ ((!CaseSensitive && c >= 'A' && c <= 'Z') ? (c | (1 << 5)) : c)) * 273 | static_cast(16777619)); 274 | } 275 | 276 | LAZY_IMPORTER_FORCEINLINE constexpr unsigned 277 | khash_impl(const char* str, unsigned value) noexcept 278 | { 279 | return (*str ? khash_impl(str + 1, hash_single(value, *str)) : value); 280 | } 281 | 282 | LAZY_IMPORTER_FORCEINLINE constexpr offset_hash_pair khash( 283 | const char* str, unsigned offset) noexcept 284 | { 285 | return ((offset_hash_pair{ offset } << 32) | khash_impl(str, offset)); 286 | } 287 | 288 | template 289 | LAZY_IMPORTER_FORCEINLINE unsigned hash(const CharT* str, unsigned offset) noexcept 290 | { 291 | unsigned value = offset; 292 | 293 | for (;;) { 294 | char c = *str++; 295 | if (!c) 296 | return value; 297 | value = hash_single(value, c); 298 | } 299 | } 300 | 301 | LAZY_IMPORTER_FORCEINLINE unsigned hash( 302 | const win::UNICODE_STRING_T& str, unsigned offset) noexcept 303 | { 304 | auto first = str.Buffer; 305 | const auto last = first + (str.Length / sizeof(wchar_t)); 306 | auto value = offset; 307 | for (; first != last; ++first) 308 | value = hash_single(value, static_cast(*first)); 309 | 310 | return value; 311 | } 312 | 313 | LAZY_IMPORTER_FORCEINLINE forwarded_hashes hash_forwarded( 314 | const char* str, unsigned offset) noexcept 315 | { 316 | forwarded_hashes res{ offset, offset }; 317 | 318 | for (; *str != '.'; ++str) 319 | res.module_hash = hash_single(res.module_hash, *str); 320 | 321 | ++str; 322 | 323 | for (; *str; ++str) 324 | res.function_hash = hash_single(res.function_hash, *str); 325 | 326 | return res; 327 | } 328 | 329 | // some helper functions 330 | LAZY_IMPORTER_FORCEINLINE const win::PEB_T* peb() noexcept 331 | { 332 | #if defined(_M_X64) || defined(__amd64__) 333 | return reinterpret_cast(__readgsqword(0x60)); 334 | #elif defined(_M_IX86) || defined(__i386__) 335 | return reinterpret_cast(__readfsdword(0x30)); 336 | #elif defined(_M_ARM) || defined(__arm__) 337 | return *reinterpret_cast(_MoveFromCoprocessor(15, 0, 13, 0, 2) + 0x30); 338 | #elif defined(_M_ARM64) || defined(__aarch64__) 339 | return *reinterpret_cast(__getReg(18) + 0x60); 340 | #elif defined(_M_IA64) || defined(__ia64__) 341 | return *reinterpret_cast(static_cast(_rdteb()) + 0x60); 342 | #else 343 | #error Unsupported platform. Open an issue and I'll probably add support. 344 | #endif 345 | } 346 | 347 | LAZY_IMPORTER_FORCEINLINE const win::PEB_LDR_DATA_T* ldr() 348 | { 349 | return reinterpret_cast(peb()->Ldr); 350 | } 351 | 352 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_NT_HEADERS* nt_headers( 353 | const char* base) noexcept 354 | { 355 | return reinterpret_cast( 356 | base + reinterpret_cast(base)->e_lfanew); 357 | } 358 | 359 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* image_export_dir( 360 | const char* base) noexcept 361 | { 362 | return reinterpret_cast( 363 | base + nt_headers(base)->OptionalHeader.DataDirectory->VirtualAddress); 364 | } 365 | 366 | LAZY_IMPORTER_FORCEINLINE const win::LDR_DATA_TABLE_ENTRY_T* ldr_data_entry() noexcept 367 | { 368 | return reinterpret_cast( 369 | ldr()->InLoadOrderModuleList.Flink); 370 | } 371 | 372 | struct exports_directory { 373 | const char* _base; 374 | const win::IMAGE_EXPORT_DIRECTORY* _ied; 375 | unsigned long _ied_size; 376 | 377 | public: 378 | using size_type = unsigned long; 379 | 380 | LAZY_IMPORTER_FORCEINLINE 381 | exports_directory(const char* base) noexcept : _base(base) 382 | { 383 | const auto ied_data_dir = nt_headers(base)->OptionalHeader.DataDirectory[0]; 384 | _ied = reinterpret_cast( 385 | base + ied_data_dir.VirtualAddress); 386 | _ied_size = ied_data_dir.Size; 387 | } 388 | 389 | LAZY_IMPORTER_FORCEINLINE explicit operator bool() const noexcept 390 | { 391 | return reinterpret_cast(_ied) != _base; 392 | } 393 | 394 | LAZY_IMPORTER_FORCEINLINE size_type size() const noexcept 395 | { 396 | return _ied->NumberOfNames; 397 | } 398 | 399 | LAZY_IMPORTER_FORCEINLINE const char* base() const noexcept { return _base; } 400 | LAZY_IMPORTER_FORCEINLINE const win::IMAGE_EXPORT_DIRECTORY* ied() const noexcept 401 | { 402 | return _ied; 403 | } 404 | 405 | LAZY_IMPORTER_FORCEINLINE const char* name(size_type index) const noexcept 406 | { 407 | return reinterpret_cast( 408 | _base + reinterpret_cast( 409 | _base + _ied->AddressOfNames)[index]); 410 | } 411 | 412 | LAZY_IMPORTER_FORCEINLINE const char* address(size_type index) const noexcept 413 | { 414 | const auto* const rva_table = 415 | reinterpret_cast(_base + _ied->AddressOfFunctions); 416 | 417 | const auto* const ord_table = reinterpret_cast( 418 | _base + _ied->AddressOfNameOrdinals); 419 | 420 | return _base + rva_table[ord_table[index]]; 421 | } 422 | 423 | LAZY_IMPORTER_FORCEINLINE bool is_forwarded( 424 | const char* export_address) const noexcept 425 | { 426 | const auto ui_ied = reinterpret_cast(_ied); 427 | return (export_address > ui_ied && export_address < ui_ied + _ied_size); 428 | } 429 | }; 430 | 431 | struct safe_module_enumerator { 432 | using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T; 433 | value_type* value; 434 | value_type* head; 435 | 436 | LAZY_IMPORTER_FORCEINLINE safe_module_enumerator() noexcept 437 | : safe_module_enumerator(ldr_data_entry()) 438 | {} 439 | 440 | LAZY_IMPORTER_FORCEINLINE 441 | safe_module_enumerator(const detail::win::LDR_DATA_TABLE_ENTRY_T* ldr) noexcept 442 | : value(ldr->load_order_next()), head(value) 443 | {} 444 | 445 | LAZY_IMPORTER_FORCEINLINE void reset() noexcept 446 | { 447 | value = head->load_order_next(); 448 | } 449 | 450 | LAZY_IMPORTER_FORCEINLINE bool next() noexcept 451 | { 452 | value = value->load_order_next(); 453 | 454 | return value != head && value->DllBase; 455 | } 456 | }; 457 | 458 | struct unsafe_module_enumerator { 459 | using value_type = const detail::win::LDR_DATA_TABLE_ENTRY_T*; 460 | value_type value; 461 | 462 | LAZY_IMPORTER_FORCEINLINE unsafe_module_enumerator() noexcept 463 | : value(ldr_data_entry()) 464 | {} 465 | 466 | LAZY_IMPORTER_FORCEINLINE void reset() noexcept { value = ldr_data_entry(); } 467 | 468 | LAZY_IMPORTER_FORCEINLINE bool next() noexcept 469 | { 470 | value = value->load_order_next(); 471 | return true; 472 | } 473 | }; 474 | 475 | // provides the cached functions which use Derive classes methods 476 | template 477 | class lazy_base { 478 | protected: 479 | // This function is needed because every templated function 480 | // with different args has its own static buffer 481 | LAZY_IMPORTER_FORCEINLINE static void*& _cache() noexcept 482 | { 483 | static void* value = nullptr; 484 | return value; 485 | } 486 | 487 | public: 488 | template 489 | LAZY_IMPORTER_FORCEINLINE static T safe() noexcept 490 | { 491 | return Derived::template get(); 492 | } 493 | 494 | template 495 | LAZY_IMPORTER_FORCEINLINE static T cached() noexcept 496 | { 497 | auto& cached = _cache(); 498 | if (!cached) 499 | cached = Derived::template get(); 500 | 501 | return (T)(cached); 502 | } 503 | 504 | template 505 | LAZY_IMPORTER_FORCEINLINE static T safe_cached() noexcept 506 | { 507 | return cached(); 508 | } 509 | }; 510 | 511 | template 512 | struct lazy_module : lazy_base> { 513 | template 514 | LAZY_IMPORTER_FORCEINLINE static T get() noexcept 515 | { 516 | Enum e; 517 | do { 518 | if (hash(e.value->BaseDllName, get_offset(OHP)) == get_hash(OHP)) 519 | return (T)(e.value->DllBase); 520 | } while (e.next()); 521 | return {}; 522 | } 523 | 524 | template 525 | LAZY_IMPORTER_FORCEINLINE static T in(Ldr ldr) noexcept 526 | { 527 | safe_module_enumerator e((const detail::win::LDR_DATA_TABLE_ENTRY_T*)(ldr)); 528 | do { 529 | if (hash(e.value->BaseDllName, get_offset(OHP)) == get_hash(OHP)) 530 | return (T)(e.value->DllBase); 531 | } while (e.next()); 532 | return {}; 533 | } 534 | 535 | template 536 | LAZY_IMPORTER_FORCEINLINE static T in_cached(Ldr ldr) noexcept 537 | { 538 | auto& cached = lazy_base>::_cache(); 539 | if (!cached) 540 | cached = in(ldr); 541 | 542 | return (T)(cached); 543 | } 544 | }; 545 | 546 | template 547 | struct lazy_function : lazy_base, T> { 548 | using base_type = lazy_base, T>; 549 | 550 | template 551 | LAZY_IMPORTER_FORCEINLINE decltype(auto) operator()(Args&&... args) const 552 | { 553 | #ifndef LAZY_IMPORTER_CACHE_OPERATOR_PARENS 554 | return get()(LAZY_IMPORTER_CPP_FORWARD(Args, args)...); 555 | #else 556 | return this->cached()(LAZY_IMPORTER_CPP_FORWARD(Args, args)...); 557 | #endif 558 | } 559 | 560 | template 561 | LAZY_IMPORTER_FORCEINLINE static F get() noexcept 562 | { 563 | // for backwards compatability. 564 | // Before 2.0 it was only possible to resolve forwarded exports when 565 | // this macro was enabled 566 | #ifdef LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS 567 | return forwarded(); 568 | #else 569 | 570 | Enum e; 571 | 572 | do { 573 | #ifdef LAZY_IMPORTER_HARDENED_MODULE_CHECKS 574 | if (!e.value->DllBase || !e.value->FullDllName.Length) 575 | continue; 576 | #endif 577 | 578 | const exports_directory exports(e.value->DllBase); 579 | 580 | if (exports) { 581 | auto export_index = exports.size(); 582 | while (export_index--) 583 | if (hash(exports.name(export_index), get_offset(OHP)) == get_hash(OHP)) 584 | return (F)(exports.address(export_index)); 585 | } 586 | } while (e.next()); 587 | return {}; 588 | #endif 589 | } 590 | 591 | template 592 | LAZY_IMPORTER_FORCEINLINE static F forwarded() noexcept 593 | { 594 | detail::win::UNICODE_STRING_T name; 595 | forwarded_hashes hashes{ 0, get_hash(OHP) }; 596 | 597 | Enum e; 598 | do { 599 | name = e.value->BaseDllName; 600 | name.Length -= 8; // get rid of .dll extension 601 | 602 | if (!hashes.module_hash || hash(name, get_offset(OHP)) == hashes.module_hash) { 603 | const exports_directory exports(e.value->DllBase); 604 | 605 | if (exports) { 606 | auto export_index = exports.size(); 607 | while (export_index--) 608 | if (hash(exports.name(export_index), get_offset(OHP)) == hashes.function_hash) { 609 | const auto addr = exports.address(export_index); 610 | 611 | if (exports.is_forwarded(addr)) { 612 | hashes = hash_forwarded( 613 | reinterpret_cast(addr), 614 | get_offset(OHP)); 615 | 616 | e.reset(); 617 | break; 618 | } 619 | return (F)(addr); 620 | } 621 | } 622 | } 623 | } while (e.next()); 624 | return {}; 625 | } 626 | 627 | template 628 | LAZY_IMPORTER_FORCEINLINE static F forwarded_safe() noexcept 629 | { 630 | return forwarded(); 631 | } 632 | 633 | template 634 | LAZY_IMPORTER_FORCEINLINE static F forwarded_cached() noexcept 635 | { 636 | auto& value = base_type::_cache(); 637 | if (!value) 638 | value = forwarded(); 639 | return (F)(value); 640 | } 641 | 642 | template 643 | LAZY_IMPORTER_FORCEINLINE static F forwarded_safe_cached() noexcept 644 | { 645 | return forwarded_cached(); 646 | } 647 | 648 | template 649 | LAZY_IMPORTER_FORCEINLINE static F in(Module m) noexcept 650 | { 651 | if (IsSafe && !m) 652 | return {}; 653 | 654 | const exports_directory exports((const char*)(m)); 655 | if (IsSafe && !exports) 656 | return {}; 657 | 658 | for (unsigned long i{};; ++i) { 659 | if (IsSafe && i == exports.size()) 660 | break; 661 | 662 | if (hash(exports.name(i), get_offset(OHP)) == get_hash(OHP)) 663 | return (F)(exports.address(i)); 664 | } 665 | return {}; 666 | } 667 | 668 | template 669 | LAZY_IMPORTER_FORCEINLINE static F in_safe(Module m) noexcept 670 | { 671 | return in(m); 672 | } 673 | 674 | template 675 | LAZY_IMPORTER_FORCEINLINE static F in_cached(Module m) noexcept 676 | { 677 | auto& value = base_type::_cache(); 678 | if (!value) 679 | value = in(m); 680 | return (F)(value); 681 | } 682 | 683 | template 684 | LAZY_IMPORTER_FORCEINLINE static F in_safe_cached(Module m) noexcept 685 | { 686 | return in_cached(m); 687 | } 688 | 689 | template 690 | LAZY_IMPORTER_FORCEINLINE static F nt() noexcept 691 | { 692 | return in(ldr_data_entry()->load_order_next()->DllBase); 693 | } 694 | 695 | template 696 | LAZY_IMPORTER_FORCEINLINE static F nt_safe() noexcept 697 | { 698 | return in_safe(ldr_data_entry()->load_order_next()->DllBase); 699 | } 700 | 701 | template 702 | LAZY_IMPORTER_FORCEINLINE static F nt_cached() noexcept 703 | { 704 | return in_cached(ldr_data_entry()->load_order_next()->DllBase); 705 | } 706 | 707 | template 708 | LAZY_IMPORTER_FORCEINLINE static F nt_safe_cached() noexcept 709 | { 710 | return in_safe_cached(ldr_data_entry()->load_order_next()->DllBase); 711 | } 712 | }; 713 | 714 | } 715 | } // namespace li::detail 716 | 717 | #endif // include guard -------------------------------------------------------------------------------- /utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace utils 5 | { 6 | inline int get_pid_from_name(const wchar_t* name) 7 | { 8 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 9 | PROCESSENTRY32 entry; 10 | entry.dwSize = sizeof(PROCESSENTRY32); 11 | Process32First(snapshot, &entry); 12 | do 13 | { 14 | if (wcscmp(entry.szExeFile, name) == 0) 15 | { 16 | return entry.th32ProcessID; 17 | } 18 | 19 | } while (Process32Next(snapshot, &entry)); 20 | 21 | return 0; // if not found 22 | } 23 | inline uintptr_t read_file_by_name(const wchar_t* file_path) 24 | { 25 | HANDLE h_dll = CreateFileW(file_path ,GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 26 | if (h_dll == INVALID_HANDLE_VALUE) return 0; 27 | int file_size = GetFileSize(h_dll, 0); 28 | PVOID buffer = VirtualAlloc(0, file_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); 29 | if (!ReadFile(h_dll, buffer, file_size, 0, FALSE) || *(int*)(buffer) != 9460301) // MZ CHECK 30 | { 31 | 32 | CloseHandle(h_dll); 33 | VirtualFree(buffer,0, MEM_RELEASE); 34 | return 0; 35 | } 36 | else 37 | { 38 | CloseHandle(h_dll); 39 | return (uintptr_t)buffer; 40 | } 41 | } 42 | inline PIMAGE_NT_HEADERS get_nt_header(uintptr_t base) 43 | { 44 | PIMAGE_DOS_HEADER dos_headers = PIMAGE_DOS_HEADER(base); 45 | return PIMAGE_NT_HEADERS(base + dos_headers->e_lfanew); 46 | } 47 | inline bool mask_compare(void* buffer, const char* pattern, const char* mask) 48 | { 49 | for (auto b = reinterpret_cast(buffer); *mask; ++pattern, ++mask, ++b) 50 | { 51 | if (*mask == 'x' && *reinterpret_cast(pattern) != *b) 52 | { 53 | return FALSE; 54 | } 55 | } 56 | return TRUE; 57 | } 58 | inline PBYTE find_pattern(const char* pattern, const char* mask) 59 | { 60 | MODULEINFO info = {0}; 61 | GetModuleInformation(GetCurrentProcess(), GetModuleHandleA(0), &info, sizeof(info)); 62 | info.SizeOfImage -= static_cast(strlen(mask)); 63 | for (auto i = 0UL; i < info.SizeOfImage; i++) 64 | { 65 | auto addr = reinterpret_cast(info.lpBaseOfDll) + i; 66 | if (mask_compare(addr, pattern, mask)) 67 | { 68 | return addr; 69 | } 70 | } 71 | } 72 | inline int get_function_length(void* funcaddress) 73 | { 74 | int length = 0; 75 | for (length = 0; *((UINT32*)(&((unsigned char*)funcaddress)[length])) != 0xCCCCCCCC; ++length); 76 | return length; 77 | } 78 | inline HWND hwndout; 79 | inline BOOL EnumWindowProcMy(HWND input, LPARAM lParam) 80 | { 81 | 82 | DWORD lpdwProcessId; 83 | GetWindowThreadProcessId(input, &lpdwProcessId); 84 | if (lpdwProcessId == lParam) 85 | { 86 | hwndout = input; 87 | return FALSE; 88 | } 89 | return true; 90 | } 91 | inline HWND get_hwnd_of_process_id(int target_process_id) 92 | { 93 | EnumWindows(EnumWindowProcMy, target_process_id); 94 | return hwndout; 95 | } 96 | 97 | 98 | } -------------------------------------------------------------------------------- /utils/xor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | template struct EnsureCompileTime { 4 | enum : int { 5 | Value = X 6 | }; 7 | }; 8 | #define Seed ((__TIME__[7] - '0') * 1 + (__TIME__[6] - '0') * 10 + \ 9 | (__TIME__[4] - '0') * 60 + (__TIME__[3] - '0') * 600 + \ 10 | (__TIME__[1] - '0') * 3600 + (__TIME__[0] - '0') * 36000) 11 | 12 | __forceinline constexpr int LinearCongruentGenerator(int Rounds) { 13 | return 1013904223 + 1664525 * ((Rounds > 0) ? LinearCongruentGenerator(Rounds - 1) : Seed & 0xFFFFFFFF); 14 | } 15 | #define Random() EnsureCompileTime::Value 16 | #define RandomNumber(Min, Max) (Min + (Random() % (Max - Min + 1))) 17 | template struct IndexList {}; 18 | template struct Append; 19 | template struct Append, Right> { 20 | typedef IndexList Result; 21 | }; 22 | template struct ConstructIndexList { 23 | typedef typename Append::Result, N - 1>::Result Result; 24 | }; 25 | template <> struct ConstructIndexList<0> { 26 | typedef IndexList<> Result; 27 | }; 28 | const char XORKEY_A = static_cast(0x13); 29 | const wchar_t XORKEY_W = static_cast(0x133); 30 | __declspec(noinline) constexpr char EncryptCharacterA(const char Character, int Index) { 31 | return Character ^ (XORKEY_A + Index); 32 | } 33 | template class CingA; 34 | template class CingA > { 35 | private: 36 | char Value[sizeof...(Index) + 1]; 37 | public: 38 | __forceinline constexpr CingA(const char* const String) 39 | : Value{ EncryptCharacterA(String[Index], Index)... } {} 40 | 41 | __forceinline char* decrypt() { 42 | for (int t = 0; t < sizeof...(Index); t++) { 43 | Value[t] = Value[t] ^ (XORKEY_A + t); 44 | } 45 | Value[sizeof...(Index)] = '\0'; 46 | return Value; 47 | } 48 | 49 | __forceinline char* get() { 50 | return Value; 51 | } 52 | }; 53 | __declspec(noinline) constexpr wchar_t EncryptCharacterW(const wchar_t Character, int Index) { 54 | return Character ^ (XORKEY_W + Index); 55 | } 56 | template class CingW; 57 | template class CingW > { 58 | private: 59 | wchar_t Value[sizeof...(Index) + 1]; 60 | public: 61 | __forceinline constexpr CingW(const wchar_t* const String) 62 | : Value{ EncryptCharacterW(String[Index], Index)... } {} 63 | 64 | __forceinline wchar_t* decrypt() { 65 | for (int t = 0; t < sizeof...(Index); t++) { 66 | Value[t] = Value[t] ^ (XORKEY_W + t); 67 | } 68 | Value[sizeof...(Index)] = '\0\0'; 69 | return Value; 70 | } 71 | 72 | __forceinline wchar_t* get() { 73 | return Value; 74 | } 75 | }; 76 | 77 | #define xor_a( String ) ( CingA::Result>( String ).decrypt() ) 78 | #define xor_w( String ) ( CingW::Result>( String ).decrypt() ) 79 | --------------------------------------------------------------------------------