VirtualBox

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

Last change on this file since 106579 was 106248, checked in by vboxsync, 8 weeks ago

IPRT/ldrPE: Fixed bug in the page hashing code getting the offset of the terminator page wrong. bugref:10771

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