VirtualBox

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

Last change on this file was 103005, checked in by vboxsync, 4 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 214.4 KB
Line 
1/* $Id: ldrPE.cpp 103005 2024-01-23 23:55:58Z vboxsync $ */
2/** @file
3 * IPRT - Binary Image Loader, Portable Executable (PE).
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_LDR
42#include <iprt/ldr.h>
43#include "internal/iprt.h"
44
45#include <iprt/assert.h>
46#include <iprt/asm.h>
47#include <iprt/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
2510 uint32_t const cbScratchReadMax = cbScratch / cbPage * cbPage;
2511 uint32_t cbScratchRead = 0;
2512 uint32_t offScratchRead = 0;
2513
2514 for (;;)
2515 {
2516 /*
2517 * Process the pages in this section.
2518 */
2519 uint32_t cPagesInSection = (cbRawData + cbPage - 1) / cbPage;
2520 for (uint32_t iPage = 0; iPage < cPagesInSection; iPage++)
2521 {
2522 uint32_t const offPageInSect = iPage * cbPage;
2523 uint32_t const offPageInFile = offRawData + offPageInSect;
2524 uint32_t const cbPageInFile = RT_MIN(cbPage, cbRawData - offPageInSect);
2525 offLastPage = offPageInFile;
2526
2527 /* Calculate and output the page offset. */
2528 *(uint32_t *)pbDst = offPageInFile;
2529 pbDst += sizeof(uint32_t);
2530
2531 /*
2532 * Read/find in the raw page.
2533 */
2534 /* Did we get a cache hit? */
2535 uint8_t *pbCur = pbScratch;
2536 if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
2537 && offPageInFile >= offScratchRead)
2538 pbCur += offPageInFile - offScratchRead;
2539 /* Missed, read more. */
2540 else
2541 {
2542 offScratchRead = offPageInFile;
2543 cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
2544 if (cbScratchRead > cbScratchReadMax)
2545 cbScratchRead = cbScratchReadMax;
2546 rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
2547 if (RT_FAILURE(rc))
2548 return VERR_LDRVI_READ_ERROR_HASH;
2549 }
2550
2551 /*
2552 * Hash it.
2553 */
2554 RTLDRPEHASHCTXUNION HashCtx;
2555 rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
2556 AssertRCReturn(rc, rc);
2557
2558 /* Deal with special places. */
2559 uint32_t cbLeft = cbPageInFile;
2560 if (offPageInFile < SpecialPlaces.offEndSpecial)
2561 {
2562 uint32_t off = offPageInFile;
2563 if (off < SpecialPlaces.offCksum)
2564 {
2565 /* Hash everything up to the checksum. */
2566 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
2567 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2568 pbCur += cbChunk;
2569 cbLeft -= cbChunk;
2570 off += cbChunk;
2571 }
2572
2573 if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
2574 {
2575 /* Skip the checksum */
2576 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
2577 pbCur += cbChunk;
2578 cbLeft -= cbChunk;
2579 off += cbChunk;
2580 }
2581
2582 if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
2583 {
2584 /* Hash everything between the checksum and the data dir entry. */
2585 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
2586 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2587 pbCur += cbChunk;
2588 cbLeft -= cbChunk;
2589 off += cbChunk;
2590 }
2591
2592 if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
2593 {
2594 /* Skip the security data directory entry. */
2595 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
2596 pbCur += cbChunk;
2597 cbLeft -= cbChunk;
2598 off += cbChunk;
2599 }
2600 }
2601
2602 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
2603 if (cbPageInFile < cbPage)
2604 rtLdrPE_HashUpdate(&HashCtx, enmDigest, g_abRTZero4K, cbPage - cbPageInFile);
2605
2606 /*
2607 * Finish the hash calculation storing it in the table.
2608 */
2609 rtLdrPE_HashFinalize(&HashCtx, enmDigest, (PRTLDRPEHASHRESUNION)pbDst);
2610 pbDst += cbHash;
2611 }
2612
2613 /*
2614 * Advance to the next section.
2615 */
2616 iSection++;
2617 if (iSection >= cSections)
2618 break;
2619 offRawData = pModPe->paSections[iSection].PointerToRawData;
2620 cbRawData = pModPe->paSections[iSection].SizeOfRawData;
2621 }
2622
2623 /*
2624 * Add the terminator entry.
2625 */
2626 *(uint32_t *)pbDst = offLastPage + cbPage;
2627 RT_BZERO(&pbDst[sizeof(uint32_t)], cbHash);
2628
2629 return VINF_SUCCESS;
2630}
2631
2632
2633/**
2634 * Creates the page hash table for the image.
2635 *
2636 * Used for handling RTLDRPROP_SHA1_PAGE_HASHES and
2637 * RTLDRPROP_SHA256_PAGE_HASHES.
2638 *
2639 * @returns IPRT status code.
2640 * @param pModPe The PE module.
2641 * @param enmDigest The digest to use when hashing the pages.
2642 * @param pvBuf Where to return the page hash table.
2643 * @param cbBuf The size of the buffer @a pvBuf points to.
2644 * @param pcbRet Where to return the output/needed size.
2645 */
2646static int rtLdrPE_QueryPageHashes(PRTLDRMODPE pModPe, RTDIGESTTYPE enmDigest, void *pvBuf, size_t cbBuf, size_t *pcbRet)
2647{
2648 /*
2649 * Check that we've got enough buffer space.
2650 */
2651 uint32_t const cbPage = _4K;
2652 uint32_t const cEntries = rtLdrPE_GetHashablePages(pModPe);
2653 uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
2654 AssertReturn(cbHash > 0, VERR_INTERNAL_ERROR_3);
2655
2656 size_t const cbNeeded = (size_t)(cbHash + 4) * cEntries;
2657 *pcbRet = cbNeeded;
2658 if (cbNeeded > cbBuf)
2659 return VERR_BUFFER_OVERFLOW;
2660
2661 /*
2662 * Allocate a scratch buffer and call worker to do the real job.
2663 */
2664# ifdef IN_RING0
2665 uint32_t cbScratch = _256K - _4K;
2666# else
2667 uint32_t cbScratch = _1M;
2668# endif
2669 void *pvScratch = RTMemTmpAlloc(cbScratch);
2670 if (!pvScratch)
2671 {
2672 cbScratch = _4K;
2673 pvScratch = RTMemTmpAlloc(cbScratch);
2674 if (!pvScratch)
2675 return VERR_NO_TMP_MEMORY;
2676 }
2677
2678 int rc = rtLdrPE_CalcPageHashes(pModPe, enmDigest, cbHash, (uint8_t *)pvBuf, (uint8_t *)pvScratch, cbScratch, cbPage);
2679
2680 RTMemTmpFree(pvScratch);
2681 return rc;
2682}
2683
2684#endif /* !IPRT_WITHOUT_LDR_PAGE_HASHING */
2685#ifndef IPRT_WITHOUT_LDR_VERIFY
2686
2687/**
2688 * Verifies image preconditions not checked by the open validation code.
2689 *
2690 * @returns IPRT status code.
2691 * @param pModPe The PE module.
2692 * @param pErrInfo Optional error info buffer.
2693 */
2694static int rtldrPE_VerifySignatureImagePrecoditions(PRTLDRMODPE pModPe, PRTERRINFO pErrInfo)
2695{
2696 /*
2697 * Validate the sections. While doing so, track the amount of section raw
2698 * section data in the file so we can use this to validate the signature
2699 * table location later.
2700 */
2701 uint32_t offNext = pModPe->cbHeaders; /* same */
2702 for (uint32_t i = 0; i < pModPe->cSections; i++)
2703 if (pModPe->paSections[i].SizeOfRawData > 0)
2704 {
2705 uint64_t offEnd = (uint64_t)pModPe->paSections[i].PointerToRawData + pModPe->paSections[i].SizeOfRawData;
2706 if (offEnd > offNext)
2707 {
2708 if (offEnd >= _2G)
2709 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_SECTION_RAW_DATA_VALUES,
2710 "Section %#u specifies file data after 2GB: PointerToRawData=%#x SizeOfRawData=%#x",
2711 i, pModPe->paSections[i].PointerToRawData, pModPe->paSections[i].SizeOfRawData);
2712 offNext = (uint32_t)offEnd;
2713 }
2714 }
2715 uint32_t offEndOfSectionData = offNext;
2716
2717 /*
2718 * Validate the signature.
2719 */
2720 if (!pModPe->SecurityDir.Size)
2721 return RTErrInfoSet(pErrInfo, VERR_LDRVI_NOT_SIGNED, "Not signed.");
2722
2723 uint32_t const offSignature = pModPe->SecurityDir.VirtualAddress;
2724 uint32_t const cbSignature = pModPe->SecurityDir.Size;
2725 if ( cbSignature <= sizeof(WIN_CERTIFICATE)
2726 || cbSignature >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE
2727 || offSignature >= _2G)
2728 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2729 "Invalid security data dir entry: cb=%#x off=%#x", cbSignature, offSignature);
2730
2731 if (offSignature < offEndOfSectionData)
2732 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2733 "Invalid security data dir entry offset: %#x offEndOfSectionData=%#x",
2734 offSignature, offEndOfSectionData);
2735
2736 if (RT_ALIGN_32(offSignature, WIN_CERTIFICATE_ALIGNMENT) != offSignature)
2737 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2738 "Misaligned security dir entry offset: %#x (alignment=%#x)",
2739 offSignature, WIN_CERTIFICATE_ALIGNMENT);
2740
2741
2742 return VINF_SUCCESS;
2743}
2744
2745
2746/**
2747 * Reads and checks the raw signature data.
2748 *
2749 * @returns IPRT status code.
2750 * @param pModPe The PE module.
2751 * @param ppSignature Where to return the pointer to the parsed
2752 * signature data. Pass to
2753 * rtldrPE_VerifySignatureDestroy when done.
2754 * @param pErrInfo Optional error info buffer.
2755 */
2756static int rtldrPE_VerifySignatureRead(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE *ppSignature, PRTERRINFO pErrInfo)
2757{
2758 *ppSignature = NULL;
2759 AssertReturn(pModPe->SecurityDir.Size > 0, VERR_INTERNAL_ERROR_2);
2760
2761 /*
2762 * Allocate memory for reading and parsing it.
2763 */
2764 if (pModPe->SecurityDir.Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
2765 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_INVALID_SECURITY_DIR_ENTRY,
2766 "Signature directory is to large: %#x", pModPe->SecurityDir.Size);
2767
2768 PRTLDRPESIGNATURE pSignature = (PRTLDRPESIGNATURE)RTMemTmpAllocZ(sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
2769 if (!pSignature)
2770 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_NO_MEMORY_SIGNATURE, "Failed to allocate %zu bytes",
2771 sizeof(*pSignature) + 64 + pModPe->SecurityDir.Size);
2772 pSignature->pRawData = RT_ALIGN_PT(pSignature + 1, 64, WIN_CERTIFICATE const *);
2773
2774
2775 /*
2776 * Read it.
2777 */
2778 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, (void *)pSignature->pRawData,
2779 pModPe->SecurityDir.Size, pModPe->SecurityDir.VirtualAddress);
2780 if (RT_SUCCESS(rc))
2781 {
2782 /*
2783 * Check the table we've read in.
2784 */
2785 uint32_t cbLeft = pModPe->SecurityDir.Size;
2786 WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
2787 for (;;)
2788 {
2789 if ( cbLeft < sizeof(*pEntry)
2790 || pEntry->dwLength > cbLeft
2791 || pEntry->dwLength < sizeof(*pEntry))
2792 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_LENGTH,
2793 "Bad WIN_CERTIFICATE length: %#x (max %#x, signature=%u)",
2794 pEntry->dwLength, cbLeft, 0);
2795 else if (pEntry->wRevision != WIN_CERT_REVISION_2_0)
2796 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_REVISION,
2797 "Unsupported WIN_CERTIFICATE revision value: %#x (signature=%u)",
2798 pEntry->wRevision, 0);
2799 else if (pEntry->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA)
2800 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_BAD_CERT_HDR_TYPE,
2801 "Unsupported WIN_CERTIFICATE certificate type: %#x (signature=%u)",
2802 pEntry->wCertificateType, 0);
2803 else
2804 {
2805 /* advance */
2806 uint32_t cbEntry = RT_ALIGN(pEntry->dwLength, WIN_CERTIFICATE_ALIGNMENT);
2807 if (cbEntry >= cbLeft)
2808 break;
2809 cbLeft -= cbEntry;
2810 pEntry = (WIN_CERTIFICATE *)((uintptr_t)pEntry + cbEntry);
2811
2812 /* For now, only one entry is supported. */
2813 rc = RTErrInfoSet(pErrInfo, VERR_LDRVI_BAD_CERT_MULTIPLE, "Multiple WIN_CERTIFICATE entries are not supported.");
2814 }
2815 break;
2816 }
2817 if (RT_SUCCESS(rc))
2818 {
2819 *ppSignature = pSignature;
2820 return VINF_SUCCESS;
2821 }
2822 }
2823 else
2824 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_SIGNATURE, "Signature read error: %Rrc", rc);
2825 RTMemTmpFree(pSignature);
2826 return rc;
2827}
2828
2829
2830/**
2831 * Destroys the parsed signature.
2832 *
2833 * @param pModPe The PE module.
2834 * @param pSignature The signature data to destroy.
2835 */
2836static void rtldrPE_VerifySignatureDestroy(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature)
2837{
2838 RT_NOREF_PV(pModPe);
2839 RTCrPkcs7ContentInfo_Delete(&pSignature->PrimaryContentInfo);
2840 if (pSignature->paNested)
2841 {
2842 RTMemTmpFree(pSignature->paNested);
2843 pSignature->paNested = NULL;
2844 }
2845 RTMemTmpFree(pSignature);
2846}
2847
2848
2849/**
2850 * Handles nested signatures.
2851 *
2852 * @returns IPRT status code.
2853 * @param pSignature The signature status structure. Returns with
2854 * cNested = 0 and paNested = NULL if no nested
2855 * signatures.
2856 * @param pErrInfo Where to return extended error info (optional).
2857 */
2858static int rtldrPE_VerifySignatureDecodeNested(PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
2859{
2860 Assert(pSignature->cNested == 0);
2861 Assert(pSignature->paNested == NULL);
2862
2863 /*
2864 * Count nested signatures.
2865 */
2866 uint32_t cNested = 0;
2867 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignature->Primary.pSignedData->SignerInfos.cItems; iSignerInfo++)
2868 {
2869 PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignature->Primary.pSignedData->SignerInfos.papItems[iSignerInfo];
2870 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
2871 {
2872 PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib];
2873 if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
2874 {
2875 Assert(pAttrib->uValues.pContentInfos);
2876 cNested += pAttrib->uValues.pContentInfos->cItems;
2877 }
2878 }
2879 }
2880 if (!cNested)
2881 return VINF_SUCCESS;
2882
2883 /*
2884 * Allocate and populate the info structures.
2885 */
2886 pSignature->paNested = (PRTLDRPESIGNATUREONE)RTMemTmpAllocZ(sizeof(pSignature->paNested[0]) * cNested);
2887 if (!pSignature->paNested)
2888 return RTErrInfoSetF(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate space for %u nested signatures", cNested);
2889 pSignature->cNested = cNested;
2890
2891 cNested = 0;
2892 for (uint32_t iSignerInfo = 0; iSignerInfo < pSignature->Primary.pSignedData->SignerInfos.cItems; iSignerInfo++)
2893 {
2894 PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignature->Primary.pSignedData->SignerInfos.papItems[iSignerInfo];
2895 for (uint32_t iAttrib = 0; iAttrib < pSignerInfo->UnauthenticatedAttributes.cItems; iAttrib++)
2896 {
2897 PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->UnauthenticatedAttributes.papItems[iAttrib];
2898 if (pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_MS_NESTED_SIGNATURE)
2899 {
2900 for (uint32_t iItem = 0; iItem < pAttrib->uValues.pContentInfos->cItems; iItem++, cNested++)
2901 {
2902 PRTLDRPESIGNATUREONE pInfo = &pSignature->paNested[cNested];
2903 PRTCRPKCS7CONTENTINFO pContentInfo = pAttrib->uValues.pContentInfos->papItems[iItem];
2904 pInfo->pContentInfo = pContentInfo;
2905 pInfo->iSignature = cNested;
2906
2907 if (RTCrPkcs7ContentInfo_IsSignedData(pInfo->pContentInfo))
2908 { /* likely */ }
2909 else
2910 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
2911 "Nested#%u: PKCS#7 is not 'signedData': %s", cNested, pInfo->pContentInfo->ContentType.szObjId);
2912 PRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
2913 pInfo->pSignedData = pSignedData;
2914
2915 /*
2916 * Check the authenticode bits.
2917 */
2918 if (!strcmp(pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
2919 { /* likely */ }
2920 else
2921 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
2922 "Nested#%u: Unknown pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
2923 cNested, pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
2924 pInfo->pIndData = pSignedData->ContentInfo.u.pIndirectDataContent;
2925 Assert(pInfo->pIndData);
2926
2927 /*
2928 * Check that things add up.
2929 */
2930 int rc = RTCrPkcs7SignedData_CheckSanity(pSignedData,
2931 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
2932 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
2933 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
2934 pErrInfo, "SD");
2935 if (RT_SUCCESS(rc))
2936 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pInfo->pIndData,
2937 pSignedData,
2938 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
2939 pErrInfo);
2940 if (RT_SUCCESS(rc))
2941 {
2942 PCRTCRX509ALGORITHMIDENTIFIER pDigestAlgorithm = &pInfo->pIndData->DigestInfo.DigestAlgorithm;
2943 pInfo->enmDigest = RTCrX509AlgorithmIdentifier_GetDigestType(pDigestAlgorithm,
2944 true /*fPureDigestsOnly*/);
2945 AssertReturn(pInfo->enmDigest != RTDIGESTTYPE_INVALID, VERR_INTERNAL_ERROR_4); /* Checked above! */
2946 }
2947 else
2948 return rc;
2949 }
2950 }
2951 }
2952 }
2953
2954 return VINF_SUCCESS;
2955}
2956
2957
2958/**
2959 * Decodes the raw signature.
2960 *
2961 * @returns IPRT status code.
2962 * @param pModPe The PE module.
2963 * @param pSignature The signature data.
2964 * @param pErrInfo Optional error info buffer.
2965 */
2966static int rtldrPE_VerifySignatureDecode(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
2967{
2968 WIN_CERTIFICATE const *pEntry = pSignature->pRawData;
2969 AssertReturn(pEntry->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA, VERR_INTERNAL_ERROR_2);
2970 AssertReturn(pEntry->wRevision == WIN_CERT_REVISION_2_0, VERR_INTERNAL_ERROR_2);
2971 RT_NOREF_PV(pModPe);
2972
2973 RTASN1CURSORPRIMARY PrimaryCursor;
2974 RTAsn1CursorInitPrimary(&PrimaryCursor,
2975 &pEntry->bCertificate[0],
2976 pEntry->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate),
2977 pErrInfo,
2978 &g_RTAsn1DefaultAllocator,
2979 0,
2980 "WinCert");
2981
2982 PRTLDRPESIGNATUREONE pInfo = &pSignature->Primary;
2983 pInfo->pContentInfo = &pSignature->PrimaryContentInfo;
2984 int rc = RTCrPkcs7ContentInfo_DecodeAsn1(&PrimaryCursor.Cursor, 0, pInfo->pContentInfo, "CI");
2985 if (RT_SUCCESS(rc))
2986 {
2987 if (RTCrPkcs7ContentInfo_IsSignedData(pInfo->pContentInfo))
2988 {
2989 pInfo->pSignedData = pInfo->pContentInfo->u.pSignedData;
2990
2991 /*
2992 * Decode the authenticode bits.
2993 */
2994 if (!strcmp(pInfo->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID))
2995 {
2996 pInfo->pIndData = pInfo->pSignedData->ContentInfo.u.pIndirectDataContent;
2997 Assert(pInfo->pIndData);
2998
2999 /*
3000 * Check that things add up.
3001 */
3002 rc = RTCrPkcs7SignedData_CheckSanity(pInfo->pSignedData,
3003 RTCRPKCS7SIGNEDDATA_SANITY_F_AUTHENTICODE
3004 | RTCRPKCS7SIGNEDDATA_SANITY_F_ONLY_KNOWN_HASH
3005 | RTCRPKCS7SIGNEDDATA_SANITY_F_SIGNING_CERT_PRESENT,
3006 pErrInfo, "SD");
3007 if (RT_SUCCESS(rc))
3008 rc = RTCrSpcIndirectDataContent_CheckSanityEx(pInfo->pIndData,
3009 pInfo->pSignedData,
3010 RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH,
3011 pErrInfo);
3012 if (RT_SUCCESS(rc))
3013 {
3014 PCRTCRX509ALGORITHMIDENTIFIER pDigestAlgorithm = &pInfo->pIndData->DigestInfo.DigestAlgorithm;
3015 pInfo->enmDigest = RTCrX509AlgorithmIdentifier_GetDigestType(pDigestAlgorithm, true /*fPureDigestsOnly*/);
3016 AssertReturn(pInfo->enmDigest != RTDIGESTTYPE_INVALID, VERR_INTERNAL_ERROR_4); /* Checked above! */
3017
3018 /*
3019 * Deal with nested signatures.
3020 */
3021 rc = rtldrPE_VerifySignatureDecodeNested(pSignature, pErrInfo);
3022 }
3023 }
3024 else
3025 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID,
3026 "Unknown pSignedData.ContentInfo.ContentType.szObjId value: %s (expected %s)",
3027 pInfo->pSignedData->ContentInfo.ContentType.szObjId, RTCRSPCINDIRECTDATACONTENT_OID);
3028 }
3029 else
3030 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
3031 "PKCS#7 is not 'signedData': %s", pInfo->pContentInfo->ContentType.szObjId);
3032 }
3033 return rc;
3034}
3035
3036
3037
3038static int rtldrPE_VerifyAllPageHashes(PRTLDRMODPE pModPe, PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib, RTDIGESTTYPE enmDigest,
3039 void *pvScratch, size_t cbScratch, uint32_t iSignature, PRTERRINFO pErrInfo)
3040{
3041 AssertReturn(cbScratch >= _4K, VERR_INTERNAL_ERROR_3);
3042
3043 /*
3044 * Calculate the special places.
3045 */
3046 RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
3047 int rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
3048 if (RT_FAILURE(rc))
3049 return rc;
3050
3051 uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
3052 uint32_t const cPages = pAttrib->u.pPageHashes->RawData.Asn1Core.cb / (cbHash + 4);
3053 if (cPages * (cbHash + 4) != pAttrib->u.pPageHashes->RawData.Asn1Core.cb)
3054 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_SIZE_OVERFLOW,
3055 "Signature #%u - Page hashes size issue in: cb=%#x cbHash=%#x",
3056 iSignature, pAttrib->u.pPageHashes->RawData.Asn1Core.cb, cbHash);
3057
3058 /*
3059 * Walk the table.
3060 */
3061 uint32_t const cbScratchReadMax = cbScratch & ~(uint32_t)(_4K - 1);
3062 uint32_t cbScratchRead = 0;
3063 uint32_t offScratchRead = 0;
3064
3065 uint32_t offPrev = 0;
3066#ifdef COMPLICATED_AND_WRONG
3067 uint32_t offSectEnd = pModPe->cbHeaders;
3068 uint32_t iSh = UINT32_MAX;
3069#endif
3070 uint8_t const *pbHashTab = pAttrib->u.pPageHashes->RawData.Asn1Core.uData.pu8;
3071 for (uint32_t iPage = 0; iPage < cPages - 1; iPage++)
3072 {
3073 /* Decode the page offset. */
3074 uint32_t const offPageInFile = RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]);
3075 if (RT_UNLIKELY(offPageInFile >= SpecialPlaces.cbToHash))
3076 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
3077 "Signature #%u - Page hash entry #%u is beyond the signature table start: %#x, %#x",
3078 iSignature, iPage, offPageInFile, SpecialPlaces.cbToHash);
3079 if (RT_UNLIKELY(offPageInFile < offPrev))
3080 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_NOT_STRICTLY_SORTED,
3081 "Signature #%u - Page hash table is not strictly sorted: entry #%u @%#x, previous @%#x\n",
3082 iSignature, iPage, offPageInFile, offPrev);
3083
3084#ifdef COMPLICATED_AND_WRONG
3085 /* Figure out how much to read and how much to zero. Need keep track
3086 of the on-disk section boundraries. */
3087 if (offPageInFile >= offSectEnd)
3088 {
3089 iSh++;
3090 if ( iSh < pModPe->cSections
3091 && offPageInFile - pModPe->paSections[iSh].PointerToRawData < pModPe->paSections[iSh].SizeOfRawData)
3092 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
3093 else
3094 {
3095 iSh = 0;
3096 while ( iSh < pModPe->cSections
3097 && offPageInFile - pModPe->paSections[iSh].PointerToRawData >= pModPe->paSections[iSh].SizeOfRawData)
3098 iSh++;
3099 if (iSh < pModPe->cSections)
3100 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
3101 else
3102 return RTErrInfoSetF(pErrInfo, VERR_PAGE_HASH_TAB_HASHES_NON_SECTION_DATA,
3103 "Signature #%u - Page hash entry #%u isn't in any section: %#x",
3104 iSignature, iPage, offPageInFile);
3105 }
3106 }
3107
3108#else
3109 /* Figure out how much to read and how much take as zero. Use the next
3110 page offset and the signature as upper boundraries. */
3111#endif
3112 uint32_t cbPageInFile = _4K;
3113#ifdef COMPLICATED_AND_WRONG
3114 if (offPageInFile + cbPageInFile > offSectEnd)
3115 cbPageInFile = offSectEnd - offPageInFile;
3116#else
3117 if (iPage + 1 < cPages)
3118 {
3119 uint32_t offNextPage = RT_MAKE_U32_FROM_U8(pbHashTab[0 + 4 + cbHash], pbHashTab[1 + 4 + cbHash],
3120 pbHashTab[2 + 4 + cbHash], pbHashTab[3 + 4 + cbHash]);
3121 if (offNextPage - offPageInFile < cbPageInFile)
3122 cbPageInFile = offNextPage - offPageInFile;
3123 }
3124#endif
3125
3126 if (offPageInFile + cbPageInFile > SpecialPlaces.cbToHash)
3127 cbPageInFile = SpecialPlaces.cbToHash - offPageInFile;
3128
3129 /* Did we get a cache hit? */
3130 uint8_t *pbCur = (uint8_t *)pvScratch;
3131 if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
3132 && offPageInFile >= offScratchRead)
3133 pbCur += offPageInFile - offScratchRead;
3134 /* Missed, read more. */
3135 else
3136 {
3137 offScratchRead = offPageInFile;
3138#ifdef COMPLICATED_AND_WRONG
3139 cbScratchRead = offSectEnd - offPageInFile;
3140#else
3141 cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
3142#endif
3143 if (cbScratchRead > cbScratchReadMax)
3144 cbScratchRead = cbScratchReadMax;
3145 rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
3146 if (RT_FAILURE(rc))
3147 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH,
3148 "Signature #%u - Page hash read error at %#x: %Rrc (cbScratchRead=%#zx)",
3149 iSignature, offScratchRead, rc, cbScratchRead);
3150 }
3151
3152 /*
3153 * Hash it.
3154 */
3155 RTLDRPEHASHCTXUNION HashCtx;
3156 rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
3157 AssertRCReturn(rc, rc);
3158
3159 /* Deal with special places. */
3160 uint32_t cbLeft = cbPageInFile;
3161 if (offPageInFile < SpecialPlaces.offEndSpecial)
3162 {
3163 uint32_t off = offPageInFile;
3164 if (off < SpecialPlaces.offCksum)
3165 {
3166 /* Hash everything up to the checksum. */
3167 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
3168 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
3169 pbCur += cbChunk;
3170 cbLeft -= cbChunk;
3171 off += cbChunk;
3172 }
3173
3174 if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
3175 {
3176 /* Skip the checksum */
3177 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
3178 pbCur += cbChunk;
3179 cbLeft -= cbChunk;
3180 off += cbChunk;
3181 }
3182
3183 if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
3184 {
3185 /* Hash everything between the checksum and the data dir entry. */
3186 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
3187 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
3188 pbCur += cbChunk;
3189 cbLeft -= cbChunk;
3190 off += cbChunk;
3191 }
3192
3193 if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
3194 {
3195 /* Skip the security data directory entry. */
3196 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
3197 pbCur += cbChunk;
3198 cbLeft -= cbChunk;
3199 off += cbChunk;
3200 }
3201 }
3202
3203 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
3204 if (cbPageInFile < _4K)
3205 rtLdrPE_HashUpdate(&HashCtx, enmDigest, g_abRTZero4K, _4K - cbPageInFile);
3206
3207 /*
3208 * Finish the hash calculation and compare the result.
3209 */
3210 RTLDRPEHASHRESUNION HashRes;
3211 rtLdrPE_HashFinalize(&HashCtx, enmDigest, &HashRes);
3212
3213 pbHashTab += 4;
3214 if (memcmp(pbHashTab, &HashRes, cbHash) != 0)
3215 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
3216 "Signature #%u - Page hash failed for page #%u, @%#x, %#x bytes: %.*Rhxs != %.*Rhxs",
3217 iSignature, iPage, offPageInFile, cbPageInFile, (size_t)cbHash, pbHashTab,
3218 (size_t)cbHash, &HashRes);
3219 pbHashTab += cbHash;
3220 offPrev = offPageInFile;
3221 }
3222
3223 /*
3224 * Check that the last table entry has a hash value of zero.
3225 */
3226 if (!ASMMemIsZero(pbHashTab + 4, cbHash))
3227 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
3228 "Signature #%u - Malformed final page hash table entry: #%u %#010x %.*Rhxs",
3229 iSignature, cPages - 1, RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]),
3230 (size_t)cbHash, pbHashTab + 4);
3231 return VINF_SUCCESS;
3232}
3233
3234
3235static int rtldrPE_VerifySignatureValidateOnePageHashes(PRTLDRMODPE pModPe, PRTLDRPESIGNATUREONE pInfo,
3236 void *pvScratch, uint32_t cbScratch, PRTERRINFO pErrInfo)
3237{
3238 /*
3239 * Compare the page hashes if present.
3240 *
3241 * Seems the difference between V1 and V2 page hash attributes is
3242 * that v1 uses SHA-1 while v2 uses SHA-256. The data structures
3243 * seems to be identical otherwise. Initially we assumed the digest
3244 * algorithm was supposed to be RTCRSPCINDIRECTDATACONTENT::DigestInfo,
3245 * i.e. the same as for the whole image hash. The initial approach
3246 * worked just fine, but this makes more sense.
3247 *
3248 * (See also comments in osslsigncode.c (google it).)
3249 */
3250 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib;
3251 /* V2 - SHA-256: */
3252 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pInfo->pIndData,
3253 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2);
3254 if (pAttrib)
3255 return rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA256, pvScratch, cbScratch,
3256 pInfo->iSignature + 1, pErrInfo);
3257
3258 /* V1 - SHA-1: */
3259 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pInfo->pIndData,
3260 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1);
3261 if (pAttrib)
3262 return rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA1, pvScratch, cbScratch,
3263 pInfo->iSignature + 1, pErrInfo);
3264
3265 /* No page hashes: */
3266 return VINF_SUCCESS;
3267}
3268
3269
3270static int rtldrPE_VerifySignatureValidateOneImageHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature,
3271 PRTLDRPESIGNATUREONE pInfo, void *pvScratch, uint32_t cbScratch,
3272 PRTERRINFO pErrInfo)
3273{
3274 /*
3275 * Assert sanity.
3276 */
3277 AssertReturn(pInfo->enmDigest > RTDIGESTTYPE_INVALID && pInfo->enmDigest < RTDIGESTTYPE_END, VERR_INTERNAL_ERROR_4);
3278 AssertPtrReturn(pInfo->pIndData, VERR_INTERNAL_ERROR_5);
3279 AssertReturn(RTASN1CORE_IS_PRESENT(&pInfo->pIndData->DigestInfo.Digest.Asn1Core), VERR_INTERNAL_ERROR_5);
3280 AssertPtrReturn(pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, VERR_INTERNAL_ERROR_5);
3281
3282 /* Check that the hash is supported by the code here before continuing. */
3283 AssertReturn(rtLdrPE_HashIsSupported(pInfo->enmDigest),
3284 RTErrInfoSetF(pErrInfo, VERR_CR_DIGEST_NOT_SUPPORTED, "Unsupported digest type: %d", pInfo->enmDigest));
3285
3286 /*
3287 * Skip it if we've already verified it.
3288 */
3289 if (pInfo->fValidatedImageHash)
3290 return VINF_SUCCESS;
3291
3292 /*
3293 * Calculate it.
3294 */
3295 uint32_t const cbHash = rtLdrPE_HashGetHashSize(pInfo->enmDigest);
3296 AssertReturn(pInfo->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash, VERR_INTERNAL_ERROR_5);
3297
3298 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, pInfo->enmDigest,
3299 &pSignature->HashCtx, &pInfo->HashRes, pErrInfo);
3300 if (RT_SUCCESS(rc))
3301 {
3302 pInfo->fValidatedImageHash = true;
3303 if (memcmp(&pInfo->HashRes, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash) == 0)
3304 {
3305 /*
3306 * Verify other signatures with the same digest type.
3307 */
3308 RTLDRPEHASHRESUNION const * const pHashRes = &pInfo->HashRes;
3309 RTDIGESTTYPE const enmDigestType = pInfo->enmDigest;
3310 for (uint32_t i = 0; i < pSignature->cNested; i++)
3311 {
3312 pInfo = &pSignature->paNested[i]; /* Note! pInfo changes! */
3313 if ( !pInfo->fValidatedImageHash
3314 && pInfo->enmDigest == enmDigestType
3315 /* paranoia from the top of this function: */
3316 && pInfo->pIndData
3317 && RTASN1CORE_IS_PRESENT(&pInfo->pIndData->DigestInfo.Digest.Asn1Core)
3318 && pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv
3319 && pInfo->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash)
3320 {
3321 pInfo->fValidatedImageHash = true;
3322 if (memcmp(pHashRes, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash) != 0)
3323 {
3324 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
3325 "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
3326 cbHash, pHashRes,
3327 cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
3328 break;
3329 }
3330 }
3331 }
3332 }
3333 else
3334 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
3335 "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
3336 cbHash, &pInfo->HashRes,
3337 cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
3338 }
3339 return rc;
3340}
3341
3342
3343/**
3344 * Validates the image hash, including page hashes if present.
3345 *
3346 * @returns IPRT status code.
3347 * @param pModPe The PE module.
3348 * @param pSignature The decoded signature data.
3349 * @param pErrInfo Optional error info buffer.
3350 */
3351static int rtldrPE_VerifySignatureValidateHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
3352{
3353 /*
3354 * Allocate a temporary memory buffer.
3355 * Note! The _4K that gets subtracted is to avoid that the 16-byte heap
3356 * block header in ring-0 (iprt) caused any unnecessary internal
3357 * heap fragmentation.
3358 */
3359# ifdef IN_RING0
3360 uint32_t cbScratch = _256K - _4K;
3361# else
3362 uint32_t cbScratch = _1M;
3363# endif
3364 void *pvScratch = RTMemTmpAlloc(cbScratch);
3365 if (!pvScratch)
3366 {
3367 cbScratch = _4K;
3368 pvScratch = RTMemTmpAlloc(cbScratch);
3369 if (!pvScratch)
3370 return RTErrInfoSet(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate 4KB of scratch space for hashing image.");
3371 }
3372
3373 /*
3374 * Verify signatures.
3375 */
3376 /* Image hashes: */
3377 int rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->Primary,
3378 pvScratch, cbScratch, pErrInfo);
3379 for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
3380 rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->paNested[i],
3381 pvScratch, cbScratch, pErrInfo);
3382
3383 /* Page hashes: */
3384 if (RT_SUCCESS(rc))
3385 {
3386 rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->Primary, pvScratch, cbScratch, pErrInfo);
3387 for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
3388 rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->paNested[i], pvScratch, cbScratch, pErrInfo);
3389 }
3390
3391 /*
3392 * Ditch the scratch buffer.
3393 */
3394 RTMemTmpFree(pvScratch);
3395 return rc;
3396}
3397
3398#endif /* !IPRT_WITHOUT_LDR_VERIFY */
3399
3400
3401/** @interface_method_impl{RTLDROPS,pfnVerifySignature} */
3402static DECLCALLBACK(int) rtldrPE_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser,
3403 PRTERRINFO pErrInfo)
3404{
3405#ifndef IPRT_WITHOUT_LDR_VERIFY
3406 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3407
3408 int rc = rtldrPE_VerifySignatureImagePrecoditions(pModPe, pErrInfo);
3409 if (RT_SUCCESS(rc))
3410 {
3411 PRTLDRPESIGNATURE pSignature = NULL;
3412 rc = rtldrPE_VerifySignatureRead(pModPe, &pSignature, pErrInfo);
3413 if (RT_SUCCESS(rc))
3414 {
3415 rc = rtldrPE_VerifySignatureDecode(pModPe, pSignature, pErrInfo);
3416 if (RT_SUCCESS(rc))
3417 rc = rtldrPE_VerifySignatureValidateHash(pModPe, pSignature, pErrInfo);
3418 if (RT_SUCCESS(rc))
3419 {
3420 /*
3421 * Work the callback.
3422 */
3423 /* The primary signature: */
3424 RTLDRSIGNATUREINFO Info;
3425 Info.iSignature = 0;
3426 Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
3427 Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
3428 Info.pvSignature = pSignature->Primary.pContentInfo;
3429 Info.cbSignature = sizeof(*pSignature->Primary.pContentInfo);
3430 Info.pvExternalData = NULL;
3431 Info.cbExternalData = 0;
3432 rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
3433
3434 /* The nested signatures: */
3435 for (uint32_t iNested = 0; iNested < pSignature->cNested && rc == VINF_SUCCESS; iNested++)
3436 {
3437 Info.iSignature = (uint16_t)(1 + iNested);
3438 Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
3439 Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
3440 Info.pvSignature = pSignature->paNested[iNested].pContentInfo;
3441 Info.cbSignature = sizeof(*pSignature->paNested[iNested].pContentInfo);
3442 Info.pvExternalData = NULL;
3443 Info.cbExternalData = 0;
3444 rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
3445 }
3446 }
3447 rtldrPE_VerifySignatureDestroy(pModPe, pSignature);
3448 }
3449 }
3450 return rc;
3451#else
3452 RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
3453 return VERR_NOT_SUPPORTED;
3454#endif
3455}
3456
3457
3458
3459/**
3460 * @interface_method_impl{RTLDROPS,pfnHashImage}
3461 */
3462static DECLCALLBACK(int) rtldrPE_HashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, uint8_t *pabHash, size_t cbHash)
3463{
3464 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3465
3466 /*
3467 * Allocate a temporary memory buffer.
3468 */
3469 uint32_t cbScratch = _16K;
3470 void *pvScratch = RTMemTmpAlloc(cbScratch);
3471 if (!pvScratch)
3472 {
3473 cbScratch = _4K;
3474 pvScratch = RTMemTmpAlloc(cbScratch);
3475 if (!pvScratch)
3476 return VERR_NO_TMP_MEMORY;
3477 }
3478
3479 /*
3480 * Do the hashing.
3481 */
3482 RTLDRPEHASHCTXUNION HashCtx;
3483 RTLDRPEHASHRESUNION HashRes;
3484 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, enmDigest, &HashCtx, &HashRes, NULL);
3485 if (RT_SUCCESS(rc))
3486 {
3487 /*
3488 * Copy out the result.
3489 */
3490 RT_NOREF(cbHash); /* verified by caller */
3491 switch (enmDigest)
3492 {
3493 case RTDIGESTTYPE_SHA512: memcpy(pabHash, HashRes.abSha512, sizeof(HashRes.abSha512)); break;
3494 case RTDIGESTTYPE_SHA256: memcpy(pabHash, HashRes.abSha256, sizeof(HashRes.abSha256)); break;
3495 case RTDIGESTTYPE_SHA1: memcpy(pabHash, HashRes.abSha1, sizeof(HashRes.abSha1)); break;
3496 case RTDIGESTTYPE_MD5: memcpy(pabHash, HashRes.abMd5, sizeof(HashRes.abMd5)); break;
3497 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
3498 }
3499 }
3500 return rc;
3501}
3502
3503
3504/**
3505 * Binary searches the lookup table.
3506 *
3507 * @returns RVA of unwind info on success, UINT32_MAX on failure.
3508 * @param paFunctions The table to lookup @a uRva in.
3509 * @param iEnd Size of the table.
3510 * @param uRva The RVA of the function we want.
3511 */
3512DECLINLINE(PCIMAGE_RUNTIME_FUNCTION_ENTRY)
3513rtldrPE_LookupRuntimeFunctionEntry(PCIMAGE_RUNTIME_FUNCTION_ENTRY paFunctions, size_t iEnd, uint32_t uRva)
3514{
3515 size_t iBegin = 0;
3516 while (iBegin < iEnd)
3517 {
3518 size_t const i = iBegin + (iEnd - iBegin) / 2;
3519 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = &paFunctions[i];
3520 if (uRva < pEntry->BeginAddress)
3521 iEnd = i;
3522 else if (uRva > pEntry->EndAddress)
3523 iBegin = i + 1;
3524 else
3525 return pEntry;
3526 }
3527 return NULL;
3528}
3529
3530
3531/**
3532 * Processes an IRET frame.
3533 *
3534 * @returns IPRT status code.
3535 * @param pState The unwind state being worked.
3536 * @param fErrCd Non-zero if there is an error code on the stack.
3537 */
3538static int rtldrPE_UnwindFrame_Amd64_IRet(PRTDBGUNWINDSTATE pState, uint8_t fErrCd)
3539{
3540 /* POP ErrCd (optional): */
3541 Assert(fErrCd <= 1);
3542 int rcRet;
3543 if (fErrCd)
3544 {
3545 pState->u.x86.uErrCd = 0;
3546 pState->u.x86.Loaded.s.fErrCd = 1;
3547 rcRet = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uErrCd);
3548 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3549 }
3550 else
3551 {
3552 pState->u.x86.Loaded.s.fErrCd = 0;
3553 rcRet = VINF_SUCCESS;
3554 }
3555
3556 /* Set return type and frame pointer. */
3557 pState->enmRetType = RTDBGRETURNTYPE_IRET64;
3558 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3559 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3560
3561 /* POP RIP: */
3562 int rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3563 if (RT_FAILURE(rc))
3564 rcRet = rc;
3565 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3566
3567 /* POP CS: */
3568 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_CS]);
3569 if (RT_FAILURE(rc))
3570 rcRet = rc;
3571 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3572
3573 /* POP RFLAGS: */
3574 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uRFlags);
3575 if (RT_FAILURE(rc))
3576 rcRet = rc;
3577 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3578
3579 /* POP RSP, part 1: */
3580 uint64_t uNewRsp = (pState->u.x86.auRegs[X86_GREG_xSP] - 8) & ~(uint64_t)15;
3581 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &uNewRsp);
3582 if (RT_FAILURE(rc))
3583 rcRet = rc;
3584 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3585
3586 /* POP SS: */
3587 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_SS]);
3588 if (RT_FAILURE(rc))
3589 rcRet = rc;
3590 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3591
3592 /* POP RSP, part 2: */
3593 pState->u.x86.auRegs[X86_GREG_xSP] = uNewRsp;
3594
3595 /* Set loaded indicators: */
3596 pState->u.x86.Loaded.s.fRegs |= RT_BIT(X86_GREG_xSP);
3597 pState->u.x86.Loaded.s.fSegs |= RT_BIT(X86_SREG_CS) | RT_BIT(X86_SREG_SS);
3598 pState->u.x86.Loaded.s.fPc = 1;
3599 pState->u.x86.Loaded.s.fFrameAddr = 1;
3600 pState->u.x86.Loaded.s.fRFlags = 1;
3601 return VINF_SUCCESS;
3602}
3603
3604
3605static int rtldrPE_UnwindFrame_Amd64(PRTLDRMODPE pThis, void const *pvBits, PRTDBGUNWINDSTATE pState, uint32_t uRvaPc,
3606 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry)
3607{
3608 /* Did we find any unwind information? */
3609 if (!pEntry)
3610 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3611
3612 /*
3613 * Do the unwinding.
3614 */
3615 IMAGE_RUNTIME_FUNCTION_ENTRY ChainedEntry;
3616 unsigned iFrameReg = ~0U;
3617 unsigned offFrameReg = 0;
3618
3619 int fInEpilog = -1; /* -1: not-determined-assume-false; 0: false; 1: true. */
3620 uint8_t cbEpilog = 0;
3621 uint8_t offEpilog = UINT8_MAX;
3622 int rcRet = VINF_SUCCESS;
3623 int rc;
3624 for (unsigned cChainLoops = 0; ; cChainLoops++)
3625 {
3626 /*
3627 * Get the info.
3628 */
3629 union
3630 {
3631 uint32_t uRva;
3632 uint8_t ab[ RT_OFFSETOF(IMAGE_UNWIND_INFO, aOpcodes)
3633 + sizeof(IMAGE_UNWIND_CODE) * 256
3634 + sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)];
3635 } uBuf;
3636 rc = rtldrPEReadPartByRvaInfoBuf(pThis, pvBits, pEntry->UnwindInfoAddress, sizeof(uBuf), &uBuf);
3637 if (RT_FAILURE(rc))
3638 return rc;
3639
3640 /*
3641 * Check the info.
3642 */
3643 ASMCompilerBarrier(); /* we're aliasing */
3644 PCIMAGE_UNWIND_INFO pInfo = (PCIMAGE_UNWIND_INFO)&uBuf;
3645
3646 if (pInfo->Version != 1 && pInfo->Version != 2)
3647 return VERR_DBG_MALFORMED_UNWIND_INFO;
3648
3649 /*
3650 * Execute the opcodes.
3651 */
3652 unsigned const cOpcodes = pInfo->CountOfCodes;
3653 unsigned iOpcode = 0;
3654
3655 /*
3656 * Check for epilog opcodes at the start and see if we're in an epilog.
3657 */
3658 if ( pInfo->Version >= 2
3659 && iOpcode < cOpcodes
3660 && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3661 {
3662 if (fInEpilog == -1)
3663 {
3664 cbEpilog = pInfo->aOpcodes[iOpcode].u.CodeOffset;
3665 Assert(cbEpilog > 0);
3666
3667 uint32_t uRvaEpilog = pEntry->EndAddress - cbEpilog;
3668 iOpcode++;
3669 if ( (pInfo->aOpcodes[iOpcode - 1].u.OpInfo & 1)
3670 && uRvaPc >= uRvaEpilog)
3671 {
3672 offEpilog = uRvaPc - uRvaEpilog;
3673 fInEpilog = 1;
3674 }
3675 else
3676 {
3677 fInEpilog = 0;
3678 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3679 {
3680 uRvaEpilog = pEntry->EndAddress
3681 - (pInfo->aOpcodes[iOpcode].u.CodeOffset + (pInfo->aOpcodes[iOpcode].u.OpInfo << 8));
3682 iOpcode++;
3683 if (uRvaPc - uRvaEpilog < cbEpilog)
3684 {
3685 offEpilog = uRvaPc - uRvaEpilog;
3686 fInEpilog = 1;
3687 break;
3688 }
3689 }
3690 }
3691 }
3692 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3693 iOpcode++;
3694 }
3695 if (fInEpilog != 1)
3696 {
3697 /*
3698 * Skip opcodes that doesn't apply to us if we're in the prolog.
3699 */
3700 uint32_t offPc = uRvaPc - pEntry->BeginAddress;
3701 if (offPc < pInfo->SizeOfProlog)
3702 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.CodeOffset > offPc)
3703 iOpcode++;
3704
3705 /*
3706 * Execute the opcodes.
3707 */
3708 if (pInfo->FrameRegister != 0)
3709 {
3710 iFrameReg = pInfo->FrameRegister;
3711 offFrameReg = pInfo->FrameOffset * 16;
3712 }
3713 while (iOpcode < cOpcodes)
3714 {
3715 Assert(pInfo->aOpcodes[iOpcode].u.CodeOffset <= offPc);
3716 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3717 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3718 switch (uUnwindOp)
3719 {
3720 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3721 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auRegs[uOpInfo]);
3722 if (RT_FAILURE(rc))
3723 rcRet = rc;
3724 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3725 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3726 iOpcode++;
3727 break;
3728
3729 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3730 if (uOpInfo == 0)
3731 {
3732 iOpcode += 2;
3733 AssertBreak(iOpcode <= cOpcodes);
3734 pState->u.x86.auRegs[X86_GREG_xSP] += pInfo->aOpcodes[iOpcode - 1].FrameOffset * 8;
3735 }
3736 else
3737 {
3738 iOpcode += 3;
3739 AssertBreak(iOpcode <= cOpcodes);
3740 pState->u.x86.auRegs[X86_GREG_xSP] += RT_MAKE_U32(pInfo->aOpcodes[iOpcode - 2].FrameOffset,
3741 pInfo->aOpcodes[iOpcode - 1].FrameOffset);
3742 }
3743 break;
3744
3745 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3746 AssertBreak(iOpcode <= cOpcodes);
3747 pState->u.x86.auRegs[X86_GREG_xSP] += uOpInfo * 8 + 8;
3748 iOpcode++;
3749 break;
3750
3751 case IMAGE_AMD64_UWOP_SET_FPREG:
3752 iFrameReg = uOpInfo;
3753 offFrameReg = pInfo->FrameOffset * 16;
3754 pState->u.x86.auRegs[X86_GREG_xSP] = pState->u.x86.auRegs[iFrameReg] - offFrameReg;
3755 iOpcode++;
3756 break;
3757
3758 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3759 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3760 {
3761 uint32_t off = 0;
3762 iOpcode++;
3763 if (iOpcode < cOpcodes)
3764 {
3765 off = pInfo->aOpcodes[iOpcode].FrameOffset;
3766 iOpcode++;
3767 if (uUnwindOp == IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR && iOpcode < cOpcodes)
3768 {
3769 off |= (uint32_t)pInfo->aOpcodes[iOpcode].FrameOffset << 16;
3770 iOpcode++;
3771 }
3772 }
3773 off *= 8;
3774 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP] + off,
3775 &pState->u.x86.auRegs[uOpInfo]);
3776 if (RT_FAILURE(rc))
3777 rcRet = rc;
3778 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3779 break;
3780 }
3781
3782 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3783 iOpcode += 2;
3784 break;
3785
3786 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3787 iOpcode += 3;
3788 break;
3789
3790 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME:
3791 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3792
3793 case IMAGE_AMD64_UWOP_EPILOG:
3794 iOpcode += 1;
3795 break;
3796
3797 case IMAGE_AMD64_UWOP_RESERVED_7:
3798 AssertFailedReturn(VERR_DBG_MALFORMED_UNWIND_INFO);
3799
3800 default:
3801 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3802 }
3803 }
3804 }
3805 else
3806 {
3807 /*
3808 * We're in the POP sequence of an epilog. The POP sequence should
3809 * mirror the PUSH sequence exactly.
3810 *
3811 * Note! We should only end up here for the initial frame (just consider
3812 * RSP, stack allocations, non-volatile register restores, ++).
3813 */
3814 while (iOpcode < cOpcodes)
3815 {
3816 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3817 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3818 switch (uUnwindOp)
3819 {
3820 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3821 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3822 if (offEpilog == 0)
3823 {
3824 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP],
3825 &pState->u.x86.auRegs[uOpInfo]);
3826 if (RT_FAILURE(rc))
3827 rcRet = rc;
3828 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3829 }
3830 else
3831 {
3832 /* Decrement offEpilog by estimated POP instruction length. */
3833 offEpilog -= 1;
3834 if (offEpilog > 0 && uOpInfo >= 8)
3835 offEpilog -= 1;
3836 }
3837 iOpcode++;
3838 break;
3839
3840 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME: /* Must terminate an epilog, so always execute this. */
3841 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3842
3843 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3844 case IMAGE_AMD64_UWOP_SET_FPREG:
3845 case IMAGE_AMD64_UWOP_EPILOG:
3846 iOpcode++;
3847 break;
3848 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3849 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3850 iOpcode += 2;
3851 break;
3852 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3853 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3854 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3855 iOpcode += 3;
3856 break;
3857
3858 default:
3859 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3860 }
3861 }
3862 }
3863
3864 /*
3865 * Chained stuff?
3866 */
3867 if (!(pInfo->Flags & IMAGE_UNW_FLAGS_CHAININFO))
3868 break;
3869 ChainedEntry = *(PCIMAGE_RUNTIME_FUNCTION_ENTRY)&pInfo->aOpcodes[(cOpcodes + 1) & ~1];
3870 pEntry = &ChainedEntry;
3871 AssertReturn(cChainLoops < 32, VERR_DBG_MALFORMED_UNWIND_INFO);
3872 }
3873
3874 /*
3875 * RSP should now give us the return address, so perform a RET.
3876 */
3877 pState->enmRetType = RTDBGRETURNTYPE_NEAR64;
3878
3879 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3880 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3881 pState->u.x86.Loaded.s.fFrameAddr = 1;
3882
3883 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3884 if (RT_FAILURE(rc))
3885 rcRet = rc;
3886 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3887 pState->u.x86.Loaded.s.fPc = 1;
3888 return rcRet;
3889}
3890
3891
3892/**
3893 * @interface_method_impl{RTLDROPS,pfnUnwindFrame}
3894 */
3895static DECLCALLBACK(int) rtldrPE_UnwindFrame(PRTLDRMODINTERNAL pMod, void const *pvBits,
3896 uint32_t iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
3897{
3898 PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
3899
3900 /*
3901 * Translate the segment + offset into an RVA.
3902 */
3903 RTLDRADDR uRvaPc = off;
3904 if (iSeg != UINT32_MAX)
3905 {
3906 int rc = rtldrPE_SegOffsetToRva(pMod, iSeg, off, &uRvaPc);
3907 if (RT_FAILURE(rc))
3908 return rc;
3909 }
3910
3911 /*
3912 * Check for unwind info and match the architecture.
3913 */
3914 if ( pThis->ExceptionDir.Size == 0
3915 || pThis->ExceptionDir.VirtualAddress < pThis->cbHeaders)
3916 return VERR_DBG_NO_UNWIND_INFO;
3917 if (pThis->Core.enmArch != pState->enmArch)
3918 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3919
3920 /* Currently only AMD64 unwinding is implemented, so head it off right away. */
3921 if (pThis->Core.enmArch != RTLDRARCH_AMD64)
3922 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3923
3924 /*
3925 * Make the lookup table available to us.
3926 */
3927 void const *pvTable = NULL;
3928 uint32_t const cbTable = pThis->ExceptionDir.Size;
3929 AssertReturn( cbTable < pThis->cbImage
3930 && pThis->ExceptionDir.VirtualAddress < pThis->cbImage
3931 && pThis->ExceptionDir.VirtualAddress + cbTable <= pThis->cbImage, VERR_INTERNAL_ERROR_3);
3932 int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, pThis->ExceptionDir.Size, &pvTable);
3933 if (RT_FAILURE(rc))
3934 return rc;
3935
3936 /*
3937 * The rest is architecture dependent.
3938 *
3939 * Note! On windows we try catch access violations so we can safely use
3940 * this code on mapped images during assertions.
3941 */
3942#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3943 __try
3944 {
3945#endif
3946 switch (pThis->Core.enmArch)
3947 {
3948 case RTLDRARCH_AMD64:
3949 rc = rtldrPE_UnwindFrame_Amd64(pThis, pvBits, pState, uRvaPc,
3950 rtldrPE_LookupRuntimeFunctionEntry((PCIMAGE_RUNTIME_FUNCTION_ENTRY)pvTable,
3951 cbTable / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY),
3952 (uint32_t)uRvaPc));
3953 break;
3954
3955 default:
3956 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3957 break;
3958 }
3959#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3960 }
3961 __except (1 /*EXCEPTION_EXECUTE_HANDLER*/)
3962 {
3963 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3964 }
3965#endif
3966 rtldrPEFreePart(pThis, pvBits, pvTable);
3967 return rc;
3968}
3969
3970
3971/** @interface_method_impl{RTLDROPS,pfnDone} */
3972static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
3973{
3974 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3975 if (pModPe->pvBits)
3976 {
3977 RTMemFree(pModPe->pvBits);
3978 pModPe->pvBits = NULL;
3979 }
3980 return VINF_SUCCESS;
3981}
3982
3983
3984/** @interface_method_impl{RTLDROPS,pfnClose} */
3985static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
3986{
3987 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3988 if (pModPe->paSections)
3989 {
3990 RTMemFree(pModPe->paSections);
3991 pModPe->paSections = NULL;
3992 }
3993 if (pModPe->pvBits)
3994 {
3995 RTMemFree(pModPe->pvBits);
3996 pModPe->pvBits = NULL;
3997 }
3998 return VINF_SUCCESS;
3999}
4000
4001
4002/**
4003 * Operations for a 32-bit PE module.
4004 */
4005static const RTLDROPSPE s_rtldrPE32Ops =
4006{
4007 {
4008 "pe32",
4009 rtldrPEClose,
4010 NULL,
4011 rtldrPEDone,
4012 rtldrPEEnumSymbols,
4013 /* ext */
4014 rtldrPEGetImageSize,
4015 rtldrPEGetBits,
4016 rtldrPERelocate,
4017 rtldrPEGetSymbolEx,
4018 rtldrPE_QueryForwarderInfo,
4019 rtldrPE_EnumDbgInfo,
4020 rtldrPE_EnumSegments,
4021 rtldrPE_LinkAddressToSegOffset,
4022 rtldrPE_LinkAddressToRva,
4023 rtldrPE_SegOffsetToRva,
4024 rtldrPE_RvaToSegOffset,
4025 NULL,
4026 rtldrPE_QueryProp,
4027 rtldrPE_VerifySignature,
4028 rtldrPE_HashImage,
4029 NULL /*pfnUnwindFrame*/,
4030 42
4031 },
4032 rtldrPEResolveImports32,
4033 42
4034};
4035
4036
4037/**
4038 * Operations for a 64-bit PE module.
4039 */
4040static const RTLDROPSPE s_rtldrPE64Ops =
4041{
4042 {
4043 "pe64",
4044 rtldrPEClose,
4045 NULL,
4046 rtldrPEDone,
4047 rtldrPEEnumSymbols,
4048 /* ext */
4049 rtldrPEGetImageSize,
4050 rtldrPEGetBits,
4051 rtldrPERelocate,
4052 rtldrPEGetSymbolEx,
4053 rtldrPE_QueryForwarderInfo,
4054 rtldrPE_EnumDbgInfo,
4055 rtldrPE_EnumSegments,
4056 rtldrPE_LinkAddressToSegOffset,
4057 rtldrPE_LinkAddressToRva,
4058 rtldrPE_SegOffsetToRva,
4059 rtldrPE_RvaToSegOffset,
4060 NULL,
4061 rtldrPE_QueryProp,
4062 rtldrPE_VerifySignature,
4063 rtldrPE_HashImage,
4064 rtldrPE_UnwindFrame,
4065 42
4066 },
4067 rtldrPEResolveImports64,
4068 42
4069};
4070
4071
4072/**
4073 * Converts the optional header from 32 bit to 64 bit.
4074 * This is a rather simple task, if you start from the right end.
4075 *
4076 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
4077 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
4078 */
4079static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
4080{
4081 /*
4082 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
4083 */
4084 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
4085 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
4086
4087 /* from LoaderFlags and out the difference is 4 * 32-bits. */
4088 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
4089 Assert( RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
4090 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
4091 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
4092 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
4093 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
4094 while (pu32Src >= pu32SrcLast)
4095 *pu32Dst-- = *pu32Src--;
4096
4097 /* the previous 4 fields are 32/64 and needs special attention. */
4098 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
4099 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
4100 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
4101 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
4102 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
4103
4104 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
4105 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
4106 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
4107 */
4108 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
4109 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
4110 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
4111 uint32_t u32ImageBase = pOptHdr32->ImageBase;
4112 pOptHdr64->ImageBase = u32ImageBase;
4113}
4114
4115
4116/**
4117 * Converts the load config directory from 32 bit to 64 bit.
4118 * This is a rather simple task, if you start from the right end.
4119 *
4120 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
4121 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
4122 */
4123static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
4124{
4125 /*
4126 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
4127 */
4128 IMAGE_LOAD_CONFIG_DIRECTORY32_V13 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32_V13 volatile *)pLoadCfg;
4129 IMAGE_LOAD_CONFIG_DIRECTORY64_V13 volatile *pLoadCfg64 = pLoadCfg;
4130
4131 pLoadCfg64->CastGuardOsDeterminedFailureMode = pLoadCfg32->CastGuardOsDeterminedFailureMode;
4132 pLoadCfg64->GuardXFGTableDispatchFunctionPointer = pLoadCfg32->GuardXFGTableDispatchFunctionPointer;
4133 pLoadCfg64->GuardXFGDispatchFunctionPointer = pLoadCfg32->GuardXFGDispatchFunctionPointer;
4134 pLoadCfg64->GuardXFGCheckFunctionPointer = pLoadCfg32->GuardXFGCheckFunctionPointer;
4135 pLoadCfg64->GuardEHContinuationCount = pLoadCfg32->GuardEHContinuationCount;
4136 pLoadCfg64->GuardEHContinuationTable = pLoadCfg32->GuardEHContinuationTable;
4137 pLoadCfg64->VolatileMetadataPointer = pLoadCfg32->VolatileMetadataPointer;
4138 pLoadCfg64->EnclaveConfigurationPointer = pLoadCfg32->EnclaveConfigurationPointer;
4139 pLoadCfg64->Reserved3 = pLoadCfg32->Reserved3;
4140 pLoadCfg64->HotPatchTableOffset = pLoadCfg32->HotPatchTableOffset;
4141 pLoadCfg64->GuardRFVerifyStackPointerFunctionPointer = pLoadCfg32->GuardRFVerifyStackPointerFunctionPointer;
4142 pLoadCfg64->Reserved2 = pLoadCfg32->Reserved2;
4143 pLoadCfg64->DynamicValueRelocTableSection = pLoadCfg32->DynamicValueRelocTableSection;
4144 pLoadCfg64->DynamicValueRelocTableOffset = pLoadCfg32->DynamicValueRelocTableOffset;
4145 pLoadCfg64->GuardRFFailureRoutineFunctionPointer = pLoadCfg32->GuardRFFailureRoutineFunctionPointer;
4146 pLoadCfg64->GuardRFFailureRoutine = pLoadCfg32->GuardRFFailureRoutine;
4147 pLoadCfg64->CHPEMetadataPointer = pLoadCfg32->CHPEMetadataPointer;
4148 pLoadCfg64->DynamicValueRelocTable = pLoadCfg32->DynamicValueRelocTable;
4149 pLoadCfg64->GuardLongJumpTargetCount = pLoadCfg32->GuardLongJumpTargetCount;
4150 pLoadCfg64->GuardLongJumpTargetTable = pLoadCfg32->GuardLongJumpTargetTable;
4151 pLoadCfg64->GuardAddressTakenIatEntryCount = pLoadCfg32->GuardAddressTakenIatEntryCount;
4152 pLoadCfg64->GuardAddressTakenIatEntryTable = pLoadCfg32->GuardAddressTakenIatEntryTable;
4153 pLoadCfg64->CodeIntegrity.Reserved = pLoadCfg32->CodeIntegrity.Reserved;
4154 pLoadCfg64->CodeIntegrity.CatalogOffset = pLoadCfg32->CodeIntegrity.CatalogOffset;
4155 pLoadCfg64->CodeIntegrity.Catalog = pLoadCfg32->CodeIntegrity.Catalog;
4156 pLoadCfg64->CodeIntegrity.Flags = pLoadCfg32->CodeIntegrity.Flags;
4157 pLoadCfg64->GuardFlags = pLoadCfg32->GuardFlags;
4158 pLoadCfg64->GuardCFFunctionCount = pLoadCfg32->GuardCFFunctionCount;
4159 pLoadCfg64->GuardCFFunctionTable = pLoadCfg32->GuardCFFunctionTable;
4160 pLoadCfg64->GuardCFDispatchFunctionPointer = pLoadCfg32->GuardCFDispatchFunctionPointer;
4161 pLoadCfg64->GuardCFCCheckFunctionPointer = pLoadCfg32->GuardCFCCheckFunctionPointer;
4162 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
4163 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
4164 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
4165 pLoadCfg64->EditList = pLoadCfg32->EditList;
4166 pLoadCfg64->DependentLoadFlags = pLoadCfg32->DependentLoadFlags;
4167 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
4168 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
4169 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
4170 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
4171 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
4172 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
4173 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
4174 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
4175 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
4176 /* the rest is equal. */
4177 Assert( RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
4178 == RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
4179}
4180
4181
4182/**
4183 * Translate the PE/COFF machine name to a string.
4184 *
4185 * @returns Name string (read-only).
4186 * @param uMachine The PE/COFF machine.
4187 */
4188static const char *rtldrPEGetArchName(uint16_t uMachine)
4189{
4190 switch (uMachine)
4191 {
4192 case IMAGE_FILE_MACHINE_I386: return "X86_32";
4193 case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
4194
4195 case IMAGE_FILE_MACHINE_UNKNOWN: return "UNKNOWN";
4196 case IMAGE_FILE_MACHINE_AM33: return "AM33";
4197 case IMAGE_FILE_MACHINE_ARM: return "ARM";
4198 case IMAGE_FILE_MACHINE_THUMB: return "THUMB";
4199 case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT";
4200 case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
4201 case IMAGE_FILE_MACHINE_EBC: return "EBC";
4202 case IMAGE_FILE_MACHINE_IA64: return "IA64";
4203 case IMAGE_FILE_MACHINE_M32R: return "M32R";
4204 case IMAGE_FILE_MACHINE_MIPS16: return "MIPS16";
4205 case IMAGE_FILE_MACHINE_MIPSFPU: return "MIPSFPU";
4206 case IMAGE_FILE_MACHINE_MIPSFPU16: return "MIPSFPU16";
4207 case IMAGE_FILE_MACHINE_WCEMIPSV2: return "WCEMIPSV2";
4208 case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
4209 case IMAGE_FILE_MACHINE_POWERPCFP: return "POWERPCFP";
4210 case IMAGE_FILE_MACHINE_R4000: return "R4000";
4211 case IMAGE_FILE_MACHINE_SH3: return "SH3";
4212 case IMAGE_FILE_MACHINE_SH3DSP: return "SH3DSP";
4213 case IMAGE_FILE_MACHINE_SH4: return "SH4";
4214 case IMAGE_FILE_MACHINE_SH5: return "SH5";
4215 default: return "UnknownMachine";
4216 }
4217}
4218
4219
4220/**
4221 * Validates the file header.
4222 *
4223 * @returns iprt status code.
4224 * @param pFileHdr Pointer to the file header that needs validating.
4225 * @param fFlags Valid RTLDR_O_XXX combination.
4226 * @param pszLogName The log name to prefix the errors with.
4227 * @param penmArch Where to store the CPU architecture.
4228 * @param pErrInfo Where to return additional error information.
4229 */
4230static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName,
4231 PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
4232{
4233 RT_NOREF_PV(pszLogName);
4234
4235 size_t cbOptionalHeader;
4236 switch (pFileHdr->Machine)
4237 {
4238 case IMAGE_FILE_MACHINE_I386:
4239 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
4240 *penmArch = RTLDRARCH_X86_32;
4241 break;
4242 case IMAGE_FILE_MACHINE_AMD64:
4243 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
4244 *penmArch = RTLDRARCH_AMD64;
4245 break;
4246
4247 default:
4248 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n", pszLogName, pFileHdr->Machine));
4249 *penmArch = RTLDRARCH_INVALID;
4250 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unsupported Machine=%#x", pFileHdr->Machine);
4251 }
4252 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
4253 {
4254 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n", pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
4255 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfOptionalHeader=%#x expected %#x",
4256 pFileHdr->SizeOfOptionalHeader, cbOptionalHeader);
4257 }
4258 /* This restriction needs to be implemented elsewhere. */
4259 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
4260 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4261 {
4262 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
4263 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "IMAGE_FILE_RELOCS_STRIPPED");
4264 }
4265 if (pFileHdr->NumberOfSections > 42)
4266 {
4267 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
4268 pszLogName, pFileHdr->NumberOfSections));
4269 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections=%d, implementation max is 42", pFileHdr->NumberOfSections);
4270 }
4271 if (pFileHdr->NumberOfSections < 1)
4272 {
4273 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
4274 pszLogName, pFileHdr->NumberOfSections));
4275 return RTERRINFO_LOG_SET(pErrInfo, VERR_BAD_EXE_FORMAT, "Image has no sections");
4276 }
4277 return VINF_SUCCESS;
4278}
4279
4280
4281/**
4282 * Validates the optional header (64/32-bit)
4283 *
4284 * @returns iprt status code.
4285 * @param pOptHdr Pointer to the optional header which needs validation.
4286 * @param pszLogName The log name to prefix the errors with.
4287 * @param offNtHdrs The offset of the NT headers from the start of the file.
4288 * @param pFileHdr Pointer to the file header (valid).
4289 * @param cbRawImage The raw image size.
4290 * @param fFlags Loader flags, RTLDR_O_XXX.
4291 * @param pErrInfo Where to return additional error information.
4292 */
4293static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
4294 const IMAGE_FILE_HEADER *pFileHdr, uint64_t cbRawImage, uint32_t fFlags, PRTERRINFO pErrInfo)
4295{
4296 RT_NOREF_PV(pszLogName);
4297
4298 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
4299 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
4300 if (pOptHdr->Magic != CorrectMagic)
4301 {
4302 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
4303 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Magic=%#x, expected %#x", pOptHdr->Magic, CorrectMagic);
4304 }
4305 const uint32_t cbImage = pOptHdr->SizeOfImage;
4306 if (cbImage > _1G)
4307 {
4308 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
4309 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x - Our limit is 1GB (%#x)", cbImage, _1G);
4310 }
4311 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
4312 if (cbImage < cbMinImageSize)
4313 {
4314 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
4315 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x to small, minimum %#x", cbImage, cbMinImageSize);
4316 }
4317 if (pOptHdr->AddressOfEntryPoint >= cbImage)
4318 {
4319 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
4320 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
4321 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4322 "AddressOfEntryPoint=%#x - beyond image size (%#x)", pOptHdr->AddressOfEntryPoint, cbImage);
4323 }
4324 if (pOptHdr->BaseOfCode >= cbImage)
4325 {
4326 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
4327 pszLogName, pOptHdr->BaseOfCode, cbImage));
4328 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4329 "BaseOfCode=%#x - beyond image size (%#x)", pOptHdr->BaseOfCode, cbImage);
4330 }
4331#if 0/* only in 32-bit header */
4332 if (pOptHdr->BaseOfData >= cbImage)
4333 {
4334 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
4335 pszLogName, pOptHdr->BaseOfData, cbImage));
4336 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "BaseOfData=%#x - beyond image size (%#x)", pOptHdr->BaseOfData, cbImage);
4337 }
4338#endif
4339 if (!RT_IS_POWER_OF_TWO(pOptHdr->SectionAlignment))
4340 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4341 "SectionAlignment=%#x - not a power of two", pOptHdr->SectionAlignment);
4342 if (pOptHdr->SectionAlignment < 16 || pOptHdr->SectionAlignment > _128K)
4343 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4344 "SectionAlignment=%#x - unsupported value, not between 16 and 128KB", pOptHdr->SectionAlignment);
4345 if (pOptHdr->SizeOfHeaders >= cbImage)
4346 {
4347 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
4348 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
4349 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4350 "SizeOfHeaders=%#x - beyond image size (%#x)", pOptHdr->SizeOfHeaders, cbImage);
4351 }
4352 /* don't know how to do the checksum, so ignore it. */
4353 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
4354 {
4355 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
4356 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Subsystem=%#x (unknown)", pOptHdr->Subsystem);
4357 }
4358 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
4359 {
4360 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
4361 pszLogName, pOptHdr->SizeOfHeaders,
4362 cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
4363 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
4364 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx",
4365 pOptHdr->SizeOfHeaders, cbMinImageSize,
4366 pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
4367 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) );
4368 }
4369 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
4370 {
4371 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
4372 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
4373 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x",
4374 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
4375 }
4376 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
4377 {
4378 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
4379 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
4380 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x\n",
4381 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
4382 }
4383
4384 /* DataDirectory */
4385 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
4386 {
4387 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
4388 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfRvaAndSizes=%d, expected %d",
4389 pOptHdr->NumberOfRvaAndSizes, RT_ELEMENTS(pOptHdr->DataDirectory));
4390 }
4391 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
4392 {
4393 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
4394 if (!pDir->Size)
4395 continue;
4396 size_t cb = cbImage;
4397 switch (i)
4398 {
4399 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
4400 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
4401 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
4402 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
4403 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
4404 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
4405 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
4406 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
4407 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
4408 break;
4409 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
4410 /* Delay inspection after section table is validated. */
4411 break;
4412
4413 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
4414 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4415 break;
4416 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4417 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4418 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_DELAY_IMPORT,
4419 "DELAY_IMPORT VirtualAddress=%#x Size=%#x: not supported", pDir->VirtualAddress, pDir->Size);
4420
4421 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
4422 /* The VirtualAddress is a PointerToRawData. */
4423 cb = (size_t)cbRawImage; Assert((uint64_t)cb == cbRawImage);
4424 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x\n", pszLogName, i, pDir->VirtualAddress, pDir->Size));
4425 if (pDir->Size < sizeof(WIN_CERTIFICATE))
4426 {
4427 Log(("rtldrPEOpen: %s: Security directory #%u is too small: %#x bytes\n", pszLogName, i, pDir->Size));
4428 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4429 "Security directory is too small: %#x bytes", pDir->Size);
4430 }
4431 if (pDir->Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
4432 {
4433 Log(("rtldrPEOpen: %s: Security directory #%u is too large: %#x bytes\n", pszLogName, i, pDir->Size));
4434 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4435 "Security directory is too large: %#x bytes", pDir->Size);
4436 }
4437 if (pDir->VirtualAddress & 7)
4438 {
4439 Log(("rtldrPEOpen: %s: Security directory #%u is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
4440 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4441 "Security directory is misaligned: %#x", pDir->VirtualAddress);
4442 }
4443 /* When using the in-memory reader with a debugger, we may get
4444 into trouble here since we might not have access to the whole
4445 physical file. So skip the tests below. Makes VBoxGuest.sys
4446 load and check out just fine, for instance. */
4447 if (fFlags & RTLDR_O_FOR_DEBUG)
4448 continue;
4449 break;
4450
4451 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
4452 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4453 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4454 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_GLOBALPTR, "GLOBALPTR VirtualAddress=%#x Size=%#x: not supported",
4455 pDir->VirtualAddress, pDir->Size);
4456
4457 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
4458 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4459 break;
4460 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4461 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4462 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_TLS, "TLS VirtualAddress=%#x Size=%#x: not supported",
4463 pDir->VirtualAddress, pDir->Size);
4464
4465 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
4466 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4467 break;
4468 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4469 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4470 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_COM_DESCRIPTOR,
4471 "COM_DESCRIPTOR VirtualAddress=%#x Size=%#x: not supported",
4472 pDir->VirtualAddress, pDir->Size);
4473
4474 default:
4475 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
4476 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4477 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x Size=%#x is not supported",
4478 i, pDir->VirtualAddress, pDir->Size);
4479 }
4480 if (pDir->VirtualAddress >= cb)
4481 {
4482 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
4483 pszLogName, i, pDir->VirtualAddress, cb));
4484 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x is invalid (limit %#x)",
4485 i, pDir->VirtualAddress, cb);
4486 }
4487 if (pDir->Size > cb - pDir->VirtualAddress)
4488 {
4489 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
4490 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
4491 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)",
4492 i, pDir->Size, pDir->VirtualAddress, cb);
4493 }
4494 }
4495 return VINF_SUCCESS;
4496}
4497
4498
4499/**
4500 * Validates and touch up the section headers.
4501 *
4502 * The touching up is restricted to setting the VirtualSize field for old-style
4503 * linkers that sets it to zero.
4504 *
4505 * @returns iprt status code.
4506 * @param paSections Pointer to the array of sections that is to be validated.
4507 * @param cSections Number of sections in that array.
4508 * @param pszLogName The log name to prefix the errors with.
4509 * @param pOptHdr Pointer to the optional header (valid).
4510 * @param cbRawImage The raw image size.
4511 * @param fFlags Loader flags, RTLDR_O_XXX.
4512 * @param fNoCode Verify that the image contains no code.
4513 */
4514static int rtldrPEValidateAndTouchUpSectionHeaders(IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
4515 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint64_t cbRawImage, uint32_t fFlags,
4516 bool fNoCode)
4517{
4518 RT_NOREF_PV(pszLogName);
4519
4520 /*
4521 * Do a quick pass to detect linker setting VirtualSize to zero.
4522 */
4523 bool fFixupVirtualSize = true;
4524 IMAGE_SECTION_HEADER *pSH = &paSections[0];
4525 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4526 if ( pSH->Misc.VirtualSize != 0
4527 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4528 {
4529 fFixupVirtualSize = false;
4530 break;
4531 }
4532
4533 /*
4534 * Actual pass.
4535 */
4536 const uint32_t cbImage = pOptHdr->SizeOfImage;
4537 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
4538 pSH = &paSections[0];
4539 Log3(("RTLdrPE: Section Headers:\n"));
4540 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4541 {
4542 const unsigned iSH = (unsigned)(pSH - &paSections[0]); NOREF(iSH);
4543 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
4544 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
4545 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
4546 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
4547 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
4548 iSH, pSH->Name, pSH->Characteristics,
4549 pSH->VirtualAddress, pSH->Misc.VirtualSize,
4550 pSH->PointerToRawData, pSH->SizeOfRawData,
4551 pSH->PointerToRelocations, pSH->NumberOfRelocations,
4552 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
4553
4554 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
4555 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
4556 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
4557 {
4558 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
4559 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
4560 return VERR_BAD_EXE_FORMAT;
4561 }
4562
4563 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
4564 || pSH->SizeOfRawData > cbRawImage
4565 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
4566 {
4567 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#llx) - section #%d '%.*s'!!!\n",
4568 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
4569 iSH, sizeof(pSH->Name), pSH->Name));
4570 return VERR_BAD_EXE_FORMAT;
4571 }
4572
4573 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
4574 {
4575 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4576 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4577 return VERR_BAD_EXE_FORMAT;
4578 }
4579
4580 if (!(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
4581 {
4582 /* Calc VirtualSize if necessary. This is for internal reasons. */
4583 if ( pSH->Misc.VirtualSize == 0
4584 && fFixupVirtualSize)
4585 {
4586 pSH->Misc.VirtualSize = cbImage - RT_MIN(pSH->VirtualAddress, cbImage);
4587 for (uint32_t i = 1; i < cSHdrsLeft; i++)
4588 if ( !(pSH[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
4589 && pSH[i].VirtualAddress >= pSH->VirtualAddress)
4590 {
4591 pSH->Misc.VirtualSize = RT_MIN(pSH[i].VirtualAddress - pSH->VirtualAddress, pSH->Misc.VirtualSize);
4592 break;
4593 }
4594 }
4595
4596 if (pSH->Misc.VirtualSize > 0)
4597 {
4598 if (pSH->VirtualAddress < uRvaPrev)
4599 {
4600 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
4601 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
4602 return VERR_BAD_EXE_FORMAT;
4603 }
4604 if (pSH->VirtualAddress > cbImage)
4605 {
4606 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
4607 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
4608 return VERR_BAD_EXE_FORMAT;
4609 }
4610
4611 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
4612 {
4613 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4614 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4615 return VERR_BAD_EXE_FORMAT;
4616 }
4617
4618#ifdef PE_FILE_OFFSET_EQUALS_RVA
4619 /* Our loader code assume rva matches the file offset. */
4620 if ( pSH->SizeOfRawData
4621 && pSH->PointerToRawData != pSH->VirtualAddress)
4622 {
4623 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
4624 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
4625 return VERR_BAD_EXE_FORMAT;
4626 }
4627#endif
4628
4629 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
4630 }
4631 }
4632
4633 /* ignore the relocations and linenumbers. */
4634 }
4635
4636 /*
4637 * Do a separate run if we need to validate the no-code claim from the
4638 * optional header.
4639 */
4640 if (fNoCode)
4641 {
4642 pSH = &paSections[0];
4643 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4644 if (pSH->Characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE))
4645 return VERR_LDR_ARCH_MISMATCH;
4646 }
4647
4648
4649 /** @todo r=bird: more sanity checks! */
4650 return VINF_SUCCESS;
4651}
4652
4653
4654/**
4655 * Reads image data by RVA using the section headers.
4656 *
4657 * @returns iprt status code.
4658 * @param pModPe The PE module instance.
4659 * @param pvBuf Where to store the bits.
4660 * @param cb Number of bytes to tread.
4661 * @param RVA Where to read from.
4662 */
4663static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
4664{
4665 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
4666 PRTLDRREADER pReader = pModPe->Core.pReader;
4667 uint32_t cbRead;
4668 int rc;
4669
4670 /*
4671 * Is it the headers, i.e. prior to the first section.
4672 */
4673 if (RVA < pModPe->cbHeaders)
4674 {
4675 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
4676 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
4677 if ( cbRead == cb
4678 || RT_FAILURE(rc))
4679 return rc;
4680 cb -= cbRead;
4681 RVA += cbRead;
4682 pvBuf = (uint8_t *)pvBuf + cbRead;
4683 }
4684
4685 /* In the zero space between headers and the first section? */
4686 if (RVA < pSH->VirtualAddress)
4687 {
4688 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
4689 memset(pvBuf, 0, cbRead);
4690 if (cbRead == cb)
4691 return VINF_SUCCESS;
4692 cb -= cbRead;
4693 RVA += cbRead;
4694 pvBuf = (uint8_t *)pvBuf + cbRead;
4695 }
4696
4697 /*
4698 * Iterate the sections.
4699 */
4700 for (unsigned cLeft = pModPe->cSections;
4701 cLeft > 0;
4702 cLeft--, pSH++)
4703 {
4704 uint32_t off = RVA - pSH->VirtualAddress;
4705 if (off < pSH->Misc.VirtualSize)
4706 {
4707 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
4708 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
4709 if ( cbRead == cb
4710 || RT_FAILURE(rc))
4711 return rc;
4712 cb -= cbRead;
4713 RVA += cbRead;
4714 pvBuf = (uint8_t *)pvBuf + cbRead;
4715 }
4716 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
4717 if (RVA < RVANext)
4718 {
4719 cbRead = RT_MIN(RVANext - RVA, cb);
4720 memset(pvBuf, 0, cbRead);
4721 if (cbRead == cb)
4722 return VINF_SUCCESS;
4723 cb -= cbRead;
4724 RVA += cbRead;
4725 pvBuf = (uint8_t *)pvBuf + cbRead;
4726 }
4727 }
4728
4729 AssertFailed();
4730 return VERR_INTERNAL_ERROR;
4731}
4732
4733
4734/**
4735 * Validates the data of some selected data directories entries and remember
4736 * important bits for later.
4737 *
4738 * This requires a valid section table and thus has to wait till after we've
4739 * read and validated it.
4740 *
4741 * @returns iprt status code.
4742 * @param pModPe The PE module instance.
4743 * @param pOptHdr Pointer to the optional header (valid).
4744 * @param fFlags Loader flags, RTLDR_O_XXX.
4745 * @param pErrInfo Where to return extended error information. Optional.
4746 */
4747static int rtldrPEValidateDirectoriesAndRememberStuff(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags,
4748 PRTERRINFO pErrInfo)
4749{
4750 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
4751 union /* combine stuff we're reading to help reduce stack usage. */
4752 {
4753 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
4754 uint8_t abZeros[sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64) * 4];
4755 } u;
4756
4757 /*
4758 * The load config entry may include lock prefix tables and whatnot which we don't implement.
4759 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
4760 * actual data before we can make up our mind about it all.
4761 */
4762 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
4763 if (Dir.Size)
4764 {
4765 const size_t cbExpectV13 = !pModPe->f64Bit
4766 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V13)
4767 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V13);
4768 const size_t cbExpectV12 = !pModPe->f64Bit
4769 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V12)
4770 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V12);
4771 const size_t cbExpectV11 = !pModPe->f64Bit
4772 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V11)
4773 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V11);
4774 const size_t cbExpectV10 = !pModPe->f64Bit
4775 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V10)
4776 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V10);
4777 const size_t cbExpectV9 = !pModPe->f64Bit
4778 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V9)
4779 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V9);
4780 const size_t cbExpectV8 = !pModPe->f64Bit
4781 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V8)
4782 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V8);
4783 const size_t cbExpectV7 = !pModPe->f64Bit
4784 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V7)
4785 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7);
4786 const size_t cbExpectV6 = !pModPe->f64Bit
4787 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V6)
4788 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V6);
4789 const size_t cbExpectV5 = !pModPe->f64Bit
4790 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V5)
4791 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V5);
4792 const size_t cbExpectV4 = !pModPe->f64Bit
4793 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V4)
4794 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V4);
4795 const size_t cbExpectV3 = !pModPe->f64Bit
4796 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V3)
4797 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V3);
4798 const size_t cbExpectV2 = !pModPe->f64Bit
4799 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V2)
4800 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2);
4801 const size_t cbExpectV1 = !pModPe->f64Bit
4802 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V1)
4803 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2) /*No V1*/;
4804 const size_t cbNewHack = cbExpectV5; /* Playing safe here since there might've been revisions between V5 and V6 we don't know about . */
4805 const size_t cbMaxKnown = cbExpectV12;
4806
4807 bool fNewerStructureHack = false;
4808 if ( Dir.Size != cbExpectV13
4809 && Dir.Size != cbExpectV12
4810 && Dir.Size != cbExpectV11
4811 && Dir.Size != cbExpectV10
4812 && Dir.Size != cbExpectV9
4813 && Dir.Size != cbExpectV8
4814 && Dir.Size != cbExpectV7
4815 && Dir.Size != cbExpectV6
4816 && Dir.Size != cbExpectV5
4817 && Dir.Size != cbExpectV4
4818 && Dir.Size != cbExpectV3
4819 && Dir.Size != cbExpectV2
4820 && Dir.Size != cbExpectV1)
4821 {
4822 fNewerStructureHack = Dir.Size > cbNewHack /* These structure changes are slowly getting to us! More futher down. */
4823 && Dir.Size <= sizeof(u);
4824 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",
4825 pszLogName, Dir.Size, cbExpectV13, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1,
4826 fNewerStructureHack ? " Will try ignore extra bytes if all zero." : ""));
4827 if (!fNewerStructureHack)
4828 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4829 "Unexpected load config dir size of %u bytes; supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
4830 Dir.Size, cbExpectV13, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4831 }
4832
4833 /*
4834 * Read, check new stuff and convert to 64-bit.
4835 *
4836 * If we accepted a newer structures when loading for debug or validation,
4837 * otherwise we require the new bits to be all zero and hope that they are
4838 * insignificant where image loading is concerned (that's mostly been the
4839 * case even for non-zero bits, only hard exception is LockPrefixTable).
4840 */
4841 RT_ZERO(u.Cfg64);
4842 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4843 if (RT_FAILURE(rc))
4844 return rc;
4845 if ( fNewerStructureHack
4846 && Dir.Size > cbMaxKnown
4847 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4848 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4849 {
4850 Log(("rtldrPEOpen: %s: load cfg dir: Unexpected bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4851 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4852 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4853 "Grown load config (%u to %u bytes) includes non-zero bytes: %.*Rhxs",
4854 cbMaxKnown, Dir.Size, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4855 }
4856 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4857
4858 if (u.Cfg64.Size != Dir.Size)
4859 {
4860 /* Kludge #1: ntdll.dll from XP seen with Dir.Size=0x40 and Cfg64.Size=0x00. */
4861 if (Dir.Size == 0x40 && u.Cfg64.Size == 0x00 && !pModPe->f64Bit)
4862 {
4863 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the XP kludge.\n",
4864 pszLogName, u.Cfg64.Size, Dir.Size));
4865 u.Cfg64.Size = Dir.Size;
4866 }
4867 /* Kludge #2: This happens a lot. Structure changes, but the linker doesn't get
4868 updated and stores some old size in the directory. Use the header size. */
4869 else if ( u.Cfg64.Size == cbExpectV13
4870 || u.Cfg64.Size == cbExpectV12
4871 || u.Cfg64.Size == cbExpectV11
4872 || u.Cfg64.Size == cbExpectV10
4873 || u.Cfg64.Size == cbExpectV9
4874 || u.Cfg64.Size == cbExpectV8
4875 || u.Cfg64.Size == cbExpectV7
4876 || u.Cfg64.Size == cbExpectV6
4877 || u.Cfg64.Size == cbExpectV5
4878 || u.Cfg64.Size == cbExpectV4
4879 || u.Cfg64.Size == cbExpectV3
4880 || u.Cfg64.Size == cbExpectV2
4881 || u.Cfg64.Size == cbExpectV1
4882 || (fNewerStructureHack = (u.Cfg64.Size > cbNewHack && u.Cfg64.Size <= sizeof(u))) )
4883 {
4884 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the old linker kludge.\n",
4885 pszLogName, u.Cfg64.Size, Dir.Size));
4886
4887 uint32_t const uOrgDir = Dir.Size;
4888 Dir.Size = u.Cfg64.Size;
4889 RT_ZERO(u.Cfg64);
4890 rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4891 if (RT_FAILURE(rc))
4892 return rc;
4893 if ( fNewerStructureHack
4894 && Dir.Size > cbMaxKnown
4895 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4896 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4897 {
4898 Log(("rtldrPEOpen: %s: load cfg dir: Unknown bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4899 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4900 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4901 "Grown load config (%u to %u bytes, dir %u) includes non-zero bytes: %.*Rhxs",
4902 cbMaxKnown, Dir.Size, uOrgDir, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4903 }
4904 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4905 AssertReturn(u.Cfg64.Size == Dir.Size,
4906 RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE, "Data changed while reading! (%d vs %d)\n",
4907 u.Cfg64.Size, Dir.Size));
4908 }
4909 else
4910 {
4911 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",
4912 pszLogName, u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1));
4913 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4914 "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",
4915 u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4916 }
4917 }
4918 if (u.Cfg64.LockPrefixTable && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4919 {
4920 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
4921 pszLogName, u.Cfg64.LockPrefixTable));
4922 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOCK_PREFIX_TABLE,
4923 "Lock prefix table not supported: %RX64", u.Cfg64.LockPrefixTable);
4924 }
4925#if 0/* this seems to be safe to ignore. */
4926 if ( u.Cfg64.SEHandlerTable
4927 || u.Cfg64.SEHandlerCount)
4928 {
4929 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
4930 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
4931 return VERR_BAD_EXE_FORMAT;
4932 }
4933#endif
4934 if (u.Cfg64.EditList && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4935 {
4936 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
4937 pszLogName, u.Cfg64.EditList));
4938 return RTErrInfoSetF(pErrInfo, VERR_BAD_EXE_FORMAT, "Load config EditList=%RX64 is not supported", u.Cfg64.EditList);
4939 }
4940 /** @todo GuardCFC? Possibly related to:
4941 * http://research.microsoft.com/pubs/69217/ccs05-cfi.pdf
4942 * Not trusting something designed by bakas who don't know how to modify a
4943 * structure without messing up its natural alignment. */
4944 if ( ( u.Cfg64.GuardCFCCheckFunctionPointer
4945 || u.Cfg64.GuardCFDispatchFunctionPointer
4946 || u.Cfg64.GuardCFFunctionTable
4947 || u.Cfg64.GuardCFFunctionCount
4948 || u.Cfg64.GuardFlags
4949 || u.Cfg64.GuardAddressTakenIatEntryTable
4950 || u.Cfg64.GuardAddressTakenIatEntryCount
4951 || u.Cfg64.GuardLongJumpTargetTable
4952 || u.Cfg64.GuardLongJumpTargetCount)
4953 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) )
4954 {
4955 Log(("rtldrPEOpen: %s: load cfg dir: Guard stuff: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!\n",
4956 pszLogName, u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4957 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4958 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4959 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount ));
4960#if 0 /* ntdll 15002 uses this. */
4961 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_GUARD_CF_STUFF,
4962 "Guard bits in load config: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!",
4963 u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4964 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4965 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4966 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount);
4967#endif
4968 }
4969 }
4970
4971 /*
4972 * If the image is signed and we're not doing this for debug purposes,
4973 * take a look at the signature.
4974 */
4975 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4976 if (Dir.Size)
4977 {
4978 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
4979 if (!pFirst)
4980 return VERR_NO_TMP_MEMORY;
4981 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
4982 if (RT_SUCCESS(rc))
4983 {
4984 uint32_t off = 0;
4985 do
4986 {
4987 PWIN_CERTIFICATE pCur = (PWIN_CERTIFICATE)((uint8_t *)pFirst + off);
4988
4989 /* validate the members. */
4990 if ( pCur->dwLength < sizeof(WIN_CERTIFICATE)
4991 || pCur->dwLength + off > Dir.Size)
4992 {
4993 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
4994 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4995 "Cert at %#x LB %#x: Bad header length value: %#x", off, Dir.Size, pCur->dwLength);
4996 break;
4997 }
4998 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
4999 && pCur->wRevision != WIN_CERT_REVISION_1_0)
5000 {
5001 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
5002 if (pCur->wRevision >= WIN_CERT_REVISION_1_0)
5003 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
5004 "Cert at %#x LB %#x: Unsupported revision: %#x", off, Dir.Size, pCur->wRevision);
5005 else
5006 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
5007 "Cert at %#x LB %#x: Malformed revision: %#x", off, Dir.Size, pCur->wRevision);
5008 break;
5009 }
5010 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
5011 && pCur->wCertificateType != WIN_CERT_TYPE_X509
5012 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
5013 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
5014 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
5015 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
5016 )
5017 {
5018 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wCertificateType=%#x\n", pszLogName, off, Dir.Size, pCur->wCertificateType));
5019 if (pCur->wCertificateType)
5020 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
5021 "Cert at %#x LB %#x: Unsupported certificate type: %#x",
5022 off, Dir.Size, pCur->wCertificateType);
5023 else
5024 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
5025 "Cert at %#x LB %#x: Malformed certificate type: %#x",
5026 off, Dir.Size, pCur->wCertificateType);
5027 break;
5028 }
5029
5030 /* Remember the first signed data certificate. */
5031 if ( pCur->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA
5032 && pModPe->offPkcs7SignedData == 0)
5033 {
5034 pModPe->offPkcs7SignedData = Dir.VirtualAddress
5035 + (uint32_t)((uintptr_t)&pCur->bCertificate[0] - (uintptr_t)pFirst);
5036 pModPe->cbPkcs7SignedData = pCur->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
5037 }
5038
5039 /* next */
5040 off += RT_ALIGN(pCur->dwLength, WIN_CERTIFICATE_ALIGNMENT);
5041 } while (off < Dir.Size);
5042 }
5043 RTMemTmpFree(pFirst);
5044 if (RT_FAILURE(rc) && !(fFlags & RTLDR_O_FOR_DEBUG))
5045 return rc;
5046 }
5047
5048 return VINF_SUCCESS;
5049}
5050
5051
5052/**
5053 * Open a PE image.
5054 *
5055 * @returns iprt status code.
5056 * @param pReader The loader reader instance which will provide the raw image bits.
5057 * @param fFlags Loader flags, RTLDR_O_XXX.
5058 * @param enmArch Architecture specifier.
5059 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
5060 * @param phLdrMod Where to store the handle.
5061 * @param pErrInfo Where to return extended error information. Optional.
5062 */
5063DECLHIDDEN(int) rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs,
5064 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
5065{
5066 /*
5067 * Read and validate the file header.
5068 */
5069 IMAGE_FILE_HEADER FileHdr;
5070 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
5071 if (RT_FAILURE(rc))
5072 return rc;
5073 RTLDRARCH enmArchImage;
5074 const char *pszLogName = pReader->pfnLogName(pReader);
5075 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage, pErrInfo);
5076 if (RT_FAILURE(rc))
5077 return rc;
5078
5079 /*
5080 * Match the CPU architecture.
5081 */
5082 bool fArchNoCodeCheckPending = false;
5083 if ( enmArch != enmArchImage
5084 && ( enmArch != RTLDRARCH_WHATEVER
5085 && !(fFlags & RTLDR_O_WHATEVER_ARCH)) )
5086 {
5087 if (!(fFlags & RTLDR_O_IGNORE_ARCH_IF_NO_CODE))
5088 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Image is for '%s', only accepting images for '%s'.",
5089 rtldrPEGetArchName(FileHdr.Machine), RTLdrArchName(enmArch));
5090 fArchNoCodeCheckPending = true;
5091 }
5092
5093 /*
5094 * Read and validate the "optional" header. Convert 32->64 if necessary.
5095 */
5096 IMAGE_OPTIONAL_HEADER64 OptHdr;
5097 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
5098 if (RT_FAILURE(rc))
5099 return rc;
5100 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
5101 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
5102 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags, pErrInfo);
5103 if (RT_FAILURE(rc))
5104 return rc;
5105 if (fArchNoCodeCheckPending && OptHdr.SizeOfCode != 0)
5106 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH,
5107 "Image is for '%s' and contains code (%#x), only accepting images for '%s' with code.",
5108 rtldrPEGetArchName(FileHdr.Machine), OptHdr.SizeOfCode, RTLdrArchName(enmArch));
5109
5110 /*
5111 * Read and validate section headers.
5112 */
5113 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
5114 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
5115 if (!paSections)
5116 return VERR_NO_MEMORY;
5117 rc = pReader->pfnRead(pReader, paSections, cbSections,
5118 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
5119 if (RT_SUCCESS(rc))
5120 {
5121 rc = rtldrPEValidateAndTouchUpSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
5122 &OptHdr, pReader->pfnSize(pReader), fFlags, fArchNoCodeCheckPending);
5123 if (RT_SUCCESS(rc))
5124 {
5125 /*
5126 * Allocate and initialize the PE module structure.
5127 */
5128 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
5129 if (pModPe)
5130 {
5131 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
5132 pModPe->Core.eState = LDR_STATE_OPENED;
5133 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
5134 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
5135 else
5136 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
5137 pModPe->Core.pReader = pReader;
5138 pModPe->Core.enmFormat= RTLDRFMT_PE;
5139 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
5140 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
5141 ? RTLDRTYPE_EXECUTABLE_FIXED
5142 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
5143 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
5144 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
5145 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
5146 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
5147 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
5148 ? RTLDRARCH_X86_32
5149 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
5150 ? RTLDRARCH_AMD64
5151 : RTLDRARCH_WHATEVER;
5152 pModPe->pvBits = NULL;
5153 pModPe->offNtHdrs = offNtHdrs;
5154 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
5155 pModPe->u16Machine = FileHdr.Machine;
5156 pModPe->fFile = FileHdr.Characteristics;
5157 pModPe->cSections = FileHdr.NumberOfSections;
5158 pModPe->paSections = paSections;
5159 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
5160 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
5161 pModPe->cbImage = OptHdr.SizeOfImage;
5162 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
5163 pModPe->uSectionAlign = OptHdr.SectionAlignment;
5164 pModPe->uTimestamp = FileHdr.TimeDateStamp;
5165 pModPe->cImports = UINT32_MAX;
5166 pModPe->f64Bit = FileHdr.SizeOfOptionalHeader == sizeof(OptHdr);
5167 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
5168 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
5169 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
5170 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
5171 pModPe->SecurityDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
5172 pModPe->ExceptionDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
5173 pModPe->fDllCharacteristics = OptHdr.DllCharacteristics;
5174
5175 /*
5176 * Perform validation of some selected data directories which requires
5177 * inspection of the actual data. This also saves some certificate
5178 * information.
5179 */
5180 rc = rtldrPEValidateDirectoriesAndRememberStuff(pModPe, &OptHdr, fFlags, pErrInfo);
5181 if (RT_SUCCESS(rc))
5182 {
5183 *phLdrMod = &pModPe->Core;
5184 return VINF_SUCCESS;
5185 }
5186 RTMemFree(pModPe);
5187 }
5188 else
5189 rc = VERR_NO_MEMORY;
5190 }
5191 }
5192 RTMemFree(paSections);
5193 return rc;
5194}
5195
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use