├── README.md └── include └── lazy_import.hpp /README.md: -------------------------------------------------------------------------------- 1 | # lazy_import 2 | 3 | > [!WARNING]\ 4 | > Deprecation warning. This repository is deprecated and no longer supported, use the newer, stable, convenient and extended version of this repository - https://github.com/annihilatorq/shadow_syscall 5 | 6 | Lazy Import technique implementation to call any import in the runtime. It will be very useful for calling undocumented functions, as well as for making it difficult for a reverse engineer to analyze your binary file, more specifically - calls to various APIs. 7 | The implementation also includes call caching for speed and optimization purposes. 8 | 9 | Currently supports and tested only: MSVC compiler, x64-x86 Debug/Release, `std::c++14 - std::c++23` 10 | Supports forwarded imports like `HeapAlloc()` etc. 11 | 12 | ### Quick example 13 | ```cpp 14 | LI(int, MessageBoxA).call(nullptr, "Hello world.", "Goodbye world...", MB_OK); 15 | ``` 16 | 17 | > [!IMPORTANT]\ 18 | > With this importer you can call absolutely any export from the DLL module you need, this is useful for conveniently calling undocumented functions. 19 | 20 | ## Detailed example 21 | 22 | ```cpp 23 | int main(void) 24 | { 25 | // We need to load a module that contains MessageBoxA export. 26 | LoadLibraryA("user32.dll"); 27 | 28 | // Creating an instance (not necessarily) 29 | auto instance = LI(int, MessageBoxA); // or LI_FROM(int, "user32.dll", MessageBoxA); 30 | 31 | // MessageBoxA default call 32 | int result = instance.call(nullptr, "Hello world.", "Goodbye world...", MB_OK); 33 | 34 | // MessageBoxA cached call 35 | for (int i = 0; i < 5; ++i) 36 | result = instance.cached_call(nullptr, "Hello world.", "Goodbye world...", MB_OK); 37 | 38 | // Check for return value 39 | std::cout << "Last MessageBoxA returned: " << result << '\n'; 40 | 41 | return EXIT_SUCCESS; 42 | } 43 | ``` 44 | 45 | ## 🚀 Features 46 | 47 | - Ability to cache every call. 48 | - Ability to disable exceptions within the code. 49 | - Doesn't leave any strings in executable memory (exclude exceptions). 50 | - Compile-time import name hashing. 51 | - Doesn't leave any imports in the executable. 52 | - Header includes only `` so that the compilation time is minimized. 53 | 54 | ## 🛠️ Configuration 55 | 56 | | `#define` | EFFECT | 57 | | ----------------------------------------- | --------------------------------------------------------------------------------------- | 58 | | `LAZY_IMPORT_DISABLE_FORCEINLINE` | disables force inlining | 59 | | `LAZY_IMPORT_DISABLE_EXCEPTIONS` | disables all exceptions and returns 0 if the function fails. | 60 | | `LAZY_IMPORT_CASE_INSENSITIVE` | disables case sensitivity in the hashing algorithm | 61 | | `LAZY_IMPORT_DISABLE_CACHING` | completely disables caching of any calls. | 62 | 63 | > [!WARNING]\ 64 | > If you use this utility to protect imports from reverse engineering, be careful when calling redirected (forwarded) exports, because when you search for the necessary string, it will be easy to see it in runtime. If you already know which main export follows the forward declaration - call it, it will also save you from resource overhead. 65 | 66 | ## IDA Pro Pseudocode Output 67 | Build configuration: MSVC compiler, Release x64, std::c++23 std::c17, /O2 flag. 68 | 69 | `#define LAZY_IMPORT_DISABLE_EXCEPTIONS` 70 | ```c 71 | __int64 __fastcall li_call(__int64 a1, __int64 import_hash) 72 | { 73 | __int64 v2; // r14 74 | PVOID *v4; // rcx 75 | PVOID *v5; // r12 76 | int *v6; // r11 77 | __int64 v7; // rax 78 | _DWORD *v8; // rbp 79 | unsigned int v9; // eax 80 | __int64 v10; // rsi 81 | unsigned int *v11; // rdi 82 | __int64 v12; // r15 83 | __int64 v13; // r10 84 | char *v14; // rbx 85 | int v15; // eax 86 | char v16; // r9 87 | char v17; // dl 88 | int v18; // ecx 89 | int v19; // eax 90 | PVOID *v21; // [rsp+30h] [rbp+8h] 91 | 92 | v2 = 0i64; 93 | v4 = &NtCurrentPeb()->Ldr->Reserved2[1]; 94 | v21 = v4; 95 | v5 = (PVOID *)*v4; 96 | if ( *v4 == v4 ) 97 | return 0i64; 98 | while ( 1 ) 99 | { 100 | if ( v5[12] ) 101 | { 102 | v6 = (int *)v5[6]; 103 | if ( v6 ) 104 | { 105 | v7 = v6[15]; 106 | v8 = (int *)((char *)v6 + *(unsigned int *)((char *)v6 + v7 + 136)); 107 | if ( *(_WORD *)v6 == 23117 && (*(_WORD *)((char *)v6 + v7 + 24) != 523 || *(int *)((char *)v6 + v7 + 140)) ) 108 | { 109 | v9 = v8[6]; 110 | if ( v9 ) 111 | { 112 | v10 = 0i64; 113 | v11 = (unsigned int *)((char *)v6 + (unsigned int)v8[8]); 114 | v12 = v9; 115 | do 116 | { 117 | LODWORD(v13) = 0; 118 | v14 = (char *)v6 + *v11; 119 | v15 = 46051127; 120 | v16 = *v14; 121 | if ( *v14 ) 122 | { 123 | do 124 | { 125 | v17 = v16 + 32; 126 | v18 = v13; 127 | if ( (unsigned __int8)(v16 - 65) > 0x19u ) 128 | v17 = v16; 129 | v19 = v17 * (v13 + 46051127) + (v17 ^ v15); 130 | if ( !(_DWORD)v13 ) 131 | v18 = 46051127; 132 | v13 = (unsigned int)(v13 + 1); 133 | v16 = v14[v13]; 134 | v15 = v17 + (v18 ^ 0x2BEAF37) * v19; 135 | } 136 | while ( v16 ); 137 | v6 = (int *)v5[6]; 138 | } 139 | if ( import_hash == v15 ) 140 | v2 = (__int64)v6 141 | + *(unsigned int *)((char *)&v6[*(unsigned __int16 *)((char *)v6 + v10 + (unsigned int)v8[9])] 142 | + (unsigned int)v8[7]); 143 | ++v11; 144 | v10 += 2i64; 145 | --v12; 146 | } 147 | while ( v12 ); 148 | v4 = v21; 149 | } 150 | if ( v2 ) 151 | break; 152 | } 153 | } 154 | } 155 | v5 = (PVOID *)*v5; 156 | if ( v5 == v4 ) 157 | return 0i64; 158 | } 159 | return v2; 160 | } 161 | ``` 162 | 163 | > [!NOTE]\ 164 | > If you notice any bug or error while using this repository - create an `Issue`, describe your problem in as much detail as possible and I will try to release a fix soon. 165 | -------------------------------------------------------------------------------- /include/lazy_import.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LAZY_IMPORT_HPP 2 | #define LAZY_IMPORT_HPP 3 | 4 | #include 5 | 6 | #define LI(type, name) ::lazy_import::internals::lazy_import_internals(LAZY_IMPORT_COMPILETIME_HASH(#name)) 7 | #define LI_FROM(type, module_name, import_name) ::lazy_import::internals::lazy_import_internals(LAZY_IMPORT_COMPILETIME_HASH(#import_name)) 8 | 9 | #ifndef LAZY_IMPORT_DISABLE_CACHING 10 | #include 11 | #endif 12 | 13 | #ifndef LAZY_IMPORT_DISABLE_EXCEPTIONS 14 | #define LAZY_IMPORT_EXCEPTION_HANDLING false 15 | #else 16 | #define LAZY_IMPORT_EXCEPTION_HANDLING true 17 | #endif 18 | 19 | #ifndef LAZY_IMPORT_DISABLE_FORCEINLINE 20 | #if defined(_MSC_VER) 21 | #define LAZY_IMPORT_FORCEINLINE __forceinline 22 | #endif 23 | #else 24 | #define LAZY_IMPORT_FORCEINLINE inline 25 | #endif 26 | 27 | #if _HAS_CXX20 28 | #define LAZY_IMPORT_CONSTEVAL consteval 29 | #else 30 | #define LAZY_IMPORT_CONSTEVAL constexpr 31 | #endif 32 | 33 | #ifndef CONTAINING_RECORD 34 | #define CONTAINING_RECORD(address, type, field) ((type *)( \ 35 | (char*)(address) - \ 36 | (unsigned long long)(&((type *)0)->field))) 37 | #endif 38 | 39 | #ifndef IMAGE_DOS_SIGNATURE 40 | #define IMAGE_DOS_SIGNATURE 0x5A4D 41 | #endif 42 | 43 | #ifndef IMAGE_NT_OPTIONAL_HDR_MAGIC 44 | #if defined(_M_X64) 45 | #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x20b 46 | #elif defined(_M_IX86) 47 | #define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b 48 | #endif 49 | #endif 50 | 51 | #ifndef LAZY_IMPORT_CASE_INSENSITIVE 52 | #define LAZY_IMPORT_CASE_SENSITIVITY true 53 | #else 54 | #define LAZY_IMPORT_CASE_SENSITIVITY false 55 | #endif 56 | 57 | #define LAZY_IMPORT_TOLOWER(c) ((c >= 'A' && c <= 'Z') ? (c + 32) : c) 58 | 59 | #define LAZY_IMPORT_COMPILETIME_HASH(x) []() { constexpr unsigned int hash = ::lazy_import::hash::chash(x); return hash; }() 60 | #define LAZY_IMPORT_RUNTIME_HASH(x) ::lazy_import::hash::hash(x) 61 | 62 | namespace lazy_import { 63 | namespace PE 64 | { 65 | struct UNICODE_STRING { 66 | unsigned short length; 67 | unsigned short maximum_length; 68 | wchar_t* buffer; 69 | }; 70 | 71 | typedef struct _LIST_ENTRY { 72 | struct _LIST_ENTRY* flink; 73 | struct _LIST_ENTRY* blink; 74 | } LIST_ENTRY, * PLIST_ENTRY, * PRLIST_ENTRY; 75 | 76 | typedef struct _LDR_DATA_TABLE_ENTRY { 77 | LIST_ENTRY in_load_order_links; 78 | LIST_ENTRY in_memory_order_links; 79 | void* reserved2[2]; 80 | void* dll_base; 81 | void* entry_point; 82 | void* reserved3; 83 | UNICODE_STRING full_dll_name; 84 | UNICODE_STRING base_dll_name; 85 | void* reserved5[3]; 86 | union { 87 | unsigned long check_sum; 88 | void* reserved6; 89 | }; 90 | unsigned long time_date_stamp; 91 | } LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY; 92 | 93 | typedef struct _PEB_LDR_DATA { 94 | unsigned long length; 95 | unsigned char initialized; 96 | void* ss_handle; 97 | LIST_ENTRY in_load_order_module_list; 98 | LIST_ENTRY in_memory_order_module_list; 99 | LIST_ENTRY in_initialization_order_module_list; 100 | } PEB_LDR_DATA, * PPEB_LDR_DATA; 101 | 102 | struct PEB { 103 | unsigned char reserved1[2]; 104 | unsigned char being_debugged; 105 | unsigned char reserved2[1]; 106 | const char* reserved3[2]; 107 | PEB_LDR_DATA* loader_data; 108 | }; 109 | 110 | typedef struct _IMAGE_EXPORT_DIRECTORY { 111 | unsigned long characteristics; 112 | unsigned long time_date_stamp; 113 | unsigned short major_version; 114 | unsigned short minor_version; 115 | unsigned long name; 116 | unsigned long base; 117 | unsigned long number_of_functions; 118 | unsigned long number_of_names; 119 | unsigned long address_of_functions; // RVA from base of image 120 | unsigned long address_of_names; // RVA from base of image 121 | unsigned long address_of_name_ordinals; // RVA from base of image 122 | } IMAGE_EXPORT_DIRECTORY, * PIMAGE_EXPORT_DIRECTORY; 123 | 124 | struct IMAGE_DOS_HEADER { // DOS .EXE header 125 | unsigned short e_magic; // Magic number 126 | unsigned short e_cblp; // Bytes on last page of file 127 | unsigned short e_cp; // Pages in file 128 | unsigned short e_crlc; // Relocations 129 | unsigned short e_cparhdr; // Size of header in paragraphs 130 | unsigned short e_minalloc; // Minimum extra paragraphs needed 131 | unsigned short e_maxalloc; // Maximum extra paragraphs needed 132 | unsigned short e_ss; // Initial (relative) SS value 133 | unsigned short e_sp; // Initial SP value 134 | unsigned short e_csum; // Checksum 135 | unsigned short e_ip; // Initial IP value 136 | unsigned short e_cs; // Initial (relative) CS value 137 | unsigned short e_lfarlc; // File address of relocation table 138 | unsigned short e_ovno; // Overlay number 139 | unsigned short e_res[4]; // Reserved words 140 | unsigned short e_oemid; // OEM identifier (for e_oeminfo) 141 | unsigned short e_oeminfo; // OEM information; e_oemid specific 142 | unsigned short e_res2[10]; // Reserved words 143 | long e_lfanew; // File address of new exe header 144 | }; 145 | 146 | struct IMAGE_FILE_HEADER { 147 | unsigned short machine; 148 | unsigned short number_of_sections; 149 | unsigned long time_date_stamp; 150 | unsigned long pointer_to_symbol_table; 151 | unsigned long number_of_symbols; 152 | unsigned short size_of_optional_header; 153 | unsigned short characteristics; 154 | }; 155 | 156 | struct IMAGE_DATA_DIRECTORY { 157 | unsigned long virtual_address; 158 | unsigned long size; 159 | }; 160 | 161 | typedef struct _IMAGE_OPTIONAL_HEADER64 { 162 | unsigned short magic; 163 | unsigned char major_linker_version; 164 | unsigned char minor_linker_version; 165 | unsigned long size_of_code; 166 | unsigned long size_of_initialized_data; 167 | unsigned long size_of_uninitialized_data; 168 | unsigned long address_of_entry_point; 169 | unsigned long base_of_code; 170 | unsigned long long image_base; 171 | unsigned long section_alignment; 172 | unsigned long file_alignment; 173 | unsigned short major_operating_system_version; 174 | unsigned short minor_operation_system_version; 175 | unsigned short major_image_version; 176 | unsigned short minor_image_version; 177 | unsigned short major_subsystem_version; 178 | unsigned short minor_subsystem_version; 179 | unsigned long win32_version_value; 180 | unsigned long size_of_image; 181 | unsigned long size_of_headers; 182 | unsigned long check_sum; 183 | unsigned short subsystem; 184 | unsigned short dll_characteristics; 185 | unsigned long long size_of_stack_reserve; 186 | unsigned long long size_of_stack_commit; 187 | unsigned long long size_of_heap_reserve; 188 | unsigned long long size_of_heap_commit; 189 | unsigned long loader_flags; 190 | unsigned long number_of_rva_and_sizes; 191 | IMAGE_DATA_DIRECTORY data_directory[16]; 192 | } IMAGE_OPTIONAL_HEADER64, * PIMAGE_OPTIONAL_HEADER64; 193 | 194 | typedef struct _IMAGE_OPTIONAL_HEADER32 { 195 | unsigned short magic; 196 | unsigned char major_linker_version; 197 | unsigned char minor_linker_version; 198 | unsigned long size_of_code; 199 | unsigned long size_of_initialized_data; 200 | unsigned long size_of_uninitialized_data; 201 | unsigned long address_of_entry_point; 202 | unsigned long base_of_code; 203 | unsigned long base_of_data; 204 | unsigned long image_base; 205 | unsigned long section_alignment; 206 | unsigned long file_alignment; 207 | unsigned short major_operating_system_version; 208 | unsigned short minor_operation_system_version; 209 | unsigned short major_image_version; 210 | unsigned short minor_image_version; 211 | unsigned short major_subsystem_version; 212 | unsigned short minor_subsystem_version; 213 | unsigned long win32_version_value; 214 | unsigned long size_of_image; 215 | unsigned long size_of_headers; 216 | unsigned long check_sum; 217 | unsigned short subsystem; 218 | unsigned short dll_characteristics; 219 | unsigned long size_of_stack_reserve; 220 | unsigned long size_of_stack_commit; 221 | unsigned long size_of_heap_reserve; 222 | unsigned long size_of_heap_commit; 223 | unsigned long loader_flags; 224 | unsigned long number_of_rva_and_sizes; 225 | IMAGE_DATA_DIRECTORY data_directory[16]; 226 | } IMAGE_OPTIONAL_HEADER32, * PIMAGE_OPTIONAL_HEADER32; 227 | 228 | typedef struct _IMAGE_NT_HEADERS { 229 | #if defined(_M_X64) 230 | using IMAGE_OPT_HEADER_ARCH = IMAGE_OPTIONAL_HEADER64; 231 | #elif defined(_M_IX86) 232 | using IMAGE_OPT_HEADER_ARCH = IMAGE_OPTIONAL_HEADER32; 233 | #endif 234 | unsigned long signature; 235 | IMAGE_FILE_HEADER file_header; 236 | IMAGE_OPT_HEADER_ARCH optional_header; 237 | } IMAGE_NT_HEADERS, * PIMAGE_NT_HEADERS; 238 | } 239 | 240 | using pointer_t = unsigned long long; 241 | 242 | #ifndef LAZY_IMPORT_DISABLE_EXCEPTIONS 243 | namespace exception 244 | { 245 | class simplest_exception 246 | { 247 | public: 248 | simplest_exception(const char* message) : m_message(message) {} 249 | 250 | const char* what() const noexcept 251 | { 252 | return m_message; 253 | } 254 | 255 | private: 256 | const char* m_message; 257 | }; 258 | } 259 | #endif 260 | 261 | namespace hash 262 | { 263 | constexpr unsigned int magic_value = (__TIME__[1] + __TIME__[4] + __TIME__[6] + __TIME__[7]) * 0x3129392013; 264 | 265 | template 266 | LAZY_IMPORT_FORCEINLINE constexpr unsigned int hash_single_char(unsigned int offset, unsigned int index, CharT c) 267 | { 268 | return static_cast(c + ((offset ^ c) + (magic_value + index) * c) * 269 | (magic_value ^ (index == 0 ? magic_value : index))); 270 | } 271 | 272 | template 273 | LAZY_IMPORT_FORCEINLINE LAZY_IMPORT_CONSTEVAL unsigned int chash(const char* str) noexcept 274 | { 275 | unsigned int result = magic_value; 276 | 277 | for (unsigned int i = 0; ; i++) 278 | { 279 | char c = str[i]; 280 | if (c == '\0') break; 281 | result = hash_single_char(result, i, CaseSensitive ? LAZY_IMPORT_TOLOWER(c) : c); 282 | } 283 | 284 | return result; 285 | } 286 | 287 | template 288 | LAZY_IMPORT_FORCEINLINE const unsigned int hash(const CharT* str) noexcept 289 | { 290 | unsigned int result = magic_value; 291 | 292 | for (unsigned int i = 0; ; i++) 293 | { 294 | CharT c = str[i]; 295 | if (c == '\0') break; 296 | result = hash_single_char(result, i, static_cast(CaseSensitive ? LAZY_IMPORT_TOLOWER(c) : c)); 297 | } 298 | 299 | return result; 300 | } 301 | } 302 | 303 | namespace utils 304 | { 305 | namespace string 306 | { 307 | LAZY_IMPORT_FORCEINLINE const char* strtrim( 308 | const char* str, char delimiter) 309 | { 310 | const char* result = str; 311 | while (*str != '\0') { 312 | if (*str == delimiter) { 313 | result = str + 1; 314 | } 315 | str++; 316 | } 317 | return result; 318 | } 319 | } 320 | 321 | LAZY_IMPORT_FORCEINLINE const ::lazy_import::PE::PEB* get_ppeb() noexcept 322 | { 323 | #if defined(_M_X64) 324 | return reinterpret_cast(__readgsqword(0x60)); 325 | #elif defined(_M_IX86) 326 | return reinterpret_cast(__readfsdword(0x30)); 327 | #endif 328 | } 329 | 330 | LAZY_IMPORT_FORCEINLINE const ::lazy_import::PE::PIMAGE_NT_HEADERS nt_header( 331 | pointer_t module_base) noexcept 332 | { 333 | return reinterpret_cast<::lazy_import::PE::PIMAGE_NT_HEADERS>(module_base + reinterpret_cast(module_base)->e_lfanew); 334 | } 335 | 336 | LAZY_IMPORT_FORCEINLINE const ::lazy_import::PE::IMAGE_DOS_HEADER* dos_header( 337 | pointer_t module_base) noexcept 338 | { 339 | return reinterpret_cast<::lazy_import::PE::IMAGE_DOS_HEADER*>(module_base); 340 | } 341 | 342 | LAZY_IMPORT_FORCEINLINE const ::lazy_import::PE::IMAGE_NT_HEADERS::IMAGE_OPT_HEADER_ARCH* optional_header( 343 | pointer_t module_base) noexcept 344 | { 345 | return &nt_header(module_base)->optional_header; 346 | } 347 | 348 | LAZY_IMPORT_FORCEINLINE const pointer_t dll_base( 349 | ::lazy_import::PE::_LDR_DATA_TABLE_ENTRY* table_entry) noexcept 350 | { 351 | return reinterpret_cast(table_entry->dll_base); 352 | } 353 | 354 | LAZY_IMPORT_FORCEINLINE ::lazy_import::PE::PEB_LDR_DATA* loader_data() noexcept 355 | { 356 | return reinterpret_cast<::lazy_import::PE::PEB_LDR_DATA*>(get_ppeb()->loader_data); 357 | } 358 | 359 | #ifndef LAZY_IMPORT_DISABLE_CACHING 360 | template 361 | LAZY_IMPORT_FORCEINLINE const bool value_stored_in_map( 362 | std::unordered_map<_Type1, _Type2> _map, _Type1 _key) noexcept 363 | { 364 | return (_map.find(_key) != _map.end()); 365 | } 366 | #endif 367 | } 368 | 369 | namespace detail 370 | { 371 | class module_export_info { 372 | public: 373 | using const_pointer_t = const pointer_t; 374 | 375 | LAZY_IMPORT_FORCEINLINE module_export_info( 376 | const_pointer_t base) noexcept : m_base(base) 377 | { 378 | const auto export_dir_data = ::lazy_import::utils::nt_header(base)->optional_header.data_directory[0]; 379 | m_export_dir = reinterpret_cast(base + export_dir_data.virtual_address); 380 | } 381 | 382 | LAZY_IMPORT_FORCEINLINE unsigned long size() const noexcept 383 | { 384 | return m_export_dir->number_of_names; 385 | } 386 | 387 | LAZY_IMPORT_FORCEINLINE const char* const name( 388 | unsigned int iterator) const noexcept 389 | { 390 | return reinterpret_cast(m_base + reinterpret_cast(m_base + m_export_dir->address_of_names)[iterator]); 391 | } 392 | 393 | LAZY_IMPORT_FORCEINLINE const_pointer_t address( 394 | unsigned int iterator) const noexcept 395 | { 396 | const auto rva_table = reinterpret_cast(m_base + m_export_dir->address_of_functions); 397 | const auto ord_table = reinterpret_cast(m_base + m_export_dir->address_of_name_ordinals); 398 | return m_base + rva_table[ord_table[iterator]]; 399 | } 400 | 401 | LAZY_IMPORT_FORCEINLINE bool module_integrity_checks() noexcept(LAZY_IMPORT_EXCEPTION_HANDLING) 402 | { 403 | if (::lazy_import::utils::dos_header(m_base)->e_magic != IMAGE_DOS_SIGNATURE) 404 | { 405 | #ifndef LAZY_IMPORT_DISABLE_EXCEPTIONS 406 | throw ::lazy_import::exception::simplest_exception("DOS header e_magic mismatch"); 407 | #else 408 | return false; 409 | #endif 410 | } 411 | 412 | if (::lazy_import::utils::nt_header(m_base)->optional_header.magic == IMAGE_NT_OPTIONAL_HDR_MAGIC && 413 | ::lazy_import::utils::optional_header(m_base)->data_directory[0].size <= 0ul) 414 | { 415 | return false; 416 | } 417 | 418 | return true; 419 | } 420 | 421 | LAZY_IMPORT_FORCEINLINE bool is_forwarded_export(const_pointer_t ptr) noexcept 422 | { 423 | ::lazy_import::PE::IMAGE_DATA_DIRECTORY export_dir = 424 | ::lazy_import::utils::optional_header(m_base)->data_directory[0]; 425 | 426 | const_pointer_t export_table_start = m_base + export_dir.virtual_address; 427 | const_pointer_t export_table_end = export_table_start + export_dir.size; 428 | 429 | return (ptr >= export_table_start) && (ptr < export_table_end); 430 | } 431 | 432 | private: 433 | const_pointer_t m_base; 434 | const ::lazy_import::PE::IMAGE_EXPORT_DIRECTORY* m_export_dir; 435 | }; 436 | 437 | template 438 | class import_enumerator 439 | { 440 | public: 441 | using const_pointer_t = const pointer_t; 442 | 443 | LAZY_IMPORT_FORCEINLINE const_pointer_t find_import( 444 | const_pointer_t import_hash) noexcept(LAZY_IMPORT_EXCEPTION_HANDLING) 445 | { 446 | auto entry = &::lazy_import::utils::loader_data()->in_load_order_module_list; 447 | bool forward_check = false; 448 | pointer_t import_address = 0; 449 | 450 | for (auto i = entry->flink; i != entry; i = i->flink) 451 | { 452 | auto module_data = CONTAINING_RECORD(i, ::lazy_import::PE::LDR_DATA_TABLE_ENTRY, in_load_order_links); 453 | 454 | if (module_data->base_dll_name.buffer == nullptr) 455 | continue; 456 | 457 | pointer_t module_base = ModuleHash != 0 ? find_target_module(module_data) : ::lazy_import::utils::dll_base(module_data); 458 | 459 | if (module_base == 0) 460 | continue; 461 | 462 | module_export_info exp(module_base); 463 | 464 | if (!exp.module_integrity_checks()) 465 | continue; 466 | 467 | for (unsigned int i = 0; i < exp.size(); ++i) { 468 | if (import_hash == LAZY_IMPORT_RUNTIME_HASH(exp.name(i))) { 469 | import_address = static_cast(exp.address(i)); 470 | 471 | // HeapAlloc, HeapReAlloc, HeapFree, etc. 472 | if (exp.is_forwarded_export(import_address)) { 473 | auto str = reinterpret_cast(import_address); 474 | auto substr = ::lazy_import::utils::string::strtrim(str, '.'); 475 | 476 | return find_import(LAZY_IMPORT_RUNTIME_HASH(substr)); 477 | } 478 | 479 | break; 480 | } 481 | } 482 | 483 | if (import_address == 0) { 484 | continue; 485 | } 486 | 487 | return import_address; 488 | } 489 | 490 | #ifndef LAZY_IMPORT_DISABLE_EXCEPTIONS 491 | // Make sure that import name is right and module is loaded. 492 | throw ::lazy_import::exception::simplest_exception("Cannot find desired import."); 493 | #endif 494 | 495 | return 0; 496 | } 497 | 498 | private: 499 | LAZY_IMPORT_FORCEINLINE const_pointer_t find_target_module( 500 | ::lazy_import::PE::LDR_DATA_TABLE_ENTRY* module_data) noexcept 501 | { 502 | if (ModuleHash == ::lazy_import::hash::hash(module_data->base_dll_name.buffer)) 503 | return ::lazy_import::utils::dll_base(module_data); 504 | 505 | return 0; 506 | } 507 | }; 508 | } 509 | 510 | namespace internals 511 | { 512 | template 513 | class lazy_import_internals 514 | { 515 | public: 516 | LAZY_IMPORT_FORCEINLINE lazy_import_internals(unsigned int import_hash) noexcept : m_import_hash(import_hash) {} 517 | 518 | template 519 | LAZY_IMPORT_FORCEINLINE ReturnType call(Args... args) noexcept 520 | { 521 | detail::import_enumerator e; 522 | pointer_t import_address = e.find_import(m_import_hash); 523 | return reinterpret_cast(import_address)(args...); 524 | } 525 | 526 | template 527 | LAZY_IMPORT_FORCEINLINE ReturnType cached_call(Args... args) noexcept 528 | { 529 | pointer_t import_address = 0; 530 | #ifndef LAZY_IMPORT_DISABLE_CACHING 531 | if (::lazy_import::utils::value_stored_in_map(m_ptr_map, m_import_hash)) { 532 | import_address = m_ptr_map.at(m_import_hash); 533 | } 534 | else 535 | { 536 | detail::import_enumerator e; 537 | import_address = e.find_import(m_import_hash); 538 | m_ptr_map.insert(std::make_pair(m_import_hash, import_address)); 539 | } 540 | #else 541 | detail::import_enumerator e; 542 | import_address = e.find_import(m_import_hash); 543 | #endif 544 | 545 | return reinterpret_cast(import_address)(args...); 546 | } 547 | 548 | private: 549 | unsigned int m_import_hash = 0; 550 | #ifndef LAZY_IMPORT_DISABLE_CACHING 551 | static inline std::unordered_map m_ptr_map; 552 | #endif 553 | }; 554 | } 555 | } 556 | 557 | #endif // LAZY_IMPORT_HPP 558 | --------------------------------------------------------------------------------