├── main.c ├── win32_imports.h ├── LICENSE.md ├── README.md ├── win32.h └── win32.c /main.c: -------------------------------------------------------------------------------- 1 | #include "win32.h" 2 | 3 | void mainCRTStartup() { 4 | WINDOWS windows; 5 | windows_load(&windows); 6 | HANDLE stdout = windows.GetStdHandle(STD_OUTPUT_HANDLE); 7 | DWORD written = 0; 8 | const char message[] = "Hello, world!\n"; 9 | windows.WriteConsoleA(stdout, message, sizeof message - 1, &written, NULL); 10 | windows_unload(&windows); 11 | } -------------------------------------------------------------------------------- /win32_imports.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_DLL 2 | #define WIN32_DLL(...) 3 | #endif 4 | #ifndef WIN32_PROC 5 | #define WIN32_PROC(...) 6 | #endif 7 | 8 | WIN32_DLL(kernel32) 9 | WIN32_PROC(kernel32, GetStdHandle, HANDLE, DWORD) 10 | WIN32_PROC(kernel32, WriteConsoleA, HANDLE, HANDLE, const VOID*, DWORD, LPDWORD, LPVOID) 11 | 12 | #undef WIN32_DLL 13 | #undef WIN32_PROC 14 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Dale Weiler 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Explicit Windows Imports 2 | 3 | A low-level technique for creating "freestanding" Windows executables, static libraries, and dynamic libraries. 4 | 5 | When Windows executes a "Win32" or "Win64" process it sets up a variety of structures accessible from the GS segment register. In particular there is a [Thread Environment Block](https://en.wikipedia.org/wiki/Win32_Thread_Information_Block) per-thread called the TEB accessible @ offset `0x30` and the TEB has a pointer to the [Process Environment Block](https://en.wikipedia.org/wiki/Process_Environment_Block) called the PEB. The PEB contains some data for the dynamic loader called `LdrData`. This data structure contains several intrusive doubly-linked lists for loaded "modules", which is what NT calls DLLs. We can walk the `InMemoryOrderModuleList` and search for `kernel32.dll` which is always mapped in the address space of every "Win32" and "Win64" process on Windows. Once we have the address of `kernel32.dll` we can start poking at the DOS header which every EXE and DLL begins with to find the offset to the [PE header](https://en.wikipedia.org/wiki/Portable_Executable#Technical_details) since all EXEs and DLLs are PE files. Once we have that header we can look through the "export directory" for a list of exported procedures. We are interested in reading only three procedures from `kernel32.dll`. The three procedures which form the basis of the dynamic loader itself. 6 | * `LoadLibraryA` 7 | * `FreeLibrary` 8 | * `GetProcAddress` 9 | 10 | With these procedures it's possible to dynamically load any of the WinAPI libraries and use them without having any explicit import libraries needed during link time or on process construction. When combined with `/NODEFAULTLIB` it also lets us compile executables that are free of all [Runtime libraries](https://en.wikipedia.org/wiki/Microsoft_Windows_library_files#Runtime_libraries) including the C standard runtime library (MSVCP*.DLL), Universal C Run Time (UCRT), and the Microsoft Visual C++ Runtime. 11 | 12 | This is especially useful for static libraries on Windows where linking against a static library often requires specifying multiple import libraries which that static library depend on. With this custom loader you can produce static libraries which can be linked against without any import libraries. 13 | 14 | In general this effectively replaces the behavior that the [Import Address Table](https://en.wikipedia.org/wiki/Portable_Executable#Import_table) provides but does so through explicit runtime C code, side-stepping link time import library requirements and the automatic import behavior that the Windows dynamic loader does on process construction automatically. As a side-effect of this, [IAT hooking](https://www.ired.team/offensive-security/code-injection-process-injection/import-adress-table-iat-hooking) is also made impossible. 15 | 16 | The loader provided by this project also ignores a feature of DLLs on Windows called "DLL forwarding". DLL forwarding is a feature of the Windows dynamic linker which allows a DLL to [redirect symbols to another DLL](https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-redirection). When DLL forwarding behavior is not implemented it means that traditional DLL hijacking and proxying tools are also thwarted. 17 | 18 | ## How to use 19 | The easiest way to use this is to include the three files into your project and to modify the `win32_imports.h` header file to explicitly list the procedures you want to import and from which DLLs using an [`XMACROS` approach](https://en.wikipedia.org/wiki/X_macro) 20 | 21 | The `WIN32_DLL` macro is used to list the DLLs you'd like to import at runtime. It only takes a single argument which is the case-insensitive name of the DLL as a C identifier (not a string). 22 | 23 | The `WIN32_PROC` macro is used to list the procedures you'd like to import at runtime. It takes a variable count of arguments, the first of which expects the name of the DLL as used by a previous `WIN32_DLL` macro to read the procedure from, the name of the procedure as a C identifier (not a string), the return type of that procedure, and then a variable list of types for the procedure arguments. 24 | 25 | Once you have done this you can load all your WinAPI DLLs and procedures with the following C code from the entry point of your application or library. 26 | ```c 27 | WINDOWS windows; 28 | windows_load(&windows); 29 | ``` 30 | 31 | You can completely unload the WinAPI runtime as well, something that is not typically possible within a process. 32 | ```c 33 | windows_unload(&windows); 34 | ``` 35 | 36 | To make any WinAPI calls you'll do so indirectly through function pointers stored off the `WINDOWS` structure like 37 | ```c 38 | windows.ProcedureName(...); 39 | ``` 40 | 41 | As you can see you cannot use the regular WinAPI headers for function prototypes. Instead it's encouraged you use the included `win32.h` header. You can define missing structures, typedefs, and constants for what you need here. You can then use them to define prototypes in the `win32_imports.h` header. 42 | 43 | ## Example 44 | There is a minimal `"Hello, world!"` example included in this repo in `main.c`. You can compile it with `clang` on Windows or Linux with the following steps 45 | ```none 46 | $ clang -target x86_64-unknown-windows -ffreestanding -nodefaultlibs -c main.c win32.c -O1 47 | ``` 48 | 49 | Linking can be done with either `lld-link` from clang 50 | ```none 51 | $ lld-link main.o win32.o -subsystem:console # clang 52 | ``` 53 | Or using the included VS linker `link.exe` 54 | ```none 55 | $ link main.o win32.o -subsystem:console # vs link 56 | ``` 57 | 58 | This should produce a rather lean ~2 KiB Win64 executable that prints `"Hello, world!"` to the console. -------------------------------------------------------------------------------- /win32.h: -------------------------------------------------------------------------------- 1 | #ifndef WIN32_H 2 | #define WIN32_H 3 | 4 | #ifdef FALSE 5 | #undef FALSE 6 | #endif 7 | 8 | #ifdef TRUE 9 | #undef TRUE 10 | #endif 11 | 12 | #ifdef NULL 13 | #undef NULL 14 | #endif 15 | 16 | #define FALSE 0 17 | #define TRUE 1 18 | 19 | #define NULL 0 20 | 21 | typedef unsigned char BOOLEAN, BYTE, UCHAR, *PBYTE, *PUCHAR; 22 | typedef unsigned short WORD, *PWORD, *LPWORD, USHORT; 23 | typedef unsigned long DWORD, *PDWORD, *LPDWORD, ULONG; 24 | typedef unsigned long long ULONGLONG, ULONG64, ULONG_PTR, SIZE_T; 25 | typedef unsigned short WCHAR, *PWCHAR, *NWPSTR, *LPWSTR, *PWSTR; 26 | typedef long LONG; 27 | typedef const LPWSTR PCWSTR, LPCWSTR; 28 | typedef void VOID, *PVOID, *LPVOID; 29 | typedef const char* LPCSTR; 30 | typedef void (*FARPROC)(void); 31 | typedef PVOID HANDLE, HINSTANCE, HMODULE; 32 | typedef int BOOL; 33 | 34 | typedef struct _LIST_ENTRY { 35 | struct _LIST_ENTRY *Flink; 36 | struct _LIST_ENTRY *Blink; 37 | } LIST_ENTRY, *PLIST_ENTRY; 38 | 39 | typedef struct _UNICODE_STRING { 40 | USHORT Length; 41 | USHORT MaximumLength; 42 | PWSTR Buffer; 43 | } UNICODE_STRING, *PUNICODE_STRING; 44 | 45 | typedef struct _PEB_LDR_DATA { 46 | ULONG Length; 47 | BOOLEAN Initialized; 48 | PVOID SsHandle; 49 | LIST_ENTRY InLoaderOrderModuleList; 50 | LIST_ENTRY InMemoryOrderModuleList; 51 | LIST_ENTRY InInitializationOrderModuleList; 52 | PVOID EntryInProgress; 53 | BOOLEAN ShutdownInProgress; 54 | HANDLE ShutdownThreadId; 55 | } PEB_LDR_DATA, *PPEB_LDR_DATA; 56 | 57 | typedef struct _LDR_DATA_TABLE_ENTRY { 58 | LIST_ENTRY InLoadOrderLinks; 59 | LIST_ENTRY InMemoryOrderLinks; 60 | LIST_ENTRY InInitializationOrderLinks; 61 | PVOID DllBase; 62 | PVOID EntryPoint; 63 | ULONG SizeOfImage; 64 | UNICODE_STRING FullDllName; 65 | UNICODE_STRING BaseDllName; 66 | ULONG Flags; 67 | USHORT LoadCount; 68 | USHORT TlsIndex; 69 | union { 70 | LIST_ENTRY HashLinks; 71 | struct { 72 | PVOID SectionPointer; 73 | ULONG CheckSum; 74 | }; 75 | }; 76 | union { 77 | ULONG TimeDateStamp; 78 | PVOID LoadedImports; 79 | }; 80 | PVOID EntryPointActivationContext; 81 | PVOID PatchInformation; 82 | LIST_ENTRY ForwarderLinks; 83 | LIST_ENTRY ServiceTagLinks; 84 | LIST_ENTRY StaticLinks; 85 | } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY; 86 | 87 | typedef struct _PEB { 88 | BOOLEAN InheritedAddressSpace; 89 | BOOLEAN ReadImageFileExecOptions; 90 | BOOLEAN BeingDebugged; 91 | UCHAR ImageUsedLargePages : 1; 92 | UCHAR IsProtectedProcess : 1; 93 | UCHAR IsImageDynamicallyRelocated : 1; 94 | UCHAR SkipPatchingUser32Forwarders : 1; 95 | UCHAR IsPackagedProcess : 1; 96 | UCHAR IsAppContainer : 1; 97 | UCHAR IsProtectedProcessLight : 1; 98 | UCHAR IsLongPathAwareProcess : 1; 99 | HANDLE Mutant; 100 | HMODULE ImageBaseAddress; 101 | PPEB_LDR_DATA LdrData; 102 | } PEB, *PPEB; 103 | 104 | typedef struct _CLIENT_ID { 105 | HANDLE UniqueProcess; 106 | HANDLE UniqueThread; 107 | } CLIENT_ID, *PCLIENT_ID; 108 | 109 | // Win64 TIB 110 | typedef struct _NT_TIB { 111 | ULONG64 ExceptionList; 112 | ULONG64 StackBase; 113 | ULONG64 StackLimit; 114 | ULONG64 SubSystemTib; 115 | ULONG64 FiberData; 116 | ULONG64 ArbitraryUserPointer; 117 | ULONG64 Self; 118 | } NT_TIB; 119 | 120 | typedef struct _TEB { 121 | NT_TIB Tib; 122 | PVOID EnvironmentPointer; 123 | CLIENT_ID ClientId; 124 | PVOID ActiveRpcHandle; 125 | PVOID ThreadLocalStoragePointer; 126 | PPEB Peb; 127 | // ... 128 | } TEB, *PTEB; 129 | 130 | typedef struct _IMAGE_DOS_HEADER { 131 | WORD e_magic; 132 | WORD e_cblp; 133 | WORD e_cp; 134 | WORD e_crlc; 135 | WORD e_cparhdr; 136 | WORD e_minalloc; 137 | WORD e_maxalloc; 138 | WORD e_ss; 139 | WORD e_sp; 140 | WORD e_csum; 141 | WORD e_ip; 142 | WORD e_cs; 143 | WORD e_lfarlc; 144 | WORD e_ovno; 145 | WORD e_res[4]; 146 | WORD e_oemid; 147 | WORD e_oeminfo; 148 | WORD e_res2[10]; 149 | DWORD e_lfanew; 150 | } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 151 | 152 | typedef struct _IMAGE_FILE_HEADER { 153 | WORD Machine; 154 | WORD NumberOfSections; 155 | DWORD TimeDateStamp; 156 | DWORD PointerToSymbolTable; 157 | DWORD NumberOfSymbols; 158 | WORD SizeOfOptionalHeader; 159 | WORD Characteristics; 160 | } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; 161 | 162 | typedef struct _IMAGE_DATA_DIRECTORY { 163 | DWORD VirtualAddress; 164 | DWORD Size; 165 | } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; 166 | 167 | #define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 168 | 169 | typedef struct _IMAGE_OPTIONAL_HEADER64 { 170 | WORD Magic; /* 0x20b */ 171 | BYTE MajorLinkerVersion; 172 | BYTE MinorLinkerVersion; 173 | DWORD SizeOfCode; 174 | DWORD SizeOfInitializedData; 175 | DWORD SizeOfUninitializedData; 176 | DWORD AddressOfEntryPoint; 177 | DWORD BaseOfCode; 178 | ULONGLONG ImageBase; 179 | DWORD SectionAlignment; 180 | DWORD FileAlignment; 181 | WORD MajorOperatingSystemVersion; 182 | WORD MinorOperatingSystemVersion; 183 | WORD MajorImageVersion; 184 | WORD MinorImageVersion; 185 | WORD MajorSubsystemVersion; 186 | WORD MinorSubsystemVersion; 187 | DWORD Win32VersionValue; 188 | DWORD SizeOfImage; 189 | DWORD SizeOfHeaders; 190 | DWORD CheckSum; 191 | WORD Subsystem; 192 | WORD DllCharacteristics; 193 | ULONGLONG SizeOfStackReserve; 194 | ULONGLONG SizeOfStackCommit; 195 | ULONGLONG SizeOfHeapReserve; 196 | ULONGLONG SizeOfHeapCommit; 197 | DWORD LoaderFlags; 198 | DWORD NumberOfRvaAndSizes; 199 | IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; 200 | } IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64; 201 | 202 | typedef struct _IMAGE_NT_HEADERS64 { 203 | DWORD Signature; 204 | IMAGE_FILE_HEADER FileHeader; 205 | IMAGE_OPTIONAL_HEADER64 OptionalHeader; 206 | } IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64; 207 | 208 | typedef struct _IMAGE_EXPORT_DIRECTORY { 209 | DWORD Characteristics; 210 | DWORD TimeDateStamp; 211 | WORD MajorVersion; 212 | WORD MinorVersion; 213 | DWORD Name; 214 | DWORD Base; 215 | DWORD NumberOfFunctions; 216 | DWORD NumberOfNames; 217 | DWORD AddressOfFunctions; 218 | DWORD AddressOfNames; 219 | DWORD AddressOfNameOrdinals; 220 | } IMAGE_EXPORT_DIRECTORY,*PIMAGE_EXPORT_DIRECTORY; 221 | 222 | #define STD_OUTPUT_HANDLE ((DWORD)-11) 223 | 224 | typedef struct _KERNEL32 { 225 | HMODULE (*LoadLibraryA)(LPCSTR); 226 | BOOL (*FreeLibrary)(HMODULE); 227 | FARPROC (*GetProcAddress)(HMODULE, LPCSTR); 228 | } KERNEL32, *PKERNEL32; 229 | 230 | typedef struct _WINDOWS { 231 | KERNEL32 kernel32; 232 | #define WIN32_DLL(name) HANDLE name ## _dll; 233 | #include "win32_imports.h" 234 | #undef WIN32_DLL 235 | #define WIN32_PROC(dll, name, ret, ...) ret (*name)(__VA_ARGS__); 236 | #include "win32_imports.h" 237 | #undef WIN32_PROC 238 | } WINDOWS, *PWINDOWS; 239 | 240 | BOOLEAN windows_load(PWINDOWS windows); 241 | void windows_unload(PWINDOWS windows); 242 | 243 | #endif // WIN32_H -------------------------------------------------------------------------------- /win32.c: -------------------------------------------------------------------------------- 1 | #include "win32.h" 2 | 3 | // On MSVC there is a __readgsqword intrinsic, on mingw we use some inline asm. 4 | #if defined(__MINGW32__) || defined(__clang__) 5 | static inline ULONG64 __readgsqword(ULONG64 offset) { 6 | ULONG64 result; 7 | __asm__("movq %%gs:%[offset], %[result]" 8 | : [result] "=r" (result) 9 | : [offset] "m" ((*(ULONG64 *)offset))); 10 | return result; 11 | } 12 | #endif // __MINGW32__ 13 | 14 | static inline void RtlInitUnicodeString(PUNICODE_STRING target, PCWSTR source) { 15 | target->Buffer = source; 16 | if (source) { 17 | SIZE_T length = 0; 18 | for (; source[length]; length++); 19 | length *= sizeof(WCHAR); 20 | if (length > 0xfffc) { 21 | length = 0xfffc; 22 | } 23 | target->Length = length; 24 | target->MaximumLength = target->Length + sizeof(WCHAR); 25 | } else { 26 | target->Length = 0; 27 | target->MaximumLength = 0; 28 | } 29 | } 30 | 31 | static inline LONG RtlCompareUnicodeStrings(const WCHAR *lhs, SIZE_T lhs_len, 32 | const WCHAR *rhs, SIZE_T rhs_len, 33 | BOOLEAN case_insensitive) 34 | { 35 | LONG result = 0; 36 | SIZE_T len = lhs_len < rhs_len ? lhs_len : rhs_len; 37 | if (case_insensitive) { 38 | // Normally Windows would use NLS here to perform the case mapping based on 39 | // the locale but we can use an ASCII case map safely without any dependency. 40 | #define CASEMAP(ch) \ 41 | (((ch) >= 'a' && (ch) <= 'z') ? (ch) - 32 : (ch)) 42 | for (; result == 0 && len; lhs++, rhs++, len--) { 43 | result = CASEMAP(*lhs) - CASEMAP(*rhs); 44 | } 45 | } else while (result == 0 && len--) { 46 | result = *lhs++ - *rhs++; 47 | } 48 | return result == 0 ? lhs_len - rhs_len : result; 49 | } 50 | 51 | static inline LONG RtlCompareUnicodeString(const PUNICODE_STRING lhs, 52 | const PUNICODE_STRING rhs, 53 | BOOLEAN case_insensitive) 54 | { 55 | return RtlCompareUnicodeStrings(lhs->Buffer, lhs->Length / sizeof(WCHAR), 56 | rhs->Buffer, rhs->Length / sizeof(WCHAR), 57 | case_insensitive); 58 | } 59 | 60 | static inline BOOLEAN RtlEqualUnicodeString(const PUNICODE_STRING lhs, 61 | const PUNICODE_STRING rhs, 62 | BOOLEAN case_insensitive) 63 | { 64 | if (lhs->Length != rhs->Length) { 65 | return FALSE; 66 | } 67 | return !RtlCompareUnicodeString(lhs, rhs, case_insensitive); 68 | } 69 | 70 | static inline PVOID RVA(HMODULE module, DWORD va) { 71 | return (PVOID)((ULONG_PTR)(module) + va); 72 | } 73 | 74 | #define IMAGE_DOS_SIGNATURE 0x5a4d 75 | #define IMAGE_NT_SIGNATURE 0x00004550 76 | #define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b 77 | 78 | static inline const PIMAGE_NT_HEADERS64 RtlImageNtHeader(HMODULE module) { 79 | const PIMAGE_DOS_HEADER dos = (const PIMAGE_DOS_HEADER)(module); 80 | if (dos->e_magic != IMAGE_DOS_SIGNATURE) { 81 | return NULL; 82 | } 83 | const PIMAGE_NT_HEADERS64 nt = (const PIMAGE_NT_HEADERS64)((ULONG_PTR)(dos) + dos->e_lfanew); 84 | if (nt->Signature != IMAGE_NT_SIGNATURE) { 85 | return NULL; 86 | } 87 | return nt; 88 | } 89 | 90 | static inline PVOID RtlImageDirectoryEntryToData(HMODULE module, 91 | BOOL image, 92 | WORD dir, 93 | ULONG *size) 94 | { 95 | const PIMAGE_NT_HEADERS64 nt = RtlImageNtHeader(module); 96 | if (!nt) { 97 | return NULL; 98 | } 99 | if (nt->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR64_MAGIC) { 100 | return NULL; 101 | } 102 | if (dir >= nt->OptionalHeader.NumberOfRvaAndSizes) { 103 | return NULL; 104 | } 105 | const PIMAGE_DATA_DIRECTORY directory = &nt->OptionalHeader.DataDirectory[dir]; 106 | const DWORD address = directory->VirtualAddress; 107 | if (address == 0) { 108 | return NULL; 109 | } 110 | *size = directory->Size; 111 | if (image || address < nt->OptionalHeader.SizeOfHeaders) { 112 | return (PVOID)((ULONG_PTR)(module) + address); 113 | } 114 | return NULL; 115 | } 116 | 117 | static inline FARPROC RtlFindExportedRoutineByName(HMODULE module, LPCSTR name) { 118 | DWORD export_size = 0; 119 | const PIMAGE_EXPORT_DIRECTORY export_directory = 120 | RtlImageDirectoryEntryToData(module, TRUE, 0, &export_size); 121 | if (!export_directory || export_size < sizeof *export_directory) { 122 | return NULL; 123 | } 124 | // Binary search for the name in the exports list. 125 | const PWORD ordinals = (const PWORD)(RVA(module, export_directory->AddressOfNameOrdinals)); 126 | const PDWORD names = (const PDWORD)(RVA(module, export_directory->AddressOfNames)); 127 | DWORD min = 0; 128 | DWORD max = export_directory->NumberOfNames - 1; 129 | DWORD ordinal = -1; 130 | while (min <= max) { 131 | DWORD pos = (min + max) / 2; 132 | LPCSTR export_name = (LPCSTR)(RVA(module, names[pos])); 133 | PBYTE lhs = (PBYTE)(export_name); 134 | PBYTE rhs = (PBYTE)(name); 135 | for (; *lhs && *lhs == *rhs; lhs++, rhs++); 136 | const int dir = (*lhs > *rhs) - (*rhs > *lhs); 137 | if (dir == 0) { 138 | ordinal = ordinals[pos]; 139 | break; 140 | } else if (dir > 0) { 141 | max = pos - 1; 142 | } else { 143 | min = pos + 1; 144 | } 145 | } 146 | if (ordinal == -1 || ordinal >= export_directory->NumberOfFunctions) { 147 | return NULL; 148 | } 149 | const PDWORD functions = (const PDWORD)(RVA(module, export_directory->AddressOfFunctions)); 150 | const DWORD function = functions[ordinal]; 151 | if (function == 0) { 152 | return NULL; 153 | } 154 | const ULONG_PTR proc = (const ULONG_PTR)(RVA(module, function)); 155 | if (proc >= (const ULONG_PTR)(export_directory) && 156 | proc < (const ULONG_PTR)(export_directory) + export_size) 157 | { 158 | return NULL; 159 | } 160 | return (FARPROC)(proc); 161 | } 162 | 163 | static inline PTEB NtCurrentTeb() { 164 | return (PTEB)__readgsqword(0x30); 165 | } 166 | 167 | static inline BOOLEAN load(PKERNEL32 kernel32) { 168 | const PTEB teb = NtCurrentTeb(); 169 | const PPEB peb = teb->Peb; 170 | const PLIST_ENTRY tail = &peb->LdrData->InMemoryOrderModuleList; 171 | UNICODE_STRING NAME; 172 | RtlInitUnicodeString(&NAME, L"kernel32.dll"); 173 | HMODULE module = NULL; 174 | for (PLIST_ENTRY node = tail->Flink; node != tail; node = node->Flink) { 175 | const PLDR_DATA_TABLE_ENTRY entry = (const PLDR_DATA_TABLE_ENTRY)(node - 1); 176 | const PUNICODE_STRING name = &entry->BaseDllName; 177 | const PWCHAR buffer = name->Buffer; 178 | if (RtlEqualUnicodeString(name, &NAME, TRUE)) { 179 | module = (HMODULE)(entry->DllBase); 180 | break; 181 | } 182 | } 183 | if (!module) { 184 | return FALSE; 185 | } 186 | *(FARPROC *)(&kernel32->LoadLibraryA) = RtlFindExportedRoutineByName(module, "LoadLibraryA"); 187 | *(FARPROC *)(&kernel32->FreeLibrary) = RtlFindExportedRoutineByName(module, "FreeLibrary"); 188 | *(FARPROC *)(&kernel32->GetProcAddress) = RtlFindExportedRoutineByName(module, "GetProcAddress"); 189 | return TRUE; 190 | } 191 | 192 | BOOLEAN windows_load(PWINDOWS windows) { 193 | if (!load(&windows->kernel32)) { 194 | return FALSE; 195 | } 196 | #define WIN32_DLL(name) \ 197 | if (!(windows->name ## _dll = windows->kernel32.LoadLibraryA(#name ".dll"))) { \ 198 | return FALSE; \ 199 | } 200 | #include "win32_imports.h" 201 | #define WIN32_PROC(dll, name, ret, ...) \ 202 | if (!(*(FARPROC *)(&windows->name) = windows->kernel32.GetProcAddress(windows->dll ## _dll, #name))) { \ 203 | return FALSE; \ 204 | } 205 | #include "win32_imports.h" 206 | return TRUE; 207 | } 208 | 209 | void windows_unload(PWINDOWS windows) { 210 | #define WIN32_DLL(name) \ 211 | windows->kernel32.FreeLibrary(windows->name ## _dll); 212 | #include "win32_imports.h" 213 | #undef WIN32_DLL 214 | } --------------------------------------------------------------------------------