xref: /reactos/sdk/lib/rtl/image.c (revision bd0a5498)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/rtl/image.c
5  * PURPOSE:         Image handling functions
6  *                  Relocate functions were previously located in
7  *                  ntoskrnl/ldr/loader.c and
8  *                  dll/ntdll/ldr/utils.c files
9  * PROGRAMMER:      Eric Kohl + original authors from loader.c and utils.c file
10  *                  Aleksey Bragin
11  */
12 
13 /* INCLUDES *****************************************************************/
14 
15 #include <rtl.h>
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
21 
22 /* FUNCTIONS *****************************************************************/
23 
24 FORCEINLINE
25 USHORT
ChkSum(ULONG Sum,PUSHORT Src,ULONG Len)26 ChkSum(ULONG Sum, PUSHORT Src, ULONG Len)
27 {
28     ULONG i;
29 
30     for (i=0; i<Len; i++)
31     {
32         /* Sum up the current word */
33         Sum += Src[i];
34 
35         /* Sum up everything above the low word as a carry */
36         Sum = (Sum & 0xFFFF) + (Sum >> 16);
37     }
38 
39     /* Apply carry one more time and clamp to the USHORT */
40     return (Sum + (Sum >> 16)) & 0xFFFF;
41 }
42 
43 BOOLEAN
44 NTAPI
LdrVerifyMappedImageMatchesChecksum(IN PVOID BaseAddress,IN SIZE_T ImageSize,IN ULONG FileLength)45 LdrVerifyMappedImageMatchesChecksum(
46     IN PVOID BaseAddress,
47     IN SIZE_T ImageSize,
48     IN ULONG FileLength)
49 {
50 #if 0
51     PIMAGE_NT_HEADERS Header;
52     PUSHORT Ptr;
53     ULONG Sum;
54     ULONG CalcSum;
55     ULONG HeaderSum;
56     ULONG i;
57 
58     // HACK: Ignore calls with ImageSize=0. Should be fixed by new MM.
59     if (ImageSize == 0) return TRUE;
60 
61     /* Get NT header to check if it's an image at all */
62     Header = RtlImageNtHeader(BaseAddress);
63     if (!Header) return FALSE;
64 
65     /* Get checksum to match */
66     HeaderSum = Header->OptionalHeader.CheckSum;
67 
68     /* Zero checksum seems to be accepted */
69     if (HeaderSum == 0) return TRUE;
70 
71     /* Calculate the checksum */
72     Sum = 0;
73     Ptr = (PUSHORT) BaseAddress;
74     for (i = 0; i < ImageSize / sizeof (USHORT); i++)
75     {
76         Sum += (ULONG)*Ptr;
77         if (HIWORD(Sum) != 0)
78         {
79             Sum = LOWORD(Sum) + HIWORD(Sum);
80         }
81         Ptr++;
82     }
83 
84     if (ImageSize & 1)
85     {
86         Sum += (ULONG)*((PUCHAR)Ptr);
87         if (HIWORD(Sum) != 0)
88         {
89             Sum = LOWORD(Sum) + HIWORD(Sum);
90         }
91     }
92 
93     CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum));
94 
95     /* Subtract image checksum from calculated checksum. */
96     /* fix low word of checksum */
97     if (LOWORD(CalcSum) >= LOWORD(HeaderSum))
98     {
99         CalcSum -= LOWORD(HeaderSum);
100     }
101     else
102     {
103         CalcSum = ((LOWORD(CalcSum) - LOWORD(HeaderSum)) & 0xFFFF) - 1;
104     }
105 
106     /* Fix high word of checksum */
107     if (LOWORD(CalcSum) >= HIWORD(HeaderSum))
108     {
109         CalcSum -= HIWORD(HeaderSum);
110     }
111     else
112     {
113         CalcSum = ((LOWORD(CalcSum) - HIWORD(HeaderSum)) & 0xFFFF) - 1;
114     }
115 
116     /* Add file length */
117     CalcSum += ImageSize;
118 
119     if (CalcSum != HeaderSum)
120         DPRINT1("Image %p checksum mismatches! 0x%x != 0x%x, ImageSize %x, FileLen %x\n", BaseAddress, CalcSum, HeaderSum, ImageSize, FileLength);
121 
122     return (BOOLEAN)(CalcSum == HeaderSum);
123 #else
124     /*
125      * FIXME: Warning, this violates the PE standard and makes ReactOS drivers
126      * and other system code when normally on Windows they would not, since
127      * we do not write the checksum in them.
128      * Our compilers should be made to write out the checksum and this function
129      * should be enabled as to reject badly checksummed code.
130      */
131     return TRUE;
132 #endif
133 }
134 
135 /*
136  * @implemented
137  */
138 NTSTATUS
139 NTAPI
RtlpImageNtHeaderEx(_In_ ULONG Flags,_In_ PVOID Base,_In_ ULONG64 Size,_Out_ PIMAGE_NT_HEADERS * OutHeaders)140 RtlpImageNtHeaderEx(
141     _In_ ULONG Flags,
142     _In_ PVOID Base,
143     _In_ ULONG64 Size,
144     _Out_ PIMAGE_NT_HEADERS *OutHeaders)
145 {
146     PIMAGE_NT_HEADERS NtHeaders;
147     PIMAGE_DOS_HEADER DosHeader;
148     BOOLEAN WantsRangeCheck;
149     ULONG NtHeaderOffset;
150 
151     /* You must want NT Headers, no? */
152     if (OutHeaders == NULL)
153     {
154         DPRINT1("OutHeaders is NULL\n");
155         return STATUS_INVALID_PARAMETER;
156     }
157 
158     /* Assume failure */
159     *OutHeaders = NULL;
160 
161     /* Validate Flags */
162     if (Flags & ~RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK)
163     {
164         DPRINT1("Invalid flags: 0x%lx\n", Flags);
165         return STATUS_INVALID_PARAMETER;
166     }
167 
168     /* Validate base */
169     if ((Base == NULL) || (Base == (PVOID)-1))
170     {
171         DPRINT1("Invalid base address: %p\n", Base);
172         return STATUS_INVALID_PARAMETER;
173     }
174 
175     /* Check if the caller wants range checks */
176     WantsRangeCheck = !(Flags & RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK);
177     if (WantsRangeCheck)
178     {
179         /* Make sure the image size is at least big enough for the DOS header */
180         if (Size < sizeof(IMAGE_DOS_HEADER))
181         {
182             DPRINT1("Size too small\n");
183             return STATUS_INVALID_IMAGE_FORMAT;
184         }
185     }
186 
187     /* Check if the DOS Signature matches */
188     DosHeader = Base;
189     if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
190     {
191         /* Not a valid COFF */
192         DPRINT1("Invalid image DOS signature!\n");
193         return STATUS_INVALID_IMAGE_FORMAT;
194     }
195 
196     /* Get the offset to the NT headers (and copy from LONG to ULONG) */
197     NtHeaderOffset = DosHeader->e_lfanew;
198 
199     /* The offset must not be larger than 256MB, as a hard-coded check.
200        In Windows this check is only done in user mode, not in kernel mode,
201        but it shouldn't harm to have it anyway. Note that without this check,
202        other overflow checks would become necessary! */
203     if (NtHeaderOffset >= (256 * 1024 * 1024))
204     {
205         /* Fail */
206         DPRINT1("NT headers offset is larger than 256MB!\n");
207         return STATUS_INVALID_IMAGE_FORMAT;
208     }
209 
210     /* Check if the caller wants validation */
211     if (WantsRangeCheck)
212     {
213         /* Make sure the file header fits into the size */
214         if ((NtHeaderOffset +
215              RTL_SIZEOF_THROUGH_FIELD(IMAGE_NT_HEADERS, FileHeader)) >= Size)
216         {
217             /* Fail */
218             DPRINT1("NT headers beyond image size!\n");
219             return STATUS_INVALID_IMAGE_FORMAT;
220         }
221     }
222 
223     /* Now get a pointer to the NT Headers */
224     NtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)Base + NtHeaderOffset);
225 
226     /* Check if the mapping is in user space */
227     if (Base <= MmHighestUserAddress)
228     {
229         /* Make sure we don't overflow into kernel space */
230         if ((PVOID)(NtHeaders + 1) > MmHighestUserAddress)
231         {
232             DPRINT1("Image overflows from user space into kernel space!\n");
233             return STATUS_INVALID_IMAGE_FORMAT;
234         }
235     }
236 
237     /* Verify the PE Signature */
238     if (NtHeaders->Signature != IMAGE_NT_SIGNATURE)
239     {
240         /* Fail */
241         DPRINT1("Invalid image NT signature!\n");
242         return STATUS_INVALID_IMAGE_FORMAT;
243     }
244 
245     /* Now return success and the NT header */
246     *OutHeaders = NtHeaders;
247     return STATUS_SUCCESS;
248 }
249 
250 /*
251  * @implemented
252  */
253 PIMAGE_NT_HEADERS
254 NTAPI
RtlImageNtHeader(IN PVOID Base)255 RtlImageNtHeader(IN PVOID Base)
256 {
257     PIMAGE_NT_HEADERS NtHeader;
258 
259     /* Call the new API */
260     RtlImageNtHeaderEx(RTL_IMAGE_NT_HEADER_EX_FLAG_NO_RANGE_CHECK,
261                        Base,
262                        0,
263                        &NtHeader);
264     return NtHeader;
265 }
266 
267 /*
268  * @implemented
269  */
270 PVOID
271 NTAPI
RtlImageDirectoryEntryToData(PVOID BaseAddress,BOOLEAN MappedAsImage,USHORT Directory,PULONG Size)272 RtlImageDirectoryEntryToData(
273     PVOID BaseAddress,
274     BOOLEAN MappedAsImage,
275     USHORT Directory,
276     PULONG Size)
277 {
278     PIMAGE_NT_HEADERS NtHeader;
279     ULONG Va;
280 
281     /* Magic flag for non-mapped images. */
282     if ((ULONG_PTR)BaseAddress & 1)
283     {
284         BaseAddress = (PVOID)((ULONG_PTR)BaseAddress & ~1);
285         MappedAsImage = FALSE;
286     }
287 
288     NtHeader = RtlImageNtHeader(BaseAddress);
289     if (NtHeader == NULL)
290         return NULL;
291 
292     if (NtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
293     {
294         PIMAGE_OPTIONAL_HEADER64 OptionalHeader = (PIMAGE_OPTIONAL_HEADER64)&NtHeader->OptionalHeader;
295 
296         if (Directory >= SWAPD(OptionalHeader->NumberOfRvaAndSizes))
297             return NULL;
298 
299         Va = SWAPD(OptionalHeader->DataDirectory[Directory].VirtualAddress);
300         if (Va == 0)
301             return NULL;
302 
303         *Size = SWAPD(OptionalHeader->DataDirectory[Directory].Size);
304 
305         if (MappedAsImage || Va < SWAPD(OptionalHeader->SizeOfHeaders))
306             return (PVOID)((ULONG_PTR)BaseAddress + Va);
307     }
308     else
309     {
310         PIMAGE_OPTIONAL_HEADER32 OptionalHeader = (PIMAGE_OPTIONAL_HEADER32)&NtHeader->OptionalHeader;
311 
312         if (Directory >= SWAPD(OptionalHeader->NumberOfRvaAndSizes))
313             return NULL;
314 
315         Va = SWAPD(OptionalHeader->DataDirectory[Directory].VirtualAddress);
316         if (Va == 0)
317             return NULL;
318 
319         *Size = SWAPD(OptionalHeader->DataDirectory[Directory].Size);
320 
321         if (MappedAsImage || Va < SWAPD(OptionalHeader->SizeOfHeaders))
322             return (PVOID)((ULONG_PTR)BaseAddress + Va);
323     }
324 
325     /* Image mapped as ordinary file, we must find raw pointer */
326     return RtlImageRvaToVa(NtHeader, BaseAddress, Va, NULL);
327 }
328 
329 /*
330  * @implemented
331  */
332 PIMAGE_SECTION_HEADER
333 NTAPI
RtlImageRvaToSection(PIMAGE_NT_HEADERS NtHeader,PVOID BaseAddress,ULONG Rva)334 RtlImageRvaToSection(
335     PIMAGE_NT_HEADERS NtHeader,
336     PVOID BaseAddress,
337     ULONG Rva)
338 {
339     PIMAGE_SECTION_HEADER Section;
340     ULONG Va;
341     ULONG Count;
342 
343     Count = SWAPW(NtHeader->FileHeader.NumberOfSections);
344     Section = IMAGE_FIRST_SECTION(NtHeader);
345 
346     while (Count--)
347     {
348         Va = SWAPD(Section->VirtualAddress);
349         if ((Va <= Rva) && (Rva < Va + SWAPD(Section->SizeOfRawData)))
350             return Section;
351         Section++;
352     }
353 
354     return NULL;
355 }
356 
357 /*
358  * @implemented
359  */
360 PVOID
361 NTAPI
RtlImageRvaToVa(PIMAGE_NT_HEADERS NtHeader,PVOID BaseAddress,ULONG Rva,PIMAGE_SECTION_HEADER * SectionHeader)362 RtlImageRvaToVa(
363     PIMAGE_NT_HEADERS NtHeader,
364     PVOID BaseAddress,
365     ULONG Rva,
366     PIMAGE_SECTION_HEADER *SectionHeader)
367 {
368     PIMAGE_SECTION_HEADER Section = NULL;
369 
370     if (SectionHeader)
371         Section = *SectionHeader;
372 
373     if ((Section == NULL) ||
374         (Rva < SWAPD(Section->VirtualAddress)) ||
375         (Rva >= SWAPD(Section->VirtualAddress) + SWAPD(Section->SizeOfRawData)))
376     {
377         Section = RtlImageRvaToSection(NtHeader, BaseAddress, Rva);
378         if (Section == NULL)
379             return NULL;
380 
381         if (SectionHeader)
382             *SectionHeader = Section;
383     }
384 
385     return (PVOID)((ULONG_PTR)BaseAddress + Rva +
386                    (ULONG_PTR)SWAPD(Section->PointerToRawData) -
387                    (ULONG_PTR)SWAPD(Section->VirtualAddress));
388 }
389 
390 PIMAGE_BASE_RELOCATION
391 NTAPI
LdrProcessRelocationBlockLongLong(IN ULONG_PTR Address,IN ULONG Count,IN PUSHORT TypeOffset,IN LONGLONG Delta)392 LdrProcessRelocationBlockLongLong(
393     IN ULONG_PTR Address,
394     IN ULONG Count,
395     IN PUSHORT TypeOffset,
396     IN LONGLONG Delta)
397 {
398     SHORT Offset;
399     USHORT Type;
400     ULONG i;
401     PUSHORT ShortPtr;
402     PULONG LongPtr;
403     PULONGLONG LongLongPtr;
404 
405     for (i = 0; i < Count; i++)
406     {
407         Offset = SWAPW(*TypeOffset) & 0xFFF;
408         Type = SWAPW(*TypeOffset) >> 12;
409         ShortPtr = (PUSHORT)(RVA(Address, Offset));
410         /*
411         * Don't relocate within the relocation section itself.
412         * GCC/LD generates sometimes relocation records for the relocation section.
413         * This is a bug in GCC/LD.
414         * Fix for it disabled, since it was only in ntoskrnl and not in ntdll
415         */
416         /*
417         if ((ULONG_PTR)ShortPtr < (ULONG_PTR)RelocationDir ||
418         (ULONG_PTR)ShortPtr >= (ULONG_PTR)RelocationEnd)
419         {*/
420         switch (Type)
421         {
422             /* case IMAGE_REL_BASED_SECTION : */
423             /* case IMAGE_REL_BASED_REL32 : */
424         case IMAGE_REL_BASED_ABSOLUTE:
425             break;
426 
427         case IMAGE_REL_BASED_HIGH:
428             *ShortPtr = HIWORD(MAKELONG(0, *ShortPtr) + (Delta & 0xFFFFFFFF));
429             break;
430 
431         case IMAGE_REL_BASED_LOW:
432             *ShortPtr = SWAPW(*ShortPtr) + LOWORD(Delta & 0xFFFF);
433             break;
434 
435         case IMAGE_REL_BASED_HIGHLOW:
436             LongPtr = (PULONG)RVA(Address, Offset);
437             *LongPtr = SWAPD(*LongPtr) + (Delta & 0xFFFFFFFF);
438             break;
439 
440         case IMAGE_REL_BASED_DIR64:
441             LongLongPtr = (PUINT64)RVA(Address, Offset);
442             *LongLongPtr = SWAPQ(*LongLongPtr) + Delta;
443             break;
444 
445         case IMAGE_REL_BASED_HIGHADJ:
446         case IMAGE_REL_BASED_MIPS_JMPADDR:
447         default:
448             DPRINT1("Unknown/unsupported fixup type %hu.\n", Type);
449             DPRINT1("Address %p, Current %u, Count %u, *TypeOffset %x\n",
450                     (PVOID)Address, i, Count, SWAPW(*TypeOffset));
451             return (PIMAGE_BASE_RELOCATION)NULL;
452         }
453 
454         TypeOffset++;
455     }
456 
457     return (PIMAGE_BASE_RELOCATION)TypeOffset;
458 }
459 
460 ULONG
461 NTAPI
LdrRelocateImage(_In_ PVOID BaseAddress,_In_opt_ PCSTR LoaderName,_In_ ULONG Success,_In_ ULONG Conflict,_In_ ULONG Invalid)462 LdrRelocateImage(
463     _In_ PVOID BaseAddress,
464     _In_opt_ PCSTR LoaderName,
465     _In_ ULONG Success,
466     _In_ ULONG Conflict,
467     _In_ ULONG Invalid)
468 {
469     return LdrRelocateImageWithBias(BaseAddress, 0, LoaderName, Success, Conflict, Invalid);
470 }
471 
472 ULONG
473 NTAPI
LdrRelocateImageWithBias(_In_ PVOID BaseAddress,_In_ LONGLONG AdditionalBias,_In_opt_ PCSTR LoaderName,_In_ ULONG Success,_In_ ULONG Conflict,_In_ ULONG Invalid)474 LdrRelocateImageWithBias(
475     _In_ PVOID BaseAddress,
476     _In_ LONGLONG AdditionalBias,
477     _In_opt_ PCSTR LoaderName,
478     _In_ ULONG Success,
479     _In_ ULONG Conflict,
480     _In_ ULONG Invalid)
481 {
482     PIMAGE_NT_HEADERS NtHeaders;
483     PIMAGE_DATA_DIRECTORY RelocationDDir;
484     PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd;
485     ULONG Count;
486     ULONG_PTR Address;
487     PUSHORT TypeOffset;
488     LONGLONG Delta;
489 
490     UNREFERENCED_PARAMETER(LoaderName);
491 
492     NtHeaders = RtlImageNtHeader(BaseAddress);
493 
494     if (NtHeaders == NULL)
495         return Invalid;
496 
497     if (SWAPW(NtHeaders->FileHeader.Characteristics) & IMAGE_FILE_RELOCS_STRIPPED)
498     {
499         return Conflict;
500     }
501 
502     RelocationDDir = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
503 
504     if (SWAPD(RelocationDDir->VirtualAddress) == 0 || SWAPD(RelocationDDir->Size) == 0)
505     {
506         return Success;
507     }
508 
509     Delta = (ULONG_PTR)BaseAddress - SWAPD(NtHeaders->OptionalHeader.ImageBase) + AdditionalBias;
510     RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)BaseAddress + SWAPD(RelocationDDir->VirtualAddress));
511     RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)RelocationDir + SWAPD(RelocationDDir->Size));
512 
513     while (RelocationDir < RelocationEnd &&
514             SWAPW(RelocationDir->SizeOfBlock) > 0)
515     {
516         Count = (SWAPW(RelocationDir->SizeOfBlock) - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(USHORT);
517         Address = (ULONG_PTR)RVA(BaseAddress, SWAPD(RelocationDir->VirtualAddress));
518         TypeOffset = (PUSHORT)(RelocationDir + 1);
519 
520         RelocationDir = LdrProcessRelocationBlockLongLong(Address,
521                         Count,
522                         TypeOffset,
523                         Delta);
524 
525         if (RelocationDir == NULL)
526         {
527             DPRINT1("Error during call to LdrProcessRelocationBlockLongLong()!\n");
528             return Invalid;
529         }
530     }
531 
532     return Success;
533 }
534 
535 /* EOF */
536