VirtualBox

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

Last change on this file since 95635 was 95635, checked in by vboxsync, 3 years ago

IPRT/ldrPE: Implemented generating page hashes (for using in signing). bugref:8691

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