Merge freeldr from amd64 branch:
[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_WINDOWS, "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_WINDOWS, "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_WINDOWS, "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_WINDOWS, "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_WINDOWS, "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_WINDOWS, "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_WINDOWS, "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 PFILE FileHandle;
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 BOOLEAN Status;
266 ULONG i, BytesRead;
267
268 CHAR ProgressString[256];
269
270 /* Inform user we are loading files */
271 sprintf(ProgressString, "Loading %s...", FileName);
272 UiDrawProgressBarCenter(1, 100, ProgressString);
273
274 /* Open the image file */
275 FileHandle = FsOpenFile(FileName);
276
277 if (FileHandle == NULL)
278 {
279 //Print(L"Can not open the file %s\n",FileName);
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 = FsReadFile(FileHandle, SECTOR_SIZE * 2, NULL, HeadersBuffer);
286 if (!Status)
287 {
288 //Print(L"Error reading from file %s\n", FileName);
289 UiMessageBox("Error reading from file");
290 FsCloseFile(FileHandle);
291 return FALSE;
292 }
293
294 /* Now read the MZ header to get the offset to the PE Header */
295 NtHeaders = RtlImageNtHeader(HeadersBuffer);
296
297 if (!NtHeaders)
298 {
299 //Print(L"Error - no NT header found in %s\n", FileName);
300 UiMessageBox("Error - no NT header found");
301 FsCloseFile(FileHandle);
302 return FALSE;
303 }
304
305 /* Ensure this is executable image */
306 if (((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0))
307 {
308 //Print(L"Not an executable image %s\n", FileName);
309 UiMessageBox("Not an executable image");
310 FsCloseFile(FileHandle);
311 return FALSE;
312 }
313
314 /* Store number of sections to read and a pointer to the first section */
315 NumberOfSections = NtHeaders->FileHeader.NumberOfSections;
316 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
317
318 /* Try to allocate this memory, if fails - allocate somewhere else */
319 PhysicalBase = MmAllocateMemoryAtAddress(NtHeaders->OptionalHeader.SizeOfImage,
320 (PVOID)((ULONG)NtHeaders->OptionalHeader.ImageBase & (KSEG0_BASE - 1)),
321 MemoryType);
322
323 if (PhysicalBase == NULL)
324 {
325 /* It's ok, we don't panic - let's allocate again at any other "low" place */
326 PhysicalBase = MmAllocateMemoryWithType(NtHeaders->OptionalHeader.SizeOfImage, MemoryType);
327
328 if (PhysicalBase == NULL)
329 {
330 //Print(L"Failed to alloc pages for image %s\n", FileName);
331 UiMessageBox("Failed to alloc pages for image");
332 FsCloseFile(FileHandle);
333 return FALSE;
334 }
335 }
336
337 /* This is the real image base - in form of a virtual address */
338 VirtualBase = PaToVa(PhysicalBase);
339
340 DPRINTM(DPRINT_WINDOWS, "Base PA: 0x%X, VA: 0x%X\n", PhysicalBase, VirtualBase);
341
342 /* Set to 0 position and fully load the file image */
343 FsSetFilePointer(FileHandle, 0);
344
345 Status = FsReadFile(FileHandle, NtHeaders->OptionalHeader.SizeOfHeaders, NULL, PhysicalBase);
346
347 if (!Status)
348 {
349 //Print(L"Error reading headers %s\n", FileName);
350 UiMessageBox("Error reading headers");
351 FsCloseFile(FileHandle);
352 return FALSE;
353 }
354
355 /* Reload the NT Header */
356 NtHeaders = RtlImageNtHeader(PhysicalBase);
357
358 /* Load the first section */
359 SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
360
361 /* Fill output parameters */
362 *ImageBasePA = PhysicalBase;
363
364 /* Walk through each section and read it (check/fix any possible
365 bad situations, if they arise) */
366 for (i = 0; i < NumberOfSections; i++)
367 {
368 VirtualSize = SectionHeader->Misc.VirtualSize;
369 SizeOfRawData = SectionHeader->SizeOfRawData;
370
371 /* Handle a case when VirtualSize equals 0 */
372 if (VirtualSize == 0)
373 VirtualSize = SizeOfRawData;
374
375 /* If PointerToRawData is 0, then force its size to be also 0 */
376 if (SectionHeader->PointerToRawData == 0)
377 {
378 SizeOfRawData = 0;
379 }
380 else
381 {
382 /* Cut the loaded size to the VirtualSize extents */
383 if (SizeOfRawData > VirtualSize)
384 SizeOfRawData = VirtualSize;
385 }
386
387 /* Actually read the section (if its size is not 0) */
388 if (SizeOfRawData != 0)
389 {
390 /* Seek to the correct position */
391 FsSetFilePointer(FileHandle, SectionHeader->PointerToRawData);
392
393 DPRINTM(DPRINT_WINDOWS, "SH->VA: 0x%X\n", SectionHeader->VirtualAddress);
394
395 /* Read this section from the file, size = SizeOfRawData */
396 Status = FsReadFile(FileHandle, SizeOfRawData, &BytesRead, (PUCHAR)PhysicalBase + SectionHeader->VirtualAddress);
397
398 if (!Status && (BytesRead == 0))
399 {
400 DPRINTM(DPRINT_WINDOWS, "WinLdrLoadImage(): Error reading section from file!\n");
401 break;
402 }
403 }
404
405 /* Size of data is less than the virtual size - fill up the remainder with zeroes */
406 if (SizeOfRawData < VirtualSize)
407 {
408 DPRINTM(DPRINT_WINDOWS, "WinLdrLoadImage(): SORD %d < VS %d\n", SizeOfRawData, VirtualSize);
409 RtlZeroMemory((PVOID)(SectionHeader->VirtualAddress + (ULONG)PhysicalBase + SizeOfRawData), VirtualSize - SizeOfRawData);
410 }
411
412 SectionHeader++;
413 }
414
415 /* We are done with the file - close it */
416 FsCloseFile(FileHandle);
417
418 /* If loading failed - return right now */
419 if (!Status)
420 return FALSE;
421
422
423 /* Relocate the image, if it needs it */
424 if (NtHeaders->OptionalHeader.ImageBase != (ULONG)VirtualBase)
425 {
426 DPRINTM(DPRINT_WINDOWS, "Relocating %p -> %p\n",
427 NtHeaders->OptionalHeader.ImageBase, VirtualBase);
428 Status = (BOOLEAN)LdrRelocateImageWithBias(PhysicalBase,
429 (ULONG_PTR)VirtualBase - (ULONG_PTR)PhysicalBase,
430 "FreeLdr",
431 TRUE,
432 TRUE, /* in case of conflict still return success */
433 FALSE);
434 }
435
436 return Status;
437 }
438
439 /* PRIVATE FUNCTIONS *******************************************************/
440
441 /* DllName - physical, UnicodeString->Buffer - virtual */
442 BOOLEAN
443 WinLdrpCompareDllName(IN PCH DllName,
444 IN PUNICODE_STRING UnicodeName)
445 {
446 PWSTR Buffer;
447 UNICODE_STRING UnicodeNamePA;
448 ULONG i, Length;
449
450 /* First obvious check: for length of two names */
451 Length = strlen(DllName);
452
453 UnicodeNamePA.Length = UnicodeName->Length;
454 UnicodeNamePA.MaximumLength = UnicodeName->MaximumLength;
455 UnicodeNamePA.Buffer = VaToPa(UnicodeName->Buffer);
456 DPRINTM(DPRINT_WINDOWS, "WinLdrpCompareDllName: %s and %wZ, Length = %d "
457 "UN->Length %d\n", DllName, &UnicodeNamePA, Length, UnicodeName->Length);
458
459 if ((Length * sizeof(WCHAR)) > UnicodeName->Length)
460 return FALSE;
461
462 /* Store pointer to unicode string's buffer */
463 Buffer = VaToPa(UnicodeName->Buffer);
464
465 /* Loop character by character */
466 for (i = 0; i < Length; i++)
467 {
468 /* Compare two characters, uppercasing them */
469 if (toupper(*DllName) != toupper((CHAR)*Buffer))
470 return FALSE;
471
472 /* Move to the next character */
473 DllName++;
474 Buffer++;
475 }
476
477 /* Check, if strings either fully match, or match till the "." (w/o extension) */
478 if ((UnicodeName->Length == Length * sizeof(WCHAR)) || (*Buffer == L'.'))
479 {
480 /* Yes they do */
481 return TRUE;
482 }
483
484 /* Strings don't match, return FALSE */
485 return FALSE;
486 }
487
488 BOOLEAN
489 WinLdrpBindImportName(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,
490 IN PVOID DllBase,
491 IN PVOID ImageBase,
492 IN PIMAGE_THUNK_DATA ThunkData,
493 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
494 IN ULONG ExportSize,
495 IN BOOLEAN ProcessForwards)
496 {
497 ULONG Ordinal;
498 PULONG NameTable, FunctionTable;
499 PUSHORT OrdinalTable;
500 LONG High, Low, Middle, Result;
501 ULONG Hint;
502
503 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): DllBase 0x%X, ImageBase 0x%X, ThunkData 0x%X, ExportDirectory 0x%X, ExportSize %d, ProcessForwards 0x%X\n",
504 // DllBase, ImageBase, ThunkData, ExportDirectory, ExportSize, ProcessForwards);
505
506 /* Check passed DllBase param */
507 if(DllBase == NULL)
508 {
509 DPRINTM(DPRINT_WINDOWS, "WARNING: DllBase == NULL!\n");
510 return FALSE;
511 }
512
513 /* Convert all non-critical pointers to PA from VA */
514 ThunkData = VaToPa(ThunkData);
515
516 /* Is the reference by ordinal? */
517 if (IMAGE_SNAP_BY_ORDINAL(ThunkData->u1.Ordinal) && !ProcessForwards)
518 {
519 /* Yes, calculate the ordinal */
520 Ordinal = (ULONG)(IMAGE_ORDINAL(ThunkData->u1.Ordinal) - (UINT32)ExportDirectory->Base);
521 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal);
522 }
523 else
524 {
525 /* It's reference by name, we have to look it up in the export directory */
526 if (!ProcessForwards)
527 {
528 /* AddressOfData in thunk entry will become a virtual address (from relative) */
529 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD was %p\n", ThunkData->u1.AddressOfData);
530 ThunkData->u1.AddressOfData =
531 (ULONG)RVA(ImageBase, ThunkData->u1.AddressOfData);
532 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): ThunkData->u1.AOD became %p\n", ThunkData->u1.AddressOfData);
533 }
534
535 /* Get pointers to Name and Ordinal tables (RVA -> VA) */
536 NameTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNames));
537 OrdinalTable = (PUSHORT)VaToPa(RVA(DllBase, ExportDirectory->AddressOfNameOrdinals));
538
539 //DPRINTM(DPRINT_WINDOWS, "NameTable 0x%X, OrdinalTable 0x%X, ED->AddressOfNames 0x%X, ED->AOFO 0x%X\n",
540 // NameTable, OrdinalTable, ExportDirectory->AddressOfNames, ExportDirectory->AddressOfNameOrdinals);
541
542 /* Get the hint, convert it to a physical pointer */
543 Hint = ((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Hint;
544 //DPRINTM(DPRINT_WINDOWS, "HintIndex %d\n", Hint);
545
546 /* If Hint is less than total number of entries in the export directory,
547 and import name == export name, then we can just get it from the OrdinalTable */
548 if (
549 (Hint < ExportDirectory->NumberOfNames) &&
550 (
551 strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]),
552 (PCHAR)VaToPa( RVA(DllBase, NameTable[Hint])) ) == 0
553 )
554 )
555 {
556 Ordinal = OrdinalTable[Hint];
557 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): Ordinal %d\n", Ordinal);
558 }
559 else
560 {
561 /* It's not the easy way, we have to lookup import name in the name table.
562 Let's use a binary search for this task. */
563
564 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName() looking up the import name using binary search...\n");
565
566 /* Low boundary is set to 0, and high boundary to the maximum index */
567 Low = 0;
568 High = ExportDirectory->NumberOfNames - 1;
569
570 /* Perform a binary-search loop */
571 while (High >= Low)
572 {
573 /* Divide by 2 by shifting to the right once */
574 Middle = (Low + High) >> 1;
575
576 /* Compare the names */
577 Result = strcmp(VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa((PVOID)ThunkData->u1.AddressOfData))->Name[0]),
578 (PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));
579
580 /*DPRINTM(DPRINT_WINDOWS, "Binary search: comparing Import '__', Export '%s'\n",*/
581 /*VaToPa(&((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name[0]),*/
582 /*(PCHAR)VaToPa(RVA(DllBase, NameTable[Middle])));*/
583
584 /*DPRINTM(DPRINT_WINDOWS, "TE->u1.AOD %p, fulladdr %p\n",
585 ThunkData->u1.AddressOfData,
586 ((PIMAGE_IMPORT_BY_NAME)VaToPa(ThunkData->u1.AddressOfData))->Name );*/
587
588
589 /* Depending on result of strcmp, perform different actions */
590 if (Result < 0)
591 {
592 /* Adjust top boundary */
593 High = Middle - 1;
594 }
595 else if (Result > 0)
596 {
597 /* Adjust bottom boundary */
598 Low = Middle + 1;
599 }
600 else
601 {
602 /* Yay, found it! */
603 break;
604 }
605 }
606
607 /* If high boundary is less than low boundary, then no result found */
608 if (High < Low)
609 {
610 //Print(L"Error in binary search\n");
611 DPRINTM(DPRINT_WINDOWS, "Error in binary search!\n");
612 return FALSE;
613 }
614
615 /* Everything allright, get the ordinal */
616 Ordinal = OrdinalTable[Middle];
617
618 //DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName() found Ordinal %d\n", Ordinal);
619 }
620 }
621
622 /* Check ordinal number for validity! */
623 if (Ordinal >= ExportDirectory->NumberOfFunctions)
624 {
625 DPRINTM(DPRINT_WINDOWS, "Ordinal number is invalid!\n");
626 return FALSE;
627 }
628
629 /* Get a pointer to the function table */
630 FunctionTable = (PULONG)VaToPa(RVA(DllBase, ExportDirectory->AddressOfFunctions));
631
632 /* Save a pointer to the function */
633 ThunkData->u1.Function = (ULONG)RVA(DllBase, FunctionTable[Ordinal]);
634
635 /* Is it a forwarder? (function pointer isn't within the export directory) */
636 if (((ULONG)VaToPa((PVOID)ThunkData->u1.Function) > (ULONG)ExportDirectory) &&
637 ((ULONG)VaToPa((PVOID)ThunkData->u1.Function) < ((ULONG)ExportDirectory + ExportSize)))
638 {
639 PLDR_DATA_TABLE_ENTRY DataTableEntry;
640 CHAR ForwardDllName[255];
641 PIMAGE_EXPORT_DIRECTORY RefExportDirectory;
642 ULONG RefExportSize;
643
644 /* Save the name of the forward dll */
645 RtlCopyMemory(ForwardDllName, (PCHAR)VaToPa((PVOID)ThunkData->u1.Function), sizeof(ForwardDllName));
646
647 /* Strip out its extension */
648 *strchr(ForwardDllName,'.') = '\0';
649
650 DPRINTM(DPRINT_WINDOWS, "WinLdrpBindImportName(): ForwardDllName %s\n", ForwardDllName);
651 if (!WinLdrCheckForLoadedDll(WinLdrBlock, ForwardDllName, &DataTableEntry))
652 {
653 /* We can't continue if DLL couldn't be loaded, so bomb out with an error */
654 //Print(L"Error loading DLL!\n");
655 DPRINTM(DPRINT_WINDOWS, "Error loading DLL!\n");
656 return FALSE;
657 }
658
659 /* Get pointer to the export directory of loaded DLL */
660 RefExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
661 RtlImageDirectoryEntryToData(VaToPa(DataTableEntry->DllBase),
662 TRUE,
663 IMAGE_DIRECTORY_ENTRY_EXPORT,
664 &RefExportSize);
665
666 /* Fail if it's NULL */
667 if (RefExportDirectory)
668 {
669 UCHAR Buffer[128];
670 IMAGE_THUNK_DATA RefThunkData;
671 PIMAGE_IMPORT_BY_NAME ImportByName;
672 PCHAR ImportName;
673 BOOLEAN Status;
674
675 /* Get pointer to the import name */
676 ImportName = strchr((PCHAR)VaToPa((PVOID)ThunkData->u1.Function), '.') + 1;
677
678 /* Create a IMAGE_IMPORT_BY_NAME structure, pointing to the local Buffer */
679 ImportByName = (PIMAGE_IMPORT_BY_NAME)Buffer;
680
681 /* Fill the name with the import name */
682 RtlCopyMemory(ImportByName->Name, ImportName, strlen(ImportName)+1);
683
684 /* Set Hint to 0 */
685 ImportByName->Hint = 0;
686
687 /* And finally point ThunkData's AddressOfData to that structure */
688 RefThunkData.u1.AddressOfData = (ULONG)ImportByName;
689
690 /* And recursively call ourselves */
691 Status = WinLdrpBindImportName(
692 WinLdrBlock,
693 DataTableEntry->DllBase,
694 ImageBase,
695 &RefThunkData,
696 RefExportDirectory,
697 RefExportSize,
698 TRUE);
699
700 /* Fill out the ThunkData with data from RefThunkData */
701 ThunkData->u1 = RefThunkData.u1;
702
703 /* Return what we got from the recursive call */
704 return Status;
705 }
706 else
707 {
708 /* Fail if ExportDirectory is NULL */
709 return FALSE;
710 }
711 }
712
713 /* Success! */
714 return TRUE;
715 }
716
717 BOOLEAN
718 WinLdrpLoadAndScanReferencedDll(PLOADER_PARAMETER_BLOCK WinLdrBlock,
719 PCCH DirectoryPath,
720 PCH ImportName,
721 PLDR_DATA_TABLE_ENTRY *DataTableEntry)
722 {
723 CHAR FullDllName[256];
724 BOOLEAN Status;
725 PVOID BasePA;
726
727 /* Prepare the full path to the file to be loaded */
728 strcpy(FullDllName, DirectoryPath);
729 strcat(FullDllName, ImportName);
730
731 DPRINTM(DPRINT_WINDOWS, "Loading referenced DLL: %s\n", FullDllName);
732 //Print(L"Loading referenced DLL: %s\n", FullDllName);
733
734 /* Load the image */
735 Status = WinLdrLoadImage(FullDllName, LoaderHalCode, &BasePA);
736
737 if (!Status)
738 {
739 DPRINTM(DPRINT_WINDOWS, "WinLdrLoadImage() failed\n");
740 return Status;
741 }
742
743 /* Allocate DTE for newly loaded DLL */
744 Status = WinLdrAllocateDataTableEntry(WinLdrBlock,
745 ImportName,
746 FullDllName,
747 BasePA,
748 DataTableEntry);
749
750 if (!Status)
751 {
752 DPRINTM(DPRINT_WINDOWS,
753 "WinLdrAllocateDataTableEntry() failed with Status=0x%X\n", Status);
754 return Status;
755 }
756
757 /* Scan its dependencies too */
758 DPRINTM(DPRINT_WINDOWS,
759 "WinLdrScanImportDescriptorTable() calling ourselves for %S\n",
760 VaToPa((*DataTableEntry)->BaseDllName.Buffer));
761 Status = WinLdrScanImportDescriptorTable(WinLdrBlock, DirectoryPath, *DataTableEntry);
762
763 if (!Status)
764 {
765 DPRINTM(DPRINT_WINDOWS,
766 "WinLdrScanImportDescriptorTable() failed with Status=0x%X\n", Status);
767 return Status;
768 }
769
770 return TRUE;
771 }
772
773 BOOLEAN
774 WinLdrpScanImportAddressTable(IN OUT PLOADER_PARAMETER_BLOCK WinLdrBlock,
775 IN PVOID DllBase,
776 IN PVOID ImageBase,
777 IN PIMAGE_THUNK_DATA ThunkData)
778 {
779 PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
780 BOOLEAN Status;
781 ULONG ExportSize;
782
783 DPRINTM(DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): DllBase 0x%X, "
784 "ImageBase 0x%X, ThunkData 0x%X\n", DllBase, ImageBase, ThunkData);
785
786 /* Obtain the export table from the DLL's base */
787 if (DllBase == NULL)
788 {
789 //Print(L"Error, DllBase == NULL!\n");
790 return FALSE;
791 }
792 else
793 {
794 ExportDirectory =
795 (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(VaToPa(DllBase),
796 TRUE,
797 IMAGE_DIRECTORY_ENTRY_EXPORT,
798 &ExportSize);
799 }
800
801 DPRINTM(DPRINT_WINDOWS, "WinLdrpScanImportAddressTable(): ExportDirectory 0x%X\n", ExportDirectory);
802
803 /* If pointer to Export Directory is */
804 if (ExportDirectory == NULL)
805 return FALSE;
806
807 /* Go through each entry in the thunk table and bind it */
808 while (((PIMAGE_THUNK_DATA)VaToPa(ThunkData))->u1.AddressOfData != 0)
809 {
810 /* Bind it */
811 Status = WinLdrpBindImportName(
812 WinLdrBlock,
813 DllBase,
814 ImageBase,
815 ThunkData,
816 ExportDirectory,
817 ExportSize,
818 FALSE);
819
820 /* Move to the next entry */
821 ThunkData++;
822
823 /* Return error if binding was unsuccessful */
824 if (!Status)
825 return Status;
826 }
827
828 /* Return success */
829 return TRUE;
830 }