VirtualBox

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

Last change on this file since 107382 was 106861, checked in by vboxsync, 3 months ago

IPRT/ldrPE.cpp: Don't be so strict about SizeOfStackReserve/SizeOfStackCommit and SizeOfHeapReserve/SizeOfHeapCommit. Ran into this with wsock32.dll from NT 3.1. Also fixed the RTLDRTYPE classification (nobody uses this), which swapped DLLs and EXEs.

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

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