VirtualBox

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

Last change on this file since 102478 was 100442, checked in by vboxsync, 20 months ago

IPRT,OpenSSL: Support ECDSA for verficiation purposes when IPRT links with OpenSSL. This required quite a bit of cleanups, so not entirely no-risk. bugref:10479 ticketref:21621

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