VirtualBox

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

最後變更 在這個檔案從99668是 99668,由 vboxsync 提交於 23 月 前

IPRT/ldrPE: Added support for SHA384 image signatures. bugref:10439

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

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