VirtualBox

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

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

scm copyright and license note update

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

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