VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/ldr/ldrPE.cpp@ 73375

Last change on this file since 73375 was 73375, checked in by vboxsync, 7 years ago

IPRT: Added RTLDRPROP_UNWIND_INFO to RTLdrQueryPropEx. Added RTDbgModImageQueryProp, RTDbgModImageGetArch, and RTDbgModImageGetFormat.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 170.5 KB
Line 
1/* $Id: ldrPE.cpp 73375 2018-07-27 07:59:25Z vboxsync $ */
2/** @file
3 * IPRT - Binary Image Loader, Portable Executable (PE).
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_LDR
32#include <iprt/ldr.h>
33#include "internal/iprt.h"
34
35#include <iprt/assert.h>
36#include <iprt/asm.h>
37#include <iprt/err.h>
38#include <iprt/log.h>
39#include <iprt/md5.h>
40#include <iprt/mem.h>
41#include <iprt/path.h>
42#include <iprt/sha.h>
43#include <iprt/string.h>
44#ifndef IPRT_WITHOUT_LDR_VERIFY
45#include <iprt/zero.h>
46# include <iprt/crypto/pkcs7.h>
47# include <iprt/crypto/spc.h>
48# include <iprt/crypto/x509.h>
49#endif
50#include <iprt/formats/codeview.h>
51#include <iprt/formats/pecoff.h>
52#include "internal/ldr.h"
53
54
55/*********************************************************************************************************************************
56* Defined Constants And Macros *
57*********************************************************************************************************************************/
58/** Converts rva to a type.
59 * @param pvBits Pointer to base of image bits.
60 * @param rva Relative virtual address.
61 * @param type Type.
62 */
63#define PE_RVA2TYPE(pvBits, rva, type) ((type) ((uintptr_t)pvBits + (uintptr_t)(rva)) )
64
65/** The max size of the security directory. */
66#ifdef IN_RING3
67# define RTLDRMODPE_MAX_SECURITY_DIR_SIZE _4M
68#else
69# define RTLDRMODPE_MAX_SECURITY_DIR_SIZE _1M
70#endif
71
72
73/*********************************************************************************************************************************
74* Structures and Typedefs *
75*********************************************************************************************************************************/
76/**
77 * The PE loader structure.
78 */
79typedef struct RTLDRMODPE
80{
81 /** Core module structure. */
82 RTLDRMODINTERNAL Core;
83 /** Pointer to internal copy of image bits.
84 * @todo the reader should take care of this. */
85 void *pvBits;
86 /** The offset of the NT headers. */
87 RTFOFF offNtHdrs;
88 /** The offset of the first byte after the section table. */
89 RTFOFF offEndOfHdrs;
90
91 /** The machine type (IMAGE_FILE_HEADER::Machine). */
92 uint16_t u16Machine;
93 /** The file flags (IMAGE_FILE_HEADER::Characteristics). */
94 uint16_t fFile;
95 /** Number of sections (IMAGE_FILE_HEADER::NumberOfSections). */
96 unsigned cSections;
97 /** Pointer to an array of the section headers related to the file. */
98 PIMAGE_SECTION_HEADER paSections;
99
100 /** The RVA of the entry point (IMAGE_OPTIONAL_HEADER32::AddressOfEntryPoint). */
101 RTUINTPTR uEntryPointRVA;
102 /** The base address of the image at link time (IMAGE_OPTIONAL_HEADER32::ImageBase). */
103 RTUINTPTR uImageBase;
104 /** The size of the loaded image (IMAGE_OPTIONAL_HEADER32::SizeOfImage). */
105 uint32_t cbImage;
106 /** Size of the header (IMAGE_OPTIONAL_HEADER32::SizeOfHeaders). */
107 uint32_t cbHeaders;
108 /** The image timestamp. */
109 uint32_t uTimestamp;
110 /** The number of imports. UINT32_MAX if not determined. */
111 uint32_t cImports;
112 /** Set if the image is 64-bit, clear if 32-bit. */
113 bool f64Bit;
114 /** The import data directory entry. */
115 IMAGE_DATA_DIRECTORY ImportDir;
116 /** The base relocation data directory entry. */
117 IMAGE_DATA_DIRECTORY RelocDir;
118 /** The export data directory entry. */
119 IMAGE_DATA_DIRECTORY ExportDir;
120 /** The debug directory entry. */
121 IMAGE_DATA_DIRECTORY DebugDir;
122 /** The security directory entry. */
123 IMAGE_DATA_DIRECTORY SecurityDir;
124 /** The exception data directory entry. */
125 IMAGE_DATA_DIRECTORY ExceptionDir;
126
127 /** Offset of the first PKCS \#7 SignedData signature if present. */
128 uint32_t offPkcs7SignedData;
129 /** Size of the first PKCS \#7 SignedData. */
130 uint32_t cbPkcs7SignedData;
131
132 /** Copy of the optional header field DllCharacteristics. */
133 uint16_t fDllCharacteristics;
134} RTLDRMODPE;
135/** Pointer to the instance data for a PE loader module. */
136typedef RTLDRMODPE *PRTLDRMODPE;
137
138
139/**
140 * PE Loader module operations.
141 *
142 * The PE loader has one operation which is a bit different between 32-bit and 64-bit PE images,
143 * and for historical and performance reasons have been split into separate functions. Thus the
144 * PE loader extends the RTLDROPS structure with this one entry.
145 */
146typedef struct RTLDROPSPE
147{
148 /** The usual ops. */
149 RTLDROPS Core;
150
151 /**
152 * Resolves all imports.
153 *
154 * @returns iprt status code.
155 * @param pModPe Pointer to the PE loader module structure.
156 * @param pvBitsR Where to read raw image bits. (optional)
157 * @param pvBitsW Where to store the imports. The size of this buffer is equal or
158 * larger to the value returned by pfnGetImageSize().
159 * @param pfnGetImport The callback function to use to resolve imports (aka unresolved externals).
160 * @param pvUser User argument to pass to the callback.
161 */
162 DECLCALLBACKMEMBER(int, pfnResolveImports)(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser);
163
164 /** Dummy entry to make sure we've initialized it all. */
165 RTUINT uDummy;
166} RTLDROPSPE, *PRTLDROPSPE;
167
168
169/**
170 * PE hash context union.
171 */
172typedef union RTLDRPEHASHCTXUNION
173{
174 RTSHA512CONTEXT Sha512;
175 RTSHA256CONTEXT Sha256;
176 RTSHA1CONTEXT Sha1;
177 RTMD5CONTEXT Md5;
178} RTLDRPEHASHCTXUNION;
179/** Pointer to a PE hash context union. */
180typedef RTLDRPEHASHCTXUNION *PRTLDRPEHASHCTXUNION;
181
182
183/**
184 * PE hash digests
185 */
186typedef union RTLDRPEHASHRESUNION
187{
188 uint8_t abSha512[RTSHA512_HASH_SIZE];
189 uint8_t abSha256[RTSHA256_HASH_SIZE];
190 uint8_t abSha1[RTSHA1_HASH_SIZE];
191 uint8_t abMd5[RTMD5_HASH_SIZE];
192} RTLDRPEHASHRESUNION;
193/** Pointer to a PE hash work set. */
194typedef RTLDRPEHASHRESUNION *PRTLDRPEHASHRESUNION;
195
196/**
197 * Special places to watch out for when hashing a PE image.
198 */
199typedef struct RTLDRPEHASHSPECIALS
200{
201 uint32_t cbToHash;
202 uint32_t offCksum;
203 uint32_t cbCksum;
204 uint32_t offSecDir;
205 uint32_t cbSecDir;
206 uint32_t offEndSpecial;
207} RTLDRPEHASHSPECIALS;
208/** Pointer to the structure with the special hash places. */
209typedef RTLDRPEHASHSPECIALS *PRTLDRPEHASHSPECIALS;
210
211
212#ifndef IPRT_WITHOUT_LDR_VERIFY
213/**
214 * Parsed signature data.
215 */
216typedef struct RTLDRPESIGNATURE
217{
218 /** The outer content info wrapper. */
219 RTCRPKCS7CONTENTINFO ContentInfo;
220 /** Pointer to the decoded SignedData inside the ContentInfo member. */
221 PRTCRPKCS7SIGNEDDATA pSignedData;
222 /** Pointer to the indirect data content. */
223 PRTCRSPCINDIRECTDATACONTENT pIndData;
224 /** The digest type employed by the signature. */
225 RTDIGESTTYPE enmDigest;
226
227 /** Pointer to the raw signatures. This is allocated in the continuation of
228 * this structure to keep things simple. The size is given by the security
229 * export directory. */
230 WIN_CERTIFICATE const *pRawData;
231
232 /** Hash scratch data. */
233 RTLDRPEHASHCTXUNION HashCtx;
234 /** Hash result. */
235 RTLDRPEHASHRESUNION HashRes;
236} RTLDRPESIGNATURE;
237/** Pointed to SigneData parsing stat and output. */
238typedef RTLDRPESIGNATURE *PRTLDRPESIGNATURE;
239#endif
240
241
242/*********************************************************************************************************************************
243* Internal Functions *
244*********************************************************************************************************************************/
245static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr);
246static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg);
247static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress, RTUINTPTR OldBaseAddress);
248
249
250
251/**
252 * Reads a section of a PE image given by RVA + size, using mapped bits if
253 * available or allocating heap memory and reading from the file.
254 *
255 * @returns IPRT status code.
256 * @param pThis Pointer to the PE loader module structure.
257 * @param pvBits Read only bits if available. NULL if not.
258 * @param uRva The RVA to read at.
259 * @param cbMem The number of bytes to read.
260 * @param ppvMem Where to return the memory on success (heap or
261 * inside pvBits).
262 */
263static int rtldrPEReadPartByRva(PRTLDRMODPE pThis, const void *pvBits, uint32_t uRva, uint32_t cbMem, void const **ppvMem)
264{
265 *ppvMem = NULL;
266 if (!cbMem)
267 return VINF_SUCCESS;
268
269 /*
270 * Use bits if we've got some.
271 */
272 if (pvBits)
273 {
274 *ppvMem = (uint8_t const *)pvBits + uRva;
275 return VINF_SUCCESS;
276 }
277 if (pThis->pvBits)
278 {
279 *ppvMem = (uint8_t const *)pThis->pvBits + uRva;
280 return VINF_SUCCESS;
281 }
282
283 /*
284 * Allocate a buffer and read the bits from the file (or whatever).
285 */
286 if (!pThis->Core.pReader)
287 return VERR_ACCESS_DENIED;
288
289 uint8_t *pbMem = (uint8_t *)RTMemAllocZ(cbMem);
290 if (!pbMem)
291 return VERR_NO_MEMORY;
292 *ppvMem = pbMem;
293
294 /* Do the reading on a per section base. */
295 RTFOFF const cbFile = pThis->Core.pReader->pfnSize(pThis->Core.pReader);
296 for (;;)
297 {
298 /* Translate the RVA into a file offset. */
299 uint32_t offFile = uRva;
300 uint32_t cbToRead = cbMem;
301 uint32_t cbToAdv = cbMem;
302
303 if (uRva < pThis->paSections[0].VirtualAddress)
304 {
305 /* Special header section. */
306 cbToRead = pThis->paSections[0].VirtualAddress - uRva;
307 if (cbToRead > cbMem)
308 cbToRead = cbMem;
309 cbToAdv = cbToRead;
310
311 /* The following capping is an approximation. */
312 uint32_t offFirstRawData = RT_ALIGN(pThis->cbHeaders, _4K);
313 if ( pThis->paSections[0].PointerToRawData > 0
314 && pThis->paSections[0].SizeOfRawData > 0)
315 offFirstRawData = pThis->paSections[0].PointerToRawData;
316 if (offFile >= offFirstRawData)
317 cbToRead = 0;
318 else if (offFile + cbToRead > offFirstRawData)
319 cbToRead = offFile - offFirstRawData;
320 }
321 else
322 {
323 /* Find the matching section and its mapping size. */
324 uint32_t j = 0;
325 uint32_t cbMapping = 0;
326 uint32_t offSection = 0;
327 while (j < pThis->cSections)
328 {
329 cbMapping = (j + 1 < pThis->cSections ? pThis->paSections[j + 1].VirtualAddress : pThis->cbImage)
330 - pThis->paSections[j].VirtualAddress;
331 offSection = uRva - pThis->paSections[j].VirtualAddress;
332 if (offSection < cbMapping)
333 break;
334 j++;
335 }
336 if (j >= cbMapping)
337 break; /* This shouldn't happen, just return zeros if it does. */
338
339 /* Adjust the sizes and calc the file offset. */
340 if (offSection + cbToAdv > cbMapping)
341 cbToAdv = cbToRead = cbMapping - offSection;
342
343 if ( pThis->paSections[j].PointerToRawData > 0
344 && pThis->paSections[j].SizeOfRawData > 0)
345 {
346 offFile = offSection;
347 if (offFile + cbToRead > pThis->paSections[j].SizeOfRawData)
348 cbToRead = pThis->paSections[j].SizeOfRawData - offFile;
349 offFile += pThis->paSections[j].PointerToRawData;
350 }
351 else
352 {
353 offFile = UINT32_MAX;
354 cbToRead = 0;
355 }
356 }
357
358 /* Perform the read after adjusting a little (paranoia). */
359 if (offFile > cbFile)
360 cbToRead = 0;
361 if (cbToRead)
362 {
363 if ((RTFOFF)offFile + cbToRead > cbFile)
364 cbToRead = (uint32_t)(cbFile - (RTFOFF)offFile);
365 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbToRead, offFile);
366 if (RT_FAILURE(rc))
367 {
368 RTMemFree((void *)*ppvMem);
369 *ppvMem = NULL;
370 return rc;
371 }
372 }
373
374 /* Advance */
375 if (cbMem <= cbToAdv)
376 break;
377 cbMem -= cbToAdv;
378 pbMem += cbToAdv;
379 uRva += cbToAdv;
380 }
381
382 return VINF_SUCCESS;
383}
384
385
386/**
387 * Reads a part of a PE file from the file and into a heap block.
388 *
389 * @returns IRPT status code.
390 * @param pThis Pointer to the PE loader module structure..
391 * @param offFile The file offset.
392 * @param cbMem The number of bytes to read.
393 * @param ppvMem Where to return the heap block with the bytes on
394 * success.
395 */
396static int rtldrPEReadPartFromFile(PRTLDRMODPE pThis, uint32_t offFile, uint32_t cbMem, void const **ppvMem)
397{
398 *ppvMem = NULL;
399 if (!cbMem)
400 return VINF_SUCCESS;
401
402 /*
403 * Allocate a buffer and read the bits from the file (or whatever).
404 */
405 if (!pThis->Core.pReader)
406 return VERR_ACCESS_DENIED;
407
408 uint8_t *pbMem = (uint8_t *)RTMemAlloc(cbMem);
409 if (!pbMem)
410 return VERR_NO_MEMORY;
411
412 int rc = pThis->Core.pReader->pfnRead(pThis->Core.pReader, pbMem, cbMem, offFile);
413 if (RT_FAILURE(rc))
414 {
415 RTMemFree((void *)*ppvMem);
416 return rc;
417 }
418
419 *ppvMem = pbMem;
420 return VINF_SUCCESS;
421}
422
423
424/**
425 * Reads a part of a PE image into memory one way or another.
426 *
427 * Either the RVA or the offFile must be valid. We'll prefer the RVA if
428 * possible.
429 *
430 * @returns IPRT status code.
431 * @param pThis Pointer to the PE loader module structure.
432 * @param pvBits Read only bits if available. NULL if not.
433 * @param uRva The RVA to read at.
434 * @param offFile The file offset.
435 * @param cbMem The number of bytes to read.
436 * @param ppvMem Where to return the memory on success (heap or
437 * inside pvBits).
438 */
439static int rtldrPEReadPart(PRTLDRMODPE pThis, const void *pvBits, RTFOFF offFile, RTLDRADDR uRva,
440 uint32_t cbMem, void const **ppvMem)
441{
442 if ( uRva == NIL_RTLDRADDR
443 || uRva > pThis->cbImage
444 || cbMem > pThis->cbImage
445 || uRva + cbMem > pThis->cbImage)
446 {
447 if (offFile < 0 || offFile >= UINT32_MAX)
448 return VERR_INVALID_PARAMETER;
449 return rtldrPEReadPartFromFile(pThis, (uint32_t)offFile, cbMem, ppvMem);
450 }
451 return rtldrPEReadPartByRva(pThis, pvBits, (uint32_t)uRva, cbMem, ppvMem);
452}
453
454
455/**
456 * Frees up memory returned by rtldrPEReadPart*.
457 *
458 * @param pThis Pointer to the PE loader module structure..
459 * @param pvBits Read only bits if available. NULL if not..
460 * @param pvMem The memory we were given by the reader method.
461 */
462static void rtldrPEFreePart(PRTLDRMODPE pThis, const void *pvBits, void const *pvMem)
463{
464 if (!pvMem)
465 return;
466
467 if (pvBits && (uintptr_t)pvMem - (uintptr_t)pvBits < pThis->cbImage)
468 return;
469 if (pThis->pvBits && (uintptr_t)pvMem - (uintptr_t)pThis->pvBits < pThis->cbImage)
470 return;
471
472 RTMemFree((void *)pvMem);
473}
474
475
476/** @interface_method_impl{RTLDROPS,pfnGetImageSize} */
477static DECLCALLBACK(size_t) rtldrPEGetImageSize(PRTLDRMODINTERNAL pMod)
478{
479 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
480 return pModPe->cbImage;
481}
482
483
484/**
485 * Reads the image into memory.
486 *
487 * @returns iprt status code.
488 * @param pModPe The PE module.
489 * @param pvBits Where to store the bits, this buffer is at least pItem->Core.cbImage in size.
490 */
491static int rtldrPEGetBitsNoImportsNorFixups(PRTLDRMODPE pModPe, void *pvBits)
492{
493 /*
494 * Both these checks are related to pfnDone().
495 */
496 PRTLDRREADER pReader = pModPe->Core.pReader;
497 if (!pReader)
498 {
499 AssertMsgFailed(("You've called done!\n"));
500 return VERR_WRONG_ORDER;
501 }
502 if (!pvBits)
503 return VERR_NO_MEMORY;
504
505 /*
506 * Zero everything (could be done per section).
507 */
508 memset(pvBits, 0, pModPe->cbImage);
509
510#ifdef PE_FILE_OFFSET_EQUALS_RVA
511 /*
512 * Read the entire image / file.
513 */
514 const RTFOFF cbRawImage = pReader->pfnSize(pReader)
515 rc = pReader->pfnRead(pReader, pvBits, RT_MIN(pModPe->cbImage, cbRawImage), 0);
516 if (RT_FAILURE(rc))
517 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!! (the entire image)\n",
518 pReader->pfnLogName(pReader), RT_MIN(pModPe->cbImage, cbRawImage), 0, rc));
519#else
520
521 /*
522 * Read the headers.
523 */
524 int rc = pReader->pfnRead(pReader, pvBits, pModPe->cbHeaders, 0);
525 if (RT_SUCCESS(rc))
526 {
527 /*
528 * Read the sections.
529 */
530 PIMAGE_SECTION_HEADER pSH = pModPe->paSections;
531 for (unsigned cLeft = pModPe->cSections; cLeft > 0; cLeft--, pSH++)
532 if ( pSH->SizeOfRawData
533 && pSH->Misc.VirtualSize
534 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
535 {
536 uint32_t const cbToRead = RT_MIN(pSH->SizeOfRawData, pModPe->cbImage - pSH->VirtualAddress);
537 Assert(pSH->VirtualAddress <= pModPe->cbImage);
538
539 rc = pReader->pfnRead(pReader, (uint8_t *)pvBits + pSH->VirtualAddress, cbToRead, pSH->PointerToRawData);
540 if (RT_FAILURE(rc))
541 {
542 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc - section #%d '%.*s'!!!\n",
543 pReader->pfnLogName(pReader), pSH->SizeOfRawData, pSH->PointerToRawData, rc,
544 pSH - pModPe->paSections, sizeof(pSH->Name), pSH->Name));
545 break;
546 }
547 }
548 }
549 else
550 Log(("rtldrPE: %s: Reading %#x bytes at offset %#x failed, %Rrc!!!\n",
551 pReader->pfnLogName(pReader), pModPe->cbHeaders, 0, rc));
552#endif
553 return rc;
554}
555
556
557/**
558 * Reads the bits into the internal buffer pointed to by PRTLDRMODPE::pvBits.
559 *
560 * @returns iprt status code.
561 * @param pModPe The PE module.
562 */
563static int rtldrPEReadBits(PRTLDRMODPE pModPe)
564{
565 Assert(!pModPe->pvBits);
566 void *pvBitsW = RTMemAllocZ(pModPe->cbImage);
567 if (!pvBitsW)
568 return VERR_NO_MEMORY;
569 int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBitsW);
570 if (RT_SUCCESS(rc))
571 pModPe->pvBits = pvBitsW;
572 else
573 RTMemFree(pvBitsW);
574 return rc;
575}
576
577
578/** @interface_method_impl{RTLDROPS,pfnGetBits} */
579static DECLCALLBACK(int) rtldrPEGetBits(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR BaseAddress, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
580{
581 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
582
583 /*
584 * Read the image.
585 */
586 int rc = rtldrPEGetBitsNoImportsNorFixups(pModPe, pvBits);
587 if (RT_SUCCESS(rc))
588 {
589 /*
590 * Resolve imports.
591 */
592 if (pfnGetImport)
593 rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pvBits, pvBits, pfnGetImport, pvUser);
594 if (RT_SUCCESS(rc))
595 {
596 /*
597 * Apply relocations.
598 */
599 rc = rtldrPEApplyFixups(pModPe, pvBits, pvBits, BaseAddress, pModPe->uImageBase);
600 if (RT_SUCCESS(rc))
601 return rc;
602 AssertMsgFailed(("Failed to apply fixups. rc=%Rrc\n", rc));
603 }
604 else
605 AssertMsgFailed(("Failed to resolve imports. rc=%Rrc\n", rc));
606 }
607 return rc;
608}
609
610
611/* The image_thunk_data32/64 structures are not very helpful except for getting RSI. keep them around till all the code has been converted. */
612typedef struct _IMAGE_THUNK_DATA32
613{
614 union
615 {
616 uint32_t ForwarderString;
617 uint32_t Function;
618 uint32_t Ordinal;
619 uint32_t AddressOfData;
620 } u1;
621} IMAGE_THUNK_DATA32;
622typedef IMAGE_THUNK_DATA32 *PIMAGE_THUNK_DATA32;
623
624
625/** @copydoc RTLDROPSPE::pfnResolveImports */
626static DECLCALLBACK(int) rtldrPEResolveImports32(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, PFNRTLDRIMPORT pfnGetImport, void *pvUser)
627{
628 /*
629 * Check if there is actually anything to work on.
630 */
631 if ( !pModPe->ImportDir.VirtualAddress
632 || !pModPe->ImportDir.Size)
633 return 0;
634
635 /*
636 * Walk the IMAGE_IMPORT_DESCRIPTOR table.
637 */
638 int rc = VINF_SUCCESS;
639 PIMAGE_IMPORT_DESCRIPTOR pImps;
640 for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
641 !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
642 pImps++)
643 {
644 AssertReturn(pImps->Name < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
645 const char *pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
646 AssertReturn(pImps->FirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
647 AssertReturn(pImps->u.OriginalFirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
648
649 Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
650 Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
651 "RTLdrPE: TimeDateStamp = %#RX32\n"
652 "RTLdrPE: ForwarderChain = %#RX32\n"
653 "RTLdrPE: Name = %#RX32\n"
654 "RTLdrPE: FirstThunk = %#RX32\n",
655 pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
656 pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
657
658 /*
659 * Walk the thunks table(s).
660 */
661 PIMAGE_THUNK_DATA32 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA32); /* update this. */
662 PIMAGE_THUNK_DATA32 pThunk = pImps->u.OriginalFirstThunk == 0 /* read from this. */
663 ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA32)
664 : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA32);
665 while (!rc && pThunk->u1.Ordinal != 0)
666 {
667 RTUINTPTR Value = 0;
668 if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)
669 {
670 rc = pfnGetImport(&pModPe->Core, pszModName, NULL, IMAGE_ORDINAL32(pThunk->u1.Ordinal), &Value, pvUser);
671 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr #%u\n" : "RTLdrPE: %08RX32 #%u rc=%Rrc\n",
672 (uint32_t)Value, IMAGE_ORDINAL32(pThunk->u1.Ordinal), rc));
673 }
674 else if ( pThunk->u1.Ordinal > 0
675 && pThunk->u1.Ordinal < pModPe->cbImage)
676 {
677 rc = pfnGetImport(&pModPe->Core, pszModName,
678 PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
679 ~0U, &Value, pvUser);
680 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %RTptr %s\n" : "RTLdrPE: %08RX32 %s rc=%Rrc\n",
681 (uint32_t)Value, PE_RVA2TYPE(pvBitsR, (char*)(uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
682 }
683 else
684 {
685 AssertMsgFailed(("bad import data thunk!\n"));
686 rc = VERR_BAD_EXE_FORMAT;
687 }
688 pFirstThunk->u1.Function = (uint32_t)Value;
689 if (pFirstThunk->u1.Function != Value)
690 {
691 AssertMsgFailed(("external symbol address to big!\n"));
692 rc = VERR_ADDRESS_CONFLICT; /** @todo get me a better error status code. */
693 }
694 pThunk++;
695 pFirstThunk++;
696 }
697 }
698
699 return rc;
700}
701
702
703/* The image_thunk_data32/64 structures are not very helpful except for getting RSI. keep them around till all the code has been converted. */
704typedef struct _IMAGE_THUNK_DATA64
705{
706 union
707 {
708 uint64_t ForwarderString;
709 uint64_t Function;
710 uint64_t Ordinal;
711 uint64_t AddressOfData;
712 } u1;
713} IMAGE_THUNK_DATA64;
714typedef IMAGE_THUNK_DATA64 *PIMAGE_THUNK_DATA64;
715
716
717/** @copydoc RTLDROPSPE::pfnResolveImports */
718static DECLCALLBACK(int) rtldrPEResolveImports64(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW,
719 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
720{
721 /*
722 * Check if there is actually anything to work on.
723 */
724 if ( !pModPe->ImportDir.VirtualAddress
725 || !pModPe->ImportDir.Size)
726 return 0;
727
728 /*
729 * Walk the IMAGE_IMPORT_DESCRIPTOR table.
730 */
731 int rc = VINF_SUCCESS;
732 PIMAGE_IMPORT_DESCRIPTOR pImps;
733 for (pImps = PE_RVA2TYPE(pvBitsR, pModPe->ImportDir.VirtualAddress, PIMAGE_IMPORT_DESCRIPTOR);
734 !rc && pImps->Name != 0 && pImps->FirstThunk != 0;
735 pImps++)
736 {
737 AssertReturn(pImps->Name < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
738 const char *pszModName = PE_RVA2TYPE(pvBitsR, pImps->Name, const char *);
739 AssertReturn(pImps->FirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
740 AssertReturn(pImps->u.OriginalFirstThunk < pModPe->cbImage, VERR_BAD_EXE_FORMAT);
741
742 Log3(("RTLdrPE: Import descriptor: %s\n", pszModName));
743 Log4(("RTLdrPE: OriginalFirstThunk = %#RX32\n"
744 "RTLdrPE: TimeDateStamp = %#RX32\n"
745 "RTLdrPE: ForwarderChain = %#RX32\n"
746 "RTLdrPE: Name = %#RX32\n"
747 "RTLdrPE: FirstThunk = %#RX32\n",
748 pImps->u.OriginalFirstThunk, pImps->TimeDateStamp,
749 pImps->ForwarderChain, pImps->Name, pImps->FirstThunk));
750
751 /*
752 * Walk the thunks table(s).
753 */
754 PIMAGE_THUNK_DATA64 pFirstThunk = PE_RVA2TYPE(pvBitsW, pImps->FirstThunk, PIMAGE_THUNK_DATA64); /* update this. */
755 PIMAGE_THUNK_DATA64 pThunk = pImps->u.OriginalFirstThunk == 0 /* read from this. */
756 ? PE_RVA2TYPE(pvBitsR, pImps->FirstThunk, PIMAGE_THUNK_DATA64)
757 : PE_RVA2TYPE(pvBitsR, pImps->u.OriginalFirstThunk, PIMAGE_THUNK_DATA64);
758 while (!rc && pThunk->u1.Ordinal != 0)
759 {
760 RTUINTPTR Value = 0;
761 if (pThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG64)
762 {
763 rc = pfnGetImport(&pModPe->Core, pszModName, NULL, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), &Value, pvUser);
764 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 #%u\n" : "RTLdrPE: %016RX64 #%u rc=%Rrc\n",
765 (uint64_t)Value, (unsigned)IMAGE_ORDINAL64(pThunk->u1.Ordinal), rc));
766 }
767 else if ( pThunk->u1.Ordinal > 0
768 && pThunk->u1.Ordinal < pModPe->cbImage)
769 {
770 /** @todo add validation of the string pointer! */
771 rc = pfnGetImport(&pModPe->Core, pszModName, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *),
772 ~0U, &Value, pvUser);
773 Log4((RT_SUCCESS(rc) ? "RTLdrPE: %016RX64 %s\n" : "RTLdrPE: %016RX64 %s rc=%Rrc\n",
774 (uint64_t)Value, PE_RVA2TYPE(pvBitsR, (uintptr_t)pThunk->u1.AddressOfData + 2, const char *), rc));
775 }
776 else
777 {
778 AssertMsgFailed(("bad import data thunk!\n"));
779 rc = VERR_BAD_EXE_FORMAT;
780 }
781 pFirstThunk->u1.Function = Value;
782 pThunk++;
783 pFirstThunk++;
784 }
785 }
786
787 return rc;
788}
789
790
791/**
792 * Applies fixups.
793 */
794static int rtldrPEApplyFixups(PRTLDRMODPE pModPe, const void *pvBitsR, void *pvBitsW, RTUINTPTR BaseAddress,
795 RTUINTPTR OldBaseAddress)
796{
797 if ( !pModPe->RelocDir.VirtualAddress
798 || !pModPe->RelocDir.Size)
799 return 0;
800
801 /*
802 * Apply delta fixups iterating fixup chunks.
803 */
804 PIMAGE_BASE_RELOCATION pbr = PE_RVA2TYPE(pvBitsR, pModPe->RelocDir.VirtualAddress, PIMAGE_BASE_RELOCATION);
805 PIMAGE_BASE_RELOCATION pBaseRelocs = pbr;
806 unsigned cbBaseRelocs = pModPe->RelocDir.Size;
807 RTUINTPTR uDelta = BaseAddress - OldBaseAddress;
808 Log2(("RTLdrPE: Fixups: uDelta=%#RTptr BaseAddress=%#RTptr OldBaseAddress=%#RTptr\n", uDelta, BaseAddress, OldBaseAddress));
809 Log4(("RTLdrPE: BASERELOC: VirtualAddres=%RX32 Size=%RX32\n", pModPe->RelocDir.VirtualAddress, pModPe->RelocDir.Size));
810 Assert(sizeof(*pbr) == sizeof(uint32_t) * 2);
811
812 while ( (uintptr_t)pbr - (uintptr_t)pBaseRelocs + 8 < cbBaseRelocs /* 8= VirtualAddress and SizeOfBlock members */
813 && pbr->SizeOfBlock >= 8)
814 {
815 uint16_t *pwoffFixup = (uint16_t *)(pbr + 1);
816 uint32_t cRelocations = (pbr->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(uint16_t);
817 Log3(("RTLdrPE: base relocs for %#010x, size %#06x (%d relocs)\n", pbr->VirtualAddress, pbr->SizeOfBlock, cRelocations));
818
819 /* Some bound checking just to be sure it works... */
820 if ((uintptr_t)pbr - (uintptr_t)pBaseRelocs + pbr->SizeOfBlock > cbBaseRelocs)
821 cRelocations = (uint32_t)( (((uintptr_t)pBaseRelocs + cbBaseRelocs) - (uintptr_t)pbr - sizeof(IMAGE_BASE_RELOCATION))
822 / sizeof(uint16_t) );
823
824 /*
825 * Loop thru the fixups in this chunk.
826 */
827 while (cRelocations != 0)
828 {
829 /*
830 * Common fixup
831 */
832 static const char * const s_apszReloc[16] =
833 {
834 "ABS", "HIGH", "LOW", "HIGHLOW", "HIGHADJ", "MIPS_JMPADDR", "RES6", "RES7",
835 "RES8", "IA64_IMM64", "DIR64", "HIGH3ADJ", "RES12", "RES13", "RES14", "RES15"
836 }; NOREF(s_apszReloc);
837 union
838 {
839 uint16_t *pu16;
840 uint32_t *pu32;
841 uint64_t *pu64;
842 } u;
843 const int offFixup = *pwoffFixup & 0xfff;
844 u.pu32 = PE_RVA2TYPE(pvBitsW, offFixup + pbr->VirtualAddress, uint32_t *);
845 const int fType = *pwoffFixup >> 12;
846 Log4(("RTLdrPE: %08x %s\n", offFixup + pbr->VirtualAddress, s_apszReloc[fType]));
847 switch (fType)
848 {
849 case IMAGE_REL_BASED_HIGHLOW: /* 32-bit, add delta. */
850 *u.pu32 += (uint32_t)uDelta;
851 break;
852 case IMAGE_REL_BASED_DIR64: /* 64-bit, add delta. */
853 *u.pu64 += (RTINTPTR)uDelta;
854 break;
855 case IMAGE_REL_BASED_ABSOLUTE: /* Alignment placeholder. */
856 break;
857 /* odd ones */
858 case IMAGE_REL_BASED_LOW: /* 16-bit, add 1st 16-bit part of the delta. */
859 *u.pu16 += (uint16_t)uDelta;
860 break;
861 case IMAGE_REL_BASED_HIGH: /* 16-bit, add 2nd 16-bit part of the delta. */
862 *u.pu16 += (uint16_t)(uDelta >> 16);
863 break;
864 /* never ever seen these next two, and I'm not 100% sure they are correctly implemented here. */
865 case IMAGE_REL_BASED_HIGHADJ:
866 {
867 if (cRelocations <= 1)
868 {
869 AssertMsgFailed(("HIGHADJ missing 2nd record!\n"));
870 return VERR_BAD_EXE_FORMAT;
871 }
872 cRelocations--;
873 pwoffFixup++;
874 int32_t i32 = (uint32_t)(*u.pu16 << 16) | *pwoffFixup;
875 i32 += (uint32_t)uDelta;
876 i32 += 0x8000; //??
877 *u.pu16 = (uint16_t)(i32 >> 16);
878 break;
879 }
880 case IMAGE_REL_BASED_HIGH3ADJ:
881 {
882 if (cRelocations <= 2)
883 {
884 AssertMsgFailed(("HIGHADJ3 missing 2nd record!\n"));
885 return VERR_BAD_EXE_FORMAT;
886 }
887 cRelocations -= 2;
888 pwoffFixup++;
889 int64_t i64 = ((uint64_t)*u.pu16 << 32) | *(uint32_t *)pwoffFixup++;
890 i64 += (int64_t)uDelta << 16; //??
891 i64 += 0x80000000;//??
892 *u.pu16 = (uint16_t)(i64 >> 32);
893 break;
894 }
895 default:
896 AssertMsgFailed(("Unknown fixup type %d offset=%#x\n", fType, offFixup));
897 break;
898 }
899
900 /*
901 * Next offset/type
902 */
903 pwoffFixup++;
904 cRelocations--;
905 } /* while loop */
906
907 /*
908 * Next Fixup chunk. (i.e. next page)
909 */
910 pbr = (PIMAGE_BASE_RELOCATION)((uintptr_t)pbr + pbr->SizeOfBlock);
911 } /* while loop */
912
913 return 0;
914}
915
916
917/** @interface_method_impl{RTLDROPS,pfnRelocate} */
918static DECLCALLBACK(int) rtldrPERelocate(PRTLDRMODINTERNAL pMod, void *pvBits, RTUINTPTR NewBaseAddress, RTUINTPTR OldBaseAddress,
919 PFNRTLDRIMPORT pfnGetImport, void *pvUser)
920{
921 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
922
923 /*
924 * Do we have to read the image bits?
925 */
926 if (!pModPe->pvBits)
927 {
928 int rc = rtldrPEReadBits(pModPe);
929 if (RT_FAILURE(rc))
930 return rc;
931 }
932
933 /*
934 * Process imports.
935 */
936 int rc = ((PRTLDROPSPE)pMod->pOps)->pfnResolveImports(pModPe, pModPe->pvBits, pvBits, pfnGetImport, pvUser);
937 if (RT_SUCCESS(rc))
938 {
939 /*
940 * Apply relocations.
941 */
942 rc = rtldrPEApplyFixups(pModPe, pModPe->pvBits, pvBits, NewBaseAddress, OldBaseAddress);
943 AssertRC(rc);
944 }
945 return rc;
946}
947
948
949/**
950 * Internal worker for pfnGetSymbolEx and pfnQueryForwarderInfo.
951 *
952 * @returns IPRT status code.
953 * @param pModPe The PE module instance.
954 * @param iOrdinal The symbol ordinal, UINT32_MAX if named symbol.
955 * @param pszSymbol The symbol name.
956 * @param ppvBits The image bits pointer (input/output).
957 * @param puRvaExport Where to return the symbol RVA.
958 * @param puOrdinal Where to return the ordinal number. Optional.
959 */
960static int rtLdrPE_ExportToRva(PRTLDRMODPE pModPe, uint32_t iOrdinal, const char *pszSymbol,
961 const void **ppvBits, uint32_t *puRvaExport, uint32_t *puOrdinal)
962{
963 /*
964 * Check if there is actually anything to work on.
965 */
966 if ( !pModPe->ExportDir.VirtualAddress
967 || !pModPe->ExportDir.Size)
968 return VERR_SYMBOL_NOT_FOUND;
969
970 /*
971 * No bits supplied? Do we need to read the bits?
972 */
973 void const *pvBits = *ppvBits;
974 if (!pvBits)
975 {
976 if (!pModPe->pvBits)
977 {
978 int rc = rtldrPEReadBits(pModPe);
979 if (RT_FAILURE(rc))
980 return rc;
981 }
982 *ppvBits = pvBits = pModPe->pvBits;
983 }
984
985 PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
986 int iExpOrdinal = 0; /* index into address table. */
987 if (iOrdinal != UINT32_MAX)
988 {
989 /*
990 * Find ordinal export: Simple table lookup.
991 */
992 if ( iOrdinal >= pExpDir->Base + RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions)
993 || iOrdinal < pExpDir->Base)
994 return VERR_SYMBOL_NOT_FOUND;
995 iExpOrdinal = iOrdinal - pExpDir->Base;
996 }
997 else
998 {
999 /*
1000 * Find Named Export: Do binary search on the name table.
1001 */
1002 uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
1003 uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
1004 int iStart = 1;
1005 int iEnd = pExpDir->NumberOfNames;
1006
1007 for (;;)
1008 {
1009 /* end of search? */
1010 if (iStart > iEnd)
1011 {
1012#ifdef RT_STRICT
1013 /* do a linear search just to verify the correctness of the above algorithm */
1014 for (unsigned i = 0; i < pExpDir->NumberOfNames; i++)
1015 {
1016 AssertMsg(i == 0 || strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *)) > 0,
1017 ("bug in binary export search!!!\n"));
1018 AssertMsg(strcmp(PE_RVA2TYPE(pvBits, paRVANames[i], const char *), pszSymbol) != 0,
1019 ("bug in binary export search!!!\n"));
1020 }
1021#endif
1022 return VERR_SYMBOL_NOT_FOUND;
1023 }
1024
1025 int i = (iEnd - iStart) / 2 + iStart;
1026 const char *pszExpName = PE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *);
1027 int diff = strcmp(pszExpName, pszSymbol);
1028 if (diff > 0) /* pszExpName > pszSymbol: search chunck before i */
1029 iEnd = i - 1;
1030 else if (diff) /* pszExpName < pszSymbol: search chunk after i */
1031 iStart = i + 1;
1032 else /* pszExpName == pszSymbol */
1033 {
1034 iExpOrdinal = paOrdinals[i - 1];
1035 break;
1036 }
1037 } /* binary search thru name table */
1038 }
1039
1040 /*
1041 * Found export (iExpOrdinal).
1042 */
1043 uint32_t *paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
1044 *puRvaExport = paAddress[iExpOrdinal];
1045 if (puOrdinal)
1046 *puOrdinal = iExpOrdinal;
1047 return VINF_SUCCESS;
1048}
1049
1050
1051/** @interface_method_impl{RTLDROPS,pfnGetSymbolEx} */
1052static DECLCALLBACK(int) rtldrPEGetSymbolEx(PRTLDRMODINTERNAL pMod, const void *pvBits, RTUINTPTR BaseAddress,
1053 uint32_t iOrdinal, const char *pszSymbol, RTUINTPTR *pValue)
1054{
1055 PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
1056 uint32_t uRvaExport;
1057 int rc = rtLdrPE_ExportToRva(pThis, iOrdinal, pszSymbol, &pvBits, &uRvaExport, NULL);
1058 if (RT_SUCCESS(rc))
1059 {
1060
1061 uint32_t offForwarder = uRvaExport - pThis->ExportDir.VirtualAddress;
1062 if (offForwarder >= pThis->ExportDir.Size)
1063 /* Get plain export address */
1064 *pValue = PE_RVA2TYPE(BaseAddress, uRvaExport, RTUINTPTR);
1065 else
1066 {
1067 /* Return the approximate length of the forwarder buffer. */
1068 const char *pszForwarder = PE_RVA2TYPE(pvBits, uRvaExport, const char *);
1069 *pValue = sizeof(RTLDRIMPORTINFO) + RTStrNLen(pszForwarder, offForwarder - pThis->ExportDir.Size);
1070 rc = VERR_LDR_FORWARDER;
1071 }
1072 }
1073 return rc;
1074}
1075
1076
1077/** @interface_method_impl{RTLDROPS,pfnQueryForwarderInfo} */
1078static DECLCALLBACK(int) rtldrPE_QueryForwarderInfo(PRTLDRMODINTERNAL pMod, const void *pvBits, uint32_t iOrdinal,
1079 const char *pszSymbol, PRTLDRIMPORTINFO pInfo, size_t cbInfo)
1080{
1081 AssertReturn(cbInfo >= sizeof(*pInfo), VERR_INVALID_PARAMETER);
1082
1083 PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
1084 uint32_t uRvaExport;
1085 int rc = rtLdrPE_ExportToRva(pThis, iOrdinal, pszSymbol, &pvBits, &uRvaExport, &iOrdinal);
1086 if (RT_SUCCESS(rc))
1087 {
1088 uint32_t offForwarder = uRvaExport - pThis->ExportDir.VirtualAddress;
1089 if (offForwarder < pThis->ExportDir.Size)
1090 {
1091 const char *pszForwarder = PE_RVA2TYPE(pvBits, uRvaExport, const char *);
1092
1093 /*
1094 * Parse and validate the string. We must make sure it's valid
1095 * UTF-8, so we restrict it to ASCII.
1096 */
1097 const char *pszEnd = RTStrEnd(pszForwarder, offForwarder - pThis->ExportDir.Size);
1098 if (pszEnd)
1099 {
1100 /* The module name. */
1101 char ch;
1102 uint32_t off = 0;
1103 while ((ch = pszForwarder[off]) != '.' && ch != '\0')
1104 {
1105 if (RT_UNLIKELY((uint8_t)ch >= 0x80))
1106 return VERR_LDR_BAD_FORWARDER;
1107 off++;
1108 }
1109 if (RT_UNLIKELY(ch != '.'))
1110 return VERR_LDR_BAD_FORWARDER;
1111 uint32_t const offDot = off;
1112 off++;
1113
1114 /* The function name or ordinal number. Ordinals starts with a hash. */
1115 uint32_t iImpOrdinal;
1116 if (pszForwarder[off] != '#')
1117 {
1118 iImpOrdinal = UINT32_MAX;
1119 while ((ch = pszForwarder[off]) != '\0')
1120 {
1121 if (RT_UNLIKELY((uint8_t)ch >= 0x80))
1122 return VERR_LDR_BAD_FORWARDER;
1123 off++;
1124 }
1125 if (RT_UNLIKELY(off == offDot + 1))
1126 return VERR_LDR_BAD_FORWARDER;
1127 }
1128 else
1129 {
1130 rc = RTStrToUInt32Full(&pszForwarder[off + 1], 10, &iImpOrdinal);
1131 if (RT_UNLIKELY(rc != VINF_SUCCESS || iImpOrdinal > UINT16_MAX))
1132 return VERR_LDR_BAD_FORWARDER;
1133 }
1134
1135 /*
1136 * Enough buffer?
1137 */
1138 uint32_t cbNeeded = RT_UOFFSETOF_DYN(RTLDRIMPORTINFO, szModule[iImpOrdinal != UINT32_MAX ? offDot + 1 : off + 1]);
1139 if (cbNeeded > cbInfo)
1140 return VERR_BUFFER_OVERFLOW;
1141
1142 /*
1143 * Fill in the return buffer.
1144 */
1145 pInfo->iSelfOrdinal = iOrdinal;
1146 pInfo->iOrdinal = iImpOrdinal;
1147 if (iImpOrdinal == UINT32_MAX)
1148 {
1149 pInfo->pszSymbol = &pInfo->szModule[offDot + 1];
1150 memcpy(&pInfo->szModule[0], pszForwarder, off + 1);
1151 }
1152 else
1153 {
1154 pInfo->pszSymbol = NULL;
1155 memcpy(&pInfo->szModule[0], pszForwarder, offDot);
1156 }
1157 pInfo->szModule[offDot] = '\0';
1158 rc = VINF_SUCCESS;
1159 }
1160 else
1161 rc = VERR_LDR_BAD_FORWARDER;
1162 }
1163 else
1164 rc = VERR_LDR_NOT_FORWARDER;
1165 }
1166 return rc;
1167}
1168
1169
1170/**
1171 * Slow version of rtldrPEEnumSymbols that'll work without all of the image
1172 * being accessible.
1173 *
1174 * This is mainly for use in debuggers and similar.
1175 */
1176static int rtldrPEEnumSymbolsSlow(PRTLDRMODPE pThis, unsigned fFlags, RTUINTPTR BaseAddress,
1177 PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1178{
1179 /*
1180 * We enumerates by ordinal, which means using a slow linear search for
1181 * getting any name
1182 */
1183 PCIMAGE_EXPORT_DIRECTORY pExpDir = NULL;
1184 int rc = rtldrPEReadPartByRva(pThis, NULL, pThis->ExportDir.VirtualAddress, pThis->ExportDir.Size,
1185 (void const **)&pExpDir);
1186 if (RT_FAILURE(rc))
1187 return rc;
1188 uint32_t const cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
1189
1190 uint32_t const *paAddress = NULL;
1191 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfFunctions, cOrdinals * sizeof(uint32_t),
1192 (void const **)&paAddress);
1193 uint32_t const *paRVANames = NULL;
1194 if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
1195 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNames, pExpDir->NumberOfNames * sizeof(uint32_t),
1196 (void const **)&paRVANames);
1197 uint16_t const *paOrdinals = NULL;
1198 if (RT_SUCCESS(rc) && pExpDir->NumberOfNames)
1199 rc = rtldrPEReadPartByRva(pThis, NULL, pExpDir->AddressOfNameOrdinals, pExpDir->NumberOfNames * sizeof(uint16_t),
1200 (void const **)&paOrdinals);
1201 if (RT_SUCCESS(rc))
1202 {
1203 uint32_t uNamePrev = 0;
1204 for (uint32_t uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
1205 {
1206 if (paAddress[uOrdinal] /* needed? */)
1207 {
1208 /*
1209 * Look for name.
1210 */
1211 uint32_t uRvaName = UINT32_MAX;
1212 /* Search from previous + 1 to the end. */
1213 unsigned uName = uNamePrev + 1;
1214 while (uName < pExpDir->NumberOfNames)
1215 {
1216 if (paOrdinals[uName] == uOrdinal)
1217 {
1218 uRvaName = paRVANames[uName];
1219 uNamePrev = uName;
1220 break;
1221 }
1222 uName++;
1223 }
1224 if (uRvaName == UINT32_MAX)
1225 {
1226 /* Search from start to the previous. */
1227 uName = 0;
1228 for (uName = 0 ; uName <= uNamePrev; uName++)
1229 {
1230 if (paOrdinals[uName] == uOrdinal)
1231 {
1232 uRvaName = paRVANames[uName];
1233 uNamePrev = uName;
1234 break;
1235 }
1236 }
1237 }
1238
1239 /*
1240 * Get address.
1241 */
1242 uint32_t uRVAExport = paAddress[uOrdinal];
1243 RTUINTPTR Value;
1244 if (uRVAExport - pThis->ExportDir.VirtualAddress >= pThis->ExportDir.Size)
1245 Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
1246 else if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
1247 Value = RTLDR_ENUM_SYMBOL_FWD_ADDRESS;
1248 else
1249 continue;
1250
1251 /* Read in the name if found one. */
1252 char szAltName[32];
1253 const char *pszName = NULL;
1254 if (uRvaName != UINT32_MAX)
1255 {
1256 uint32_t cbName = 0x1000 - (uRvaName & 0xfff);
1257 if (cbName < 10 || cbName > 512)
1258 cbName = 128;
1259 rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
1260 while (RT_SUCCESS(rc) && RTStrNLen(pszName, cbName) == cbName)
1261 {
1262 rtldrPEFreePart(pThis, NULL, pszName);
1263 pszName = NULL;
1264 if (cbName >= _4K)
1265 break;
1266 cbName += 128;
1267 rc = rtldrPEReadPartByRva(pThis, NULL, uRvaName, cbName, (void const **)&pszName);
1268 }
1269 }
1270 if (!pszName)
1271 {
1272 RTStrPrintf(szAltName, sizeof(szAltName), "Ordinal%#x", uOrdinal);
1273 pszName = szAltName;
1274 }
1275
1276 /*
1277 * Call back.
1278 */
1279 rc = pfnCallback(&pThis->Core, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
1280 if (pszName != szAltName && pszName)
1281 rtldrPEFreePart(pThis, NULL, pszName);
1282 if (rc)
1283 break;
1284 }
1285 }
1286 }
1287
1288 rtldrPEFreePart(pThis, NULL, paOrdinals);
1289 rtldrPEFreePart(pThis, NULL, paRVANames);
1290 rtldrPEFreePart(pThis, NULL, paAddress);
1291 rtldrPEFreePart(pThis, NULL, pExpDir);
1292 return rc;
1293
1294}
1295
1296
1297/** @interface_method_impl{RTLDROPS,pfnEnumSymbols} */
1298static DECLCALLBACK(int) rtldrPEEnumSymbols(PRTLDRMODINTERNAL pMod, unsigned fFlags, const void *pvBits, RTUINTPTR BaseAddress,
1299 PFNRTLDRENUMSYMS pfnCallback, void *pvUser)
1300{
1301 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1302 NOREF(fFlags); /* ignored ... */
1303
1304 /*
1305 * Check if there is actually anything to work on.
1306 */
1307 if ( !pModPe->ExportDir.VirtualAddress
1308 || !pModPe->ExportDir.Size)
1309 return VERR_SYMBOL_NOT_FOUND;
1310
1311 /*
1312 * No bits supplied? Do we need to read the bits?
1313 */
1314 if (!pvBits)
1315 {
1316 if (!pModPe->pvBits)
1317 {
1318 int rc = rtldrPEReadBits(pModPe);
1319 if (RT_FAILURE(rc))
1320 return rtldrPEEnumSymbolsSlow(pModPe, fFlags, BaseAddress, pfnCallback, pvUser);
1321 }
1322 pvBits = pModPe->pvBits;
1323 }
1324
1325 /*
1326 * We enumerates by ordinal, which means using a slow linear search for
1327 * getting any name
1328 */
1329 PIMAGE_EXPORT_DIRECTORY pExpDir = PE_RVA2TYPE(pvBits, pModPe->ExportDir.VirtualAddress, PIMAGE_EXPORT_DIRECTORY);
1330 uint32_t *paAddress = PE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, uint32_t *);
1331 uint32_t *paRVANames = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, uint32_t *);
1332 uint16_t *paOrdinals = PE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, uint16_t *);
1333 uint32_t uNamePrev = 0;
1334 unsigned cOrdinals = RT_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions);
1335 for (unsigned uOrdinal = 0; uOrdinal < cOrdinals; uOrdinal++)
1336 {
1337 if (paAddress[uOrdinal] /* needed? */)
1338 {
1339 /*
1340 * Look for name.
1341 */
1342 const char *pszName = NULL;
1343 /* Search from previous + 1 to the end. */
1344 uint32_t uName = uNamePrev + 1;
1345 while (uName < pExpDir->NumberOfNames)
1346 {
1347 if (paOrdinals[uName] == uOrdinal)
1348 {
1349 pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
1350 uNamePrev = uName;
1351 break;
1352 }
1353 uName++;
1354 }
1355 if (!pszName)
1356 {
1357 /* Search from start to the previous. */
1358 uName = 0;
1359 for (uName = 0 ; uName <= uNamePrev; uName++)
1360 {
1361 if (paOrdinals[uName] == uOrdinal)
1362 {
1363 pszName = PE_RVA2TYPE(pvBits, paRVANames[uName], const char *);
1364 uNamePrev = uName;
1365 break;
1366 }
1367 }
1368 }
1369
1370 /*
1371 * Get address.
1372 */
1373 uint32_t uRVAExport = paAddress[uOrdinal];
1374 RTUINTPTR Value;
1375 if (uRVAExport - pModPe->ExportDir.VirtualAddress >= pModPe->ExportDir.Size)
1376 Value = PE_RVA2TYPE(BaseAddress, uRVAExport, RTUINTPTR);
1377 else if (!(fFlags & RTLDR_ENUM_SYMBOL_FLAGS_NO_FWD))
1378 Value = RTLDR_ENUM_SYMBOL_FWD_ADDRESS;
1379 else
1380 continue;
1381
1382 /*
1383 * Call back.
1384 */
1385 int rc = pfnCallback(pMod, pszName, uOrdinal + pExpDir->Base, Value, pvUser);
1386 if (rc)
1387 return rc;
1388 }
1389 }
1390
1391 return VINF_SUCCESS;
1392}
1393
1394
1395/** @interface_method_impl{RTLDROPS,pfnEnumDbgInfo} */
1396static DECLCALLBACK(int) rtldrPE_EnumDbgInfo(PRTLDRMODINTERNAL pMod, const void *pvBits,
1397 PFNRTLDRENUMDBG pfnCallback, void *pvUser)
1398{
1399 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1400 int rc;
1401
1402 /*
1403 * Debug info directory empty?
1404 */
1405 if ( !pModPe->DebugDir.VirtualAddress
1406 || !pModPe->DebugDir.Size)
1407 return VINF_SUCCESS;
1408
1409 /*
1410 * Allocate temporary memory for a path buffer (this code is also compiled
1411 * and maybe even used in stack starved environments).
1412 */
1413 char *pszPath = (char *)RTMemTmpAlloc(RTPATH_MAX);
1414 if (!pszPath)
1415 return VERR_NO_TMP_MEMORY;
1416
1417 /*
1418 * Get the debug directory.
1419 */
1420 if (!pvBits)
1421 pvBits = pModPe->pvBits;
1422
1423 PCIMAGE_DEBUG_DIRECTORY paDbgDir;
1424 int rcRet = rtldrPEReadPartByRva(pModPe, pvBits, pModPe->DebugDir.VirtualAddress, pModPe->DebugDir.Size,
1425 (void const **)&paDbgDir);
1426 if (RT_FAILURE(rcRet))
1427 {
1428 RTMemTmpFree(pszPath);
1429 return rcRet;
1430 }
1431
1432 /*
1433 * Enumerate the debug directory.
1434 */
1435 uint32_t const cEntries = pModPe->DebugDir.Size / sizeof(paDbgDir[0]);
1436 for (uint32_t i = 0; i < cEntries; i++)
1437 {
1438 if (paDbgDir[i].PointerToRawData < pModPe->offEndOfHdrs)
1439 continue;
1440 if (paDbgDir[i].SizeOfData < 4)
1441 continue;
1442
1443 void const *pvPart = NULL;
1444 RTLDRDBGINFO DbgInfo;
1445 RT_ZERO(DbgInfo.u);
1446 DbgInfo.iDbgInfo = i;
1447 DbgInfo.offFile = paDbgDir[i].PointerToRawData;
1448 DbgInfo.LinkAddress = paDbgDir[i].AddressOfRawData < pModPe->cbImage
1449 && paDbgDir[i].AddressOfRawData >= pModPe->offEndOfHdrs
1450 ? paDbgDir[i].AddressOfRawData : NIL_RTLDRADDR;
1451 DbgInfo.cb = paDbgDir[i].SizeOfData;
1452 DbgInfo.pszExtFile = NULL;
1453
1454 rc = VINF_SUCCESS;
1455 switch (paDbgDir[i].Type)
1456 {
1457 case IMAGE_DEBUG_TYPE_CODEVIEW:
1458 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW;
1459 DbgInfo.u.Cv.cbImage = pModPe->cbImage;
1460 DbgInfo.u.Cv.uMajorVer = paDbgDir[i].MajorVersion;
1461 DbgInfo.u.Cv.uMinorVer = paDbgDir[i].MinorVersion;
1462 DbgInfo.u.Cv.uTimestamp = paDbgDir[i].TimeDateStamp;
1463 if ( paDbgDir[i].SizeOfData < RTPATH_MAX
1464 && paDbgDir[i].SizeOfData > 16
1465 && ( DbgInfo.LinkAddress != NIL_RTLDRADDR
1466 || DbgInfo.offFile > 0)
1467 )
1468 {
1469 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1470 if (RT_SUCCESS(rc))
1471 {
1472 PCCVPDB20INFO pCv20 = (PCCVPDB20INFO)pvPart;
1473 if ( pCv20->u32Magic == CVPDB20INFO_MAGIC
1474 && pCv20->offDbgInfo == 0
1475 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB20INFO, szPdbFilename) )
1476 {
1477 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB20;
1478 DbgInfo.u.Pdb20.cbImage = pModPe->cbImage;
1479 DbgInfo.u.Pdb20.uTimestamp = pCv20->uTimestamp;
1480 DbgInfo.u.Pdb20.uAge = pCv20->uAge;
1481 DbgInfo.pszExtFile = (const char *)&pCv20->szPdbFilename[0];
1482 }
1483 else if ( pCv20->u32Magic == CVPDB70INFO_MAGIC
1484 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(CVPDB70INFO, szPdbFilename) )
1485 {
1486 PCCVPDB70INFO pCv70 = (PCCVPDB70INFO)pCv20;
1487 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_PDB70;
1488 DbgInfo.u.Pdb70.cbImage = pModPe->cbImage;
1489 DbgInfo.u.Pdb70.Uuid = pCv70->PdbUuid;
1490 DbgInfo.u.Pdb70.uAge = pCv70->uAge;
1491 DbgInfo.pszExtFile = (const char *)&pCv70->szPdbFilename[0];
1492 }
1493 }
1494 else
1495 rcRet = rc;
1496 }
1497 break;
1498
1499 case IMAGE_DEBUG_TYPE_MISC:
1500 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1501 if ( paDbgDir[i].SizeOfData < RTPATH_MAX
1502 && paDbgDir[i].SizeOfData > RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data))
1503 {
1504 DbgInfo.enmType = RTLDRDBGINFOTYPE_CODEVIEW_DBG;
1505 DbgInfo.u.Dbg.cbImage = pModPe->cbImage;
1506 if (DbgInfo.LinkAddress != NIL_RTLDRADDR)
1507 DbgInfo.u.Dbg.uTimestamp = paDbgDir[i].TimeDateStamp;
1508 else
1509 DbgInfo.u.Dbg.uTimestamp = pModPe->uTimestamp; /* NT4 SP1 ntfs.sys hack. Generic? */
1510
1511 rc = rtldrPEReadPart(pModPe, pvBits, DbgInfo.offFile, DbgInfo.LinkAddress, paDbgDir[i].SizeOfData, &pvPart);
1512 if (RT_SUCCESS(rc))
1513 {
1514 PCIMAGE_DEBUG_MISC pMisc = (PCIMAGE_DEBUG_MISC)pvPart;
1515 if ( pMisc->DataType == IMAGE_DEBUG_MISC_EXENAME
1516 && pMisc->Length == paDbgDir[i].SizeOfData)
1517 {
1518 if (!pMisc->Unicode)
1519 DbgInfo.pszExtFile = (const char *)&pMisc->Data[0];
1520 else
1521 {
1522 rc = RTUtf16ToUtf8Ex((PCRTUTF16)&pMisc->Data[0],
1523 (pMisc->Length - RT_UOFFSETOF(IMAGE_DEBUG_MISC, Data)) / sizeof(RTUTF16),
1524 &pszPath, RTPATH_MAX, NULL);
1525 if (RT_SUCCESS(rc))
1526 DbgInfo.pszExtFile = pszPath;
1527 else
1528 rcRet = rc; /* continue without a filename. */
1529 }
1530 }
1531 }
1532 else
1533 rcRet = rc; /* continue without a filename. */
1534 }
1535 break;
1536
1537 case IMAGE_DEBUG_TYPE_COFF:
1538 DbgInfo.enmType = RTLDRDBGINFOTYPE_COFF;
1539 DbgInfo.u.Coff.cbImage = pModPe->cbImage;
1540 DbgInfo.u.Coff.uMajorVer = paDbgDir[i].MajorVersion;
1541 DbgInfo.u.Coff.uMinorVer = paDbgDir[i].MinorVersion;
1542 DbgInfo.u.Coff.uTimestamp = paDbgDir[i].TimeDateStamp;
1543 break;
1544
1545 default:
1546 DbgInfo.enmType = RTLDRDBGINFOTYPE_UNKNOWN;
1547 break;
1548 }
1549
1550 /* Fix (hack) the file name encoding. We don't have Windows-1252 handy,
1551 so we'll be using Latin-1 as a reasonable approximation.
1552 (I don't think we know exactly which encoding this is anyway, as
1553 it's probably the current ANSI/Windows code page for the process
1554 generating the image anyways.) */
1555 if (DbgInfo.pszExtFile && DbgInfo.pszExtFile != pszPath)
1556 {
1557 rc = RTLatin1ToUtf8Ex(DbgInfo.pszExtFile,
1558 paDbgDir[i].SizeOfData - ((uintptr_t)DbgInfo.pszExtFile - (uintptr_t)pvBits),
1559 &pszPath, RTPATH_MAX, NULL);
1560 if (RT_FAILURE(rc))
1561 {
1562 rcRet = rc;
1563 DbgInfo.pszExtFile = NULL;
1564 }
1565 }
1566 if (DbgInfo.pszExtFile)
1567 RTPathChangeToUnixSlashes(pszPath, true /*fForce*/);
1568
1569 rc = pfnCallback(pMod, &DbgInfo, pvUser);
1570 rtldrPEFreePart(pModPe, pvBits, pvPart);
1571 if (rc != VINF_SUCCESS)
1572 {
1573 rcRet = rc;
1574 break;
1575 }
1576 }
1577
1578 rtldrPEFreePart(pModPe, pvBits, paDbgDir);
1579 RTMemTmpFree(pszPath);
1580 return rcRet;
1581}
1582
1583
1584/** @interface_method_impl{RTLDROPS,pfnEnumSegments} */
1585static DECLCALLBACK(int) rtldrPE_EnumSegments(PRTLDRMODINTERNAL pMod, PFNRTLDRENUMSEGS pfnCallback, void *pvUser)
1586{
1587 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1588 RTLDRSEG SegInfo;
1589
1590 /*
1591 * The first section is a fake one covering the headers.
1592 */
1593 SegInfo.pszName = "NtHdrs";
1594 SegInfo.cchName = 6;
1595 SegInfo.SelFlat = 0;
1596 SegInfo.Sel16bit = 0;
1597 SegInfo.fFlags = 0;
1598 SegInfo.fProt = RTMEM_PROT_READ;
1599 SegInfo.Alignment = 1;
1600 SegInfo.LinkAddress = pModPe->uImageBase;
1601 SegInfo.RVA = 0;
1602 SegInfo.offFile = 0;
1603 SegInfo.cb = pModPe->cbHeaders;
1604 SegInfo.cbFile = pModPe->cbHeaders;
1605 SegInfo.cbMapped = pModPe->cbHeaders;
1606 if ((pModPe->paSections[0].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1607 SegInfo.cbMapped = pModPe->paSections[0].VirtualAddress;
1608 int rc = pfnCallback(pMod, &SegInfo, pvUser);
1609
1610 /*
1611 * Then all the normal sections.
1612 */
1613 PCIMAGE_SECTION_HEADER pSh = pModPe->paSections;
1614 for (uint32_t i = 0; i < pModPe->cSections && rc == VINF_SUCCESS; i++, pSh++)
1615 {
1616 char szName[32];
1617 SegInfo.pszName = (const char *)&pSh->Name[0];
1618 SegInfo.cchName = (uint32_t)RTStrNLen(SegInfo.pszName, sizeof(pSh->Name));
1619 if (SegInfo.cchName >= sizeof(pSh->Name))
1620 {
1621 memcpy(szName, &pSh->Name[0], sizeof(pSh->Name));
1622 szName[sizeof(pSh->Name)] = '\0';
1623 SegInfo.pszName = szName;
1624 }
1625 else if (SegInfo.cchName == 0)
1626 {
1627 SegInfo.pszName = szName;
1628 SegInfo.cchName = (uint32_t)RTStrPrintf(szName, sizeof(szName), "UnamedSect%02u", i);
1629 }
1630 SegInfo.SelFlat = 0;
1631 SegInfo.Sel16bit = 0;
1632 SegInfo.fFlags = 0;
1633 SegInfo.fProt = RTMEM_PROT_NONE;
1634 if (pSh->Characteristics & IMAGE_SCN_MEM_READ)
1635 SegInfo.fProt |= RTMEM_PROT_READ;
1636 if (pSh->Characteristics & IMAGE_SCN_MEM_WRITE)
1637 SegInfo.fProt |= RTMEM_PROT_WRITE;
1638 if (pSh->Characteristics & IMAGE_SCN_MEM_EXECUTE)
1639 SegInfo.fProt |= RTMEM_PROT_EXEC;
1640 SegInfo.Alignment = (pSh->Characteristics & IMAGE_SCN_ALIGN_MASK) >> IMAGE_SCN_ALIGN_SHIFT;
1641 if (SegInfo.Alignment > 0)
1642 SegInfo.Alignment = RT_BIT_64(SegInfo.Alignment - 1);
1643 if (pSh->Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1644 {
1645 SegInfo.LinkAddress = NIL_RTLDRADDR;
1646 SegInfo.RVA = NIL_RTLDRADDR;
1647 SegInfo.cbMapped = pSh->Misc.VirtualSize;
1648 }
1649 else
1650 {
1651 SegInfo.LinkAddress = pSh->VirtualAddress + pModPe->uImageBase;
1652 SegInfo.RVA = pSh->VirtualAddress;
1653 SegInfo.cbMapped = RT_ALIGN(SegInfo.cb, SegInfo.Alignment);
1654 if (i + 1 < pModPe->cSections && !(pSh[1].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1655 SegInfo.cbMapped = pSh[1].VirtualAddress - pSh->VirtualAddress;
1656 }
1657 SegInfo.cb = pSh->Misc.VirtualSize;
1658 if (pSh->PointerToRawData == 0 || pSh->SizeOfRawData == 0)
1659 {
1660 SegInfo.offFile = -1;
1661 SegInfo.cbFile = 0;
1662 }
1663 else
1664 {
1665 SegInfo.offFile = pSh->PointerToRawData;
1666 SegInfo.cbFile = pSh->SizeOfRawData;
1667 }
1668
1669 rc = pfnCallback(pMod, &SegInfo, pvUser);
1670 }
1671
1672 return rc;
1673}
1674
1675
1676/** @interface_method_impl{RTLDROPS,pfnLinkAddressToSegOffset} */
1677static DECLCALLBACK(int) rtldrPE_LinkAddressToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress,
1678 uint32_t *piSeg, PRTLDRADDR poffSeg)
1679{
1680 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1681
1682 LinkAddress -= pModPe->uImageBase;
1683
1684 /* Special header segment. */
1685 if (LinkAddress < pModPe->paSections[0].VirtualAddress)
1686 {
1687 *piSeg = 0;
1688 *poffSeg = LinkAddress;
1689 return VINF_SUCCESS;
1690 }
1691
1692 /*
1693 * Search the normal sections. (Could do this in binary fashion, they're
1694 * sorted, but too much bother right now.)
1695 */
1696 if (LinkAddress > pModPe->cbImage)
1697 return VERR_LDR_INVALID_LINK_ADDRESS;
1698 uint32_t i = pModPe->cSections;
1699 PCIMAGE_SECTION_HEADER paShs = pModPe->paSections;
1700 while (i-- > 0)
1701 if (!(paShs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD))
1702 {
1703 uint32_t uAddr = paShs[i].VirtualAddress;
1704 if (LinkAddress >= uAddr)
1705 {
1706 *poffSeg = LinkAddress - uAddr;
1707 *piSeg = i + 1;
1708 return VINF_SUCCESS;
1709 }
1710 }
1711
1712 return VERR_LDR_INVALID_LINK_ADDRESS;
1713}
1714
1715
1716/** @interface_method_impl{RTLDROPS,pfnLinkAddressToRva} */
1717static DECLCALLBACK(int) rtldrPE_LinkAddressToRva(PRTLDRMODINTERNAL pMod, RTLDRADDR LinkAddress, PRTLDRADDR pRva)
1718{
1719 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1720
1721 LinkAddress -= pModPe->uImageBase;
1722 if (LinkAddress > pModPe->cbImage)
1723 return VERR_LDR_INVALID_LINK_ADDRESS;
1724 *pRva = LinkAddress;
1725
1726 return VINF_SUCCESS;
1727}
1728
1729
1730/** @interface_method_impl{RTLDROPS,pfnSegOffsetToRva} */
1731static DECLCALLBACK(int) rtldrPE_SegOffsetToRva(PRTLDRMODINTERNAL pMod, uint32_t iSeg, RTLDRADDR offSeg,
1732 PRTLDRADDR pRva)
1733{
1734 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1735
1736 if (iSeg > pModPe->cSections)
1737 return VERR_LDR_INVALID_SEG_OFFSET;
1738
1739 /** @todo should validate offSeg here... too lazy right now. */
1740 if (iSeg == 0)
1741 *pRva = offSeg;
1742 else if (pModPe->paSections[iSeg].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
1743 return VERR_LDR_INVALID_SEG_OFFSET;
1744 else
1745 *pRva = offSeg + pModPe->paSections[iSeg].VirtualAddress;
1746 return VINF_SUCCESS;
1747}
1748
1749
1750/** @interface_method_impl{RTLDROPS,pfnRvaToSegOffset} */
1751static DECLCALLBACK(int) rtldrPE_RvaToSegOffset(PRTLDRMODINTERNAL pMod, RTLDRADDR Rva,
1752 uint32_t *piSeg, PRTLDRADDR poffSeg)
1753{
1754 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1755 int rc = rtldrPE_LinkAddressToSegOffset(pMod, Rva + pModPe->uImageBase, piSeg, poffSeg);
1756 if (RT_FAILURE(rc))
1757 rc = VERR_LDR_INVALID_RVA;
1758 return rc;
1759}
1760
1761
1762/**
1763 * Worker for rtLdrPE_QueryProp and rtLdrPE_QueryImportModule that counts the
1764 * number of imports, storing the result in RTLDRMODPE::cImports.
1765 *
1766 * @returns IPRT status code.
1767 * @param pThis The PE module instance.
1768 * @param pvBits Image bits if the caller had them available, NULL if
1769 * not. Saves a couple of file accesses.
1770 */
1771static int rtLdrPE_CountImports(PRTLDRMODPE pThis, void const *pvBits)
1772{
1773 PCIMAGE_IMPORT_DESCRIPTOR paImpDescs;
1774 int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ImportDir.VirtualAddress, pThis->ImportDir.Size,
1775 (void const **)&paImpDescs);
1776 if (RT_SUCCESS(rc))
1777 {
1778 uint32_t const cMax = pThis->ImportDir.Size / sizeof(IMAGE_IMPORT_DESCRIPTOR);
1779 uint32_t i = 0;
1780 while ( i < cMax
1781 && paImpDescs[i].Name > pThis->offNtHdrs
1782 && paImpDescs[i].Name < pThis->cbImage
1783 && paImpDescs[i].FirstThunk > pThis->offNtHdrs
1784 && paImpDescs[i].FirstThunk < pThis->cbImage)
1785 i++;
1786 pThis->cImports = i;
1787
1788 rtldrPEFreePart(pThis, pvBits, paImpDescs);
1789 }
1790 return rc;
1791}
1792
1793/**
1794 * Worker for rtLdrPE_QueryImportModule and rtLdrPE_QueryInternalName that
1795 * copies a zero termianted string at the given RVA into the RTLdrQueryPropEx
1796 * output buffer.
1797 *
1798 * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
1799 * @param pThis The PE module instance.
1800 * @param pvBits Image bits if the caller had them available, NULL if
1801 * not. Saves a couple of file accesses.
1802 * @param uRvaString The RVA of the string to copy.
1803 * @param cbMaxString The max string length.
1804 * @param pvBuf The output buffer.
1805 * @param cbBuf The buffer size.
1806 * @param pcbRet Where to return the number of bytes we've returned
1807 * (or in case of VERR_BUFFER_OVERFLOW would have).
1808 */
1809static int rtLdrPE_QueryNameAtRva(PRTLDRMODPE pThis, void const *pvBits, uint32_t uRvaString, uint32_t cbMaxString,
1810 void *pvBuf, size_t cbBuf, size_t *pcbRet)
1811{
1812 int rc;
1813 if ( uRvaString >= pThis->cbHeaders
1814 && uRvaString < pThis->cbImage)
1815 {
1816 /*
1817 * Limit the string.
1818 */
1819 uint32_t cbMax = pThis->cbImage - uRvaString;
1820 if (cbMax > cbMaxString)
1821 cbMax = cbMaxString;
1822 char *pszString;
1823 rc = rtldrPEReadPartByRva(pThis, pvBits, uRvaString, cbMax, (void const **)&pszString);
1824 if (RT_SUCCESS(rc))
1825 {
1826 /*
1827 * Make sure it's null terminated and valid UTF-8 encoding.
1828 *
1829 * Which encoding this really is isn't defined, I think,
1830 * but we need to make sure we don't get bogus UTF-8 into
1831 * the process, so making sure it's valid UTF-8 is a good
1832 * as anything else since it covers ASCII.
1833 */
1834 size_t cchString = RTStrNLen(pszString, cbMaxString);
1835 if (cchString < cbMaxString)
1836 {
1837 rc = RTStrValidateEncodingEx(pszString, cchString, 0 /*fFlags*/);
1838 if (RT_SUCCESS(rc))
1839 {
1840 /*
1841 * Copy out the result and we're done.
1842 * (We have to do all the cleanup code though, so no return success here.)
1843 */
1844 *pcbRet = cchString + 1;
1845 if (cbBuf >= cchString + 1)
1846 memcpy(pvBuf, pszString, cchString + 1);
1847 else
1848 rc = VERR_BUFFER_OVERFLOW;
1849 }
1850 }
1851 else
1852 rc = VERR_BAD_EXE_FORMAT;
1853 rtldrPEFreePart(pThis, pvBits, pszString);
1854 }
1855 }
1856 else
1857 rc = VERR_BAD_EXE_FORMAT;
1858 return rc;
1859}
1860
1861
1862/**
1863 * Worker for rtLdrPE_QueryProp that retrievs the name of an import DLL.
1864 *
1865 * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
1866 * @param pThis The PE module instance.
1867 * @param pvBits Image bits if the caller had them available, NULL if
1868 * not. Saves a couple of file accesses.
1869 * @param iImport The index of the import table descriptor to fetch
1870 * the name from.
1871 * @param pvBuf The output buffer.
1872 * @param cbBuf The buffer size.
1873 * @param pcbRet Where to return the number of bytes we've returned
1874 * (or in case of VERR_BUFFER_OVERFLOW would have).
1875 */
1876static int rtLdrPE_QueryImportModule(PRTLDRMODPE pThis, void const *pvBits, uint32_t iImport,
1877 void *pvBuf, size_t cbBuf, size_t *pcbRet)
1878{
1879 /*
1880 * Make sure we got the import count.
1881 */
1882 int rc;
1883 if (pThis->cImports == UINT32_MAX)
1884 {
1885 rc = rtLdrPE_CountImports(pThis, pvBits);
1886 if (RT_FAILURE(rc))
1887 return rc;
1888 }
1889
1890 /*
1891 * Check the index first, converting it to an RVA.
1892 */
1893 if (iImport < pThis->cImports)
1894 {
1895 uint32_t offEntry = iImport * sizeof(IMAGE_IMPORT_DESCRIPTOR) + pThis->ImportDir.VirtualAddress;
1896
1897 /*
1898 * Retrieve the import table descriptor.
1899 * Using 1024 as the max name length (should be more than enough).
1900 */
1901 PCIMAGE_IMPORT_DESCRIPTOR pImpDesc;
1902 rc = rtldrPEReadPartByRva(pThis, pvBits, offEntry, sizeof(*pImpDesc), (void const **)&pImpDesc);
1903 if (RT_SUCCESS(rc))
1904 {
1905 rc = rtLdrPE_QueryNameAtRva(pThis, pvBits, pImpDesc->Name, 1024 /*cchMaxString*/, pvBuf, cbBuf, pcbRet);
1906 rtldrPEFreePart(pThis, pvBits, pImpDesc);
1907 }
1908 }
1909 else
1910 rc = VERR_NOT_FOUND;
1911
1912 if (RT_SUCCESS(rc))
1913 return VINF_SUCCESS;
1914
1915 *pcbRet = 0;
1916 return rc;
1917}
1918
1919
1920/**
1921 * Worker for rtLdrPE_QueryProp that retrieves the internal module name.
1922 *
1923 * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
1924 * @param pThis The PE module instance.
1925 * @param pvBits Image bits if the caller had them available, NULL if
1926 * not. Saves a couple of file accesses.
1927 * @param pvBuf The output buffer.
1928 * @param cbBuf The buffer size.
1929 * @param pcbRet Where to return the number of bytes we've returned
1930 * (or in case of VERR_BUFFER_OVERFLOW would have).
1931 */
1932static int rtLdrPE_QueryInternalName(PRTLDRMODPE pThis, void const *pvBits, void *pvBuf, size_t cbBuf, size_t *pcbRet)
1933{
1934 *pcbRet = 0;
1935
1936 if ( pThis->ExportDir.Size < sizeof(IMAGE_EXPORT_DIRECTORY)
1937 || pThis->ExportDir.VirtualAddress == 0)
1938 return VERR_NOT_FOUND;
1939
1940 PCIMAGE_EXPORT_DIRECTORY pExpDir;
1941 int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExportDir.VirtualAddress, sizeof(*pExpDir), (void const **)&pExpDir);
1942 if (RT_SUCCESS(rc))
1943 {
1944 rc = rtLdrPE_QueryNameAtRva(pThis, pvBits, pExpDir->Name, 1024 /*cchMaxString*/, pvBuf, cbBuf, pcbRet);
1945 rtldrPEFreePart(pThis, pvBits, pExpDir);
1946 }
1947
1948 return rc;
1949}
1950
1951
1952/**
1953 * Worker for rtLdrPE_QueryProp that retrieves unwind information.
1954 *
1955 * @returns IPRT status code. If VERR_BUFFER_OVERFLOW, pcbBuf is required size.
1956 * @param pThis The PE module instance.
1957 * @param pvBits Image bits if the caller had them available, NULL if
1958 * not. Saves a couple of file accesses.
1959 * @param pvBuf The output buffer.
1960 * @param cbBuf The buffer size.
1961 * @param pcbRet Where to return the number of bytes we've returned
1962 * (or in case of VERR_BUFFER_OVERFLOW would have).
1963 */
1964static int rtLdrPE_QueryUnwindInfo(PRTLDRMODPE pThis, void const *pvBits, void *pvBuf, size_t cbBuf, size_t *pcbRet)
1965{
1966 int rc;
1967 uint32_t const cbSrc = pThis->ExceptionDir.Size;
1968 if ( cbSrc > 0
1969 && pThis->ExceptionDir.VirtualAddress > 0)
1970 {
1971 *pcbRet = cbSrc;
1972 if (cbBuf >= cbSrc)
1973 {
1974 void const *pvSrc = NULL;
1975 rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, cbSrc, &pvSrc);
1976 if (RT_SUCCESS(rc))
1977 {
1978 memcpy(pvBuf, pvSrc, cbSrc);
1979 rtldrPEFreePart(pThis, pvBits, pvSrc);
1980 }
1981 }
1982 else
1983 rc = VERR_BUFFER_OVERFLOW;
1984 }
1985 else
1986 {
1987 *pcbRet = 0;
1988 rc = VERR_NOT_FOUND;
1989 }
1990 return rc;
1991}
1992
1993
1994/** @interface_method_impl{RTLDROPS,pfnQueryProp} */
1995static DECLCALLBACK(int) rtldrPE_QueryProp(PRTLDRMODINTERNAL pMod, RTLDRPROP enmProp, void const *pvBits,
1996 void *pvBuf, size_t cbBuf, size_t *pcbRet)
1997{
1998 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
1999 switch (enmProp)
2000 {
2001 case RTLDRPROP_TIMESTAMP_SECONDS:
2002 Assert(*pcbRet == cbBuf);
2003 if (cbBuf == sizeof(int32_t))
2004 *(int32_t *)pvBuf = pModPe->uTimestamp;
2005 else if (cbBuf == sizeof(int64_t))
2006 *(int64_t *)pvBuf = pModPe->uTimestamp;
2007 else
2008 AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2009 break;
2010
2011 case RTLDRPROP_IS_SIGNED:
2012 Assert(cbBuf == sizeof(bool));
2013 Assert(*pcbRet == cbBuf);
2014 *(bool *)pvBuf = pModPe->offPkcs7SignedData != 0;
2015 break;
2016
2017 case RTLDRPROP_PKCS7_SIGNED_DATA:
2018 {
2019 if (pModPe->cbPkcs7SignedData == 0)
2020 return VERR_NOT_FOUND;
2021 Assert(pModPe->offPkcs7SignedData > pModPe->SecurityDir.VirtualAddress);
2022
2023 *pcbRet = pModPe->cbPkcs7SignedData;
2024 if (cbBuf < pModPe->cbPkcs7SignedData)
2025 return VERR_BUFFER_OVERFLOW;
2026 return pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pvBuf, pModPe->cbPkcs7SignedData,
2027 pModPe->offPkcs7SignedData);
2028 }
2029
2030 case RTLDRPROP_SIGNATURE_CHECKS_ENFORCED:
2031 Assert(cbBuf == sizeof(bool));
2032 Assert(*pcbRet == cbBuf);
2033 *(bool *)pvBuf = pModPe->offPkcs7SignedData > 0
2034 && (pModPe->fDllCharacteristics & IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY);
2035 break;
2036
2037 case RTLDRPROP_IMPORT_COUNT:
2038 Assert(cbBuf == sizeof(uint32_t));
2039 Assert(*pcbRet == cbBuf);
2040 if (pModPe->cImports == UINT32_MAX)
2041 {
2042 int rc = rtLdrPE_CountImports(pModPe, pvBits);
2043 if (RT_FAILURE(rc))
2044 return rc;
2045 }
2046 *(uint32_t *)pvBuf = pModPe->cImports;
2047 break;
2048
2049 case RTLDRPROP_IMPORT_MODULE:
2050 Assert(cbBuf >= sizeof(uint32_t));
2051 return rtLdrPE_QueryImportModule(pModPe, pvBits, *(uint32_t *)pvBuf, pvBuf, cbBuf, pcbRet);
2052
2053 case RTLDRPROP_FILE_OFF_HEADER:
2054 Assert(cbBuf == sizeof(uint32_t) || cbBuf == sizeof(uint64_t));
2055 if (cbBuf == sizeof(uint32_t))
2056 *(uint32_t *)pvBuf = pModPe->offNtHdrs;
2057 else
2058 *(uint64_t *)pvBuf = pModPe->offNtHdrs;
2059 return VINF_SUCCESS;
2060
2061 case RTLDRPROP_INTERNAL_NAME:
2062 return rtLdrPE_QueryInternalName(pModPe, pvBits, pvBuf, cbBuf, pcbRet);
2063
2064 case RTLDRPROP_UNWIND_INFO:
2065 return rtLdrPE_QueryUnwindInfo(pModPe, pvBits, pvBuf, cbBuf, pcbRet);
2066
2067 default:
2068 return VERR_NOT_FOUND;
2069 }
2070 return VINF_SUCCESS;
2071}
2072
2073
2074
2075/*
2076 * Lots of Authenticode fun ahead.
2077 */
2078
2079
2080/**
2081 * Initializes the hash context.
2082 *
2083 * @returns VINF_SUCCESS or VERR_NOT_SUPPORTED.
2084 * @param pHashCtx The hash context union.
2085 * @param enmDigest The hash type we're calculating..
2086 */
2087static int rtLdrPE_HashInit(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest)
2088{
2089 switch (enmDigest)
2090 {
2091 case RTDIGESTTYPE_SHA512: RTSha512Init(&pHashCtx->Sha512); break;
2092 case RTDIGESTTYPE_SHA256: RTSha256Init(&pHashCtx->Sha256); break;
2093 case RTDIGESTTYPE_SHA1: RTSha1Init(&pHashCtx->Sha1); break;
2094 case RTDIGESTTYPE_MD5: RTMd5Init(&pHashCtx->Md5); break;
2095 default: AssertFailedReturn(VERR_NOT_SUPPORTED);
2096 }
2097 return VINF_SUCCESS;
2098}
2099
2100
2101/**
2102 * Updates the hash with more data.
2103 *
2104 * @param pHashCtx The hash context union.
2105 * @param enmDigest The hash type we're calculating..
2106 * @param pvBuf Pointer to a buffer with bytes to add to thash.
2107 * @param cbBuf How many bytes to add from @a pvBuf.
2108 */
2109static void rtLdrPE_HashUpdate(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest, void const *pvBuf, size_t cbBuf)
2110{
2111 switch (enmDigest)
2112 {
2113 case RTDIGESTTYPE_SHA512: RTSha512Update(&pHashCtx->Sha512, pvBuf, cbBuf); break;
2114 case RTDIGESTTYPE_SHA256: RTSha256Update(&pHashCtx->Sha256, pvBuf, cbBuf); break;
2115 case RTDIGESTTYPE_SHA1: RTSha1Update(&pHashCtx->Sha1, pvBuf, cbBuf); break;
2116 case RTDIGESTTYPE_MD5: RTMd5Update(&pHashCtx->Md5, pvBuf, cbBuf); break;
2117 default: AssertReleaseFailed();
2118 }
2119}
2120
2121
2122/**
2123 * Finalizes the hash calculations.
2124 *
2125 * @param pHashCtx The hash context union.
2126 * @param enmDigest The hash type we're calculating..
2127 * @param pHashRes The hash result union.
2128 */
2129static void rtLdrPE_HashFinalize(PRTLDRPEHASHCTXUNION pHashCtx, RTDIGESTTYPE enmDigest, PRTLDRPEHASHRESUNION pHashRes)
2130{
2131 switch (enmDigest)
2132 {
2133 case RTDIGESTTYPE_SHA512: RTSha512Final(&pHashCtx->Sha512, pHashRes->abSha512); break;
2134 case RTDIGESTTYPE_SHA256: RTSha256Final(&pHashCtx->Sha256, pHashRes->abSha256); break;
2135 case RTDIGESTTYPE_SHA1: RTSha1Final(&pHashCtx->Sha1, pHashRes->abSha1); break;
2136 case RTDIGESTTYPE_MD5: RTMd5Final(pHashRes->abMd5, &pHashCtx->Md5); break;
2137 default: AssertReleaseFailed();
2138 }
2139}
2140
2141
2142#ifndef IPRT_WITHOUT_LDR_VERIFY
2143/**
2144 * Returns the digest size for the given digest type.
2145 *
2146 * @returns Size in bytes.
2147 * @param enmDigest The hash type in question.
2148 */
2149static uint32_t rtLdrPE_HashGetHashSize(RTDIGESTTYPE enmDigest)
2150{
2151 switch (enmDigest)
2152 {
2153 case RTDIGESTTYPE_SHA512: return RTSHA512_HASH_SIZE;
2154 case RTDIGESTTYPE_SHA256: return RTSHA256_HASH_SIZE;
2155 case RTDIGESTTYPE_SHA1: return RTSHA1_HASH_SIZE;
2156 case RTDIGESTTYPE_MD5: return RTMD5_HASH_SIZE;
2157 default: AssertReleaseFailedReturn(0);
2158 }
2159}
2160#endif
2161
2162
2163/**
2164 * Calculate the special too watch out for when hashing the image.
2165 *
2166 * @returns IPRT status code.
2167 * @param pModPe The PE module.
2168 * @param pPlaces The structure where to store the special places.
2169 * @param pErrInfo Optional error info.
2170 */
2171static int rtldrPe_CalcSpecialHashPlaces(PRTLDRMODPE pModPe, PRTLDRPEHASHSPECIALS pPlaces, PRTERRINFO pErrInfo)
2172{
2173 /*
2174 * If we're here despite a missing signature, we need to get the file size.
2175 */
2176 pPlaces->cbToHash = pModPe->SecurityDir.VirtualAddress;
2177 if (pPlaces->cbToHash == 0)
2178 {
2179 RTFOFF cbFile = pModPe->Core.pReader->pfnSize(pModPe->Core.pReader);
2180 pPlaces->cbToHash = (uint32_t)cbFile;
2181 if (pPlaces->cbToHash != (RTFOFF)cbFile)
2182 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_FILE_LENGTH_ERROR, "File is too large: %RTfoff", cbFile);
2183 }
2184
2185 /*
2186 * Calculate the special places.
2187 */
2188 pPlaces->offCksum = (uint32_t)pModPe->offNtHdrs
2189 + (pModPe->f64Bit
2190 ? RT_UOFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.CheckSum)
2191 : RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.CheckSum));
2192 pPlaces->cbCksum = RT_SIZEOFMEMB(IMAGE_NT_HEADERS32, OptionalHeader.CheckSum);
2193 pPlaces->offSecDir = (uint32_t)pModPe->offNtHdrs
2194 + (pModPe->f64Bit
2195 ? RT_UOFFSETOF(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY])
2196 : RT_UOFFSETOF(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]));
2197 pPlaces->cbSecDir = sizeof(IMAGE_DATA_DIRECTORY);
2198 pPlaces->offEndSpecial = pPlaces->offSecDir + pPlaces->cbSecDir;
2199 return VINF_SUCCESS;
2200}
2201
2202
2203/**
2204 * Calculates the whole image hash.
2205 *
2206 * The Authenticode_PE.docx version 1.0 explains how the hash is calculated,
2207 * points 8 thru 14 are bogus. If you study them a little carefully, it is
2208 * clear that the algorithm will only work if the raw data for the section have
2209 * no gaps between them or in front of them. So, this elaborate section sorting
2210 * by PointerToRawData and working them section by section could simply be
2211 * replaced by one point:
2212 *
2213 * 8. Add all the file content between SizeOfHeaders and the
2214 * attribute certificate table to the hash. Then finalize
2215 * the hash.
2216 *
2217 * Not sure if Microsoft is screwing with us on purpose here or whether they
2218 * assigned some of this work to less talented engineers and tech writers. I
2219 * love fact that they say it's "simplified" and should yield the correct hash
2220 * for "almost all" files. Stupid, Stupid, Microsofties!!
2221 *
2222 * My simplified implementation that just hashes the entire file up to the
2223 * signature or end of the file produces the same SHA1 values as "signtool
2224 * verify /v" does both for edited executables with gaps between/before/after
2225 * sections raw data and normal executables without any gaps.
2226 *
2227 * @returns IPRT status code.
2228 * @param pModPe The PE module.
2229 * @param pvScratch Scratch buffer.
2230 * @param cbScratch Size of the scratch buffer.
2231 * @param enmDigest The hash digest type we're calculating.
2232 * @param pHashCtx Hash context scratch area.
2233 * @param pHashRes Hash result buffer.
2234 * @param pErrInfo Optional error info buffer.
2235 */
2236static int rtldrPE_HashImageCommon(PRTLDRMODPE pModPe, void *pvScratch, uint32_t cbScratch, RTDIGESTTYPE enmDigest,
2237 PRTLDRPEHASHCTXUNION pHashCtx, PRTLDRPEHASHRESUNION pHashRes, PRTERRINFO pErrInfo)
2238{
2239 int rc = rtLdrPE_HashInit(pHashCtx, enmDigest);
2240 if (RT_FAILURE(rc))
2241 return rc;
2242
2243 /*
2244 * Calculate the special places.
2245 */
2246 RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
2247 rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
2248 if (RT_FAILURE(rc))
2249 return rc;
2250
2251 /*
2252 * Work our way thru the image data.
2253 */
2254 uint32_t off = 0;
2255 while (off < SpecialPlaces.cbToHash)
2256 {
2257 uint32_t cbRead = RT_MIN(SpecialPlaces.cbToHash - off, cbScratch);
2258 uint8_t *pbCur = (uint8_t *)pvScratch;
2259 rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbRead, off);
2260 if (RT_FAILURE(rc))
2261 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH, "Hash read error at %#x: %Rrc (cbRead=%#zx)",
2262 off, rc, cbRead);
2263
2264 if (off < SpecialPlaces.offEndSpecial)
2265 {
2266 if (off < SpecialPlaces.offCksum)
2267 {
2268 /* Hash everything up to the checksum. */
2269 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbRead);
2270 rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbChunk);
2271 pbCur += cbChunk;
2272 cbRead -= cbChunk;
2273 off += cbChunk;
2274 }
2275
2276 if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
2277 {
2278 /* Skip the checksum */
2279 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbRead);
2280 pbCur += cbChunk;
2281 cbRead -= cbChunk;
2282 off += cbChunk;
2283 }
2284
2285 if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
2286 {
2287 /* Hash everything between the checksum and the data dir entry. */
2288 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbRead);
2289 rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbChunk);
2290 pbCur += cbChunk;
2291 cbRead -= cbChunk;
2292 off += cbChunk;
2293 }
2294
2295 if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
2296 {
2297 /* Skip the security data directory entry. */
2298 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbRead);
2299 pbCur += cbChunk;
2300 cbRead -= cbChunk;
2301 off += cbChunk;
2302 }
2303 }
2304
2305 rtLdrPE_HashUpdate(pHashCtx, enmDigest, pbCur, cbRead);
2306
2307 /* Advance */
2308 off += cbRead;
2309 }
2310
2311 /*
2312 * If there isn't a signature, experiments with signtool indicates that we
2313 * have to zero padd the file size until it's a multiple of 8. (This is
2314 * most likely to give 64-bit values in the certificate a natural alignment
2315 * when memory mapped.)
2316 */
2317 if ( pModPe->SecurityDir.Size != SpecialPlaces.cbToHash
2318 && SpecialPlaces.cbToHash != RT_ALIGN_32(SpecialPlaces.cbToHash, WIN_CERTIFICATE_ALIGNMENT))
2319 {
2320 static const uint8_t s_abZeros[WIN_CERTIFICATE_ALIGNMENT] = { 0,0,0,0, 0,0,0,0 };
2321 rtLdrPE_HashUpdate(pHashCtx, enmDigest, s_abZeros,
2322 RT_ALIGN_32(SpecialPlaces.cbToHash, WIN_CERTIFICATE_ALIGNMENT) - SpecialPlaces.cbToHash);
2323 }
2324
2325 /*
2326 * Done. Finalize the hashes.
2327 */
2328 rtLdrPE_HashFinalize(pHashCtx, enmDigest, pHashRes);
2329 return VINF_SUCCESS;
2330}
2331
2332#ifndef IPRT_WITHOUT_LDR_VERIFY
2333
2334/**
2335 * Verifies image preconditions not checked by the open validation code.
2336 *
2337 * @returns IPRT status code.
2338 * @param pModPe The PE module.
2339 * @param pErrInfo Optional error info buffer.
2340 */
2341static int rtldrPE_VerifySignatureImagePrecoditions(PRTLDRMODPE pModPe, PRTERRINFO pErrInfo)
2342{
2343 /*
2344 * Validate the sections. While doing so, track the amount of section raw
2345 * section data in the file so we can use this to validate the signature
2346 * table location later.
2347 */
2348 uint32_t offNext = pModPe->cbHeaders; /* same */
2349 for (uint32_t i = 0; i < pModPe->cSections; i++)
2350 if (pModPe->paSections[i].SizeOfRawData > 0)
2351 {
2352 uint64_t offEnd = (uint64_t)pModPe->paSections[i].PointerToRawData + pModPe->paSections[i].SizeOfRawData;
2353 if (offEnd > offNext)
2354 {
2355 if (offEnd >= _2G)
2356 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_SECTION_RAW_DATA_VALUES,
2357 "Section %#u specifies file data after 2GB: PointerToRawData=%#x SizeOfRawData=%#x",
2358 i, pModPe->paSections[i].PointerToRawData, pModPe->paSections[i].SizeOfRawData);
2359 offNext = (uint32_t)offEnd;
2360 }
2361 }
2362 uint32_t offEndOfSectionData = offNext;
2363
2364 /*
2365 * Validate the signature.
2366 */
2367 if (!pModPe->SecurityDir.Size)
2368 return RTErrInfoSet(pErrInfo, VERR_LDRVI_NOT_SIGNED, "Not signed.");
2369
2370 uint32_t const offSignature = pModPe->SecurityDir.VirtualAddress;
2371 uint32_t const cbSignature = pModPe->SecurityDir.Size;
2372 if ( cbSignature <= sizeof(WIN_CERTIFICATE)
2373 || cbSignature >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE
2374 || offSignature >= _2G)
2375 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2376 "Invalid security data dir entry: cb=%#x off=%#x", cbSignature, offSignature);
2377
2378 if (offSignature < offEndOfSectionData)
2379 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2380 "Invalid security data dir entry offset: %#x offEndOfSectionData=%#x",
2381 offSignature, offEndOfSectionData);
2382
2383 if (RT_ALIGN_32(offSignature, WIN_CERTIFICATE_ALIGNMENT) != offSignature)
2384 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2385 "Misaligned security dir entry offset: %#x (alignment=%#x)",
2386 offSignature, WIN_CERTIFICATE_ALIGNMENT);
2387
2388
2389 return VINF_SUCCESS;
2390}
2391
2392
2393/**
2394 * Reads and checks the raw signature data.
2395 *
2396 * @returns IPRT status code.
2397 * @param pModPe The PE module.
2398 * @param ppSignature Where to return the pointer to the parsed
2399 * signature data. Pass to
2400 * rtldrPE_VerifySignatureDestroy when done.
2401 * @param pErrInfo Optional error info buffer.
2402 */
2403static int rtldrPE_VerifySignatureRead(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE *ppSignature, PRTERRINFO pErrInfo)
2404{
2405 *ppSignature = NULL;
2406 AssertReturn(pModPe->SecurityDir.Size > 0, VERR_INTERNAL_ERROR_2);
2407
2408 /*
2409 * Allocate memory for reading and parsing it.
2410 */
2411 if (pModPe->SecurityDir.Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
2412 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2413 "Signature directory is to large: %#x", pModPe->SecurityDir.Size);
2414
2415 PRTLDRPESIGNATURE pSignature = (PRTLDRPESIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
2416 if (!pSignature)
2417 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_NO_MEMORY_SIGNATURE, "Failed to allocate %zu bytes",
2418 sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
2419 pSignature->pRawData = RT_ALIGN_PT(pSignature + 1, 64, WIN_CERTIFICATE const *);
2420
2421
2422 /*
2423 * Read it.
2424 */
2425 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, (void *)pSignature->pRawData,
2426 pModPe->SecurityDir.Size, pModPe->SecurityDir.VirtualAddress);
2427 if (RT_SUCCESS(rc))
2428 {
2429 /*
2430 * Check the table we've read in.
2431 */
2432 uint32_t cbLeft = pModPe->SecurityDir.Size;
2433 WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
2434 for (;;)
2435 {
2436 if ( cbLeft < sizeof(*pEntry)
2437 || pEntry->dwLength > cbLeft
2438 || pEntry->dwLength < sizeof(*pEntry))
2439 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_LENGTH,
2440 "Bad WIN_CERTIFICATE length: %#x (max %#x, signature=%u)",
2441 pEntry->dwLength, cbLeft, 0);
2442 else if (pEntry->wRevision != WIN_CERT_REVISION_2_0)
2443 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_REVISION,
2444 "Unsupported WIN_CERTIFICATE revision value: %#x (signature=%u)",
2445 pEntry->wRevision, 0);
2446 else if (pEntry->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA)
2447 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_TYPE,
2448 "Unsupported WIN_CERTIFICATE certificate type: %#x (signature=%u)",
2449 pEntry->wCertificateType, 0);
2450 else
2451 {
2452 /* advance */
2453 uint32_t cbEntry = RT_ALIGN(pEntry->dwLength, WIN_CERTIFICATE_ALIGNMENT);
2454 if (cbEntry >= cbLeft)
2455 break;
2456 cbLeft -= cbEntry;
2457 pEntry = (WIN_CERTIFICATE *)((uintptr_t)pEntry + cbEntry);
2458
2459 /* For now, only one entry is supported. */
2460 rc = RTErrInfoSet(pErrInfo, VERR_LDRVI_BAD_CERT_MULTIPLE, "Multiple WIN_CERTIFICATE entries are not supported.");
2461 }
2462 break;
2463 }
2464 if (RT_SUCCESS(rc))
2465 {
2466 *ppSignature = pSignature;
2467 return VINF_SUCCESS;
2468 }
2469 }
2470 else
2471 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_SIGNATURE, "Signature read error: %Rrc", rc);
2472 RTMemTmpFree(pSignature);
2473 return rc;
2474}
2475
2476
2477/**
2478 * Destroys the parsed signature.
2479 *
2480 * @param pModPe The PE module.
2481 * @param pSignature The signature data to destroy.
2482 */
2483static void rtldrPE_VerifySignatureDestroy(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature)
2484{
2485 RT_NOREF_PV(pModPe);
2486 RTCrPkcs7ContentInfo_Delete(&pSignature->ContentInfo);
2487 RTMemTmpFree(pSignature);
2488}
2489
2490
2491/**
2492 * Decodes the raw signature.
2493 *
2494 * @returns IPRT status code.
2495 * @param pModPe The PE module.
2496 * @param pSignature The signature data.
2497 * @param pErrInfo Optional error info buffer.
2498 */
2499static int rtldrPE_VerifySignatureDecode(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
2500{
2501 WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
2502 AssertReturn(pEntry->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA, VERR_INTERNAL_ERROR_2);
2503 AssertReturn(pEntry->wRevision == WIN_CERT_REVISION_2_0, VERR_INTERNAL_ERROR_2);
2504 RT_NOREF_PV(pModPe);
2505
2506 RTASN1CURSORPRIMARY PrimaryCursor;
2507 RTAsn1CursorInitPrimary(&PrimaryCursor,
2508 &pEntry->bCertificate[0],
2509 pEntry->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate),
2510 pErrInfo,
2511 &g_RTAsn1DefaultAllocator,
2512 0,
2513 "WinCert");
2514
2515 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, &pSignature->ContentInfo, "CI");
2516 if (RT_SUCCESS(rc))
2517 {
2518 if (RTCrPkcs7ContentInfo_IsSignedData(&pSignature->ContentInfo))
2519 {
2520 pSignature->pSignedData = pSignature->ContentInfo.u.pSignedData;
2521
2522 /*
2523 * Decode the authenticode bits.
2524 */
2525 if (!strcmp(pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
2526 {
2527 pSignature->pIndData = pSignature->pSignedData->ContentInfo.u.pIndirectDataContent;
2528 Assert(pSignature->pIndData);
2529
2530 /*
2531 * Check that things add up.
2532 */
2533 if (RT_SUCCESS(rc))
2534 rc = RTCrPkcs7SignedData_CheckSanity(pSignature->pSignedData,
2535 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
2536 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
2537 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
2538 pErrInfo, "SD");
2539 if (RT_SUCCESS(rc))
2540 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pSignature->pIndData,
2541 pSignature->pSignedData,
2542 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
2543 pErrInfo);
2544 if (RT_SUCCESS(rc))
2545 {
2546 PCRTCRX509ALGORITHMIDENTIFIER pDigestAlgorithm = &pSignature->pIndData->DigestInfo.DigestAlgorithm;
2547 pSignature->enmDigest = RTCrX509AlgorithmIdentifier_QueryDigestType(pDigestAlgorithm);
2548 AssertReturn(pSignature->enmDigest != RTDIGESTTYPE_INVALID, VERR_INTERNAL_ERROR_4); /* Checked above! */
2549 }
2550 }
2551 else
2552 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
2553 "Unknown pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
2554 pSignature->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
2555 }
2556 }
2557 return rc;
2558}
2559
2560
2561static int rtldrPE_VerifyAllPageHashes(PRTLDRMODPE pModPe, PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib, RTDIGESTTYPE enmDigest,
2562 void *pvScratch, size_t cbScratch, PRTERRINFO pErrInfo)
2563{
2564 AssertReturn(cbScratch >= _4K, VERR_INTERNAL_ERROR_3);
2565
2566 /*
2567 * Calculate the special places.
2568 */
2569 RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
2570 int rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
2571 if (RT_FAILURE(rc))
2572 return rc;
2573
2574 uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
2575 uint32_t const cPages = pAttrib->u.pPageHashes->RawData.Asn1Core.cb / (cbHash + 4);
2576 if (cPages * (cbHash + 4) != pAttrib->u.pPageHashes->RawData.Asn1Core.cb)
2577 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_SIZE_OVERFLOW,
2578 "Page hashes size issue: cb=%#x cbHash=%#x",
2579 pAttrib->u.pPageHashes->RawData.Asn1Core.cb, cbHash);
2580
2581 /*
2582 * Walk the table.
2583 */
2584 uint32_t const cbScratchReadMax = cbScratch & ~(uint32_t)(_4K - 1);
2585 uint32_t cbScratchRead = 0;
2586 uint32_t offScratchRead = 0;
2587
2588 uint32_t offPrev = 0;
2589#ifdef COMPLICATED_AND_WRONG
2590 uint32_t offSectEnd = pModPe->cbHeaders;
2591 uint32_t iSh = UINT32_MAX;
2592#endif
2593 uint8_t const *pbHashTab = pAttrib->u.pPageHashes->RawData.Asn1Core.uData.pu8;
2594 for (uint32_t iPage = 0; iPage < cPages - 1; iPage++)
2595 {
2596 /* Decode the page offset. */
2597 uint32_t const offPageInFile = RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]);
2598 if (RT_UNLIKELY(offPageInFile >= SpecialPlaces.cbToHash))
2599 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
2600 "Page hash entry #%u is beyond the signature table start: %#x, %#x",
2601 iPage, offPageInFile, SpecialPlaces.cbToHash);
2602 if (RT_UNLIKELY(offPageInFile < offPrev))
2603 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_NOT_STRICTLY_SORTED,
2604 "Page hash table is not strictly sorted: entry #%u @%#x, previous @%#x\n",
2605 iPage, offPageInFile, offPrev);
2606
2607#ifdef COMPLICATED_AND_WRONG
2608 /* Figure out how much to read and how much to zero. Need keep track
2609 of the on-disk section boundraries. */
2610 if (offPageInFile >= offSectEnd)
2611 {
2612 iSh++;
2613 if ( iSh < pModPe->cSections
2614 && offPageInFile - pModPe->paSections[iSh].PointerToRawData < pModPe->paSections[iSh].SizeOfRawData)
2615 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
2616 else
2617 {
2618 iSh = 0;
2619 while ( iSh < pModPe->cSections
2620 && offPageInFile - pModPe->paSections[iSh].PointerToRawData >= pModPe->paSections[iSh].SizeOfRawData)
2621 iSh++;
2622 if (iSh < pModPe->cSections)
2623 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
2624 else
2625 return RTErrInfoSetF(pErrInfo, VERR_PAGE_HASH_TAB_HASHES_NON_SECTION_DATA,
2626 "Page hash entry #%u isn't in any section: %#x", iPage, offPageInFile);
2627 }
2628 }
2629
2630#else
2631 /* Figure out how much to read and how much take as zero. Use the next
2632 page offset and the signature as upper boundraries. */
2633#endif
2634 uint32_t cbPageInFile = _4K;
2635#ifdef COMPLICATED_AND_WRONG
2636 if (offPageInFile + cbPageInFile > offSectEnd)
2637 cbPageInFile = offSectEnd - offPageInFile;
2638#else
2639 if (iPage + 1 < cPages)
2640 {
2641 uint32_t offNextPage = RT_MAKE_U32_FROM_U8(pbHashTab[0 + 4 + cbHash], pbHashTab[1 + 4 + cbHash],
2642 pbHashTab[2 + 4 + cbHash], pbHashTab[3 + 4 + cbHash]);
2643 if (offNextPage - offPageInFile < cbPageInFile)
2644 cbPageInFile = offNextPage - offPageInFile;
2645 }
2646#endif
2647
2648 if (offPageInFile + cbPageInFile > SpecialPlaces.cbToHash)
2649 cbPageInFile = SpecialPlaces.cbToHash - offPageInFile;
2650
2651 /* Did we get a cache hit? */
2652 uint8_t *pbCur = (uint8_t *)pvScratch;
2653 if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
2654 && offPageInFile >= offScratchRead)
2655 pbCur += offPageInFile - offScratchRead;
2656 /* Missed, read more. */
2657 else
2658 {
2659 offScratchRead = offPageInFile;
2660#ifdef COMPLICATED_AND_WRONG
2661 cbScratchRead = offSectEnd - offPageInFile;
2662#else
2663 cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
2664#endif
2665 if (cbScratchRead > cbScratchReadMax)
2666 cbScratchRead = cbScratchReadMax;
2667 rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
2668 if (RT_FAILURE(rc))
2669 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH,
2670 "Page hash read error at %#x: %Rrc (cbScratchRead=%#zx)",
2671 offScratchRead, rc, cbScratchRead);
2672 }
2673
2674 /*
2675 * Hash it.
2676 */
2677 RTLDRPEHASHCTXUNION HashCtx;
2678 rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
2679 AssertRCReturn(rc, rc);
2680
2681 /* Deal with special places. */
2682 uint32_t cbLeft = cbPageInFile;
2683 if (offPageInFile < SpecialPlaces.offEndSpecial)
2684 {
2685 uint32_t off = offPageInFile;
2686 if (off < SpecialPlaces.offCksum)
2687 {
2688 /* Hash everything up to the checksum. */
2689 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
2690 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2691 pbCur += cbChunk;
2692 cbLeft -= cbChunk;
2693 off += cbChunk;
2694 }
2695
2696 if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
2697 {
2698 /* Skip the checksum */
2699 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
2700 pbCur += cbChunk;
2701 cbLeft -= cbChunk;
2702 off += cbChunk;
2703 }
2704
2705 if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
2706 {
2707 /* Hash everything between the checksum and the data dir entry. */
2708 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
2709 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2710 pbCur += cbChunk;
2711 cbLeft -= cbChunk;
2712 off += cbChunk;
2713 }
2714
2715 if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
2716 {
2717 /* Skip the security data directory entry. */
2718 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
2719 pbCur += cbChunk;
2720 cbLeft -= cbChunk;
2721 off += cbChunk;
2722 }
2723 }
2724
2725 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
2726 if (cbPageInFile < _4K)
2727 rtLdrPE_HashUpdate(&HashCtx, enmDigest, &g_abRTZero4K[cbPageInFile], _4K - cbPageInFile);
2728
2729 /*
2730 * Finish the hash calculation and compare the result.
2731 */
2732 RTLDRPEHASHRESUNION HashRes;
2733 rtLdrPE_HashFinalize(&HashCtx, enmDigest, &HashRes);
2734
2735 pbHashTab += 4;
2736 if (memcmp(pbHashTab, &HashRes, cbHash) != 0)
2737 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
2738 "Page hash failed for page #%u, @%#x, %#x bytes: %.*Rhxs != %.*Rhxs",
2739 iPage, offPageInFile, cbPageInFile, (size_t)cbHash, pbHashTab, (size_t)cbHash, &HashRes);
2740 pbHashTab += cbHash;
2741 offPrev = offPageInFile;
2742 }
2743
2744 /*
2745 * Check that the last table entry has a hash value of zero.
2746 */
2747 if (!ASMMemIsZero(pbHashTab + 4, cbHash))
2748 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
2749 "Maltform final page hash table entry: #%u %#010x %.*Rhxs",
2750 cPages - 1, RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]),
2751 (size_t)cbHash, pbHashTab + 4);
2752 return VINF_SUCCESS;
2753}
2754
2755
2756/**
2757 * Validates the image hash, including page hashes if present.
2758 *
2759 * @returns IPRT status code.
2760 * @param pModPe The PE module.
2761 * @param pSignature The decoded signature data.
2762 * @param pErrInfo Optional error info buffer.
2763 */
2764static int rtldrPE_VerifySignatureValidateHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
2765{
2766 AssertReturn(pSignature->enmDigest > RTDIGESTTYPE_INVALID && pSignature->enmDigest < RTDIGESTTYPE_END, VERR_INTERNAL_ERROR_4);
2767 AssertPtrReturn(pSignature->pIndData, VERR_INTERNAL_ERROR_5);
2768 AssertReturn(RTASN1CORE_IS_PRESENT(&pSignature->pIndData->DigestInfo.Digest.Asn1Core), VERR_INTERNAL_ERROR_5);
2769 AssertPtrReturn(pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, VERR_INTERNAL_ERROR_5);
2770
2771 uint32_t const cbHash = rtLdrPE_HashGetHashSize(pSignature->enmDigest);
2772 AssertReturn(pSignature->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash, VERR_INTERNAL_ERROR_5);
2773
2774 /*
2775 * Allocate a temporary memory buffer.
2776 * Note! The _4K that gets subtracted is to avoid that the 16-byte heap
2777 * block header in ring-0 (iprt) caused any unnecessary internal
2778 * heap fragmentation.
2779 */
2780#ifdef IN_RING0
2781 uint32_t cbScratch = _256K - _4K;
2782#else
2783 uint32_t cbScratch = _1M;
2784#endif
2785 void *pvScratch = RTMemTmpAlloc(cbScratch);
2786 if (!pvScratch)
2787 {
2788 cbScratch = _4K;
2789 pvScratch = RTMemTmpAlloc(cbScratch);
2790 if (!pvScratch)
2791 return RTErrInfoSet(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate 4KB of scratch space for hashing image.");
2792 }
2793
2794 /*
2795 * Calculate and compare the full image hash.
2796 */
2797 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, pSignature->enmDigest,
2798 &pSignature->HashCtx, &pSignature->HashRes, pErrInfo);
2799 if (RT_SUCCESS(rc))
2800 {
2801 if (!memcmp(&pSignature->HashRes, pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash))
2802 {
2803 /*
2804 * Compare the page hashes if present.
2805 *
2806 * Seems the difference between V1 and V2 page hash attributes is
2807 * that v1 uses SHA-1 while v2 uses SHA-256. The data structures to
2808 * be identical otherwise. Initially we assumed the digest
2809 * algorithm was supposed to be RTCRSPCINDIRECTDATACONTENT::DigestInfo,
2810 * i.e. the same as for the whole image hash. The initial approach
2811 * worked just fine, but this makes more sense.
2812 *
2813 * (See also comments in osslsigncode.c (google it).)
2814 */
2815 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib;
2816 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pSignature->pIndData,
2817 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2);
2818 if (pAttrib)
2819 rc = rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA256, pvScratch, cbScratch, pErrInfo);
2820 else
2821 {
2822 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pSignature->pIndData,
2823 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1);
2824 if (pAttrib)
2825 rc = rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA1, pvScratch, cbScratch, pErrInfo);
2826 }
2827 }
2828 else
2829 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
2830 "Full image signature mismatch: %.*Rhxs, expected %.*Rhxs",
2831 cbHash, &pSignature->HashRes,
2832 cbHash, pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
2833 }
2834
2835 RTMemTmpFree(pvScratch);
2836 return rc;
2837}
2838
2839#endif /* !IPRT_WITHOUT_LDR_VERIFY */
2840
2841
2842/** @interface_method_impl{RTLDROPS,pfnVerifySignature} */
2843static DECLCALLBACK(int) rtldrPE_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser,
2844 PRTERRINFO pErrInfo)
2845{
2846#ifndef IPRT_WITHOUT_LDR_VERIFY
2847 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2848
2849 int rc = rtldrPE_VerifySignatureImagePrecoditions(pModPe, pErrInfo);
2850 if (RT_SUCCESS(rc))
2851 {
2852 PRTLDRPESIGNATURE pSignature = NULL;
2853 rc = rtldrPE_VerifySignatureRead(pModPe, &pSignature, pErrInfo);
2854 if (RT_SUCCESS(rc))
2855 {
2856 rc = rtldrPE_VerifySignatureDecode(pModPe, pSignature, pErrInfo);
2857 if (RT_SUCCESS(rc))
2858 rc = rtldrPE_VerifySignatureValidateHash(pModPe, pSignature, pErrInfo);
2859 if (RT_SUCCESS(rc))
2860 {
2861 rc = pfnCallback(&pModPe->Core, RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA,
2862 &pSignature->ContentInfo, sizeof(pSignature->ContentInfo),
2863 pErrInfo, pvUser);
2864 }
2865 rtldrPE_VerifySignatureDestroy(pModPe, pSignature);
2866 }
2867 }
2868 return rc;
2869#else
2870 RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
2871 return VERR_NOT_SUPPORTED;
2872#endif
2873}
2874
2875
2876
2877/** @interface_method_impl{RTLDROPS,pfnHashImage} */
2878static DECLCALLBACK(int) rtldrPE_HashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, char *pszDigest, size_t cbDigest)
2879{
2880 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2881
2882 /*
2883 * Allocate a temporary memory buffer.
2884 */
2885 uint32_t cbScratch = _16K;
2886 void *pvScratch = RTMemTmpAlloc(cbScratch);
2887 if (!pvScratch)
2888 {
2889 cbScratch = _4K;
2890 pvScratch = RTMemTmpAlloc(cbScratch);
2891 if (!pvScratch)
2892 return VERR_NO_TMP_MEMORY;
2893 }
2894
2895 /*
2896 * Do the hashing.
2897 */
2898 RTLDRPEHASHCTXUNION HashCtx;
2899 RTLDRPEHASHRESUNION HashRes;
2900 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, enmDigest, &HashCtx, &HashRes, NULL);
2901 if (RT_SUCCESS(rc))
2902 {
2903 /*
2904 * Format the digest into as human readable hash string.
2905 */
2906 switch (enmDigest)
2907 {
2908 case RTDIGESTTYPE_SHA512: rc = RTSha512ToString(HashRes.abSha512, pszDigest, cbDigest); break;
2909 case RTDIGESTTYPE_SHA256: rc = RTSha256ToString(HashRes.abSha256, pszDigest, cbDigest); break;
2910 case RTDIGESTTYPE_SHA1: rc = RTSha1ToString(HashRes.abSha1, pszDigest, cbDigest); break;
2911 case RTDIGESTTYPE_MD5: rc = RTMd5ToString(HashRes.abMd5, pszDigest, cbDigest); break;
2912 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2913 }
2914 }
2915 return rc;
2916}
2917
2918
2919/** @interface_method_impl{RTLDROPS,pfnDone} */
2920static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
2921{
2922 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2923 if (pModPe->pvBits)
2924 {
2925 RTMemFree(pModPe->pvBits);
2926 pModPe->pvBits = NULL;
2927 }
2928 return VINF_SUCCESS;
2929}
2930
2931
2932/** @interface_method_impl{RTLDROPS,pfnClose} */
2933static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
2934{
2935 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2936 if (pModPe->paSections)
2937 {
2938 RTMemFree(pModPe->paSections);
2939 pModPe->paSections = NULL;
2940 }
2941 if (pModPe->pvBits)
2942 {
2943 RTMemFree(pModPe->pvBits);
2944 pModPe->pvBits = NULL;
2945 }
2946 return VINF_SUCCESS;
2947}
2948
2949
2950/**
2951 * Operations for a 32-bit PE module.
2952 */
2953static const RTLDROPSPE s_rtldrPE32Ops =
2954{
2955 {
2956 "pe32",
2957 rtldrPEClose,
2958 NULL,
2959 rtldrPEDone,
2960 rtldrPEEnumSymbols,
2961 /* ext */
2962 rtldrPEGetImageSize,
2963 rtldrPEGetBits,
2964 rtldrPERelocate,
2965 rtldrPEGetSymbolEx,
2966 rtldrPE_QueryForwarderInfo,
2967 rtldrPE_EnumDbgInfo,
2968 rtldrPE_EnumSegments,
2969 rtldrPE_LinkAddressToSegOffset,
2970 rtldrPE_LinkAddressToRva,
2971 rtldrPE_SegOffsetToRva,
2972 rtldrPE_RvaToSegOffset,
2973 NULL,
2974 rtldrPE_QueryProp,
2975 rtldrPE_VerifySignature,
2976 rtldrPE_HashImage,
2977 42
2978 },
2979 rtldrPEResolveImports32,
2980 42
2981};
2982
2983
2984/**
2985 * Operations for a 64-bit PE module.
2986 */
2987static const RTLDROPSPE s_rtldrPE64Ops =
2988{
2989 {
2990 "pe64",
2991 rtldrPEClose,
2992 NULL,
2993 rtldrPEDone,
2994 rtldrPEEnumSymbols,
2995 /* ext */
2996 rtldrPEGetImageSize,
2997 rtldrPEGetBits,
2998 rtldrPERelocate,
2999 rtldrPEGetSymbolEx,
3000 rtldrPE_QueryForwarderInfo,
3001 rtldrPE_EnumDbgInfo,
3002 rtldrPE_EnumSegments,
3003 rtldrPE_LinkAddressToSegOffset,
3004 rtldrPE_LinkAddressToRva,
3005 rtldrPE_SegOffsetToRva,
3006 rtldrPE_RvaToSegOffset,
3007 NULL,
3008 rtldrPE_QueryProp,
3009 rtldrPE_VerifySignature,
3010 rtldrPE_HashImage,
3011 42
3012 },
3013 rtldrPEResolveImports64,
3014 42
3015};
3016
3017
3018/**
3019 * Converts the optional header from 32 bit to 64 bit.
3020 * This is a rather simple task, if you start from the right end.
3021 *
3022 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
3023 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
3024 */
3025static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
3026{
3027 /*
3028 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3029 */
3030 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
3031 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
3032
3033 /* from LoaderFlags and out the difference is 4 * 32-bits. */
3034 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
3035 Assert( RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
3036 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
3037 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3038 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3039 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
3040 while (pu32Src >= pu32SrcLast)
3041 *pu32Dst-- = *pu32Src--;
3042
3043 /* the previous 4 fields are 32/64 and needs special attention. */
3044 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
3045 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
3046 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
3047 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
3048 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
3049
3050 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
3051 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
3052 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
3053 */
3054 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
3055 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
3056 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
3057 uint32_t u32ImageBase = pOptHdr32->ImageBase;
3058 pOptHdr64->ImageBase = u32ImageBase;
3059}
3060
3061
3062/**
3063 * Converts the load config directory from 32 bit to 64 bit.
3064 * This is a rather simple task, if you start from the right end.
3065 *
3066 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
3067 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
3068 */
3069static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
3070{
3071 /*
3072 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3073 */
3074 IMAGE_LOAD_CONFIG_DIRECTORY32_V9 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32_V9 volatile *)pLoadCfg;
3075 IMAGE_LOAD_CONFIG_DIRECTORY64_V9 volatile *pLoadCfg64 = pLoadCfg;
3076
3077 pLoadCfg64->AddressOfSomeUnicodeString = pLoadCfg32->AddressOfSomeUnicodeString;
3078 pLoadCfg64->HotPatchTableOffset = pLoadCfg32->HotPatchTableOffset;
3079 pLoadCfg64->GuardRFVerifyStackPointerFunctionPointer = pLoadCfg32->GuardRFVerifyStackPointerFunctionPointer;
3080 pLoadCfg64->Reserved2 = pLoadCfg32->Reserved2;
3081 pLoadCfg64->DynamicValueRelocTableSection = pLoadCfg32->DynamicValueRelocTableSection;
3082 pLoadCfg64->DynamicValueRelocTableOffset = pLoadCfg32->DynamicValueRelocTableOffset;
3083 pLoadCfg64->GuardRFFailureRoutineFunctionPointer = pLoadCfg32->GuardRFFailureRoutineFunctionPointer;
3084 pLoadCfg64->GuardRFFailureRoutine = pLoadCfg32->GuardRFFailureRoutine;
3085 pLoadCfg64->CHPEMetadataPointer = pLoadCfg32->CHPEMetadataPointer;
3086 pLoadCfg64->DynamicValueRelocTable = pLoadCfg32->DynamicValueRelocTable;
3087 pLoadCfg64->GuardLongJumpTargetCount = pLoadCfg32->GuardLongJumpTargetCount;
3088 pLoadCfg64->GuardLongJumpTargetTable = pLoadCfg32->GuardLongJumpTargetTable;
3089 pLoadCfg64->GuardAddressTakenIatEntryCount = pLoadCfg32->GuardAddressTakenIatEntryCount;
3090 pLoadCfg64->GuardAddressTakenIatEntryTable = pLoadCfg32->GuardAddressTakenIatEntryTable;
3091 pLoadCfg64->CodeIntegrity.Reserved = pLoadCfg32->CodeIntegrity.Reserved;
3092 pLoadCfg64->CodeIntegrity.CatalogOffset = pLoadCfg32->CodeIntegrity.CatalogOffset;
3093 pLoadCfg64->CodeIntegrity.Catalog = pLoadCfg32->CodeIntegrity.Catalog;
3094 pLoadCfg64->CodeIntegrity.Flags = pLoadCfg32->CodeIntegrity.Flags;
3095 pLoadCfg64->GuardFlags = pLoadCfg32->GuardFlags;
3096 pLoadCfg64->GuardCFFunctionCount = pLoadCfg32->GuardCFFunctionCount;
3097 pLoadCfg64->GuardCFFunctionTable = pLoadCfg32->GuardCFFunctionTable;
3098 pLoadCfg64->GuardCFDispatchFunctionPointer = pLoadCfg32->GuardCFDispatchFunctionPointer;
3099 pLoadCfg64->GuardCFCCheckFunctionPointer = pLoadCfg32->GuardCFCCheckFunctionPointer;
3100 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
3101 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
3102 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
3103 pLoadCfg64->EditList = pLoadCfg32->EditList;
3104 pLoadCfg64->DependentLoadFlags = pLoadCfg32->DependentLoadFlags;
3105 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
3106 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
3107 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
3108 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
3109 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
3110 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
3111 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
3112 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
3113 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
3114 /* the rest is equal. */
3115 Assert( RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
3116 == RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
3117}
3118
3119
3120/**
3121 * Translate the PE/COFF machine name to a string.
3122 *
3123 * @returns Name string (read-only).
3124 * @param uMachine The PE/COFF machine.
3125 */
3126static const char *rtldrPEGetArchName(uint16_t uMachine)
3127{
3128 switch (uMachine)
3129 {
3130 case IMAGE_FILE_MACHINE_I386: return "X86_32";
3131 case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
3132
3133 case IMAGE_FILE_MACHINE_UNKNOWN: return "UNKNOWN";
3134 case IMAGE_FILE_MACHINE_AM33: return "AM33";
3135 case IMAGE_FILE_MACHINE_ARM: return "ARM";
3136 case IMAGE_FILE_MACHINE_THUMB: return "THUMB";
3137 case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT";
3138 case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
3139 case IMAGE_FILE_MACHINE_EBC: return "EBC";
3140 case IMAGE_FILE_MACHINE_IA64: return "IA64";
3141 case IMAGE_FILE_MACHINE_M32R: return "M32R";
3142 case IMAGE_FILE_MACHINE_MIPS16: return "MIPS16";
3143 case IMAGE_FILE_MACHINE_MIPSFPU: return "MIPSFPU";
3144 case IMAGE_FILE_MACHINE_MIPSFPU16: return "MIPSFPU16";
3145 case IMAGE_FILE_MACHINE_WCEMIPSV2: return "WCEMIPSV2";
3146 case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
3147 case IMAGE_FILE_MACHINE_POWERPCFP: return "POWERPCFP";
3148 case IMAGE_FILE_MACHINE_R4000: return "R4000";
3149 case IMAGE_FILE_MACHINE_SH3: return "SH3";
3150 case IMAGE_FILE_MACHINE_SH3DSP: return "SH3DSP";
3151 case IMAGE_FILE_MACHINE_SH4: return "SH4";
3152 case IMAGE_FILE_MACHINE_SH5: return "SH5";
3153 default: return "UnknownMachine";
3154 }
3155}
3156
3157
3158/**
3159 * Validates the file header.
3160 *
3161 * @returns iprt status code.
3162 * @param pFileHdr Pointer to the file header that needs validating.
3163 * @param fFlags Valid RTLDR_O_XXX combination.
3164 * @param pszLogName The log name to prefix the errors with.
3165 * @param penmArch Where to store the CPU architecture.
3166 * @param pErrInfo Where to return additional error information.
3167 */
3168static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName,
3169 PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
3170{
3171 RT_NOREF_PV(pszLogName);
3172
3173 size_t cbOptionalHeader;
3174 switch (pFileHdr->Machine)
3175 {
3176 case IMAGE_FILE_MACHINE_I386:
3177 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
3178 *penmArch = RTLDRARCH_X86_32;
3179 break;
3180 case IMAGE_FILE_MACHINE_AMD64:
3181 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
3182 *penmArch = RTLDRARCH_AMD64;
3183 break;
3184
3185 default:
3186 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n", pszLogName, pFileHdr->Machine));
3187 *penmArch = RTLDRARCH_INVALID;
3188 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unsupported Machine=%#x", pFileHdr->Machine);
3189 }
3190 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
3191 {
3192 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n", pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
3193 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfOptionalHeader=%#x expected %#x",
3194 pFileHdr->SizeOfOptionalHeader, cbOptionalHeader);
3195 }
3196 /* This restriction needs to be implemented elsewhere. */
3197 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
3198 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
3199 {
3200 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
3201 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "IMAGE_FILE_RELOCS_STRIPPED");
3202 }
3203 if (pFileHdr->NumberOfSections > 42)
3204 {
3205 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
3206 pszLogName, pFileHdr->NumberOfSections));
3207 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections=%d, implementation max is 42", pFileHdr->NumberOfSections);
3208 }
3209 if (pFileHdr->NumberOfSections < 1)
3210 {
3211 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
3212 pszLogName, pFileHdr->NumberOfSections));
3213 return RTERRINFO_LOG_SET(pErrInfo, VERR_BAD_EXE_FORMAT, "Image has no sections");
3214 }
3215 return VINF_SUCCESS;
3216}
3217
3218
3219/**
3220 * Validates the optional header (64/32-bit)
3221 *
3222 * @returns iprt status code.
3223 * @param pOptHdr Pointer to the optional header which needs validation.
3224 * @param pszLogName The log name to prefix the errors with.
3225 * @param offNtHdrs The offset of the NT headers from the start of the file.
3226 * @param pFileHdr Pointer to the file header (valid).
3227 * @param cbRawImage The raw image size.
3228 * @param fFlags Loader flags, RTLDR_O_XXX.
3229 * @param pErrInfo Where to return additional error information.
3230 */
3231static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
3232 const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage, uint32_t fFlags, PRTERRINFO pErrInfo)
3233{
3234 RT_NOREF_PV(pszLogName);
3235
3236 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
3237 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
3238 if (pOptHdr->Magic != CorrectMagic)
3239 {
3240 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
3241 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Magic=%#x, expected %#x", pOptHdr->Magic, CorrectMagic);
3242 }
3243 const uint32_t cbImage = pOptHdr->SizeOfImage;
3244 if (cbImage > _1G)
3245 {
3246 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
3247 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x - Our limit is 1GB (%#x)", cbImage, _1G);
3248 }
3249 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
3250 if (cbImage < cbMinImageSize)
3251 {
3252 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
3253 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x to small, minimum %#x", cbImage, cbMinImageSize);
3254 }
3255 if (pOptHdr->AddressOfEntryPoint >= cbImage)
3256 {
3257 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
3258 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
3259 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3260 "AddressOfEntryPoint=%#x - beyond image size (%#x)", pOptHdr->AddressOfEntryPoint, cbImage);
3261 }
3262 if (pOptHdr->BaseOfCode >= cbImage)
3263 {
3264 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
3265 pszLogName, pOptHdr->BaseOfCode, cbImage));
3266 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3267 "BaseOfCode=%#x - beyond image size (%#x)", pOptHdr->BaseOfCode, cbImage);
3268 }
3269#if 0/* only in 32-bit header */
3270 if (pOptHdr->BaseOfData >= cbImage)
3271 {
3272 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
3273 pszLogName, pOptHdr->BaseOfData, cbImage));
3274 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "BaseOfData=%#x - beyond image size (%#x)", pOptHdr->BaseOfData, cbImage);
3275 }
3276#endif
3277 if (pOptHdr->SizeOfHeaders >= cbImage)
3278 {
3279 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
3280 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
3281 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3282 "SizeOfHeaders=%#x - beyond image size (%#x)", pOptHdr->SizeOfHeaders, cbImage);
3283 }
3284 /* don't know how to do the checksum, so ignore it. */
3285 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
3286 {
3287 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
3288 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Subsystem=%#x (unknown)", pOptHdr->Subsystem);
3289 }
3290 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
3291 {
3292 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
3293 pszLogName, pOptHdr->SizeOfHeaders,
3294 cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
3295 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
3296 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx",
3297 pOptHdr->SizeOfHeaders, cbMinImageSize,
3298 pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
3299 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) );
3300 }
3301 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
3302 {
3303 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
3304 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
3305 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x",
3306 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
3307 }
3308 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
3309 {
3310 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
3311 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
3312 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x\n",
3313 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
3314 }
3315
3316 /* DataDirectory */
3317 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
3318 {
3319 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
3320 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfRvaAndSizes=%d, expected %d",
3321 pOptHdr->NumberOfRvaAndSizes, RT_ELEMENTS(pOptHdr->DataDirectory));
3322 }
3323 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
3324 {
3325 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
3326 if (!pDir->Size)
3327 continue;
3328 size_t cb = cbImage;
3329 switch (i)
3330 {
3331 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
3332 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
3333 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
3334 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
3335 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
3336 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
3337 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
3338 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
3339 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
3340 break;
3341 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
3342 /* Delay inspection after section table is validated. */
3343 break;
3344
3345 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
3346 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3347 break;
3348 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3349 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3350 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_DELAY_IMPORT,
3351 "DELAY_IMPORT VirtualAddress=%#x Size=%#x: not supported", pDir->VirtualAddress, pDir->Size);
3352
3353 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
3354 /* The VirtualAddress is a PointerToRawData. */
3355 cb = (size_t)cbRawImage; Assert((RTFOFF)cb == cbRawImage);
3356 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3357 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3358 if (pDir->Size < sizeof(WIN_CERTIFICATE))
3359 {
3360 Log(("rtldrPEOpen: %s: Security directory #%u is too small: %#x bytes\n", pszLogName, i, pDir->Size));
3361 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3362 "Security directory is too small: %#x bytes", pDir->Size);
3363 }
3364 if (pDir->Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
3365 {
3366 Log(("rtldrPEOpen: %s: Security directory #%u is too large: %#x bytes\n", pszLogName, i, pDir->Size));
3367 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3368 "Security directory is too large: %#x bytes", pDir->Size);
3369 }
3370 if (pDir->VirtualAddress & 7)
3371 {
3372 Log(("rtldrPEOpen: %s: Security directory #%u is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
3373 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3374 "Security directory is misaligned: %#x", pDir->VirtualAddress);
3375 }
3376 /* When using the in-memory reader with a debugger, we may get
3377 into trouble here since we might not have access to the whole
3378 physical file. So skip the tests below. Makes VBoxGuest.sys
3379 load and check out just fine, for instance. */
3380 if (fFlags & RTLDR_O_FOR_DEBUG)
3381 continue;
3382 break;
3383
3384 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
3385 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3386 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3387 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_GLOBALPTR, "GLOBALPTR VirtualAddress=%#x Size=%#x: not supported",
3388 pDir->VirtualAddress, pDir->Size);
3389
3390 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
3391 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3392 break;
3393 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3394 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3395 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_TLS, "TLS VirtualAddress=%#x Size=%#x: not supported",
3396 pDir->VirtualAddress, pDir->Size);
3397
3398 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
3399 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3400 break;
3401 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3402 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3403 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_COM_DESCRIPTOR,
3404 "COM_DESCRIPTOR VirtualAddress=%#x Size=%#x: not supported",
3405 pDir->VirtualAddress, pDir->Size);
3406
3407 default:
3408 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
3409 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3410 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x Size=%#x is not supported",
3411 i, pDir->VirtualAddress, pDir->Size);
3412 }
3413 if (pDir->VirtualAddress >= cb)
3414 {
3415 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
3416 pszLogName, i, pDir->VirtualAddress, cb));
3417 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x is invalid (limit %#x)",
3418 i, pDir->VirtualAddress, cb);
3419 }
3420 if (pDir->Size > cb - pDir->VirtualAddress)
3421 {
3422 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
3423 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
3424 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)",
3425 i, pDir->Size, pDir->VirtualAddress, cb);
3426 }
3427 }
3428 return VINF_SUCCESS;
3429}
3430
3431
3432/**
3433 * Validates and touch up the section headers.
3434 *
3435 * The touching up is restricted to setting the VirtualSize field for old-style
3436 * linkers that sets it to zero.
3437 *
3438 * @returns iprt status code.
3439 * @param paSections Pointer to the array of sections that is to be validated.
3440 * @param cSections Number of sections in that array.
3441 * @param pszLogName The log name to prefix the errors with.
3442 * @param pOptHdr Pointer to the optional header (valid).
3443 * @param cbRawImage The raw image size.
3444 * @param fFlags Loader flags, RTLDR_O_XXX.
3445 * @param fNoCode Verify that the image contains no code.
3446 */
3447static int rtldrPEValidateAndTouchUpSectionHeaders(IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
3448 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage, uint32_t fFlags,
3449 bool fNoCode)
3450{
3451 RT_NOREF_PV(pszLogName);
3452
3453 /*
3454 * Do a quick pass to detect linker setting VirtualSize to zero.
3455 */
3456 bool fFixupVirtualSize = true;
3457 IMAGE_SECTION_HEADER *pSH = &paSections[0];
3458 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
3459 if ( pSH->Misc.VirtualSize != 0
3460 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3461 {
3462 fFixupVirtualSize = false;
3463 break;
3464 }
3465
3466 /*
3467 * Actual pass.
3468 */
3469 const uint32_t cbImage = pOptHdr->SizeOfImage;
3470 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
3471 pSH = &paSections[0];
3472 Log3(("RTLdrPE: Section Headers:\n"));
3473 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
3474 {
3475 const unsigned iSH = (unsigned)(pSH - &paSections[0]); NOREF(iSH);
3476 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
3477 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
3478 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
3479 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
3480 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
3481 iSH, pSH->Name, pSH->Characteristics,
3482 pSH->VirtualAddress, pSH->Misc.VirtualSize,
3483 pSH->PointerToRawData, pSH->SizeOfRawData,
3484 pSH->PointerToRelocations, pSH->NumberOfRelocations,
3485 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
3486
3487 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
3488 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
3489 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
3490 {
3491 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
3492 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
3493 return VERR_BAD_EXE_FORMAT;
3494 }
3495
3496 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
3497 || pSH->SizeOfRawData > cbRawImage
3498 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
3499 {
3500 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#x) - section #%d '%.*s'!!!\n",
3501 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
3502 iSH, sizeof(pSH->Name), pSH->Name));
3503 return VERR_BAD_EXE_FORMAT;
3504 }
3505
3506 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
3507 {
3508 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
3509 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
3510 return VERR_BAD_EXE_FORMAT;
3511 }
3512
3513 if (!(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
3514 {
3515 /* Calc VirtualSize if necessary. This is for internal reasons. */
3516 if ( pSH->Misc.VirtualSize == 0
3517 && fFixupVirtualSize)
3518 {
3519 pSH->Misc.VirtualSize = cbImage - RT_MIN(pSH->VirtualAddress, cbImage);
3520 for (uint32_t i = 1; i < cSHdrsLeft; i++)
3521 if ( !(pSH[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
3522 && pSH[i].VirtualAddress >= pSH->VirtualAddress)
3523 {
3524 pSH->Misc.VirtualSize = RT_MIN(pSH[i].VirtualAddress - pSH->VirtualAddress, pSH->Misc.VirtualSize);
3525 break;
3526 }
3527 }
3528
3529 if (pSH->Misc.VirtualSize > 0)
3530 {
3531 if (pSH->VirtualAddress < uRvaPrev)
3532 {
3533 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
3534 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
3535 return VERR_BAD_EXE_FORMAT;
3536 }
3537 if (pSH->VirtualAddress > cbImage)
3538 {
3539 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
3540 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
3541 return VERR_BAD_EXE_FORMAT;
3542 }
3543
3544 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
3545 {
3546 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
3547 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
3548 return VERR_BAD_EXE_FORMAT;
3549 }
3550
3551#ifdef PE_FILE_OFFSET_EQUALS_RVA
3552 /* Our loader code assume rva matches the file offset. */
3553 if ( pSH->SizeOfRawData
3554 && pSH->PointerToRawData != pSH->VirtualAddress)
3555 {
3556 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
3557 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
3558 return VERR_BAD_EXE_FORMAT;
3559 }
3560#endif
3561
3562 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
3563 }
3564 }
3565
3566 /* ignore the relocations and linenumbers. */
3567 }
3568
3569 /*
3570 * Do a separate run if we need to validate the no-code claim from the
3571 * optional header.
3572 */
3573 if (fNoCode)
3574 {
3575 pSH = &paSections[0];
3576 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
3577 if (pSH->Characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE))
3578 return VERR_LDR_ARCH_MISMATCH;
3579 }
3580
3581
3582 /** @todo r=bird: more sanity checks! */
3583 return VINF_SUCCESS;
3584}
3585
3586
3587/**
3588 * Reads image data by RVA using the section headers.
3589 *
3590 * @returns iprt status code.
3591 * @param pModPe The PE module instance.
3592 * @param pvBuf Where to store the bits.
3593 * @param cb Number of bytes to tread.
3594 * @param RVA Where to read from.
3595 */
3596static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
3597{
3598 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
3599 PRTLDRREADER pReader = pModPe->Core.pReader;
3600 uint32_t cbRead;
3601 int rc;
3602
3603 /*
3604 * Is it the headers, i.e. prior to the first section.
3605 */
3606 if (RVA < pModPe->cbHeaders)
3607 {
3608 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
3609 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
3610 if ( cbRead == cb
3611 || RT_FAILURE(rc))
3612 return rc;
3613 cb -= cbRead;
3614 RVA += cbRead;
3615 pvBuf = (uint8_t *)pvBuf + cbRead;
3616 }
3617
3618 /* In the zero space between headers and the first section? */
3619 if (RVA < pSH->VirtualAddress)
3620 {
3621 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
3622 memset(pvBuf, 0, cbRead);
3623 if (cbRead == cb)
3624 return VINF_SUCCESS;
3625 cb -= cbRead;
3626 RVA += cbRead;
3627 pvBuf = (uint8_t *)pvBuf + cbRead;
3628 }
3629
3630 /*
3631 * Iterate the sections.
3632 */
3633 for (unsigned cLeft = pModPe->cSections;
3634 cLeft > 0;
3635 cLeft--, pSH++)
3636 {
3637 uint32_t off = RVA - pSH->VirtualAddress;
3638 if (off < pSH->Misc.VirtualSize)
3639 {
3640 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
3641 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
3642 if ( cbRead == cb
3643 || RT_FAILURE(rc))
3644 return rc;
3645 cb -= cbRead;
3646 RVA += cbRead;
3647 pvBuf = (uint8_t *)pvBuf + cbRead;
3648 }
3649 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
3650 if (RVA < RVANext)
3651 {
3652 cbRead = RT_MIN(RVANext - RVA, cb);
3653 memset(pvBuf, 0, cbRead);
3654 if (cbRead == cb)
3655 return VINF_SUCCESS;
3656 cb -= cbRead;
3657 RVA += cbRead;
3658 pvBuf = (uint8_t *)pvBuf + cbRead;
3659 }
3660 }
3661
3662 AssertFailed();
3663 return VERR_INTERNAL_ERROR;
3664}
3665
3666
3667/**
3668 * Validates the data of some selected data directories entries and remember
3669 * important bits for later.
3670 *
3671 * This requires a valid section table and thus has to wait till after we've
3672 * read and validated it.
3673 *
3674 * @returns iprt status code.
3675 * @param pModPe The PE module instance.
3676 * @param pOptHdr Pointer to the optional header (valid).
3677 * @param fFlags Loader flags, RTLDR_O_XXX.
3678 * @param pErrInfo Where to return extended error information. Optional.
3679 */
3680static int rtldrPEValidateDirectoriesAndRememberStuff(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags,
3681 PRTERRINFO pErrInfo)
3682{
3683 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
3684 union /* combine stuff we're reading to help reduce stack usage. */
3685 {
3686 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
3687 uint8_t abZeros[sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7) * 4];
3688 } u;
3689
3690 /*
3691 * The load config entry may include lock prefix tables and whatnot which we don't implement.
3692 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
3693 * actual data before we can make up our mind about it all.
3694 */
3695 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
3696 if (Dir.Size)
3697 {
3698 const size_t cbExpectV9 = !pModPe->f64Bit
3699 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V9)
3700 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V9);
3701 const size_t cbExpectV8 = !pModPe->f64Bit
3702 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V8)
3703 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V8);
3704 const size_t cbExpectV7 = !pModPe->f64Bit
3705 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V7)
3706 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7);
3707 const size_t cbExpectV6 = !pModPe->f64Bit
3708 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V6)
3709 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V6);
3710 const size_t cbExpectV5 = !pModPe->f64Bit
3711 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V5)
3712 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V5);
3713 const size_t cbExpectV4 = !pModPe->f64Bit
3714 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V4)
3715 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V4);
3716 const size_t cbExpectV3 = !pModPe->f64Bit
3717 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V3)
3718 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V3);
3719 const size_t cbExpectV2 = !pModPe->f64Bit
3720 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V2)
3721 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2);
3722 const size_t cbExpectV1 = !pModPe->f64Bit
3723 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V1)
3724 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2) /*No V1*/;
3725 const size_t cbNewHack = cbExpectV5; /* Playing safe here since there might've been revisions between V5 and V6 we don't know about . */
3726 const size_t cbMaxKnown = cbExpectV9;
3727
3728 bool fNewerStructureHack = false;
3729 if ( Dir.Size != cbExpectV9
3730 && Dir.Size != cbExpectV8
3731 && Dir.Size != cbExpectV7
3732 && Dir.Size != cbExpectV6
3733 && Dir.Size != cbExpectV5
3734 && Dir.Size != cbExpectV4
3735 && Dir.Size != cbExpectV3
3736 && Dir.Size != cbExpectV2
3737 && Dir.Size != cbExpectV1)
3738 {
3739 fNewerStructureHack = Dir.Size > cbNewHack /* These structure changes are slowly getting to us! More futher down. */
3740 && Dir.Size <= sizeof(u);
3741 Log(("rtldrPEOpen: %s: load cfg dir: unexpected dir size of %u bytes, expected %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu.%s\n",
3742 pszLogName, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1,
3743 fNewerStructureHack ? " Will try ignore extra bytes if all zero." : ""));
3744 if (!fNewerStructureHack)
3745 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
3746 "Unexpected load config dir size of %u bytes; supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
3747 Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
3748 }
3749
3750 /*
3751 * Read, check new stuff and convert to 64-bit.
3752 *
3753 * If we accepted a newer structure, we check whether the new bits are
3754 * all zero. This PRAYING/ASSUMING that the nothing new weird stuff is
3755 * activated by a zero value and that it'll mostly be unused in areas
3756 * we care about (which has been the case till now).
3757 */
3758 RT_ZERO(u.Cfg64);
3759 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
3760 if (RT_FAILURE(rc))
3761 return rc;
3762 if ( fNewerStructureHack
3763 && Dir.Size > cbMaxKnown
3764 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
3765 {
3766 Log(("rtldrPEOpen: %s: load cfg dir: Unexpected bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
3767 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
3768 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
3769 "Grown load config (%u to %u bytes) includes non-zero bytes: %.*Rhxs",
3770 cbMaxKnown, Dir.Size, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
3771 }
3772 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
3773
3774 if (u.Cfg64.Size != Dir.Size)
3775 {
3776 /* Kludge #1: ntdll.dll from XP seen with Dir.Size=0x40 and Cfg64.Size=0x00. */
3777 if (Dir.Size == 0x40 && u.Cfg64.Size == 0x00 && !pModPe->f64Bit)
3778 {
3779 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the XP kludge.\n",
3780 pszLogName, u.Cfg64.Size, Dir.Size));
3781 u.Cfg64.Size = Dir.Size;
3782 }
3783 /* Kludge #2: This happens a lot. Structure changes, but the linker doesn't get
3784 updated and stores some old size in the directory. Use the header size. */
3785 else if ( u.Cfg64.Size == cbExpectV9
3786 || u.Cfg64.Size == cbExpectV8
3787 || u.Cfg64.Size == cbExpectV7
3788 || u.Cfg64.Size == cbExpectV6
3789 || u.Cfg64.Size == cbExpectV5
3790 || u.Cfg64.Size == cbExpectV4
3791 || u.Cfg64.Size == cbExpectV3
3792 || u.Cfg64.Size == cbExpectV2
3793 || u.Cfg64.Size == cbExpectV1
3794 || (fNewerStructureHack = (u.Cfg64.Size > cbNewHack && u.Cfg64.Size <= sizeof(u))) )
3795 {
3796 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the old linker kludge.\n",
3797 pszLogName, u.Cfg64.Size, Dir.Size));
3798
3799 Dir.Size = u.Cfg64.Size;
3800 uint32_t const uOrgDir = Dir.Size;
3801 RT_ZERO(u.Cfg64);
3802 rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
3803 if (RT_FAILURE(rc))
3804 return rc;
3805 if ( fNewerStructureHack
3806 && Dir.Size > cbMaxKnown
3807 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
3808 {
3809 Log(("rtldrPEOpen: %s: load cfg dir: Unknown bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
3810 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
3811 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
3812 "Grown load config (%u to %u bytes, dir %u) includes non-zero bytes: %.*Rhxs",
3813 cbMaxKnown, Dir.Size, uOrgDir, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
3814 }
3815 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
3816 AssertReturn(u.Cfg64.Size == Dir.Size,
3817 RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE, "Data changed while reading! (%d vs %d)\n",
3818 u.Cfg64.Size, Dir.Size));
3819 }
3820 else
3821 {
3822 Log(("rtldrPEOpen: %s: load cfg hdr: unexpected hdr size of %u bytes (dir %u), expected %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu.\n",
3823 pszLogName, u.Cfg64.Size, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1));
3824 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
3825 "Unexpected load config header size of %u bytes (dir %u); supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
3826 u.Cfg64.Size, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
3827 }
3828 }
3829 if (u.Cfg64.LockPrefixTable && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
3830 {
3831 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
3832 pszLogName, u.Cfg64.LockPrefixTable));
3833 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOCK_PREFIX_TABLE,
3834 "Lock prefix table not supported: %RX64", u.Cfg64.LockPrefixTable);
3835 }
3836#if 0/* this seems to be safe to ignore. */
3837 if ( u.Cfg64.SEHandlerTable
3838 || u.Cfg64.SEHandlerCount)
3839 {
3840 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
3841 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
3842 return VERR_BAD_EXE_FORMAT;
3843 }
3844#endif
3845 if (u.Cfg64.EditList && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
3846 {
3847 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
3848 pszLogName, u.Cfg64.EditList));
3849 return RTErrInfoSetF(pErrInfo, VERR_BAD_EXE_FORMAT, "Load config EditList=%RX64 is not supported", u.Cfg64.EditList);
3850 }
3851 /** @todo GuardCFC? Possibly related to:
3852 * http://research.microsoft.com/pubs/69217/ccs05-cfi.pdf
3853 * Not trusting something designed by bakas who don't know how to modify a
3854 * structure without messing up its natural alignment. */
3855 if ( ( u.Cfg64.GuardCFCCheckFunctionPointer
3856 || u.Cfg64.GuardCFDispatchFunctionPointer
3857 || u.Cfg64.GuardCFFunctionTable
3858 || u.Cfg64.GuardCFFunctionCount
3859 || u.Cfg64.GuardFlags
3860 || u.Cfg64.GuardAddressTakenIatEntryTable
3861 || u.Cfg64.GuardAddressTakenIatEntryCount
3862 || u.Cfg64.GuardLongJumpTargetTable
3863 || u.Cfg64.GuardLongJumpTargetCount)
3864 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) )
3865 {
3866 Log(("rtldrPEOpen: %s: load cfg dir: Guard stuff: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!\n",
3867 pszLogName, u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
3868 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
3869 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
3870 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount ));
3871#if 0 /* ntdll 15002 uses this. */
3872 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_GUARD_CF_STUFF,
3873 "Guard bits in load config: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!",
3874 u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
3875 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
3876 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
3877 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount);
3878#endif
3879 }
3880 }
3881
3882 /*
3883 * If the image is signed and we're not doing this for debug purposes,
3884 * take a look at the signature.
3885 */
3886 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
3887 if (Dir.Size)
3888 {
3889 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
3890 if (!pFirst)
3891 return VERR_NO_TMP_MEMORY;
3892 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
3893 if (RT_SUCCESS(rc))
3894 {
3895 uint32_t off = 0;
3896 do
3897 {
3898 PWIN_CERTIFICATE pCur = (PWIN_CERTIFICATE)((uint8_t *)pFirst + off);
3899
3900 /* validate the members. */
3901 if ( pCur->dwLength < sizeof(WIN_CERTIFICATE)
3902 || pCur->dwLength + off > Dir.Size)
3903 {
3904 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
3905 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3906 "Cert at %#x LB %#x: Bad header length value: %#x", off, Dir.Size, pCur->dwLength);
3907 break;
3908 }
3909 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
3910 && pCur->wRevision != WIN_CERT_REVISION_1_0)
3911 {
3912 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
3913 if (pCur->wRevision >= WIN_CERT_REVISION_1_0)
3914 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
3915 "Cert at %#x LB %#x: Unsupported revision: %#x", off, Dir.Size, pCur->wRevision);
3916 else
3917 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3918 "Cert at %#x LB %#x: Malformed revision: %#x", off, Dir.Size, pCur->wRevision);
3919 break;
3920 }
3921 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
3922 && pCur->wCertificateType != WIN_CERT_TYPE_X509
3923 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
3924 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
3925 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
3926 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
3927 )
3928 {
3929 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wCertificateType=%#x\n", pszLogName, off, Dir.Size, pCur->wCertificateType));
3930 if (pCur->wCertificateType)
3931 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
3932 "Cert at %#x LB %#x: Unsupported certificate type: %#x",
3933 off, Dir.Size, pCur->wCertificateType);
3934 else
3935 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3936 "Cert at %#x LB %#x: Malformed certificate type: %#x",
3937 off, Dir.Size, pCur->wCertificateType);
3938 break;
3939 }
3940
3941 /* Remember the first signed data certificate. */
3942 if ( pCur->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA
3943 && pModPe->offPkcs7SignedData == 0)
3944 {
3945 pModPe->offPkcs7SignedData = Dir.VirtualAddress
3946 + (uint32_t)((uintptr_t)&pCur->bCertificate[0] - (uintptr_t)pFirst);
3947 pModPe->cbPkcs7SignedData = pCur->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
3948 }
3949
3950 /* next */
3951 off += RT_ALIGN(pCur->dwLength, WIN_CERTIFICATE_ALIGNMENT);
3952 } while (off < Dir.Size);
3953 }
3954 RTMemTmpFree(pFirst);
3955 if (RT_FAILURE(rc) && !(fFlags & RTLDR_O_FOR_DEBUG))
3956 return rc;
3957 }
3958
3959 return VINF_SUCCESS;
3960}
3961
3962
3963/**
3964 * Open a PE image.
3965 *
3966 * @returns iprt status code.
3967 * @param pReader The loader reader instance which will provide the raw image bits.
3968 * @param fFlags Loader flags, RTLDR_O_XXX.
3969 * @param enmArch Architecture specifier.
3970 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
3971 * @param phLdrMod Where to store the handle.
3972 * @param pErrInfo Where to return extended error information. Optional.
3973 */
3974int rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs,
3975 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
3976{
3977 /*
3978 * Read and validate the file header.
3979 */
3980 IMAGE_FILE_HEADER FileHdr;
3981 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
3982 if (RT_FAILURE(rc))
3983 return rc;
3984 RTLDRARCH enmArchImage;
3985 const char *pszLogName = pReader->pfnLogName(pReader);
3986 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage, pErrInfo);
3987 if (RT_FAILURE(rc))
3988 return rc;
3989
3990 /*
3991 * Match the CPU architecture.
3992 */
3993 bool fArchNoCodeCheckPending = false;
3994 if ( enmArch != enmArchImage
3995 && ( enmArch != RTLDRARCH_WHATEVER
3996 && !(fFlags & RTLDR_O_WHATEVER_ARCH)) )
3997 {
3998 if (!(fFlags & RTLDR_O_IGNORE_ARCH_IF_NO_CODE))
3999 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Image is for '%s', only accepting images for '%s'.",
4000 rtldrPEGetArchName(FileHdr.Machine), RTLdrArchName(enmArch));
4001 fArchNoCodeCheckPending = true;
4002 }
4003
4004 /*
4005 * Read and validate the "optional" header. Convert 32->64 if necessary.
4006 */
4007 IMAGE_OPTIONAL_HEADER64 OptHdr;
4008 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
4009 if (RT_FAILURE(rc))
4010 return rc;
4011 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
4012 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
4013 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags, pErrInfo);
4014 if (RT_FAILURE(rc))
4015 return rc;
4016 if (fArchNoCodeCheckPending && OptHdr.SizeOfCode != 0)
4017 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH,
4018 "Image is for '%s' and contains code (%#x), only accepting images for '%s' with code.",
4019 rtldrPEGetArchName(FileHdr.Machine), OptHdr.SizeOfCode, RTLdrArchName(enmArch));
4020
4021 /*
4022 * Read and validate section headers.
4023 */
4024 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
4025 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
4026 if (!paSections)
4027 return VERR_NO_MEMORY;
4028 rc = pReader->pfnRead(pReader, paSections, cbSections,
4029 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
4030 if (RT_SUCCESS(rc))
4031 {
4032 rc = rtldrPEValidateAndTouchUpSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
4033 &OptHdr, pReader->pfnSize(pReader), fFlags, fArchNoCodeCheckPending);
4034 if (RT_SUCCESS(rc))
4035 {
4036 /*
4037 * Allocate and initialize the PE module structure.
4038 */
4039 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
4040 if (pModPe)
4041 {
4042 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
4043 pModPe->Core.eState = LDR_STATE_OPENED;
4044 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
4045 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
4046 else
4047 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
4048 pModPe->Core.pReader = pReader;
4049 pModPe->Core.enmFormat= RTLDRFMT_PE;
4050 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
4051 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4052 ? RTLDRTYPE_EXECUTABLE_FIXED
4053 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
4054 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4055 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
4056 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
4057 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
4058 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
4059 ? RTLDRARCH_X86_32
4060 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
4061 ? RTLDRARCH_AMD64
4062 : RTLDRARCH_WHATEVER;
4063 pModPe->pvBits = NULL;
4064 pModPe->offNtHdrs = offNtHdrs;
4065 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
4066 pModPe->u16Machine = FileHdr.Machine;
4067 pModPe->fFile = FileHdr.Characteristics;
4068 pModPe->cSections = FileHdr.NumberOfSections;
4069 pModPe->paSections = paSections;
4070 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
4071 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
4072 pModPe->cbImage = OptHdr.SizeOfImage;
4073 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
4074 pModPe->uTimestamp = FileHdr.TimeDateStamp;
4075 pModPe->cImports = UINT32_MAX;
4076 pModPe->f64Bit = FileHdr.SizeOfOptionalHeader == sizeof(OptHdr);
4077 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
4078 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
4079 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
4080 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
4081 pModPe->SecurityDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4082 pModPe->ExceptionDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
4083 pModPe->fDllCharacteristics = OptHdr.DllCharacteristics;
4084
4085 /*
4086 * Perform validation of some selected data directories which requires
4087 * inspection of the actual data. This also saves some certificate
4088 * information.
4089 */
4090 rc = rtldrPEValidateDirectoriesAndRememberStuff(pModPe, &OptHdr, fFlags, pErrInfo);
4091 if (RT_SUCCESS(rc))
4092 {
4093 *phLdrMod = &pModPe->Core;
4094 return VINF_SUCCESS;
4095 }
4096 RTMemFree(pModPe);
4097 }
4098 else
4099 rc = VERR_NO_MEMORY;
4100 }
4101 }
4102 RTMemFree(paSections);
4103 return rc;
4104}
4105
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette