Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions core/win32/drwinapi/kernel32_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -237,15 +237,26 @@ redirect_GetModuleFileNameW(HMODULE modbase, wchar_t *buf, DWORD bufcnt)
acquire_recursive_lock(&privload_lock);
mod = privload_lookup_by_base((app_pc)modbase);
if (mod != NULL) {
cnt = (DWORD)strlen(mod->path);
if (cnt >= bufcnt) {
cnt = bufcnt;
// Calculate length of narrow string
size_t pathlen = strlen(mod->path);

// Check if buffer is large enough (need space for null terminator)
if (pathlen >= bufcnt) {
cnt = bufcnt - 1; // Leave room for null terminator
set_last_error(ERROR_INSUFFICIENT_BUFFER);
} else {
cnt = (DWORD)pathlen;
}
_snwprintf(buf, bufcnt, L"%s", mod->path);
buf[bufcnt - 1] = L'\0';

// Manually convert narrow string to wide string
for (uint32 i = 0; i < cnt; i++) {
buf[i] = (wchar_t)(unsigned char)mod->path[i];
}
buf[cnt] = L'\0'; // Null terminate

LOG(GLOBAL, LOG_LOADER, 2, "%s: " PFX " => %s\n", __FUNCTION__, mod, mod->path);
}

release_recursive_lock(&privload_lock);
if (mod == NULL) {
/* XXX: should set more appropriate error code */
Expand Down
187 changes: 187 additions & 0 deletions core/win32/drwinapi/ntdll_redir.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ static strhash_table_t *ntdll_win7_table;
static const redirect_import_t redirect_ntdll[] = {
{ "LdrGetProcedureAddress", (app_pc)redirect_LdrGetProcedureAddress },
{ "LdrLoadDll", (app_pc)redirect_LdrLoadDll },
{ "LdrResolveDelayLoadedAPI", (app_pc)redirect_LdrResolveDelayLoadedAPI },
{ "RtlPcToFileHeader", (app_pc)redirect_RtlPcToFileHeader },
/* kernel32 passes some of its routines to ntdll where they are
* stored in function pointers. xref PR 215408 where on x64 we had
Expand Down Expand Up @@ -811,6 +812,192 @@ redirect_LdrLoadDll(DR_PARAM_IN PWSTR path OPTIONAL,
}
}

PVOID NTAPI
redirect_LdrResolveDelayLoadedAPI(PVOID ParentModuleBase,
PCIMAGE_DELAYLOAD_DESCRIPTOR DelayloadDescriptor,
PDELAYLOAD_FAILURE_DLL_CALLBACK FailureDllHook,
PVOID FailureSystemHook, PIMAGE_THUNK_DATA ThunkAddress,
ULONG Flags)
{
app_pc caller_base = (app_pc)ParentModuleBase;
HMODULE *pModuleHandle;
HMODULE hTargetModule;
PIMAGE_THUNK_DATA pIAT_base;
PIMAGE_THUNK_DATA pINT_base;
PIMAGE_THUNK_DATA pINT_entry;
INT_PTR thunk_index;
LPCSTR pszDllName;
LPCSTR pszProcName = NULL;
FARPROC pfnTarget = NULL;
PVOID result = NULL;

LOG(GLOBAL, LOG_LOADER, 2, "%s: ParentModuleBase=" PFX ", ThunkAddress=" PFX "\n",
__FUNCTION__, ParentModuleBase, ThunkAddress);

/* Delay-load support (i#233): Resolve delay-loaded functions similar to
* Windows delay-load helper (__delayLoadHelper2).
* Algorithm:
* 1. Check if target DLL is loaded (via ModuleHandleRVA storage)
* 2. Load DLL if needed using locate_and_load_private_library
* 3. Find function name/ordinal from Import Name Table
* 4. Resolve function using redirect_LdrGetProcedureAddress
* 5. Patch IAT entry with resolved address
* 6. Return resolved function pointer
*/

if (DelayloadDescriptor == NULL || ThunkAddress == NULL) {
LOG(GLOBAL, LOG_LOADER, 1,
"%s: ERROR - Invalid parameters (DelayloadDescriptor or ThunkAddress is NULL)\n",
__FUNCTION__);
return NULL;
}

/* 1. Get the address where the HMODULE is (or will be) stored.
* The ModuleHandleRVA field is an RVA to a HMODULE variable.
*/
if (DelayloadDescriptor->ModuleHandleRVA == 0) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: ERROR - ModuleHandleRVA is 0\n", __FUNCTION__);
return NULL;
}
pModuleHandle = (HMODULE *)(caller_base + DelayloadDescriptor->ModuleHandleRVA);

/* 2. Check if the DLL is already loaded. */
if (*pModuleHandle == NULL) {
/* It's not loaded. Get the DLL name (RVA) and load it. */
app_pc loaded_base;

if (DelayloadDescriptor->DllNameRVA == 0) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: ERROR - DllNameRVA is 0\n", __FUNCTION__);
return NULL;
}
pszDllName = (LPCSTR)(caller_base + DelayloadDescriptor->DllNameRVA);

LOG(GLOBAL, LOG_LOADER, 2, "%s: Loading delay-load DLL: %s\n", __FUNCTION__,
pszDllName);

/* Use locate_and_load_private_library to load the DLL */
loaded_base = locate_and_load_private_library(pszDllName, false /*!reachable*/);
if (loaded_base == NULL) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: ERROR - Failed to load DLL: %s\n",
__FUNCTION__, pszDllName);
return NULL;
}

hTargetModule = (HMODULE)loaded_base;

/* Store the handle back into the pointer for future calls. */
*pModuleHandle = hTargetModule;
} else {
hTargetModule = *pModuleHandle;
}

LOG(GLOBAL, LOG_LOADER, 2, "%s: Target module handle: " PFX "\n", __FUNCTION__,
hTargetModule);

/* 3. Find the function name or ordinal.
* We need to find the "index" of our thunk to look in the parallel name table.
*/

/* Get the base of the IAT (pIAT) and INT (pINT) arrays. */
if (DelayloadDescriptor->ImportAddressTableRVA == 0 ||
DelayloadDescriptor->ImportNameTableRVA == 0) {
LOG(GLOBAL, LOG_LOADER, 1, "%s: ERROR - IAT or INT RVA is 0\n", __FUNCTION__);
return NULL;
}

pIAT_base =
(PIMAGE_THUNK_DATA)(caller_base + DelayloadDescriptor->ImportAddressTableRVA);
pINT_base =
(PIMAGE_THUNK_DATA)(caller_base + DelayloadDescriptor->ImportNameTableRVA);

/* Calculate the index of our function by its position in the IAT.
* ThunkAddress is the entry we are resolving (e.g., &pIAT_base[2])
*/
thunk_index = ThunkAddress - pIAT_base;
if (thunk_index < 0) {
LOG(GLOBAL, LOG_LOADER, 1,
"%s: ERROR - ThunkAddress " PFX " is before IAT base " PFX "\n", __FUNCTION__,
ThunkAddress, pIAT_base);
return NULL;
}

LOG(GLOBAL, LOG_LOADER, 2, "%s: Resolving thunk at index %d\n", __FUNCTION__,
thunk_index);

/* 4. Get the corresponding entry from the Import Name Table (pINT). */
pINT_entry = &pINT_base[thunk_index];

/* 5. Check if we are importing by ordinal or by name.
* The high bit of the thunk data's value tells us if it's an ordinal.
*/
if (TEST(IMAGE_ORDINAL_FLAG, pINT_entry->u1.Function)) {
/* Import by ordinal. The lower bits are the ordinal number. */
const char *forwarder = NULL;
DWORD ord = (DWORD)(pINT_entry->u1.AddressOfData & ~(IMAGE_ORDINAL_FLAG));

LOG(GLOBAL, LOG_LOADER, 2, "%s: Resolving by ordinal: %d\n", __FUNCTION__, ord);

/* Call get_proc_address_by_ordinal directly */
pfnTarget =
(FARPROC)get_proc_address_by_ordinal((app_pc)hTargetModule, ord, &forwarder);
if (pfnTarget == NULL) {
LOG(GLOBAL, LOG_LOADER, 1,
"%s: ERROR - get_proc_address_by_ordinal failed for ordinal %d\n",
__FUNCTION__, ord);
return NULL;
}

/* XXX i#233: Handle forwarders if needed */
if (forwarder != NULL) {
LOG(GLOBAL, LOG_LOADER, 2, "%s: WARNING - Ordinal %d is forwarded to %s\n",
__FUNCTION__, ord, forwarder);
}
} else {
/* Import by name. The value is an RVA to an IMAGE_IMPORT_BY_NAME struct. */
NTSTATUS status;
PVOID resolved_addr;
ANSI_STRING func_name;
IMAGE_IMPORT_BY_NAME *pImportByName =
(IMAGE_IMPORT_BY_NAME *)(caller_base + pINT_entry->u1.AddressOfData);

/* The 'Name' field is the null-terminated function name string. */
pszProcName = (LPCSTR)pImportByName->Name;

LOG(GLOBAL, LOG_LOADER, 2, "%s: Resolving by name: %s\n", __FUNCTION__,
pszProcName);

/* Initialize ANSI_STRING for the function name */
func_name.Buffer = (PCHAR)pszProcName;
func_name.Length = (USHORT)strlen(pszProcName);
func_name.MaximumLength = func_name.Length + 1;

/* Call redirect_LdrGetProcedureAddress with ANSI_STRING */
status =
redirect_LdrGetProcedureAddress(hTargetModule, &func_name, 0, &resolved_addr);
if (!NT_SUCCESS(status)) {
LOG(GLOBAL, LOG_LOADER, 1,
"%s: ERROR - LdrGetProcedureAddress failed for %s (status=%x)\n",
__FUNCTION__, pszProcName, status);
return NULL;
}
pfnTarget = (FARPROC)resolved_addr;
}

LOG(GLOBAL, LOG_LOADER, 2, "%s: Resolved function address: " PFX "\n", __FUNCTION__,
pfnTarget);

/* 7. Patch the IAT entry with the resolved address. */
ThunkAddress->u1.Function = (ptr_uint_t)pfnTarget;

/* 8. Return the real function address. */
result = (PVOID)pfnTarget;

LOG(GLOBAL, LOG_LOADER, 2, "%s: Returning function pointer " PFX "\n", __FUNCTION__,
result);

return result;
}

PVOID NTAPI
redirect_RtlPcToFileHeader(__in PVOID PcValue, __out PVOID *BaseOfImage)
{
Expand Down
9 changes: 9 additions & 0 deletions core/win32/drwinapi/ntdll_redir.h
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,15 @@ redirect_LdrLoadDll(DR_PARAM_IN PWSTR path OPTIONAL,
DR_PARAM_IN PULONG characteristics OPTIONAL,
DR_PARAM_IN PUNICODE_STRING name, DR_PARAM_OUT PVOID *handle);

PVOID NTAPI
redirect_LdrResolveDelayLoadedAPI(DR_PARAM_IN PVOID ParentModuleBase,
DR_PARAM_IN PCIMAGE_DELAYLOAD_DESCRIPTOR
DelayloadDescriptor,
DR_PARAM_IN PDELAYLOAD_FAILURE_DLL_CALLBACK FailureDllHook,
DR_PARAM_IN PVOID FailureSystemHook,
DR_PARAM_OUT PIMAGE_THUNK_DATA ThunkAddress,
DR_PARAM_IN ULONG Flags);

/* This is exported by some kernel32.dll versions, but it's just forwarded
* directly or there's a stub that calls the real impl in ntdll.
*/
Expand Down
Loading
Loading