VirtualBox

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

Last change on this file since 82608 was 81120, checked in by vboxsync, 5 years ago

IPRT/ldrPE: Slienced import assertion for IN_SUP_HARDENED_R3 as it would trigger when conemu and such infiltrates the first process in a hardening chain. bugref:3103

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

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