VirtualBox

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

Last change on this file since 61936 was 61936, checked in by vboxsync, 9 years ago

nits

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