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