VirtualBox

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

Last change on this file since 74355 was 73758, checked in by vboxsync, 7 years ago

IPRT/ldrPE: Fixed bug in Seg+Offset -> RVA translation method.

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