Merge trunk HEAD (r46369)
[reactos.git] / reactos / 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 %s, 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 /* Low boundary is set to 0, and high boundary to the maximum index */
572 Low = 0;
573 High = ExportDirectory->NumberOfNames - 1;
574
575 DPRINTM(DPRINT_PELOADER, "WinLdrpBindImportName() looking up import '%s' in #0..#%d\n",
576 VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]), High);
577
578 /* Perform a binary-search loop */
579 while (High >= Low)
580 {
581 /* Divide by 2 by shifting to the right once */
582 Middle = (Low + High) >> 1;
583
584 /* Compare the names */
585 Result = strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]),
586 (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));
587
588 DPRINTM(DPRINT_PELOADER, "Binary search: comparing Import '%s', Export #%ld:'%s' -> %d\n",
589 VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),
590 Middle, (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])), Result);
591
592 /*DPRINTM(DPRINT_PELOADER, "TE->u1.AOD %p, fulladdr %p\n",
593 ThunkData->u1.AddressOfData,
594 ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );*/
595
596
597 /* Depending on result of strcmp, perform different actions */
598 if (Result < 0)
599 {
600 /* Adjust top boundary */
601 High = Middle - 1;
602 }
603 else if (Result > 0)
604 {
605 /* Adjust bottom boundary */
606 Low = Middle + 1;
607 }
608 else
609 {
610 /* Yay, found it! */
611 break;
612 }
613 }
614
615 /* If high boundary is less than low boundary, then no result found */
616 if (High < Low)
617 {
618 //Print(L"Error in binary search\n");
619 DPRINTM(DPRINT_PELOADER, "Error in binary search!\n");
620 return FALSE;
621 }
622
623 /* Everything allright, get the ordinal */
624 Ordinal = OrdinalTable[Middle];
625
626 //DPRINTM(DPRINT_PELOADER, "WinLdrpBindImportName() found Ordinal %d\n", Ordinal);
627 }
628 }
629
630 /* Check ordinal number for validity! */
631 if (Ordinal >= ExportDirectory->NumberOfFunctions)
632 {
633 DPRINTM(DPRINT_PELOADER, "Ordinal number is invalid!\n");
634 return FALSE;
635 }
636
637 /* Get a pointer to the function table */
638 FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions));
639
640 /* Save a pointer to the function */
641 ThunkData->u1.Function = (ULONG_PTR)RVA(DllBase, FunctionTable[Ordinal]);
642
643 /* Is it a forwarder? (function pointer isn't within the export directory) */
644 if (((ULONG_PTR)VaToPa((PVOID)ThunkData->u1.Function) > (ULONG_PTR)ExportDirectory) &&
645 ((ULONG_PTR)VaToPa((PVOID)ThunkData->u1.Function) < ((ULONG_PTR)ExportDirectory + ExportSize)))
646 {
647 PLDR_DATA_TABLE_ENTRY DataTableEntry;
648 CHAR ForwardDllName[255];
649 PIMAGE_EXPORT_DIRECTORY RefExportDirectory;
650 ULONG RefExportSize;
651
652 /* Save the name of the forward dll */
653 RtlCopyMemory(ForwardDllName, (PCHAR)VaToPa((PVOID)ThunkData->u1.Function), sizeof(ForwardDllName));
654
655 /* Strip out its extension */
656 *strchr(ForwardDllName,'.') = '\0';
657
658 DPRINTM(DPRINT_PELOADER, "WinLdrpBindImportName(): ForwardDllName %s\n", ForwardDllName);
659 if (!WinLdrCheckForLoadedDll(WinLdrBlock, ForwardDllName, &DataTableEntry))
660 {
661 /* We can't continue if DLL couldn't be loaded, so bomb out with an error */
662 //Print(L"Error loading DLL!\n");
663 DPRINTM(DPRINT_PELOADER, "Error loading DLL!\n");
664 return FALSE;
665 }
666
667 /* Get pointer to the export directory of loaded DLL */
668 RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
669 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase),
670 TRUE,
671 IMAGE_DIRECTORY_ENTRY_EXPORT,
672 &RefExportSize);
673
674 /* Fail if it's NULL */
675 if (RefExportDirectory)
676 {
677 UCHAR Buffer[128];
678 IMAGE_THUNK_DATA RefThunkData;
679 PIMAGE_IMPORT_BY_NAME ImportByName;
680 PCHAR ImportName;
681 BOOLEAN Status;
682
683 /* Get pointer to the import name */
684 ImportName = strchr((PCHAR)VaToPa((PVOID)ThunkData->u1.Function), '.') + 1;
685
686 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
687 ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer;
688
689 /* Fill the name with the import name */
690 RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1);
691
692 /* Set Hint to 0 */
693 ImportByName->Hint = 0;
694
695 /* And finally point ThunkData's AddressOfData to that structure */
696 RefThunkData.u1.AddressOfData = (ULONG_PTR)ImportByName;
697
698 /* And recursively call ourselves */
699 Status = WinLdrpBindImportName(
700 WinLdrBlock,
701 DataTableEntry->DllBase,
702 ImageBase,
703 &RefThunkData,
704 RefExportDirectory,
705 RefExportSize,
706 TRUE);
707
708 /* Fill out the ThunkData with data from RefThunkData */
709 ThunkData->u1 = RefThunkData.u1;
710
711 /* Return what we got from the recursive call */
712 return Status;
713 }
714 else
715 {
716 /* Fail if ExportDirectory is NULL */
717 return FALSE;
718 }
719 }
720
721 /* Success! */
722 return TRUE;
723 }
724
725 BOOLEAN
726 WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock,
727 PCCH DirectoryPath,
728 PCH ImportName,
729 PLDR_DATA_TABLE_ENTRY *DataTableEntry)
730 {
731 CHAR FullDllName[256];
732 BOOLEAN Status;
733 PVOID BasePA;
734
735 /* Prepare the full path to the file to be loaded */
736 strcpy(FullDllName, DirectoryPath);
737 strcat(FullDllName, ImportName);
738
739 DPRINTM(DPRINT_PELOADER, "Loading referenced DLL: %s\n", FullDllName);
740 //Print(L"Loading referenced DLL: %s\n", FullDllName);
741
742 /* Load the image */
743 Status = WinLdrLoadImage(FullDllName, LoaderHalCode, &BasePA);
744
745 if (!Status)
746 {
747 DPRINTM(DPRINT_PELOADER, "WinLdrLoadImage() failed\n");
748 return Status;
749 }
750
751 /* Allocate DTE for newly loaded DLL */
752 Status = WinLdrAllocateDataTableEntry(WinLdrBlock,
753 ImportName,
754 FullDllName,
755 BasePA,
756 DataTableEntry);
757
758 if (!Status)
759 {
760 DPRINTM(DPRINT_PELOADER,
761 "WinLdrAllocateDataTableEntry() failed with Status=0x%X\n", Status);
762 return Status;
763 }
764
765 /* Scan its dependencies too */
766 DPRINTM(DPRINT_PELOADER,
767 "WinLdrScanImportDescriptorTable() calling ourselves for %S\n",
768 VaToPa((*DataTableEntry)->BaseDllName.Buffer));
769 Status = WinLdrScanImportDescriptorTable(WinLdrBlock, DirectoryPath, *DataTableEntry);
770
771 if (!Status)
772 {
773 DPRINTM(DPRINT_PELOADER,
774 "WinLdrScanImportDescriptorTable() failed with Status=0x%X\n", Status);
775 return Status;
776 }
777
778 return TRUE;
779 }
780
781 BOOLEAN
782 WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,
783 IN PVOID DllBase,
784 IN PVOID ImageBase,
785 IN PIMAGE_THUNK_DATA ThunkData)
786 {
787 PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
788 BOOLEAN Status;
789 ULONG ExportSize;
790
791 DPRINTM(DPRINT_PELOADER, "WinLdrpScanImportAddressTable(): DllBase 0x%X, "
792 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData);
793
794 /* Obtain the export table from the DLL's base */
795 if (DllBase == NULL)
796 {
797 //Print(L"Error, DllBase == NULL!\n");
798 return FALSE;
799 }
800 else
801 {
802 ExportDirectory =
803 (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase),
804 TRUE,
805 IMAGE_DIRECTORY_ENTRY_EXPORT,
806 &ExportSize);
807 }
808
809 DPRINTM(DPRINT_PELOADER, "WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory);
810
811 /* If pointer to Export Directory is */
812 if (ExportDirectory == NULL)
813 return FALSE;
814
815 /* Go through each entry in the thunk table and bind it */
816 while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0)
817 {
818 /* Bind it */
819 Status = WinLdrpBindImportName(
820 WinLdrBlock,
821 DllBase,
822 ImageBase,
823 ThunkData,
824 ExportDirectory,
825 ExportSize,
826 FALSE);
827
828 /* Move to the next entry */
829 ThunkData++;
830
831 /* Return error if binding was unsuccessful */
832 if (!Status)
833 return Status;
834 }
835
836 /* Return success */
837 return TRUE;
838 }