From: Hermès Bélusca-Maïto Date: Thu, 19 Sep 2019 00:47:29 +0000 (+0200) Subject: [FREELDR] Rename the PE loader functions 'PeLdr*' instead of 'WinLdr*'. X-Git-Tag: 0.4.14-dev~68 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=658d5a3ff5e9f1dcf88d602ac443039faa6c7a8c [FREELDR] Rename the PE loader functions 'PeLdr*' instead of 'WinLdr*'. --- diff --git a/boot/freeldr/freeldr/disk/scsiport.c b/boot/freeldr/freeldr/disk/scsiport.c index 6cb204d4c31..a42cadc6164 100644 --- a/boot/freeldr/freeldr/disk/scsiport.c +++ b/boot/freeldr/freeldr/disk/scsiport.c @@ -1639,7 +1639,7 @@ LoadBootDeviceDriver(VOID) strcat(NtBootDdPath, "\\NTBOOTDD.SYS"); /* Load file */ - Success = WinLdrLoadImage(NtBootDdPath, LoaderBootDriver, &ImageBase); + Success = PeLdrLoadImage(NtBootDdPath, LoaderBootDriver, &ImageBase); if (!Success) { /* That's OK. File simply doesn't exist */ @@ -1647,15 +1647,15 @@ LoadBootDeviceDriver(VOID) } /* Allocate a DTE for ntbootdd */ - Success = WinLdrAllocateDataTableEntry(&ModuleListHead, "ntbootdd.sys", - "NTBOOTDD.SYS", ImageBase, &BootDdDTE); + Success = PeLdrAllocateDataTableEntry(&ModuleListHead, "ntbootdd.sys", + "NTBOOTDD.SYS", ImageBase, &BootDdDTE); if (!Success) return EIO; /* Add the PE part of freeldr.sys to the list of loaded executables, it contains ScsiPort* exports, imported by ntbootdd.sys */ - Success = WinLdrAllocateDataTableEntry(&ModuleListHead, "scsiport.sys", - "FREELDR.SYS", &__ImageBase, &FreeldrDTE); + Success = PeLdrAllocateDataTableEntry(&ModuleListHead, "scsiport.sys", + "FREELDR.SYS", &__ImageBase, &FreeldrDTE); if (!Success) { RemoveEntryList(&BootDdDTE->InLoadOrderLinks); @@ -1663,7 +1663,7 @@ LoadBootDeviceDriver(VOID) } /* Fix imports */ - Success = WinLdrScanImportDescriptorTable(&ModuleListHead, "", BootDdDTE); + Success = PeLdrScanImportDescriptorTable(&ModuleListHead, "", BootDdDTE); /* Now unlinkt the DTEs, they won't be valid later */ RemoveEntryList(&BootDdDTE->InLoadOrderLinks); diff --git a/boot/freeldr/freeldr/include/peloader.h b/boot/freeldr/freeldr/include/peloader.h index 9d4b1178819..a582f760c29 100644 --- a/boot/freeldr/freeldr/include/peloader.h +++ b/boot/freeldr/freeldr/include/peloader.h @@ -19,23 +19,27 @@ #pragma once BOOLEAN -WinLdrLoadImage(IN PCHAR FileName, - TYPE_OF_MEMORY MemoryType, - OUT PVOID *ImageBasePA); +PeLdrLoadImage( + IN PCHAR FileName, + IN TYPE_OF_MEMORY MemoryType, + OUT PVOID *ImageBasePA); BOOLEAN -WinLdrAllocateDataTableEntry(IN OUT PLIST_ENTRY ModuleListHead, - IN PCCH BaseDllName, - IN PCCH FullDllName, - IN PVOID BasePA, - OUT PLDR_DATA_TABLE_ENTRY *NewEntry); +PeLdrAllocateDataTableEntry( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH BaseDllName, + IN PCCH FullDllName, + IN PVOID BasePA, + OUT PLDR_DATA_TABLE_ENTRY *NewEntry); BOOLEAN -WinLdrScanImportDescriptorTable(IN OUT PLIST_ENTRY ModuleListHead, - IN PCCH DirectoryPath, - IN PLDR_DATA_TABLE_ENTRY ScanDTE); +PeLdrScanImportDescriptorTable( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH DirectoryPath, + IN PLDR_DATA_TABLE_ENTRY ScanDTE); BOOLEAN -WinLdrCheckForLoadedDll(IN OUT PLIST_ENTRY ModuleListHead, - IN PCH DllName, - OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry); +PeLdrCheckForLoadedDll( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCH DllName, + OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry); diff --git a/boot/freeldr/freeldr/lib/peloader.c b/boot/freeldr/freeldr/lib/peloader.c index c306375816e..53462a574fa 100644 --- a/boot/freeldr/freeldr/lib/peloader.c +++ b/boot/freeldr/freeldr/lib/peloader.c @@ -16,854 +16,841 @@ * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/ */ -/* INCLUDES ***************************************************************/ +/* INCLUDES ******************************************************************/ #include #include DBG_DEFAULT_CHANNEL(PELOADER); -/* FUNCTIONS **************************************************************/ - -static BOOLEAN -WinLdrpCompareDllName(IN PCH DllName, - IN PUNICODE_STRING UnicodeName); - -static BOOLEAN -WinLdrpBindImportName(IN OUT PLIST_ENTRY ModuleListHead, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, - IN ULONG ExportSize, - IN BOOLEAN ProcessForwards, - IN PCSTR DirectoryPath); +/* PRIVATE FUNCTIONS *********************************************************/ +/* DllName - physical, UnicodeString->Buffer - virtual */ static BOOLEAN -WinLdrpLoadAndScanReferencedDll(PLIST_ENTRY ModuleListHead, - PCCH DirectoryPath, - PCH ImportName, - PLDR_DATA_TABLE_ENTRY *DataTableEntry); +PeLdrpCompareDllName( + IN PCH DllName, + IN PUNICODE_STRING UnicodeName) +{ + PWSTR Buffer; + SIZE_T i, Length; -static BOOLEAN -WinLdrpScanImportAddressTable(IN OUT PLIST_ENTRY ModuleListHead, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PCSTR DirectoryPath); + /* First obvious check: for length of two names */ + Length = strlen(DllName); +#if DBG + { + UNICODE_STRING UnicodeNamePA; + UnicodeNamePA.Length = UnicodeName->Length; + UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength; + UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer); + TRACE("PeLdrpCompareDllName: %s and %wZ, Length = %d " + "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length); + } +#endif -/* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */ -BOOLEAN -WinLdrCheckForLoadedDll(IN OUT PLIST_ENTRY ModuleListHead, - IN PCH DllName, - OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry) -{ - PLDR_DATA_TABLE_ENTRY DataTableEntry; - LIST_ENTRY *ModuleEntry; + if ((Length * sizeof(WCHAR)) > UnicodeName->Length) + return FALSE; - TRACE("WinLdrCheckForLoadedDll: DllName %s\n", DllName); + /* Store pointer to unicode string's buffer */ + Buffer = VaToPa(UnicodeName->Buffer); - /* Just go through each entry in the LoadOrderList and compare loaded module's - name with a given name */ - ModuleEntry = ModuleListHead->Flink; - while (ModuleEntry != ModuleListHead) + /* Loop character by character */ + for (i = 0; i < Length; i++) { - /* Get pointer to the current DTE */ - DataTableEntry = CONTAINING_RECORD(ModuleEntry, - LDR_DATA_TABLE_ENTRY, - InLoadOrderLinks); - - TRACE("WinLdrCheckForLoadedDll: DTE %p, EP %p, base %p name '%.*ws'\n", - DataTableEntry, DataTableEntry->EntryPoint, DataTableEntry->DllBase, - DataTableEntry->BaseDllName.Length / 2, VaToPa(DataTableEntry->BaseDllName.Buffer)); + /* Compare two characters, uppercasing them */ + if (toupper(*DllName) != toupper((CHAR)*Buffer)) + return FALSE; - /* Compare names */ - if (WinLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName)) - { - /* Yes, found it, report pointer to the loaded module's DTE - to the caller and increase load count for it */ - *LoadedEntry = DataTableEntry; - DataTableEntry->LoadCount++; - TRACE("WinLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry); - return TRUE; - } + /* Move to the next character */ + DllName++; + Buffer++; + } - /* Go to the next entry */ - ModuleEntry = ModuleEntry->Flink; + /* Check, if strings either fully match, or match till the "." (w/o extension) */ + if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.')) + { + /* Yes they do */ + return TRUE; } - /* Nothing found */ + /* Strings don't match, return FALSE */ return FALSE; } -BOOLEAN -WinLdrScanImportDescriptorTable(IN OUT PLIST_ENTRY ModuleListHead, - IN PCCH DirectoryPath, - IN PLDR_DATA_TABLE_ENTRY ScanDTE) +static BOOLEAN +PeLdrpLoadAndScanReferencedDll( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH DirectoryPath, + IN PCH ImportName, + OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry); + +static BOOLEAN +PeLdrpBindImportName( + IN OUT PLIST_ENTRY ModuleListHead, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData, + IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, + IN ULONG ExportSize, + IN BOOLEAN ProcessForwards, + IN PCSTR DirectoryPath) { - PLDR_DATA_TABLE_ENTRY DataTableEntry; - PIMAGE_IMPORT_DESCRIPTOR ImportTable; - ULONG ImportTableSize; - PCH ImportName; + ULONG Ordinal; + PULONG NameTable, FunctionTable; + PUSHORT OrdinalTable; + LONG High, Low, Middle, Result; + ULONG Hint; + PIMAGE_IMPORT_BY_NAME ImportData; + PCHAR ExportName, ForwarderName; BOOLEAN Success; - /* Get a pointer to the import table of this image */ - ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(VaToPa(ScanDTE->DllBase), - TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportTableSize); + //TRACE("PeLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n", + // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards); -#if DBG + /* Check passed DllBase param */ + if(DllBase == NULL) { - UNICODE_STRING BaseName; - BaseName.Buffer = VaToPa(ScanDTE->BaseDllName.Buffer); - BaseName.MaximumLength = ScanDTE->BaseDllName.MaximumLength; - BaseName.Length = ScanDTE->BaseDllName.Length; - TRACE("WinLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n", - &BaseName, ImportTable); + WARN("DllBase == NULL!\n"); + return FALSE; } -#endif - /* If image doesn't have any import directory - just return success */ - if (ImportTable == NULL) - return TRUE; + /* Convert all non-critical pointers to PA from VA */ + ThunkData = VaToPa(ThunkData); - /* Loop through all entries */ - for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++) + /* Is the reference by ordinal? */ + if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards) { - /* Get pointer to the name */ - ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name)); - TRACE("WinLdrScanImportDescriptorTable(): Looking at %s\n", ImportName); - - /* In case we get a reference to ourselves - just skip it */ - if (WinLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName)) - continue; - - /* Load the DLL if it is not already loaded */ - if (!WinLdrCheckForLoadedDll(ModuleListHead, ImportName, &DataTableEntry)) + /* Yes, calculate the ordinal */ + Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base); + //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal); + } + else + { + /* It's reference by name, we have to look it up in the export directory */ + if (!ProcessForwards) { - Success = WinLdrpLoadAndScanReferencedDll(ModuleListHead, - DirectoryPath, - ImportName, - &DataTableEntry); - if (!Success) - { - ERR("WinLdrpLoadAndScanReferencedDll() failed\n"); - return Success; - } + /* AddressOfData in thunk entry will become a virtual address (from relative) */ + //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData); + ThunkData->u1.AddressOfData = + (ULONG_PTR)RVA(ImageBase, ThunkData->u1.AddressOfData); + //TRACE("PeLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData); } - /* Scan its import address table */ - Success = WinLdrpScanImportAddressTable(ModuleListHead, - DataTableEntry->DllBase, - ScanDTE->DllBase, - (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk), - DirectoryPath); + /* Get the import name */ + ImportData = VaToPa((PVOID)ThunkData->u1.AddressOfData); - if (!Success) + /* Get pointers to Name and Ordinal tables (RVA -> VA) */ + NameTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames)); + OrdinalTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals)); + + //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n", + // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals); + + /* Get the hint, convert it to a physical pointer */ + Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint; + //TRACE("HintIndex %d\n", Hint); + + /* Get the export name from the hint */ + ExportName = VaToPa(RVA(DllBase, NameTable[Hint])); + + /* If Hint is less than total number of entries in the export directory, + and import name == export name, then we can just get it from the OrdinalTable */ + if ((Hint < ExportDirectory->NumberOfNames) && + (strcmp(ExportName, (PCHAR)ImportData->Name) == 0)) { - ERR("WinLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n", - ImportName, DirectoryPath); - return Success; + Ordinal = OrdinalTable[Hint]; + //TRACE("PeLdrpBindImportName(): Ordinal %d\n", Ordinal); } - } + else + { + /* It's not the easy way, we have to lookup import name in the name table. + Let's use a binary search for this task. */ - return TRUE; -} + //TRACE("PeLdrpBindImportName() looking up the import name using binary search...\n"); -BOOLEAN -WinLdrAllocateDataTableEntry(IN OUT PLIST_ENTRY ModuleListHead, - IN PCCH BaseDllName, - IN PCCH FullDllName, - IN PVOID BasePA, - OUT PLDR_DATA_TABLE_ENTRY *NewEntry) -{ - PVOID BaseVA = PaToVa(BasePA); - PWSTR Buffer; - PLDR_DATA_TABLE_ENTRY DataTableEntry; - PIMAGE_NT_HEADERS NtHeaders; - USHORT Length; + /* Low boundary is set to 0, and high boundary to the maximum index */ + Low = 0; + High = ExportDirectory->NumberOfNames - 1; - TRACE("WinLdrAllocateDataTableEntry(, '%s', '%s', %p)\n", - BaseDllName, FullDllName, BasePA); + /* Perform a binary-search loop */ + while (High >= Low) + { + /* Divide by 2 by shifting to the right once */ + Middle = (Low + High) / 2; - /* Allocate memory for a data table entry, zero-initialize it */ - DataTableEntry = (PLDR_DATA_TABLE_ENTRY)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY), - TAG_WLDR_DTE); - if (DataTableEntry == NULL) - return FALSE; - RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY)); + /* Get the name from the name table */ + ExportName = VaToPa(RVA(DllBase, NameTable[Middle])); - /* Get NT headers from the image */ - NtHeaders = RtlImageNtHeader(BasePA); + /* Compare the names */ + Result = strcmp(ExportName, (PCHAR)ImportData->Name); - /* Initialize corresponding fields of DTE based on NT headers value */ - DataTableEntry->DllBase = BaseVA; - DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; - DataTableEntry->EntryPoint = RVA(BaseVA, NtHeaders->OptionalHeader.AddressOfEntryPoint); - DataTableEntry->SectionPointer = 0; - DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; + // TRACE("Binary search: comparing Import '__', Export '%s'\n", + // VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]), + // (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))); - /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName - by simple conversion - copying each character */ - Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR)); - Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME); - if (Buffer == NULL) - { - FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE); - return FALSE; - } - RtlZeroMemory(Buffer, Length); + // TRACE("TE->u1.AOD %p, fulladdr %p\n", + // ThunkData->u1.AddressOfData, + // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name ); - DataTableEntry->BaseDllName.Length = Length; - DataTableEntry->BaseDllName.MaximumLength = Length; - DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer); - while (*BaseDllName != 0) - { - *Buffer++ = *BaseDllName++; + /* Depending on result of strcmp, perform different actions */ + if (Result > 0) + { + /* Adjust top boundary */ + High = Middle - 1; + } + else if (Result < 0) + { + /* Adjust bottom boundary */ + Low = Middle + 1; + } + else + { + /* Yay, found it! */ + break; + } + } + + /* If high boundary is less than low boundary, then no result found */ + if (High < Low) + { + ERR("Did not find export '%s'!\n", (PCHAR)ImportData->Name); + return FALSE; + } + + /* Everything alright, get the ordinal */ + Ordinal = OrdinalTable[Middle]; + + //TRACE("PeLdrpBindImportName() found Ordinal %d\n", Ordinal); + } } - /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName - using the same method */ - Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR)); - Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME); - if (Buffer == NULL) + /* Check ordinal number for validity! */ + if (Ordinal >= ExportDirectory->NumberOfFunctions) { - FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE); + ERR("Ordinal number is invalid!\n"); return FALSE; } - RtlZeroMemory(Buffer, Length); - DataTableEntry->FullDllName.Length = Length; - DataTableEntry->FullDllName.MaximumLength = Length; - DataTableEntry->FullDllName.Buffer = PaToVa(Buffer); - while (*FullDllName != 0) + /* Get a pointer to the function table */ + FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions)); + + /* Save a pointer to the function */ + ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]); + + /* Is it a forwarder? (function pointer is within the export directory) */ + ForwarderName = (PCHAR)VaToPa((PVOID)ThunkData->u1.Function); + if (((ULONG_PTR)ForwarderName > (ULONG_PTR)ExportDirectory) && + ((ULONG_PTR)ForwarderName < ((ULONG_PTR)ExportDirectory + ExportSize))) { - *Buffer++ = *FullDllName++; - } + PLDR_DATA_TABLE_ENTRY DataTableEntry; + CHAR ForwardDllName[255]; + PIMAGE_EXPORT_DIRECTORY RefExportDirectory; + ULONG RefExportSize; - /* Initialize what's left - LoadCount which is 1, and set Flags so that - we know this entry is processed */ - DataTableEntry->Flags = LDRP_ENTRY_PROCESSED; - DataTableEntry->LoadCount = 1; + TRACE("PeLdrpBindImportName(): ForwarderName %s\n", ForwarderName); - /* Insert this DTE to a list in the LPB */ - InsertTailList(ModuleListHead, &DataTableEntry->InLoadOrderLinks); - TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry, - DataTableEntry->BaseDllName.Length / 2, - VaToPa(DataTableEntry->BaseDllName.Buffer), - DataTableEntry->DllBase); + /* Save the name of the forward dll */ + RtlCopyMemory(ForwardDllName, ForwarderName, sizeof(ForwardDllName)); - /* Save pointer to a newly allocated and initialized entry */ - *NewEntry = DataTableEntry; + /* Strip out the symbol name */ + *strrchr(ForwardDllName,'.') = '\0'; - /* Return success */ + /* Check if the target image is already loaded */ + if (!PeLdrCheckForLoadedDll(ModuleListHead, ForwardDllName, &DataTableEntry)) + { + /* Check if the forward dll name has an extension */ + if (strchr(ForwardDllName, '.') == NULL) + { + /* Name does not have an extension, append '.dll' */ + strcat(ForwardDllName, ".dll"); + } + + /* Now let's try to load it! */ + Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead, + DirectoryPath, + ForwardDllName, + &DataTableEntry); + if (!Success) + { + ERR("PeLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n"); + return Success; + } + } + + /* Get pointer to the export directory of loaded DLL */ + RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY) + RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase), + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &RefExportSize); + + /* Fail if it's NULL */ + if (RefExportDirectory) + { + UCHAR Buffer[128]; + IMAGE_THUNK_DATA RefThunkData; + PIMAGE_IMPORT_BY_NAME ImportByName; + PCHAR ImportName; + + /* Get pointer to the import name */ + ImportName = strrchr(ForwarderName, '.') + 1; + + /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */ + ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer; + + /* Fill the name with the import name */ + RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1); + + /* Set Hint to 0 */ + ImportByName->Hint = 0; + + /* And finally point ThunkData's AddressOfData to that structure */ + RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName; + + /* And recursively call ourselves */ + Success = PeLdrpBindImportName(ModuleListHead, + DataTableEntry->DllBase, + ImageBase, + &RefThunkData, + RefExportDirectory, + RefExportSize, + TRUE, + DirectoryPath); + + /* Fill out the ThunkData with data from RefThunkData */ + ThunkData->u1 = RefThunkData.u1; + + /* Return what we got from the recursive call */ + return Success; + } + else + { + /* Fail if ExportDirectory is NULL */ + return FALSE; + } + } + + /* Success! */ return TRUE; } -/* - * WinLdrLoadImage loads the specified image from the file (it doesn't - * perform any additional operations on the filename, just directly - * calls the file I/O routines), and relocates it so that it's ready - * to be used when paging is enabled. - * Addressing mode: physical - */ -BOOLEAN -WinLdrLoadImage(IN PCHAR FileName, - TYPE_OF_MEMORY MemoryType, - OUT PVOID *ImageBasePA) +static BOOLEAN +PeLdrpLoadAndScanReferencedDll( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH DirectoryPath, + IN PCH ImportName, + OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry) { - ULONG FileId; - PVOID PhysicalBase; - PVOID VirtualBase = NULL; - UCHAR HeadersBuffer[SECTOR_SIZE * 2]; - PIMAGE_NT_HEADERS NtHeaders; - PIMAGE_SECTION_HEADER SectionHeader; - ULONG VirtualSize, SizeOfRawData, NumberOfSections; - ARC_STATUS Status; - LARGE_INTEGER Position; - ULONG i, BytesRead; + CHAR FullDllName[256]; + BOOLEAN Success; + PVOID BasePA = NULL; - TRACE("WinLdrLoadImage(%s, %ld, *)\n", FileName, MemoryType); + /* Prepare the full path to the file to be loaded */ + strcpy(FullDllName, DirectoryPath); + strcat(FullDllName, ImportName); - /* Open the image file */ - Status = ArcOpen((PSTR)FileName, OpenReadOnly, &FileId); - if (Status != ESUCCESS) - { - WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName, Status); - return FALSE; - } + TRACE("Loading referenced DLL: %s\n", FullDllName); - /* Load the first 2 sectors of the image so we can read the PE header */ - Status = ArcRead(FileId, HeadersBuffer, SECTOR_SIZE * 2, &BytesRead); - if (Status != ESUCCESS) + /* Load the image */ + Success = PeLdrLoadImage(FullDllName, LoaderBootDriver, &BasePA); + if (!Success) { - ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status); - UiMessageBox("Error reading from file."); - ArcClose(FileId); - return FALSE; + ERR("PeLdrLoadImage() failed\n"); + return Success; } - /* Now read the MZ header to get the offset to the PE Header */ - NtHeaders = RtlImageNtHeader(HeadersBuffer); - if (!NtHeaders) + /* Allocate DTE for newly loaded DLL */ + Success = PeLdrAllocateDataTableEntry(ModuleListHead, + ImportName, + FullDllName, + BasePA, + DataTableEntry); + if (!Success) { - ERR("No NT header found in \"%s\"\n", FileName); - UiMessageBox("Error - no NT header found."); - ArcClose(FileId); - return FALSE; + ERR("PeLdrAllocateDataTableEntry() failed\n"); + return Success; } - /* Ensure this is executable image */ - if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) + /* Scan its dependencies too */ + TRACE("PeLdrScanImportDescriptorTable() calling ourselves for %S\n", + VaToPa((*DataTableEntry)->BaseDllName.Buffer)); + Success = PeLdrScanImportDescriptorTable(ModuleListHead, DirectoryPath, *DataTableEntry); + if (!Success) { - ERR("Not an executable image \"%s\"\n", FileName); - UiMessageBox("Not an executable image."); - ArcClose(FileId); - return FALSE; + ERR("PeLdrScanImportDescriptorTable() failed\n"); + return Success; } - /* Store number of sections to read and a pointer to the first section */ - NumberOfSections = NtHeaders->FileHeader.NumberOfSections; - SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); + return TRUE; +} - /* Try to allocate this memory, if fails - allocate somewhere else */ - PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage, - (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)), - MemoryType); +static BOOLEAN +PeLdrpScanImportAddressTable( + IN OUT PLIST_ENTRY ModuleListHead, + IN PVOID DllBase, + IN PVOID ImageBase, + IN PIMAGE_THUNK_DATA ThunkData, + IN PCSTR DirectoryPath) +{ + PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; + BOOLEAN Success; + ULONG ExportSize; - if (PhysicalBase == NULL) - { - /* It's ok, we don't panic - let's allocate again at any other "low" place */ - PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType); + TRACE("PeLdrpScanImportAddressTable(): DllBase 0x%X, " + "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData); - if (PhysicalBase == NULL) - { - ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders->OptionalHeader.SizeOfImage, FileName); - UiMessageBox("Failed to alloc pages for image."); - ArcClose(FileId); - return FALSE; - } + /* Obtain the export table from the DLL's base */ + if (DllBase == NULL) + { + ERR("Error, DllBase == NULL!\n"); + return FALSE; + } + else + { + ExportDirectory = + (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase), + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportSize); } - /* This is the real image base - in form of a virtual address */ - VirtualBase = PaToVa(PhysicalBase); - - TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase); + TRACE("PeLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory); - /* Set to 0 position and fully load the file image */ - Position.QuadPart = 0; - Status = ArcSeek(FileId, &Position, SeekAbsolute); - if (Status != ESUCCESS) + /* If pointer to Export Directory is */ + if (ExportDirectory == NULL) { - ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName, Status); - UiMessageBox("Error seeking the start of a file."); - ArcClose(FileId); + ERR("DllBase=%p(%p)\n", DllBase, VaToPa(DllBase)); return FALSE; } - Status = ArcRead(FileId, PhysicalBase, NtHeaders->OptionalHeader.SizeOfHeaders, &BytesRead); - if (Status != ESUCCESS) + /* Go through each entry in the thunk table and bind it */ + while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0) { - ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status); - UiMessageBox("Error reading headers."); - ArcClose(FileId); - return FALSE; + /* Bind it */ + Success = PeLdrpBindImportName(ModuleListHead, + DllBase, + ImageBase, + ThunkData, + ExportDirectory, + ExportSize, + FALSE, + DirectoryPath); + + /* Move to the next entry */ + ThunkData++; + + /* Return error if binding was unsuccessful */ + if (!Success) + return Success; } - /* Reload the NT Header */ - NtHeaders = RtlImageNtHeader(PhysicalBase); + /* Return success */ + return TRUE; +} - /* Load the first section */ - SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); - /* Fill output parameters */ - *ImageBasePA = PhysicalBase; +/* FUNCTIONS *****************************************************************/ - /* Walk through each section and read it (check/fix any possible - bad situations, if they arise) */ - for (i = 0; i < NumberOfSections; i++) - { - VirtualSize = SectionHeader->Misc.VirtualSize; - SizeOfRawData = SectionHeader->SizeOfRawData; +/* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */ +BOOLEAN +PeLdrCheckForLoadedDll( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCH DllName, + OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry) +{ + PLDR_DATA_TABLE_ENTRY DataTableEntry; + LIST_ENTRY *ModuleEntry; - /* Handle a case when VirtualSize equals 0 */ - if (VirtualSize == 0) - VirtualSize = SizeOfRawData; + TRACE("PeLdrCheckForLoadedDll: DllName %s\n", DllName); - /* If PointerToRawData is 0, then force its size to be also 0 */ - if (SectionHeader->PointerToRawData == 0) - { - SizeOfRawData = 0; - } - else - { - /* Cut the loaded size to the VirtualSize extents */ - if (SizeOfRawData > VirtualSize) - SizeOfRawData = VirtualSize; - } - - /* Actually read the section (if its size is not 0) */ - if (SizeOfRawData != 0) - { - /* Seek to the correct position */ - Position.LowPart = SectionHeader->PointerToRawData; - Status = ArcSeek(FileId, &Position, SeekAbsolute); - - TRACE("SH->VA: 0x%X\n", SectionHeader->VirtualAddress); + /* Just go through each entry in the LoadOrderList and compare loaded module's + name with a given name */ + ModuleEntry = ModuleListHead->Flink; + while (ModuleEntry != ModuleListHead) + { + /* Get pointer to the current DTE */ + DataTableEntry = CONTAINING_RECORD(ModuleEntry, + LDR_DATA_TABLE_ENTRY, + InLoadOrderLinks); - /* Read this section from the file, size = SizeOfRawData */ - Status = ArcRead(FileId, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress, SizeOfRawData, &BytesRead); - if (Status != ESUCCESS) - { - ERR("WinLdrLoadImage(): Error reading section from file!\n"); - break; - } - } + TRACE("PeLdrCheckForLoadedDll: DTE %p, EP %p, base %p name '%.*ws'\n", + DataTableEntry, DataTableEntry->EntryPoint, DataTableEntry->DllBase, + DataTableEntry->BaseDllName.Length / 2, VaToPa(DataTableEntry->BaseDllName.Buffer)); - /* Size of data is less than the virtual size - fill up the remainder with zeroes */ - if (SizeOfRawData < VirtualSize) + /* Compare names */ + if (PeLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName)) { - TRACE("WinLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize); - RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG_PTR)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData); + /* Yes, found it, report pointer to the loaded module's DTE + to the caller and increase load count for it */ + *LoadedEntry = DataTableEntry; + DataTableEntry->LoadCount++; + TRACE("PeLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry); + return TRUE; } - SectionHeader++; - } - - /* We are done with the file - close it */ - ArcClose(FileId); - - /* If loading failed - return right now */ - if (Status != ESUCCESS) - return FALSE; - - /* Relocate the image, if it needs it */ - if (NtHeaders->OptionalHeader.ImageBase != (ULONG_PTR)VirtualBase) - { - WARN("Relocating %p -> %p\n", NtHeaders->OptionalHeader.ImageBase, - VirtualBase); - return (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase, - (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase, - "FreeLdr", - TRUE, - TRUE, /* in case of conflict still return success */ - FALSE); + /* Go to the next entry */ + ModuleEntry = ModuleEntry->Flink; } - TRACE("WinLdrLoadImage() done, PA = %p\n", *ImageBasePA); - return TRUE; + /* Nothing found */ + return FALSE; } -/* PRIVATE FUNCTIONS *******************************************************/ - -/* DllName - physical, UnicodeString->Buffer - virtual */ -static BOOLEAN -WinLdrpCompareDllName(IN PCH DllName, - IN PUNICODE_STRING UnicodeName) +BOOLEAN +PeLdrScanImportDescriptorTable( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH DirectoryPath, + IN PLDR_DATA_TABLE_ENTRY ScanDTE) { - PWSTR Buffer; - SIZE_T i, Length; + PLDR_DATA_TABLE_ENTRY DataTableEntry; + PIMAGE_IMPORT_DESCRIPTOR ImportTable; + ULONG ImportTableSize; + PCH ImportName; + BOOLEAN Success; - /* First obvious check: for length of two names */ - Length = strlen(DllName); + /* Get a pointer to the import table of this image */ + ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(VaToPa(ScanDTE->DllBase), + TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportTableSize); #if DBG { - UNICODE_STRING UnicodeNamePA; - UnicodeNamePA.Length = UnicodeName->Length; - UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength; - UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer); - TRACE("WinLdrpCompareDllName: %s and %wZ, Length = %d " - "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length); + UNICODE_STRING BaseName; + BaseName.Buffer = VaToPa(ScanDTE->BaseDllName.Buffer); + BaseName.MaximumLength = ScanDTE->BaseDllName.MaximumLength; + BaseName.Length = ScanDTE->BaseDllName.Length; + TRACE("PeLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n", + &BaseName, ImportTable); } #endif - if ((Length * sizeof(WCHAR)) > UnicodeName->Length) - return FALSE; - - /* Store pointer to unicode string's buffer */ - Buffer = VaToPa(UnicodeName->Buffer); + /* If image doesn't have any import directory - just return success */ + if (ImportTable == NULL) + return TRUE; - /* Loop character by character */ - for (i = 0; i < Length; i++) + /* Loop through all entries */ + for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++) { - /* Compare two characters, uppercasing them */ - if (toupper(*DllName) != toupper((CHAR)*Buffer)) - return FALSE; + /* Get pointer to the name */ + ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name)); + TRACE("PeLdrScanImportDescriptorTable(): Looking at %s\n", ImportName); - /* Move to the next character */ - DllName++; - Buffer++; - } + /* In case we get a reference to ourselves - just skip it */ + if (PeLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName)) + continue; - /* Check, if strings either fully match, or match till the "." (w/o extension) */ - if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.')) - { - /* Yes they do */ - return TRUE; + /* Load the DLL if it is not already loaded */ + if (!PeLdrCheckForLoadedDll(ModuleListHead, ImportName, &DataTableEntry)) + { + Success = PeLdrpLoadAndScanReferencedDll(ModuleListHead, + DirectoryPath, + ImportName, + &DataTableEntry); + if (!Success) + { + ERR("PeLdrpLoadAndScanReferencedDll() failed\n"); + return Success; + } + } + + /* Scan its import address table */ + Success = PeLdrpScanImportAddressTable(ModuleListHead, + DataTableEntry->DllBase, + ScanDTE->DllBase, + (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk), + DirectoryPath); + + if (!Success) + { + ERR("PeLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n", + ImportName, DirectoryPath); + return Success; + } } - /* Strings don't match, return FALSE */ - return FALSE; + return TRUE; } -static BOOLEAN -WinLdrpBindImportName(IN OUT PLIST_ENTRY ModuleListHead, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PIMAGE_EXPORT_DIRECTORY ExportDirectory, - IN ULONG ExportSize, - IN BOOLEAN ProcessForwards, - IN PCSTR DirectoryPath) +BOOLEAN +PeLdrAllocateDataTableEntry( + IN OUT PLIST_ENTRY ModuleListHead, + IN PCCH BaseDllName, + IN PCCH FullDllName, + IN PVOID BasePA, + OUT PLDR_DATA_TABLE_ENTRY *NewEntry) { - ULONG Ordinal; - PULONG NameTable, FunctionTable; - PUSHORT OrdinalTable; - LONG High, Low, Middle, Result; - ULONG Hint; - PIMAGE_IMPORT_BY_NAME ImportData; - PCHAR ExportName, ForwarderName; - BOOLEAN Success; + PVOID BaseVA = PaToVa(BasePA); + PWSTR Buffer; + PLDR_DATA_TABLE_ENTRY DataTableEntry; + PIMAGE_NT_HEADERS NtHeaders; + USHORT Length; - //TRACE("WinLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n", - // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards); + TRACE("PeLdrAllocateDataTableEntry(, '%s', '%s', %p)\n", + BaseDllName, FullDllName, BasePA); - /* Check passed DllBase param */ - if(DllBase == NULL) - { - WARN("DllBase == NULL!\n"); + /* Allocate memory for a data table entry, zero-initialize it */ + DataTableEntry = (PLDR_DATA_TABLE_ENTRY)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY), + TAG_WLDR_DTE); + if (DataTableEntry == NULL) return FALSE; - } + RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY)); - /* Convert all non-critical pointers to PA from VA */ - ThunkData = VaToPa(ThunkData); + /* Get NT headers from the image */ + NtHeaders = RtlImageNtHeader(BasePA); - /* Is the reference by ordinal? */ - if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards) + /* Initialize corresponding fields of DTE based on NT headers value */ + DataTableEntry->DllBase = BaseVA; + DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage; + DataTableEntry->EntryPoint = RVA(BaseVA, NtHeaders->OptionalHeader.AddressOfEntryPoint); + DataTableEntry->SectionPointer = 0; + DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum; + + /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName + by simple conversion - copying each character */ + Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR)); + Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME); + if (Buffer == NULL) { - /* Yes, calculate the ordinal */ - Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base); - //TRACE("WinLdrpBindImportName(): Ordinal %d\n", Ordinal); + FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE); + return FALSE; } - else - { - /* It's reference by name, we have to look it up in the export directory */ - if (!ProcessForwards) - { - /* AddressOfData in thunk entry will become a virtual address (from relative) */ - //TRACE("WinLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData); - ThunkData->u1.AddressOfData = - (ULONG_PTR)RVA(ImageBase, ThunkData->u1.AddressOfData); - //TRACE("WinLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData); - } - - /* Get the import name */ - ImportData = VaToPa((PVOID)ThunkData->u1.AddressOfData); - - /* Get pointers to Name and Ordinal tables (RVA -> VA) */ - NameTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames)); - OrdinalTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals)); - - //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n", - // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals); - - /* Get the hint, convert it to a physical pointer */ - Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint; - //TRACE("HintIndex %d\n", Hint); - - /* Get the export name from the hint */ - ExportName = VaToPa(RVA(DllBase, NameTable[Hint])); - - /* If Hint is less than total number of entries in the export directory, - and import name == export name, then we can just get it from the OrdinalTable */ - if ((Hint < ExportDirectory->NumberOfNames) && - (strcmp(ExportName, (PCHAR)ImportData->Name) == 0)) - { - Ordinal = OrdinalTable[Hint]; - //TRACE("WinLdrpBindImportName(): Ordinal %d\n", Ordinal); - } - else - { - /* It's not the easy way, we have to lookup import name in the name table. - Let's use a binary search for this task. */ - - //TRACE("WinLdrpBindImportName() looking up the import name using binary search...\n"); - - /* Low boundary is set to 0, and high boundary to the maximum index */ - Low = 0; - High = ExportDirectory->NumberOfNames - 1; + RtlZeroMemory(Buffer, Length); - /* Perform a binary-search loop */ - while (High >= Low) - { - /* Divide by 2 by shifting to the right once */ - Middle = (Low + High) / 2; + DataTableEntry->BaseDllName.Length = Length; + DataTableEntry->BaseDllName.MaximumLength = Length; + DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer); + while (*BaseDllName != 0) + { + *Buffer++ = *BaseDllName++; + } - /* Get the name from the name table */ - ExportName = VaToPa(RVA(DllBase, NameTable[Middle])); + /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName + using the same method */ + Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR)); + Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME); + if (Buffer == NULL) + { + FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE); + return FALSE; + } + RtlZeroMemory(Buffer, Length); - /* Compare the names */ - Result = strcmp(ExportName, (PCHAR)ImportData->Name); + DataTableEntry->FullDllName.Length = Length; + DataTableEntry->FullDllName.MaximumLength = Length; + DataTableEntry->FullDllName.Buffer = PaToVa(Buffer); + while (*FullDllName != 0) + { + *Buffer++ = *FullDllName++; + } - // TRACE("Binary search: comparing Import '__', Export '%s'\n", - // VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]), - // (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle]))); + /* Initialize what's left - LoadCount which is 1, and set Flags so that + we know this entry is processed */ + DataTableEntry->Flags = LDRP_ENTRY_PROCESSED; + DataTableEntry->LoadCount = 1; - // TRACE("TE->u1.AOD %p, fulladdr %p\n", - // ThunkData->u1.AddressOfData, - // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name ); + /* Insert this DTE to a list in the LPB */ + InsertTailList(ModuleListHead, &DataTableEntry->InLoadOrderLinks); + TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry, + DataTableEntry->BaseDllName.Length / 2, + VaToPa(DataTableEntry->BaseDllName.Buffer), + DataTableEntry->DllBase); - /* Depending on result of strcmp, perform different actions */ - if (Result > 0) - { - /* Adjust top boundary */ - High = Middle - 1; - } - else if (Result < 0) - { - /* Adjust bottom boundary */ - Low = Middle + 1; - } - else - { - /* Yay, found it! */ - break; - } - } + /* Save pointer to a newly allocated and initialized entry */ + *NewEntry = DataTableEntry; - /* If high boundary is less than low boundary, then no result found */ - if (High < Low) - { - ERR("Did not find export '%s'!\n", (PCHAR)ImportData->Name); - return FALSE; - } + /* Return success */ + return TRUE; +} - /* Everything alright, get the ordinal */ - Ordinal = OrdinalTable[Middle]; +/* + * PeLdrLoadImage loads the specified image from the file (it doesn't + * perform any additional operations on the filename, just directly + * calls the file I/O routines), and relocates it so that it's ready + * to be used when paging is enabled. + * Addressing mode: physical + */ +BOOLEAN +PeLdrLoadImage( + IN PCHAR FileName, + IN TYPE_OF_MEMORY MemoryType, + OUT PVOID *ImageBasePA) +{ + ULONG FileId; + PVOID PhysicalBase; + PVOID VirtualBase = NULL; + UCHAR HeadersBuffer[SECTOR_SIZE * 2]; + PIMAGE_NT_HEADERS NtHeaders; + PIMAGE_SECTION_HEADER SectionHeader; + ULONG VirtualSize, SizeOfRawData, NumberOfSections; + ARC_STATUS Status; + LARGE_INTEGER Position; + ULONG i, BytesRead; - //TRACE("WinLdrpBindImportName() found Ordinal %d\n", Ordinal); - } - } + TRACE("PeLdrLoadImage(%s, %ld, *)\n", FileName, MemoryType); - /* Check ordinal number for validity! */ - if (Ordinal >= ExportDirectory->NumberOfFunctions) + /* Open the image file */ + Status = ArcOpen((PSTR)FileName, OpenReadOnly, &FileId); + if (Status != ESUCCESS) { - ERR("Ordinal number is invalid!\n"); + WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName, Status); return FALSE; } - /* Get a pointer to the function table */ - FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions)); + /* Load the first 2 sectors of the image so we can read the PE header */ + Status = ArcRead(FileId, HeadersBuffer, SECTOR_SIZE * 2, &BytesRead); + if (Status != ESUCCESS) + { + ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status); + UiMessageBox("Error reading from file."); + ArcClose(FileId); + return FALSE; + } - /* Save a pointer to the function */ - ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]); + /* Now read the MZ header to get the offset to the PE Header */ + NtHeaders = RtlImageNtHeader(HeadersBuffer); + if (!NtHeaders) + { + ERR("No NT header found in \"%s\"\n", FileName); + UiMessageBox("Error: No NT header found."); + ArcClose(FileId); + return FALSE; + } - /* Is it a forwarder? (function pointer is within the export directory) */ - ForwarderName = (PCHAR)VaToPa((PVOID)ThunkData->u1.Function); - if (((ULONG_PTR)ForwarderName > (ULONG_PTR)ExportDirectory) && - ((ULONG_PTR)ForwarderName < ((ULONG_PTR)ExportDirectory + ExportSize))) + /* Ensure this is executable image */ + if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0)) { - PLDR_DATA_TABLE_ENTRY DataTableEntry; - CHAR ForwardDllName[255]; - PIMAGE_EXPORT_DIRECTORY RefExportDirectory; - ULONG RefExportSize; + ERR("Not an executable image \"%s\"\n", FileName); + UiMessageBox("Not an executable image."); + ArcClose(FileId); + return FALSE; + } - TRACE("WinLdrpBindImportName(): ForwarderName %s\n", ForwarderName); + /* Store number of sections to read and a pointer to the first section */ + NumberOfSections = NtHeaders->FileHeader.NumberOfSections; + SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); - /* Save the name of the forward dll */ - RtlCopyMemory(ForwardDllName, ForwarderName, sizeof(ForwardDllName)); + /* Try to allocate this memory, if fails - allocate somewhere else */ + PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage, + (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)), + MemoryType); - /* Strip out the symbol name */ - *strrchr(ForwardDllName,'.') = '\0'; + if (PhysicalBase == NULL) + { + /* It's ok, we don't panic - let's allocate again at any other "low" place */ + PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType); - /* Check if the target image is already loaded */ - if (!WinLdrCheckForLoadedDll(ModuleListHead, ForwardDllName, &DataTableEntry)) + if (PhysicalBase == NULL) { - /* Check if the forward dll name has an extension */ - if (strchr(ForwardDllName, '.') == NULL) - { - /* Name does not have an extension, append '.dll' */ - strcat(ForwardDllName, ".dll"); - } - - /* Now let's try to load it! */ - Success = WinLdrpLoadAndScanReferencedDll(ModuleListHead, - DirectoryPath, - ForwardDllName, - &DataTableEntry); - if (!Success) - { - ERR("WinLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n"); - return Success; - } + ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders->OptionalHeader.SizeOfImage, FileName); + UiMessageBox("Failed to alloc pages for image."); + ArcClose(FileId); + return FALSE; } + } - /* Get pointer to the export directory of loaded DLL */ - RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY) - RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase), - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &RefExportSize); + /* This is the real image base - in form of a virtual address */ + VirtualBase = PaToVa(PhysicalBase); - /* Fail if it's NULL */ - if (RefExportDirectory) - { - UCHAR Buffer[128]; - IMAGE_THUNK_DATA RefThunkData; - PIMAGE_IMPORT_BY_NAME ImportByName; - PCHAR ImportName; + TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase); - /* Get pointer to the import name */ - ImportName = strrchr(ForwarderName, '.') + 1; + /* Set to 0 position and fully load the file image */ + Position.QuadPart = 0; + Status = ArcSeek(FileId, &Position, SeekAbsolute); + if (Status != ESUCCESS) + { + ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName, Status); + UiMessageBox("Error seeking the start of a file."); + ArcClose(FileId); + return FALSE; + } - /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */ - ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer; + Status = ArcRead(FileId, PhysicalBase, NtHeaders->OptionalHeader.SizeOfHeaders, &BytesRead); + if (Status != ESUCCESS) + { + ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status); + UiMessageBox("Error reading headers."); + ArcClose(FileId); + return FALSE; + } - /* Fill the name with the import name */ - RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1); + /* Reload the NT Header */ + NtHeaders = RtlImageNtHeader(PhysicalBase); - /* Set Hint to 0 */ - ImportByName->Hint = 0; + /* Load the first section */ + SectionHeader = IMAGE_FIRST_SECTION(NtHeaders); - /* And finally point ThunkData's AddressOfData to that structure */ - RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName; + /* Fill output parameters */ + *ImageBasePA = PhysicalBase; - /* And recursively call ourselves */ - Success = WinLdrpBindImportName(ModuleListHead, - DataTableEntry->DllBase, - ImageBase, - &RefThunkData, - RefExportDirectory, - RefExportSize, - TRUE, - DirectoryPath); + /* Walk through each section and read it (check/fix any possible + bad situations, if they arise) */ + for (i = 0; i < NumberOfSections; i++) + { + VirtualSize = SectionHeader->Misc.VirtualSize; + SizeOfRawData = SectionHeader->SizeOfRawData; - /* Fill out the ThunkData with data from RefThunkData */ - ThunkData->u1 = RefThunkData.u1; + /* Handle a case when VirtualSize equals 0 */ + if (VirtualSize == 0) + VirtualSize = SizeOfRawData; - /* Return what we got from the recursive call */ - return Success; + /* If PointerToRawData is 0, then force its size to be also 0 */ + if (SectionHeader->PointerToRawData == 0) + { + SizeOfRawData = 0; } else { - /* Fail if ExportDirectory is NULL */ - return FALSE; + /* Cut the loaded size to the VirtualSize extents */ + if (SizeOfRawData > VirtualSize) + SizeOfRawData = VirtualSize; } - } - - /* Success! */ - return TRUE; -} - -static BOOLEAN -WinLdrpLoadAndScanReferencedDll(PLIST_ENTRY ModuleListHead, - PCCH DirectoryPath, - PCH ImportName, - PLDR_DATA_TABLE_ENTRY *DataTableEntry) -{ - CHAR FullDllName[256]; - BOOLEAN Success; - PVOID BasePA = NULL; - - /* Prepare the full path to the file to be loaded */ - strcpy(FullDllName, DirectoryPath); - strcat(FullDllName, ImportName); - - TRACE("Loading referenced DLL: %s\n", FullDllName); - - /* Load the image */ - Success = WinLdrLoadImage(FullDllName, LoaderBootDriver, &BasePA); - if (!Success) - { - ERR("WinLdrLoadImage() failed\n"); - return Success; - } - - /* Allocate DTE for newly loaded DLL */ - Success = WinLdrAllocateDataTableEntry(ModuleListHead, - ImportName, - FullDllName, - BasePA, - DataTableEntry); - if (!Success) - { - ERR("WinLdrAllocateDataTableEntry() failed\n"); - return Success; - } - /* Scan its dependencies too */ - TRACE("WinLdrScanImportDescriptorTable() calling ourselves for %S\n", - VaToPa((*DataTableEntry)->BaseDllName.Buffer)); - Success = WinLdrScanImportDescriptorTable(ModuleListHead, DirectoryPath, *DataTableEntry); - if (!Success) - { - ERR("WinLdrScanImportDescriptorTable() failed\n"); - return Success; - } + /* Actually read the section (if its size is not 0) */ + if (SizeOfRawData != 0) + { + /* Seek to the correct position */ + Position.LowPart = SectionHeader->PointerToRawData; + Status = ArcSeek(FileId, &Position, SeekAbsolute); - return TRUE; -} + TRACE("SH->VA: 0x%X\n", SectionHeader->VirtualAddress); -static BOOLEAN -WinLdrpScanImportAddressTable(IN OUT PLIST_ENTRY ModuleListHead, - IN PVOID DllBase, - IN PVOID ImageBase, - IN PIMAGE_THUNK_DATA ThunkData, - IN PCSTR DirectoryPath) -{ - PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL; - BOOLEAN Success; - ULONG ExportSize; + /* Read this section from the file, size = SizeOfRawData */ + Status = ArcRead(FileId, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress, SizeOfRawData, &BytesRead); + if (Status != ESUCCESS) + { + ERR("PeLdrLoadImage(): Error reading section from file!\n"); + break; + } + } - TRACE("WinLdrpScanImportAddressTable(): DllBase 0x%X, " - "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData); + /* Size of data is less than the virtual size - fill up the remainder with zeroes */ + if (SizeOfRawData < VirtualSize) + { + TRACE("PeLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize); + RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG_PTR)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData); + } - /* Obtain the export table from the DLL's base */ - if (DllBase == NULL) - { - ERR("Error, DllBase == NULL!\n"); - return FALSE; - } - else - { - ExportDirectory = - (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase), - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &ExportSize); + SectionHeader++; } - TRACE("WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory); + /* We are done with the file - close it */ + ArcClose(FileId); - /* If pointer to Export Directory is */ - if (ExportDirectory == NULL) - { - ERR("DllBase=%p(%p)\n", DllBase, VaToPa(DllBase)); + /* If loading failed - return right now */ + if (Status != ESUCCESS) return FALSE; - } - /* Go through each entry in the thunk table and bind it */ - while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0) + /* Relocate the image, if it needs it */ + if (NtHeaders->OptionalHeader.ImageBase != (ULONG_PTR)VirtualBase) { - /* Bind it */ - Success = WinLdrpBindImportName(ModuleListHead, - DllBase, - ImageBase, - ThunkData, - ExportDirectory, - ExportSize, - FALSE, - DirectoryPath); - - /* Move to the next entry */ - ThunkData++; - - /* Return error if binding was unsuccessful */ - if (!Success) - return Success; + WARN("Relocating %p -> %p\n", NtHeaders->OptionalHeader.ImageBase, VirtualBase); + return (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase, + (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase, + "FreeLdr", + TRUE, + TRUE, /* in case of conflict still return success */ + FALSE); } - /* Return success */ + TRACE("PeLdrLoadImage() done, PA = %p\n", *ImageBasePA); return TRUE; } diff --git a/boot/freeldr/freeldr/ntldr/winldr.c b/boot/freeldr/freeldr/ntldr/winldr.c index fd3104060aa..4ad35a21f6a 100644 --- a/boot/freeldr/freeldr/ntldr/winldr.c +++ b/boot/freeldr/freeldr/ntldr/winldr.c @@ -258,7 +258,7 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead, TRACE("DriverPath: '%s', DllName: '%s', LPB\n", DriverPath, DllName); // Check if driver is already loaded - Success = WinLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE); + Success = PeLdrCheckForLoadedDll(LoadOrderListHead, DllName, DriverDTE); if (Success) { // We've got the pointer to its DTE, just return success @@ -267,15 +267,15 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead, // It's not loaded, we have to load it RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%wZ", BootPath, FilePath); - Success = WinLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase); + Success = PeLdrLoadImage(FullPath, LoaderBootDriver, &DriverBase); if (!Success) return FALSE; // Allocate a DTE for it - Success = WinLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE); + Success = PeLdrAllocateDataTableEntry(LoadOrderListHead, DllName, DllName, DriverBase, DriverDTE); if (!Success) { - ERR("WinLdrAllocateDataTableEntry() failed\n"); + ERR("PeLdrAllocateDataTableEntry() failed\n"); return FALSE; } @@ -284,10 +284,10 @@ WinLdrLoadDeviceDriver(PLIST_ENTRY LoadOrderListHead, // Look for any dependencies it may have, and load them too RtlStringCbPrintfA(FullPath, sizeof(FullPath), "%s%s", BootPath, DriverPath); - Success = WinLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE); + Success = PeLdrScanImportDescriptorTable(LoadOrderListHead, FullPath, *DriverDTE); if (!Success) { - ERR("WinLdrScanImportDescriptorTable() failed for %s\n", FullPath); + ERR("PeLdrScanImportDescriptorTable() failed for %s\n", FullPath); return FALSE; } @@ -451,7 +451,7 @@ LoadModule( RtlStringCbCopyA(FullFileName, sizeof(FullFileName), Path); RtlStringCbCatA(FullFileName, sizeof(FullFileName), File); - Success = WinLdrLoadImage(FullFileName, MemoryType, &BaseAddress); + Success = PeLdrLoadImage(FullFileName, MemoryType, &BaseAddress); if (!Success) { TRACE("Loading %s failed\n", File); @@ -464,11 +464,11 @@ LoadModule( * the Kernel Debugger Transport DLL, to make the * PE loader happy. */ - Success = WinLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead, - ImportName, - FullFileName, - BaseAddress, - Dte); + Success = PeLdrAllocateDataTableEntry(&LoaderBlock->LoadOrderListHead, + ImportName, + FullFileName, + BaseAddress, + Dte); return Success; } @@ -640,11 +640,11 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion, } /* Load all referenced DLLs for Kernel, HAL and Kernel Debugger Transport DLL */ - Success = WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE); - Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE); + Success = PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, *KernelDTE); + Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, HalDTE); if (KdComDTE) { - Success &= WinLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE); + Success &= PeLdrScanImportDescriptorTable(&LoaderBlock->LoadOrderListHead, DirPath, KdComDTE); } return Success;