VirtualBox

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

最後變更 在這個檔案從106726是 106726,由 vboxsync 提交於 5 月 前

IPRT/ldrPE.cpp: Adjustments to new IMAGE_RUNTIME_FUNCTION_ENTRY definitions in pecoff.h. jiraref:VBP-1253

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

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