VirtualBox

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

Last change on this file since 99844 was 99669, checked in by vboxsync, 2 years ago

IPRT/ldrPE: Added support for SHA384 image signatures. [build fix] bugref:10439

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

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