[FREELDR] Demote the ArcOpen() trace in WinLdrLoadImage() to a warning only.
[reactos.git] / boot / freeldr / freeldr / lib / peloader.c
1 /*
2 * PROJECT: FreeLoader
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Provides routines for loading PE files.
5 * (Deprecated remark) To be merged with arch/i386/loader.c in future.
6 *
7 * COPYRIGHT: Copyright 1998-2003 Brian Palmer <brianp@sginet.com>
8 * Copyright 2006-2019 Aleksey Bragin <aleksey@reactos.org>
9 *
10 * NOTES: The source code in this file is based on the work of respective
11 * authors of PE loading code in ReactOS and Brian Palmer and
12 * Alex Ionescu's arch/i386/loader.c, and my research project
13 * (creating a native EFI loader for Windows).
14 *
15 * This article was very handy during development:
16 * http://msdn.microsoft.com/msdnmag/issues/02/03/PE2/
17 */
18
19 /* INCLUDES ***************************************************************/
20
21 #include <freeldr.h>
22 #include <debug.h>
23
24 DBG_DEFAULT_CHANNEL(PELOADER);
25
26 /* FUNCTIONS **************************************************************/
27
28 static BOOLEAN
29 WinLdrpCompareDllName(IN PCH DllName,
30 IN PUNICODE_STRING UnicodeName);
31
32 static BOOLEAN
33 WinLdrpBindImportName(IN OUT PLIST_ENTRY ModuleListHead,
34 IN PVOID DllBase,
35 IN PVOID ImageBase,
36 IN PIMAGE_THUNK_DATA ThunkData,
37 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
38 IN ULONG ExportSize,
39 IN BOOLEAN ProcessForwards,
40 IN PCSTR DirectoryPath);
41
42 static BOOLEAN
43 WinLdrpLoadAndScanReferencedDll(PLIST_ENTRY ModuleListHead,
44 PCCH DirectoryPath,
45 PCH ImportName,
46 PLDR_DATA_TABLE_ENTRY *DataTableEntry);
47
48 static BOOLEAN
49 WinLdrpScanImportAddressTable(IN OUT PLIST_ENTRY ModuleListHead,
50 IN PVOID DllBase,
51 IN PVOID ImageBase,
52 IN PIMAGE_THUNK_DATA ThunkData,
53 IN PCSTR DirectoryPath);
54
55
56 /* Returns TRUE if DLL has already been loaded - looks in LoadOrderList in LPB */
57 BOOLEAN
58 WinLdrCheckForLoadedDll(IN OUT PLIST_ENTRY ModuleListHead,
59 IN PCH DllName,
60 OUT PLDR_DATA_TABLE_ENTRY *LoadedEntry)
61 {
62 PLDR_DATA_TABLE_ENTRY DataTableEntry;
63 LIST_ENTRY *ModuleEntry;
64
65 TRACE("WinLdrCheckForLoadedDll: DllName %s\n", DllName);
66
67 /* Just go through each entry in the LoadOrderList and compare loaded module's
68 name with a given name */
69 ModuleEntry = ModuleListHead->Flink;
70 while (ModuleEntry != ModuleListHead)
71 {
72 /* Get pointer to the current DTE */
73 DataTableEntry = CONTAINING_RECORD(ModuleEntry,
74 LDR_DATA_TABLE_ENTRY,
75 InLoadOrderLinks);
76
77 TRACE("WinLdrCheckForLoadedDll: DTE %p, EP %p, base %p name '%.*ws'\n",
78 DataTableEntry, DataTableEntry->EntryPoint, DataTableEntry->DllBase,
79 DataTableEntry->BaseDllName.Length / 2, VaToPa(DataTableEntry->BaseDllName.Buffer));
80
81 /* Compare names */
82 if (WinLdrpCompareDllName(DllName, &DataTableEntry->BaseDllName))
83 {
84 /* Yes, found it, report pointer to the loaded module's DTE
85 to the caller and increase load count for it */
86 *LoadedEntry = DataTableEntry;
87 DataTableEntry->LoadCount++;
88 TRACE("WinLdrCheckForLoadedDll: LoadedEntry %X\n", DataTableEntry);
89 return TRUE;
90 }
91
92 /* Go to the next entry */
93 ModuleEntry = ModuleEntry->Flink;
94 }
95
96 /* Nothing found */
97 return FALSE;
98 }
99
100 BOOLEAN
101 WinLdrScanImportDescriptorTable(IN OUT PLIST_ENTRY ModuleListHead,
102 IN PCCH DirectoryPath,
103 IN PLDR_DATA_TABLE_ENTRY ScanDTE)
104 {
105 PLDR_DATA_TABLE_ENTRY DataTableEntry;
106 PIMAGE_IMPORT_DESCRIPTOR ImportTable;
107 ULONG ImportTableSize;
108 PCH ImportName;
109 BOOLEAN Success;
110
111 /* Get a pointer to the import table of this image */
112 ImportTable = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(VaToPa(ScanDTE->DllBase),
113 TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &ImportTableSize);
114
115 #if DBG
116 {
117 UNICODE_STRING BaseName;
118 BaseName.Buffer = VaToPa(ScanDTE->BaseDllName.Buffer);
119 BaseName.MaximumLength = ScanDTE->BaseDllName.MaximumLength;
120 BaseName.Length = ScanDTE->BaseDllName.Length;
121 TRACE("WinLdrScanImportDescriptorTable(): %wZ ImportTable = 0x%X\n",
122 &BaseName, ImportTable);
123 }
124 #endif
125
126 /* If image doesn't have any import directory - just return success */
127 if (ImportTable == NULL)
128 return TRUE;
129
130 /* Loop through all entries */
131 for (;(ImportTable->Name != 0) && (ImportTable->FirstThunk != 0);ImportTable++)
132 {
133 /* Get pointer to the name */
134 ImportName = (PCH)VaToPa(RVA(ScanDTE->DllBase, ImportTable->Name));
135 TRACE("WinLdrScanImportDescriptorTable(): Looking at %s\n", ImportName);
136
137 /* In case we get a reference to ourselves - just skip it */
138 if (WinLdrpCompareDllName(ImportName, &ScanDTE->BaseDllName))
139 continue;
140
141 /* Load the DLL if it is not already loaded */
142 if (!WinLdrCheckForLoadedDll(ModuleListHead, ImportName, &DataTableEntry))
143 {
144 Success = WinLdrpLoadAndScanReferencedDll(ModuleListHead,
145 DirectoryPath,
146 ImportName,
147 &DataTableEntry);
148 if (!Success)
149 {
150 ERR("WinLdrpLoadAndScanReferencedDll() failed\n");
151 return Success;
152 }
153 }
154
155 /* Scan its import address table */
156 Success = WinLdrpScanImportAddressTable(ModuleListHead,
157 DataTableEntry->DllBase,
158 ScanDTE->DllBase,
159 (PIMAGE_THUNK_DATA)RVA(ScanDTE->DllBase, ImportTable->FirstThunk),
160 DirectoryPath);
161
162 if (!Success)
163 {
164 ERR("WinLdrpScanImportAddressTable() failed: ImportName = '%s', DirectoryPath = '%s'\n",
165 ImportName, DirectoryPath);
166 return Success;
167 }
168 }
169
170 return TRUE;
171 }
172
173 BOOLEAN
174 WinLdrAllocateDataTableEntry(IN OUT PLIST_ENTRY ModuleListHead,
175 IN PCCH BaseDllName,
176 IN PCCH FullDllName,
177 IN PVOID BasePA,
178 OUT PLDR_DATA_TABLE_ENTRY *NewEntry)
179 {
180 PVOID BaseVA = PaToVa(BasePA);
181 PWSTR Buffer;
182 PLDR_DATA_TABLE_ENTRY DataTableEntry;
183 PIMAGE_NT_HEADERS NtHeaders;
184 USHORT Length;
185
186 TRACE("WinLdrAllocateDataTableEntry(, '%s', '%s', %p)\n",
187 BaseDllName, FullDllName, BasePA);
188
189 /* Allocate memory for a data table entry, zero-initialize it */
190 DataTableEntry = (PLDR_DATA_TABLE_ENTRY)FrLdrHeapAlloc(sizeof(LDR_DATA_TABLE_ENTRY),
191 TAG_WLDR_DTE);
192 if (DataTableEntry == NULL)
193 return FALSE;
194 RtlZeroMemory(DataTableEntry, sizeof(LDR_DATA_TABLE_ENTRY));
195
196 /* Get NT headers from the image */
197 NtHeaders = RtlImageNtHeader(BasePA);
198
199 /* Initialize corresponding fields of DTE based on NT headers value */
200 DataTableEntry->DllBase = BaseVA;
201 DataTableEntry->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
202 DataTableEntry->EntryPoint = RVA(BaseVA, NtHeaders->OptionalHeader.AddressOfEntryPoint);
203 DataTableEntry->SectionPointer = 0;
204 DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
205
206 /* Initialize BaseDllName field (UNICODE_STRING) from the Ansi BaseDllName
207 by simple conversion - copying each character */
208 Length = (USHORT)(strlen(BaseDllName) * sizeof(WCHAR));
209 Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME);
210 if (Buffer == NULL)
211 {
212 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
213 return FALSE;
214 }
215 RtlZeroMemory(Buffer, Length);
216
217 DataTableEntry->BaseDllName.Length = Length;
218 DataTableEntry->BaseDllName.MaximumLength = Length;
219 DataTableEntry->BaseDllName.Buffer = PaToVa(Buffer);
220 while (*BaseDllName != 0)
221 {
222 *Buffer++ = *BaseDllName++;
223 }
224
225 /* Initialize FullDllName field (UNICODE_STRING) from the Ansi FullDllName
226 using the same method */
227 Length = (USHORT)(strlen(FullDllName) * sizeof(WCHAR));
228 Buffer = (PWSTR)FrLdrHeapAlloc(Length, TAG_WLDR_NAME);
229 if (Buffer == NULL)
230 {
231 FrLdrHeapFree(DataTableEntry, TAG_WLDR_DTE);
232 return FALSE;
233 }
234 RtlZeroMemory(Buffer, Length);
235
236 DataTableEntry->FullDllName.Length = Length;
237 DataTableEntry->FullDllName.MaximumLength = Length;
238 DataTableEntry->FullDllName.Buffer = PaToVa(Buffer);
239 while (*FullDllName != 0)
240 {
241 *Buffer++ = *FullDllName++;
242 }
243
244 /* Initialize what's left - LoadCount which is 1, and set Flags so that
245 we know this entry is processed */
246 DataTableEntry->Flags = LDRP_ENTRY_PROCESSED;
247 DataTableEntry->LoadCount = 1;
248
249 /* Insert this DTE to a list in the LPB */
250 InsertTailList(ModuleListHead, &DataTableEntry->InLoadOrderLinks);
251 TRACE("Inserting DTE %p, name='%.*S' DllBase=%p \n", DataTableEntry,
252 DataTableEntry->BaseDllName.Length / 2,
253 VaToPa(DataTableEntry->BaseDllName.Buffer),
254 DataTableEntry->DllBase);
255
256 /* Save pointer to a newly allocated and initialized entry */
257 *NewEntry = DataTableEntry;
258
259 /* Return success */
260 return TRUE;
261 }
262
263 /*
264 * WinLdrLoadImage loads the specified image from the file (it doesn't
265 * perform any additional operations on the filename, just directly
266 * calls the file I/O routines), and relocates it so that it's ready
267 * to be used when paging is enabled.
268 * Addressing mode: physical
269 */
270 BOOLEAN
271 WinLdrLoadImage(IN PCHAR FileName,
272 TYPE_OF_MEMORY MemoryType,
273 OUT PVOID *ImageBasePA)
274 {
275 ULONG FileId;
276 PVOID PhysicalBase;
277 PVOID VirtualBase = NULL;
278 UCHAR HeadersBuffer[SECTOR_SIZE * 2];
279 PIMAGE_NT_HEADERS NtHeaders;
280 PIMAGE_SECTION_HEADER SectionHeader;
281 ULONG VirtualSize, SizeOfRawData, NumberOfSections;
282 ARC_STATUS Status;
283 LARGE_INTEGER Position;
284 ULONG i, BytesRead;
285
286 TRACE("WinLdrLoadImage(%s, %ld, *)\n", FileName, MemoryType);
287
288 /* Open the image file */
289 Status = ArcOpen(FileName, OpenReadOnly, &FileId);
290 if (Status != ESUCCESS)
291 {
292 WARN("ArcOpen(FileName: '%s') failed. Status: %u\n", FileName, Status);
293 return FALSE;
294 }
295
296 /* Load the first 2 sectors of the image so we can read the PE header */
297 Status = ArcRead(FileId, HeadersBuffer, SECTOR_SIZE * 2, &BytesRead);
298 if (Status != ESUCCESS)
299 {
300 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
301 UiMessageBox("Error reading from file.");
302 ArcClose(FileId);
303 return FALSE;
304 }
305
306 /* Now read the MZ header to get the offset to the PE Header */
307 NtHeaders = RtlImageNtHeader(HeadersBuffer);
308 if (!NtHeaders)
309 {
310 ERR("No NT header found in \"%s\"\n", FileName);
311 UiMessageBox("Error - no NT header found.");
312 ArcClose(FileId);
313 return FALSE;
314 }
315
316 /* Ensure this is executable image */
317 if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0))
318 {
319 ERR("Not an executable image \"%s\"\n", FileName);
320 UiMessageBox("Not an executable image.");
321 ArcClose(FileId);
322 return FALSE;
323 }
324
325 /* Store number of sections to read and a pointer to the first section */
326 NumberOfSections = NtHeaders->FileHeader.NumberOfSections;
327 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
328
329 /* Try to allocate this memory, if fails - allocate somewhere else */
330 PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage,
331 (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)),
332 MemoryType);
333
334 if (PhysicalBase == NULL)
335 {
336 /* It's ok, we don't panic - let's allocate again at any other "low" place */
337 PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType);
338
339 if (PhysicalBase == NULL)
340 {
341 ERR("Failed to alloc %lu bytes for image %s\n", NtHeaders->OptionalHeader.SizeOfImage, FileName);
342 UiMessageBox("Failed to alloc pages for image.");
343 ArcClose(FileId);
344 return FALSE;
345 }
346 }
347
348 /* This is the real image base - in form of a virtual address */
349 VirtualBase = PaToVa(PhysicalBase);
350
351 TRACE("Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase);
352
353 /* Set to 0 position and fully load the file image */
354 Position.QuadPart = 0;
355 Status = ArcSeek(FileId, &Position, SeekAbsolute);
356 if (Status != ESUCCESS)
357 {
358 ERR("ArcSeek(File: '%s') failed. Status: 0x%lx\n", FileName, Status);
359 UiMessageBox("Error seeking the start of a file.");
360 ArcClose(FileId);
361 return FALSE;
362 }
363
364 Status = ArcRead(FileId, PhysicalBase, NtHeaders->OptionalHeader.SizeOfHeaders, &BytesRead);
365 if (Status != ESUCCESS)
366 {
367 ERR("ArcRead(File: '%s') failed. Status: %u\n", FileName, Status);
368 UiMessageBox("Error reading headers.");
369 ArcClose(FileId);
370 return FALSE;
371 }
372
373 /* Reload the NT Header */
374 NtHeaders = RtlImageNtHeader(PhysicalBase);
375
376 /* Load the first section */
377 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
378
379 /* Fill output parameters */
380 *ImageBasePA = PhysicalBase;
381
382 /* Walk through each section and read it (check/fix any possible
383 bad situations, if they arise) */
384 for (i = 0; i < NumberOfSections; i++)
385 {
386 VirtualSize = SectionHeader->Misc.VirtualSize;
387 SizeOfRawData = SectionHeader->SizeOfRawData;
388
389 /* Handle a case when VirtualSize equals 0 */
390 if (VirtualSize == 0)
391 VirtualSize = SizeOfRawData;
392
393 /* If PointerToRawData is 0, then force its size to be also 0 */
394 if (SectionHeader->PointerToRawData == 0)
395 {
396 SizeOfRawData = 0;
397 }
398 else
399 {
400 /* Cut the loaded size to the VirtualSize extents */
401 if (SizeOfRawData > VirtualSize)
402 SizeOfRawData = VirtualSize;
403 }
404
405 /* Actually read the section (if its size is not 0) */
406 if (SizeOfRawData != 0)
407 {
408 /* Seek to the correct position */
409 Position.LowPart = SectionHeader->PointerToRawData;
410 Status = ArcSeek(FileId, &Position, SeekAbsolute);
411
412 TRACE("SH->VA: 0x%X\n", SectionHeader->VirtualAddress);
413
414 /* Read this section from the file, size = SizeOfRawData */
415 Status = ArcRead(FileId, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress, SizeOfRawData, &BytesRead);
416 if (Status != ESUCCESS)
417 {
418 ERR("WinLdrLoadImage(): Error reading section from file!\n");
419 break;
420 }
421 }
422
423 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
424 if (SizeOfRawData < VirtualSize)
425 {
426 TRACE("WinLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize);
427 RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG_PTR)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData);
428 }
429
430 SectionHeader++;
431 }
432
433 /* We are done with the file - close it */
434 ArcClose(FileId);
435
436 /* If loading failed - return right now */
437 if (Status != ESUCCESS)
438 return FALSE;
439
440 /* Relocate the image, if it needs it */
441 if (NtHeaders->OptionalHeader.ImageBase != (ULONG_PTR)VirtualBase)
442 {
443 WARN("Relocating %p -> %p\n", NtHeaders->OptionalHeader.ImageBase,
444 VirtualBase);
445 return (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase,
446 (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase,
447 "FreeLdr",
448 TRUE,
449 TRUE, /* in case of conflict still return success */
450 FALSE);
451 }
452
453 TRACE("WinLdrLoadImage() done, PA = %p\n", *ImageBasePA);
454 return TRUE;
455 }
456
457 /* PRIVATE FUNCTIONS *******************************************************/
458
459 /* DllName - physical, UnicodeString->Buffer - virtual */
460 static BOOLEAN
461 WinLdrpCompareDllName(IN PCH DllName,
462 IN PUNICODE_STRING UnicodeName)
463 {
464 PWSTR Buffer;
465 SIZE_T i, Length;
466
467 /* First obvious check: for length of two names */
468 Length = strlen(DllName);
469
470 #if DBG
471 {
472 UNICODE_STRING UnicodeNamePA;
473 UnicodeNamePA.Length = UnicodeName->Length;
474 UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength;
475 UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer);
476 TRACE("WinLdrpCompareDllName: %s and %wZ, Length = %d "
477 "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length);
478 }
479 #endif
480
481 if ((Length * sizeof(WCHAR)) > UnicodeName->Length)
482 return FALSE;
483
484 /* Store pointer to unicode string's buffer */
485 Buffer = VaToPa(UnicodeName->Buffer);
486
487 /* Loop character by character */
488 for (i = 0; i < Length; i++)
489 {
490 /* Compare two characters, uppercasing them */
491 if (toupper(*DllName) != toupper((CHAR)*Buffer))
492 return FALSE;
493
494 /* Move to the next character */
495 DllName++;
496 Buffer++;
497 }
498
499 /* Check, if strings either fully match, or match till the "." (w/o extension) */
500 if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.'))
501 {
502 /* Yes they do */
503 return TRUE;
504 }
505
506 /* Strings don't match, return FALSE */
507 return FALSE;
508 }
509
510 static BOOLEAN
511 WinLdrpBindImportName(IN OUT PLIST_ENTRY ModuleListHead,
512 IN PVOID DllBase,
513 IN PVOID ImageBase,
514 IN PIMAGE_THUNK_DATA ThunkData,
515 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
516 IN ULONG ExportSize,
517 IN BOOLEAN ProcessForwards,
518 IN PCSTR DirectoryPath)
519 {
520 ULONG Ordinal;
521 PULONG NameTable, FunctionTable;
522 PUSHORT OrdinalTable;
523 LONG High, Low, Middle, Result;
524 ULONG Hint;
525 PIMAGE_IMPORT_BY_NAME ImportData;
526 PCHAR ExportName, ForwarderName;
527 BOOLEAN Success;
528
529 //TRACE("WinLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n",
530 // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards);
531
532 /* Check passed DllBase param */
533 if(DllBase == NULL)
534 {
535 WARN("DllBase == NULL!\n");
536 return FALSE;
537 }
538
539 /* Convert all non-critical pointers to PA from VA */
540 ThunkData = VaToPa(ThunkData);
541
542 /* Is the reference by ordinal? */
543 if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards)
544 {
545 /* Yes, calculate the ordinal */
546 Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base);
547 //TRACE("WinLdrpBindImportName(): Ordinal %d\n", Ordinal);
548 }
549 else
550 {
551 /* It's reference by name, we have to look it up in the export directory */
552 if (!ProcessForwards)
553 {
554 /* AddressOfData in thunk entry will become a virtual address (from relative) */
555 //TRACE("WinLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData);
556 ThunkData->u1.AddressOfData =
557 (ULONG_PTR)RVA(ImageBase, ThunkData->u1.AddressOfData);
558 //TRACE("WinLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData);
559 }
560
561 /* Get the import name */
562 ImportData = VaToPa((PVOID)ThunkData->u1.AddressOfData);
563
564 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
565 NameTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames));
566 OrdinalTable = VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals));
567
568 //TRACE("NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
569 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
570
571 /* Get the hint, convert it to a physical pointer */
572 Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint;
573 //TRACE("HintIndex %d\n", Hint);
574
575 /* Get the export name from the hint */
576 ExportName = VaToPa(RVA(DllBase, NameTable[Hint]));
577
578 /* If Hint is less than total number of entries in the export directory,
579 and import name == export name, then we can just get it from the OrdinalTable */
580 if ((Hint < ExportDirectory->NumberOfNames) &&
581 (strcmp(ExportName, (PCHAR)ImportData->Name) == 0))
582 {
583 Ordinal = OrdinalTable[Hint];
584 //TRACE("WinLdrpBindImportName(): Ordinal %d\n", Ordinal);
585 }
586 else
587 {
588 /* It's not the easy way, we have to lookup import name in the name table.
589 Let's use a binary search for this task. */
590
591 //TRACE("WinLdrpBindImportName() looking up the import name using binary search...\n");
592
593 /* Low boundary is set to 0, and high boundary to the maximum index */
594 Low = 0;
595 High = ExportDirectory->NumberOfNames - 1;
596
597 /* Perform a binary-search loop */
598 while (High >= Low)
599 {
600 /* Divide by 2 by shifting to the right once */
601 Middle = (Low + High) / 2;
602
603 /* Get the name from the name table */
604 ExportName = VaToPa(RVA(DllBase, NameTable[Middle]));
605
606 /* Compare the names */
607 Result = strcmp(ExportName, (PCHAR)ImportData->Name);
608
609 // TRACE("Binary search: comparing Import '__', Export '%s'\n",
610 // VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),
611 // (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));
612
613 // TRACE("TE->u1.AOD %p, fulladdr %p\n",
614 // ThunkData->u1.AddressOfData,
615 // ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );
616
617 /* Depending on result of strcmp, perform different actions */
618 if (Result > 0)
619 {
620 /* Adjust top boundary */
621 High = Middle - 1;
622 }
623 else if (Result < 0)
624 {
625 /* Adjust bottom boundary */
626 Low = Middle + 1;
627 }
628 else
629 {
630 /* Yay, found it! */
631 break;
632 }
633 }
634
635 /* If high boundary is less than low boundary, then no result found */
636 if (High < Low)
637 {
638 ERR("Did not find export '%s'!\n", (PCHAR)ImportData->Name);
639 return FALSE;
640 }
641
642 /* Everything alright, get the ordinal */
643 Ordinal = OrdinalTable[Middle];
644
645 //TRACE("WinLdrpBindImportName() found Ordinal %d\n", Ordinal);
646 }
647 }
648
649 /* Check ordinal number for validity! */
650 if (Ordinal >= ExportDirectory->NumberOfFunctions)
651 {
652 ERR("Ordinal number is invalid!\n");
653 return FALSE;
654 }
655
656 /* Get a pointer to the function table */
657 FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions));
658
659 /* Save a pointer to the function */
660 ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]);
661
662 /* Is it a forwarder? (function pointer is within the export directory) */
663 ForwarderName = (PCHAR)VaToPa((PVOID)ThunkData->u1.Function);
664 if (((ULONG_PTR)ForwarderName > (ULONG_PTR)ExportDirectory) &&
665 ((ULONG_PTR)ForwarderName < ((ULONG_PTR)ExportDirectory + ExportSize)))
666 {
667 PLDR_DATA_TABLE_ENTRY DataTableEntry;
668 CHAR ForwardDllName[255];
669 PIMAGE_EXPORT_DIRECTORY RefExportDirectory;
670 ULONG RefExportSize;
671
672 TRACE("WinLdrpBindImportName(): ForwarderName %s\n", ForwarderName);
673
674 /* Save the name of the forward dll */
675 RtlCopyMemory(ForwardDllName, ForwarderName, sizeof(ForwardDllName));
676
677 /* Strip out the symbol name */
678 *strrchr(ForwardDllName,'.') = '\0';
679
680 /* Check if the target image is already loaded */
681 if (!WinLdrCheckForLoadedDll(ModuleListHead, ForwardDllName, &DataTableEntry))
682 {
683 /* Check if the forward dll name has an extension */
684 if (strchr(ForwardDllName, '.') == NULL)
685 {
686 /* Name does not have an extension, append '.dll' */
687 strcat(ForwardDllName, ".dll");
688 }
689
690 /* Now let's try to load it! */
691 Success = WinLdrpLoadAndScanReferencedDll(ModuleListHead,
692 DirectoryPath,
693 ForwardDllName,
694 &DataTableEntry);
695 if (!Success)
696 {
697 ERR("WinLdrpLoadAndScanReferencedDll() failed to load forwarder dll.\n");
698 return Success;
699 }
700 }
701
702 /* Get pointer to the export directory of loaded DLL */
703 RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
704 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase),
705 TRUE,
706 IMAGE_DIRECTORY_ENTRY_EXPORT,
707 &RefExportSize);
708
709 /* Fail if it's NULL */
710 if (RefExportDirectory)
711 {
712 UCHAR Buffer[128];
713 IMAGE_THUNK_DATA RefThunkData;
714 PIMAGE_IMPORT_BY_NAME ImportByName;
715 PCHAR ImportName;
716
717 /* Get pointer to the import name */
718 ImportName = strrchr(ForwarderName, '.') + 1;
719
720 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
721 ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer;
722
723 /* Fill the name with the import name */
724 RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1);
725
726 /* Set Hint to 0 */
727 ImportByName->Hint = 0;
728
729 /* And finally point ThunkData's AddressOfData to that structure */
730 RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName;
731
732 /* And recursively call ourselves */
733 Success = WinLdrpBindImportName(ModuleListHead,
734 DataTableEntry->DllBase,
735 ImageBase,
736 &RefThunkData,
737 RefExportDirectory,
738 RefExportSize,
739 TRUE,
740 DirectoryPath);
741
742 /* Fill out the ThunkData with data from RefThunkData */
743 ThunkData->u1 = RefThunkData.u1;
744
745 /* Return what we got from the recursive call */
746 return Success;
747 }
748 else
749 {
750 /* Fail if ExportDirectory is NULL */
751 return FALSE;
752 }
753 }
754
755 /* Success! */
756 return TRUE;
757 }
758
759 static BOOLEAN
760 WinLdrpLoadAndScanReferencedDll(PLIST_ENTRY ModuleListHead,
761 PCCH DirectoryPath,
762 PCH ImportName,
763 PLDR_DATA_TABLE_ENTRY *DataTableEntry)
764 {
765 CHAR FullDllName[256];
766 BOOLEAN Success;
767 PVOID BasePA = NULL;
768
769 /* Prepare the full path to the file to be loaded */
770 strcpy(FullDllName, DirectoryPath);
771 strcat(FullDllName, ImportName);
772
773 TRACE("Loading referenced DLL: %s\n", FullDllName);
774
775 /* Load the image */
776 Success = WinLdrLoadImage(FullDllName, LoaderBootDriver, &BasePA);
777 if (!Success)
778 {
779 ERR("WinLdrLoadImage() failed\n");
780 return Success;
781 }
782
783 /* Allocate DTE for newly loaded DLL */
784 Success = WinLdrAllocateDataTableEntry(ModuleListHead,
785 ImportName,
786 FullDllName,
787 BasePA,
788 DataTableEntry);
789 if (!Success)
790 {
791 ERR("WinLdrAllocateDataTableEntry() failed\n");
792 return Success;
793 }
794
795 /* Scan its dependencies too */
796 TRACE("WinLdrScanImportDescriptorTable() calling ourselves for %S\n",
797 VaToPa((*DataTableEntry)->BaseDllName.Buffer));
798 Success = WinLdrScanImportDescriptorTable(ModuleListHead, DirectoryPath, *DataTableEntry);
799 if (!Success)
800 {
801 ERR("WinLdrScanImportDescriptorTable() failed\n");
802 return Success;
803 }
804
805 return TRUE;
806 }
807
808 static BOOLEAN
809 WinLdrpScanImportAddressTable(IN OUT PLIST_ENTRY ModuleListHead,
810 IN PVOID DllBase,
811 IN PVOID ImageBase,
812 IN PIMAGE_THUNK_DATA ThunkData,
813 IN PCSTR DirectoryPath)
814 {
815 PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
816 BOOLEAN Success;
817 ULONG ExportSize;
818
819 TRACE("WinLdrpScanImportAddressTable(): DllBase 0x%X, "
820 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData);
821
822 /* Obtain the export table from the DLL's base */
823 if (DllBase == NULL)
824 {
825 ERR("Error, DllBase == NULL!\n");
826 return FALSE;
827 }
828 else
829 {
830 ExportDirectory =
831 (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase),
832 TRUE,
833 IMAGE_DIRECTORY_ENTRY_EXPORT,
834 &ExportSize);
835 }
836
837 TRACE("WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory);
838
839 /* If pointer to Export Directory is */
840 if (ExportDirectory == NULL)
841 {
842 ERR("DllBase=%p(%p)\n", DllBase, VaToPa(DllBase));
843 return FALSE;
844 }
845
846 /* Go through each entry in the thunk table and bind it */
847 while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0)
848 {
849 /* Bind it */
850 Success = WinLdrpBindImportName(ModuleListHead,
851 DllBase,
852 ImageBase,
853 ThunkData,
854 ExportDirectory,
855 ExportSize,
856 FALSE,
857 DirectoryPath);
858
859 /* Move to the next entry */
860 ThunkData++;
861
862 /* Return error if binding was unsuccessful */
863 if (!Success)
864 return Success;
865 }
866
867 /* Return success */
868 return TRUE;
869 }