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