diff --git a/include/dlfcn.h b/include/dlfcn.h index db2915e..1f8f8fa 100644 --- a/include/dlfcn.h +++ b/include/dlfcn.h @@ -1,5 +1,5 @@ /* dlfcn.h -*/ + */ #ifndef _DLFCN_H #define _DLFCN_H @@ -7,49 +7,140 @@ #include #include -#ifdef __cplusplus -extern "C" -{ -#endif +__BEGIN_DECLS + +/// @brief Loads a DLL module into the process's address space. +/// +/// This function mimics the POSIX dlopen interface on Windows by using LoadLibraryExA. +/// +/// @param __file The path to the DLL file to load. +/// @param __mode Mode flags controlling symbol resolution behavior. Supports: +/// - RTLD_LAZY: Load the DLL without resolving references immediately (maps to DONT_RESOLVE_DLL_REFERENCES). +/// +/// @return Handle to the loaded module on success, or NULL on failure. +/// On failure, error details are available via dlerror(). +/// +/// @note If __file is NULL, sets ERROR_INVALID_PARAMETER and returns NULL. +extern void * +dlopen (const char *__file, int __mode); + +/// @brief Retrieves the address of a symbol in a loaded module. +/// +/// On first call for a module, this patches the module's Import Address Table +/// (IAT) if necessary, resolving imports manually if the module was loaded with +/// delayed or lazy loading. +/// +/// @param __handle Handle to the loaded module. +/// @param __name Name of the symbol to locate. +/// +/// @return Pointer to the symbol on success, or NULL on failure. +/// On failure, thread-local error message is set and retrievable via dlerror(). +extern void * +dlsym (void *__restrict __handle, const char *__restrict __name); + +/// @brief Unloads a dynamically loaded module. +/// +/// Calls FreeLibrary on the given handle, releasing the loaded module. +/// +/// @param __handle Handle to the module to unload (returned by dlopen). +/// +/// @return 0 on success, -1 on failure. +/// On failure, the Windows last error code is set. +/// +/// @note On failure, you can call dlerror() to get a textual error description. +extern int +dlclose (void *__handle); -/* declarations used for dynamic linking support routines */ - extern void *dlopen (const char *__file, int __mode); - extern void *dlsym (void *__restrict __handle, - const char *__restrict __name); - extern int dlclose (void *__handle); - extern char *dlerror (void); +/// @brief Returns a human-readable string describing the last error that occurred. +/// +/// Retrieves the Windows last error code via GetLastError(), formats it into a +/// descriptive message, and returns a thread-local buffer with this message. +/// +/// If there is no error (GetLastError returns 0), returns NULL. +/// +/// @return Pointer to a thread-local static buffer containing the error message, +/// or NULL if no error. +/// +/// @note Calling this function clears the Windows last error (sets it to 0). +extern char * +dlerror (void); -/* following doesn't exist in Win32 API ... */ +/** + * @def RTLD_DEFAULT + * POSIX macro not used in Windows, define as NULL. + */ #define RTLD_DEFAULT NULL -/* valid values for mode argument to dlopen */ -#define RTLD_LOCAL 0 /* Symbols in this dlopen'ed obj are not */ - /* visible to other dlopen'ed objs. */ -#define RTLD_LAZY 1 /* Lazy function call binding. */ -#define RTLD_NOW 2 /* Immediate function call binding. */ -#define RTLD_GLOBAL 4 /* Symbols in this dlopen'ed obj are visible */ - /* to other dlopen'ed objs. */ -/* Non-standard GLIBC extensions */ -#define RTLD_NODELETE 8 /* Don't unload lib in dlclose. */ -#define RTLD_NOLOAD 16 /* Don't load lib, just return handle if lib */ - /* is already loaded, NULL otherwise. */ -#define RTLD_DEEPBIND 32 /* Place lookup scope so that this lib is */ - /* preferred over global scope. */ - - typedef struct Dl_info Dl_info; - - struct Dl_info - { - const char *dli_fname; /* Filename of defining object */ - void *dli_fbase; /* Load address of that object */ - const char *dli_sname; /* Name of nearest lower symbol */ - void *dli_saddr; /* Exact value of nearest symbol */ - }; - - extern int dladdr (const void *addr, Dl_info * info); - -#ifdef __cplusplus -} -#endif - -#endif /* _DLFCN_H */ +/** + * @def RTLD_LOCAL + * The symbols defined in this library are not made available to resolve references in subsequently loaded libraries. + */ +#define RTLD_LOCAL 0 + +/** + * @def RTLD_LAZY + * Perform lazy binding. Only resolve symbols as the code that references them is executed. + */ +#define RTLD_LAZY 1 + +/** + * @def RTLD_NOW + * Perform all necessary relocations when `dlopen` is called. + */ +#define RTLD_NOW 2 + +/** + * @def RTLD_GLOBAL + * Make symbols available for symbol resolution of subsequently loaded libraries. + */ +#define RTLD_GLOBAL 4 + +/** + * @def RTLD_NODELETE + * Do not unload the library during dlclose. + * Non-standard GLIBC extension, no effect on Windows. + */ +#define RTLD_NODELETE 8 + +/** + * @def RTLD_NOLOAD + * Do not load the library if it is not already loaded. + * Non-standard GLIBC extension, not supported on Windows. + */ +#define RTLD_NOLOAD 16 + +/** + * @def RTLD_DEEPBIND + * Place the library at the top of the symbol resolution scope. + * Non-standard GLIBC extension, not supported on Windows. + */ +#define RTLD_DEEPBIND 32 + +/** + * @struct Dl_info + * Structure returned by dladdr with symbolic information about a pointer. + */ +typedef struct Dl_info +{ + const char *dli_fname; /**< Filename of the shared object that contains the address */ + void *dli_fbase; /**< Base address at which the object is loaded */ + const char *dli_sname; /**< Name of the nearest symbol with address lower than or equal to the specified address */ + void *dli_saddr; /**< Exact address of the symbol named in dli_sname */ +} Dl_info; + +/// @brief Provides symbolic information about a given address. +/// +/// If successful, info will contain the filename of the module, the base address, +/// the nearest symbol name, and the exact address of that symbol. +/// +/// @param addr Pointer/address to query. +/// @param info Pointer to Dl_info struct to fill. +/// @return Non-zero on success, zero on failure. +/// +/// @note The caller is responsible for freeing dli_fname and dli_sname in Dl_info. +extern int +dladdr (const void *addr, Dl_info *info); + +__END_DECLS + +#endif /* _DLFCN_H */ diff --git a/source/dladdr.c b/source/dladdr.c index 90cf291..fb9ce08 100644 --- a/source/dladdr.c +++ b/source/dladdr.c @@ -1,145 +1,97 @@ +#include #include -#include #include +#include #include +#include + +#pragma comment(lib, "dbghelp.lib") -static PIMAGE_NT_HEADERS -ImageNtHeader (PVOID Base) +/// @brief Shared Reader-Writer lock to guard dbghelp usage. +static SRWLOCK g_dbghelp_lock = SRWLOCK_INIT; + +/// @brief One-time initialization for dbghelp symbols per process +static BOOL CALLBACK +SymInitOnceCallback (PINIT_ONCE InitOnce, PVOID Parameter, PVOID * Context) { - PIMAGE_DOS_HEADER imageDosHeader = Base; - return (PIMAGE_NT_HEADERS) ((char *) (imageDosHeader) + - imageDosHeader->e_lfanew); + HANDLE process = GetCurrentProcess (); + SymSetOptions (SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_NO_PROMPTS); + return SymInitialize (process, NULL, TRUE); } + int -dladdr (const void *addr, Dl_info * info) +dladdr (const LPVOID addr, Dl_info * info) { - DWORD dwErrCode = GetLastError (); - pid_t mepid = getpid (); - HANDLE hProcess = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, mepid); - const char *root = "\\\\?\\GLOBALROOT"; - LPSTR lpFilename; - DWORD nSize = 1; - HANDLE hFile; - MEMORY_BASIC_INFORMATION lpBuffer; - LPSTR lpszFilePath; - PIMAGE_NT_HEADERS imageNtHeaders; - PIMAGE_EXPORT_DIRECTORY imageExportDirectory; - DWORD i; - DWORD NumberOfNames; - DWORD *AddressOfNames; - DWORD *AddressOfFunctions; - DWORD AddressOfFunction; - char *dli_sname; - void *dli_saddr; - SetLastError (ERROR_SUCCESS); - nSize += strlen (root) / sizeof (*lpFilename); - lpFilename = malloc (nSize); - lpFilename += strlen (root) / sizeof (*lpFilename); - nSize -= strlen (root) / sizeof (*lpFilename); - GetMappedFileName (hProcess, (LPVOID) addr, lpFilename, nSize); - while (GetLastError () == ERROR_INSUFFICIENT_BUFFER) + if (!addr || !info) { - nSize += 4096; - lpFilename -= strlen (root) / sizeof (*lpFilename); - nSize += strlen (root) / sizeof (*lpFilename); - free (lpFilename); - lpFilename = malloc (nSize); - if (lpFilename == 0) - { - return 0; - } - lpFilename += strlen (root) / sizeof (*lpFilename); - nSize -= strlen (root) / sizeof (*lpFilename); - GetMappedFileName (hProcess, (LPVOID) addr, lpFilename, nSize); + SetLastError (ERROR_INVALID_PARAMETER); + return 0; } - if (GetLastError () != ERROR_SUCCESS) + + memset (info, 0, sizeof (*info)); + HANDLE process = GetCurrentProcess (); + + static INIT_ONCE initOnce = INIT_ONCE_STATIC_INIT; + + if (!InitOnceExecuteOnce (&initOnce, SymInitOnceCallback, NULL, NULL)) { return 0; } - lpFilename -= strlen (root) / sizeof (*lpFilename); - memcpy (lpFilename, root, strlen (root)); - nSize = 1; - - hFile = CreateFile (lpFilename, GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); - if (hFile != INVALID_HANDLE_VALUE) - { - nSize = GetFinalPathNameByHandle (hFile, 0, 0, VOLUME_NAME_DOS); - if (nSize != 0) + AcquireSRWLockShared (&g_dbghelp_lock); + + DWORD64 displacement = 0; + BYTE symbolBuffer[sizeof (SYMBOL_INFO) + MAX_SYM_NAME * sizeof (char)] = + { 0 }; + PSYMBOL_INFO symbol = (PSYMBOL_INFO) symbolBuffer; + symbol->SizeOfStruct = sizeof (SYMBOL_INFO); + symbol->MaxNameLen = MAX_SYM_NAME; + + BOOL hasSymbol = + SymFromAddr (process, (DWORD64) (uintptr_t) addr, &displacement, symbol); + + IMAGEHLP_MODULE64 moduleInfo = { 0 }; + moduleInfo.SizeOfStruct = sizeof (moduleInfo); + BOOL hasModule = + SymGetModuleInfo64 (process, (DWORD64) (uintptr_t) addr, &moduleInfo); + + ReleaseSRWLockShared (&g_dbghelp_lock); + + if (!hasModule) + return 0; + + info->dli_fname = _strdup (moduleInfo.ImageName); + if (!info->dli_fname) + return 0; + info->dli_fbase = (LPVOID) (uintptr_t) moduleInfo.BaseOfImage; + + if (hasSymbol) + { + char demangled[MAX_SYM_NAME]; + if (UnDecorateSymbolName + (symbol->Name, demangled, sizeof (demangled), + UNDNAME_COMPLETE | UNDNAME_NO_LEADING_UNDERSCORES)) { - nSize++; - lpszFilePath = (char *) malloc (nSize); - if (!lpszFilePath || !GetFinalPathNameByHandle (hFile, - lpszFilePath, nSize, - VOLUME_NAME_DOS)) - { - free (lpszFilePath); - lpszFilePath = lpFilename; - } - else - { - free (lpFilename); - } + info->dli_sname = _strdup (demangled); } else { - lpszFilePath = lpFilename; + info->dli_sname = _strdup (symbol->Name); } + if (!info->dli_sname) + { + free (info->dli_fname); + info->dli_fname = NULL; + return 0; + } + info->dli_saddr = (LPVOID) (uintptr_t) symbol->Address; } else { - lpszFilePath = lpFilename; - } - - if (!VirtualQuery (addr, &lpBuffer, sizeof (lpBuffer))) - { - lpBuffer.AllocationBase = (void *) addr; - } - - imageNtHeaders = ImageNtHeader (lpBuffer.AllocationBase); - imageExportDirectory = - (PIMAGE_EXPORT_DIRECTORY) ((size_t) lpBuffer.AllocationBase + - imageNtHeaders->OptionalHeader.DataDirectory - [IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); - if (imageExportDirectory->NumberOfNames < - imageExportDirectory->NumberOfFunctions) - { - NumberOfNames = imageExportDirectory->NumberOfNames; - } - else - { - NumberOfNames = imageExportDirectory->NumberOfFunctions; - } - AddressOfNames = - (DWORD *) ((size_t) lpBuffer.AllocationBase + - imageExportDirectory->AddressOfNames); - AddressOfFunctions = - (DWORD *) ((size_t) lpBuffer.AllocationBase + - imageExportDirectory->AddressOfFunctions); - AddressOfFunction = (size_t) addr - (size_t) lpBuffer.AllocationBase; - dli_sname = 0; - dli_saddr = 0; - - for (i = 0; i != NumberOfNames; i++) - { - if (AddressOfFunctions[i] <= AddressOfFunction) - { - dli_sname = - (char *) ((size_t) lpBuffer.AllocationBase + AddressOfNames[i]); - dli_saddr = - (void *) ((size_t) lpBuffer.AllocationBase + - AddressOfFunctions[i]); - i = NumberOfNames - 1; - } + info->dli_sname = NULL; + info->dli_saddr = NULL; } - info->dli_fname = lpszFilePath; - info->dli_fbase = lpBuffer.AllocationBase; - info->dli_sname = dli_sname; - info->dli_saddr = dli_saddr; - SetLastError (dwErrCode); return 1; } diff --git a/source/dlclose.c b/source/dlclose.c index dd12126..1a38876 100644 --- a/source/dlclose.c +++ b/source/dlclose.c @@ -2,7 +2,19 @@ #include int -dlclose (void *__handle) +dlclose (LPVOID __handle) { - return !FreeLibrary (__handle); + if (!__handle) + { + SetLastError (ERROR_INVALID_PARAMETER); + return -1; + } + + BOOL success = FreeLibrary ((HMODULE) __handle); + if (!success) + { + return -1; + } + + return 0; } diff --git a/source/dlerror.c b/source/dlerror.c index 07d5b3a..d22a82c 100644 --- a/source/dlerror.c +++ b/source/dlerror.c @@ -1,5 +1,48 @@ +#include +#include + +/// @brief Thread-local buffer to hold error messages for dlerror(). +static +__declspec (thread) + char dlerror_buffer[512] = { 0 }; + char * dlerror (void) { - return 0; + DWORD err = GetLastError (); + if (err == 0) + { + return NULL; + } + + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + + DWORD length = FormatMessageA (flags, + NULL, + err, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + dlerror_buffer, + sizeof (dlerror_buffer), + NULL); + + if (length == 0) + { + // Failed to format the message, fallback to numeric error code string + snprintf (dlerror_buffer, sizeof (dlerror_buffer), + "Unknown error 0x%08lx", err); + } + else + { + // Trim trailing newline characters (CRLF) + while (length > 0 + && (dlerror_buffer[length - 1] == '\n' + || dlerror_buffer[length - 1] == '\r')) + { + dlerror_buffer[--length] = '\0'; + } + } + + SetLastError (0); + + return dlerror_buffer; } diff --git a/source/dlopen.c b/source/dlopen.c index 6cd47e3..5fe81b6 100644 --- a/source/dlopen.c +++ b/source/dlopen.c @@ -1,13 +1,83 @@ #include #include +/// @brief Thread-local buffer to hold error messages for dlerror(). +static +__declspec (thread) + char dlerror_buffer[512] = { 0 }; + +/// @brief Sets the thread-local dlerror buffer from the current Windows last error. +/// +/// Retrieves the last error code via GetLastError() and formats the error message +/// string into `dlerror_buffer`. If no error code is set (0), clears the buffer. +/// +/// Trims trailing newline characters from the formatted message. +/// +/// If `FormatMessageA` fails, stores a fallback string with the numeric error code. +/// +/// @note This function resets the Windows last error to 0 after capturing it. +static void +set_dlerror_from_last_error (void) +{ + DWORD err = GetLastError (); + if (err == 0) + { + dlerror_buffer[0] = '\0'; + return; + } + + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + + DWORD length = FormatMessageA (flags, + NULL, + err, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + dlerror_buffer, + sizeof (dlerror_buffer), + NULL); + + if (length == 0) + { + // Failed to format the message, fallback to numeric error code string + snprintf (dlerror_buffer, sizeof (dlerror_buffer), + "Unknown error 0x%08lx", err); + } + else + { + // Trim trailing newline characters (CRLF) + while (length > 0 + && (dlerror_buffer[length - 1] == '\n' + || dlerror_buffer[length - 1] == '\r')) + { + dlerror_buffer[--length] = '\0'; + } + } +} + void * dlopen (const char *__file, int __mode) { + if (!__file) + { + SetLastError (ERROR_INVALID_PARAMETER); + set_dlerror_from_last_error (); + return NULL; + } + DWORD dwFlags = 0; - if (__mode == RTLD_LAZY) + + if (__mode & RTLD_LAZY) { dwFlags = DONT_RESOLVE_DLL_REFERENCES; } - return LoadLibraryExA (__file, NULL, dwFlags); + + HMODULE module = LoadLibraryExA (__file, NULL, dwFlags); + if (!module) + { + set_dlerror_from_last_error (); + return NULL; + } + + dlerror_buffer[0] = '\0'; + return (LPVOID) module; } diff --git a/source/dlsym.c b/source/dlsym.c index 1845846..e6eed3e 100644 --- a/source/dlsym.c +++ b/source/dlsym.c @@ -1,74 +1,244 @@ #include #include +#include +/// @brief Thread-local buffer to hold error messages for dlerror(). +static +__declspec (thread) + char dlerror_buffer[512] = { 0 }; + +/// @brief SRW lock to synchronize access to patched modules list. +static SRWLOCK g_imports_lock = SRWLOCK_INIT; + +/// @brief Sets the thread-local dlerror buffer from the current Windows last error. +/// +/// Retrieves the last error code via GetLastError() and formats the error message +/// string into `dlerror_buffer`. If no error code is set (0), clears the buffer. +/// +/// Trims trailing newline characters from the formatted message. +/// +/// If `FormatMessageA` fails, stores a fallback string with the numeric error code. +/// +/// @note This function resets the Windows last error to 0 after capturing it. +static void +set_dlerror_from_last_error (void) +{ + DWORD err = GetLastError (); + if (err == 0) + { + dlerror_buffer[0] = '\0'; + return; + } + + DWORD length = + FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + err, + MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), + dlerror_buffer, + sizeof (dlerror_buffer), + NULL); + + if (length == 0) + snprintf (dlerror_buffer, sizeof (dlerror_buffer), + "Unknown error 0x%08lx", err); + else + while (length > 0 + && (dlerror_buffer[length - 1] == '\n' + || dlerror_buffer[length - 1] == '\r')) + { + dlerror_buffer[--length] = '\0'; + } + SetLastError (0); +} + +/// @brief Gets the NT Headers from the base address of a loaded module. +/// +/// @param base Pointer to the base address of a loaded module. +/// +/// @return Pointer to the IMAGE_NT_HEADERS if valid, otherwise NULL. +/// @note On failure, sets the thread-local dlerror buffer with an appropriate error message. static PIMAGE_NT_HEADERS -ImageNtHeader (PVOID Base) +ImageNtHeader (void *base) +{ + if (!base) + { + SetLastError (ERROR_INVALID_PARAMETER); + set_dlerror_from_last_error (); + return NULL; + } + + PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER) base; + if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) + { + SetLastError (ERROR_BAD_FORMAT); + set_dlerror_from_last_error (); + return NULL; + } + + PIMAGE_NT_HEADERS ntHeaders = + (PIMAGE_NT_HEADERS) ((BYTE *) base + dosHeader->e_lfanew); + if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) + { + SetLastError (ERROR_BAD_FORMAT); + set_dlerror_from_last_error (); + return NULL; + } + + return ntHeaders; +} + +/// @brief Tracks modules that have had their import address tables fixed. +/// +/// Prevents repeated patching by maintaining a static list protected by SRW lock. +/// +/// @param module Handle of the loaded module. +/// +/// @return TRUE if imports have already been fixed for this module, FALSE otherwise. +static BOOL +HasFixedImports (HMODULE module) +{ + static HMODULE patchedModules[64]; + static size_t count = 0; + + AcquireSRWLockExclusive (&g_imports_lock); + + for (size_t i = 0; i < count; i++) + { + if (patchedModules[i] == module) + { + ReleaseSRWLockExclusive (&g_imports_lock); + return TRUE; + } + } + + if (count < sizeof (patchedModules) / sizeof (patchedModules[0])) + patchedModules[count++] = module; + + ReleaseSRWLockExclusive (&g_imports_lock); + return FALSE; +} + +/// @brief Writes the actual function address into the import address table thunk. +/// +/// @param thunk Pointer to the import thunk to patch. +/// @param procAddress Address of the function to patch into the IAT. +static inline void +PatchIATEntry (PIMAGE_THUNK_DATA thunk, FARPROC procAddress) { - PIMAGE_DOS_HEADER imageDosHeader = Base; - return (PIMAGE_NT_HEADERS) ((char *) (imageDosHeader) + - imageDosHeader->e_lfanew); +#ifdef _WIN64 + thunk->u1.Function = (ULONGLONG) procAddress; +#else + thunk->u1.Function = (DWORD) procAddress; +#endif } +/// @brief Signature for DLL entry point function (DllMain). +typedef BOOL (WINAPI * DllEntryProc) (HINSTANCE, DWORD, LPVOID); + void * dlsym (void *__restrict __handle, const char *__restrict __name) { - PIMAGE_NT_HEADERS imageNtHeaders = ImageNtHeader (__handle); + if (!__handle || !__name) + { + SetLastError (ERROR_INVALID_PARAMETER); + set_dlerror_from_last_error (); + return NULL; + } + + HMODULE module = (HMODULE) __handle; + + PIMAGE_NT_HEADERS ntHeaders = ImageNtHeader (__handle); + if (!ntHeaders) + { + return NULL; + } + + IMAGE_DATA_DIRECTORY importDir = + ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; + + // No imports to patch, just return symbol + if (importDir.VirtualAddress == 0 || importDir.Size == 0) + return (LPVOID) GetProcAddress (module, __name); + + BYTE *baseAddr = (BYTE *) __handle; + PIMAGE_IMPORT_DESCRIPTOR importDesc = + (PIMAGE_IMPORT_DESCRIPTOR) (baseAddr + importDir.VirtualAddress); + MEMORY_BASIC_INFORMATION mbi; - VirtualQuery ((char *) (imageNtHeaders) + - imageNtHeaders->OptionalHeader. - DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, - &mbi, sizeof (mbi)); - if (mbi.Protect & PAGE_WRITECOPY) + if (VirtualQuery (importDesc, &mbi, sizeof (mbi)) == 0) + { + set_dlerror_from_last_error (); + return NULL; + } + + if ((mbi.Protect & PAGE_WRITECOPY) && !HasFixedImports (module)) { - PIMAGE_IMPORT_DESCRIPTOR imageImportDescriptor; - imageImportDescriptor = - (PIMAGE_IMPORT_DESCRIPTOR) ((char *) (__handle) + - imageNtHeaders->OptionalHeader. - DataDirectory - [IMAGE_DIRECTORY_ENTRY_IMPORT]. - VirtualAddress); - LPCSTR libFileName; - HMODULE hModule; - LPCSTR procName; - PIMAGE_THUNK_DATA imageThunkData; - while (imageImportDescriptor->Name != 0) + // Patch IAT for all imported modules + for (; importDesc->Name != 0; importDesc++) { - libFileName = (char *) __handle + imageImportDescriptor->Name; - hModule = LoadLibraryA (libFileName); - if (hModule) + LPCSTR libName = (LPCSTR) (baseAddr + importDesc->Name); + HMODULE hModule = LoadLibraryA (libName); + if (!hModule) + continue; + + PIMAGE_THUNK_DATA importLookupTable = + (PIMAGE_THUNK_DATA) (baseAddr + importDesc->OriginalFirstThunk); + PIMAGE_THUNK_DATA importAddressTable = + (PIMAGE_THUNK_DATA) (baseAddr + importDesc->FirstThunk); + + if (!importLookupTable) + importLookupTable = importAddressTable; + + for (; importLookupTable->u1.AddressOfData != 0; + importLookupTable++, importAddressTable++) { - imageThunkData = - (PIMAGE_THUNK_DATA) ((char *) (__handle) + - imageImportDescriptor->FirstThunk); - while (imageThunkData->u1.AddressOfData != 0) + FARPROC procAddress = NULL; + + if (importLookupTable->u1.Ordinal & IMAGE_ORDINAL_FLAG) + { + WORD ordinal = + (WORD) (importLookupTable->u1.Ordinal & 0xFFFF); + procAddress = + GetProcAddress (hModule, (LPCSTR) (uintptr_t) ordinal); + } + else { - if (IMAGE_SNAP_BY_ORDINAL (imageThunkData->u1.Ordinal)) - { - procName = - (LPCSTR) IMAGE_ORDINAL (imageThunkData->u1.Ordinal); - } - else - { - procName = - ((PIMAGE_IMPORT_BY_NAME) - ((char *) (__handle) + - imageThunkData->u1.AddressOfData))->Name; - } - imageThunkData->u1.Function = - (DWORD_PTR) GetProcAddress (hModule, procName); - ++imageThunkData; + PIMAGE_IMPORT_BY_NAME importByName = + (PIMAGE_IMPORT_BY_NAME) (baseAddr + + importLookupTable->u1. + AddressOfData); + procAddress = GetProcAddress (hModule, importByName->Name); } + + if (!procAddress) + { + procAddress = NULL; + } + + PatchIATEntry (importAddressTable, procAddress); + } + } + + // Call DLL entry point with DLL_PROCESS_ATTACH + FARPROC entryPoint = + (FARPROC) (baseAddr + ntHeaders->OptionalHeader.AddressOfEntryPoint); + if (entryPoint) + { + DllEntryProc DllEntry = (DllEntryProc) entryPoint; + + if (!DllEntry ((HINSTANCE) __handle, DLL_PROCESS_ATTACH, NULL)) + { + SetLastError (ERROR_DLL_INIT_FAILED); + return NULL; } - imageImportDescriptor++; } - BOOL WINAPI (*DllEntry) (HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved) = - (BOOL - WINAPI (*)(HINSTANCE hinstDLL, DWORD fdwReason, - LPVOID lpvReserved)) ((char *) (__handle) + - imageNtHeaders->OptionalHeader. - AddressOfEntryPoint); - (*DllEntry) ((HINSTANCE) __handle, DLL_PROCESS_ATTACH, 0); } - return GetProcAddress (__handle, __name); + + void *result = (LPVOID) GetProcAddress (module, __name); + if (!result) + set_dlerror_from_last_error (); + + return result; }