VirtualBox

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

Last change on this file since 74745 was 74721, checked in by vboxsync, 7 years ago

IPRT/ldr: More Mach-O signing hacking. bugref:9232

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 189.4 KB
Line 
1/* $Id: ldrPE.cpp 74721 2018-10-09 16:04:49Z 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.215389.xyz. 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 else
2592 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_EXPECTED_INDIRECT_DATA_CONTENT_OID, /** @todo error code*/
2593 "PKCS#7 is not 'signedData': %s", pSignature->ContentInfo.ContentType.szObjId);
2594 }
2595 return rc;
2596}
2597
2598
2599static int rtldrPE_VerifyAllPageHashes(PRTLDRMODPE pModPe, PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib, RTDIGESTTYPE enmDigest,
2600 void *pvScratch, size_t cbScratch, PRTERRINFO pErrInfo)
2601{
2602 AssertReturn(cbScratch >= _4K, VERR_INTERNAL_ERROR_3);
2603
2604 /*
2605 * Calculate the special places.
2606 */
2607 RTLDRPEHASHSPECIALS SpecialPlaces = { 0, 0, 0, 0, 0, 0 }; /* shut up gcc */
2608 int rc = rtldrPe_CalcSpecialHashPlaces(pModPe, &SpecialPlaces, pErrInfo);
2609 if (RT_FAILURE(rc))
2610 return rc;
2611
2612 uint32_t const cbHash = rtLdrPE_HashGetHashSize(enmDigest);
2613 uint32_t const cPages = pAttrib->u.pPageHashes->RawData.Asn1Core.cb / (cbHash + 4);
2614 if (cPages * (cbHash + 4) != pAttrib->u.pPageHashes->RawData.Asn1Core.cb)
2615 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_SIZE_OVERFLOW,
2616 "Page hashes size issue: cb=%#x cbHash=%#x",
2617 pAttrib->u.pPageHashes->RawData.Asn1Core.cb, cbHash);
2618
2619 /*
2620 * Walk the table.
2621 */
2622 uint32_t const cbScratchReadMax = cbScratch & ~(uint32_t)(_4K - 1);
2623 uint32_t cbScratchRead = 0;
2624 uint32_t offScratchRead = 0;
2625
2626 uint32_t offPrev = 0;
2627#ifdef COMPLICATED_AND_WRONG
2628 uint32_t offSectEnd = pModPe->cbHeaders;
2629 uint32_t iSh = UINT32_MAX;
2630#endif
2631 uint8_t const *pbHashTab = pAttrib->u.pPageHashes->RawData.Asn1Core.uData.pu8;
2632 for (uint32_t iPage = 0; iPage < cPages - 1; iPage++)
2633 {
2634 /* Decode the page offset. */
2635 uint32_t const offPageInFile = RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]);
2636 if (RT_UNLIKELY(offPageInFile >= SpecialPlaces.cbToHash))
2637 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
2638 "Page hash entry #%u is beyond the signature table start: %#x, %#x",
2639 iPage, offPageInFile, SpecialPlaces.cbToHash);
2640 if (RT_UNLIKELY(offPageInFile < offPrev))
2641 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_NOT_STRICTLY_SORTED,
2642 "Page hash table is not strictly sorted: entry #%u @%#x, previous @%#x\n",
2643 iPage, offPageInFile, offPrev);
2644
2645#ifdef COMPLICATED_AND_WRONG
2646 /* Figure out how much to read and how much to zero. Need keep track
2647 of the on-disk section boundraries. */
2648 if (offPageInFile >= offSectEnd)
2649 {
2650 iSh++;
2651 if ( iSh < pModPe->cSections
2652 && offPageInFile - pModPe->paSections[iSh].PointerToRawData < pModPe->paSections[iSh].SizeOfRawData)
2653 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
2654 else
2655 {
2656 iSh = 0;
2657 while ( iSh < pModPe->cSections
2658 && offPageInFile - pModPe->paSections[iSh].PointerToRawData >= pModPe->paSections[iSh].SizeOfRawData)
2659 iSh++;
2660 if (iSh < pModPe->cSections)
2661 offSectEnd = pModPe->paSections[iSh].PointerToRawData + pModPe->paSections[iSh].SizeOfRawData;
2662 else
2663 return RTErrInfoSetF(pErrInfo, VERR_PAGE_HASH_TAB_HASHES_NON_SECTION_DATA,
2664 "Page hash entry #%u isn't in any section: %#x", iPage, offPageInFile);
2665 }
2666 }
2667
2668#else
2669 /* Figure out how much to read and how much take as zero. Use the next
2670 page offset and the signature as upper boundraries. */
2671#endif
2672 uint32_t cbPageInFile = _4K;
2673#ifdef COMPLICATED_AND_WRONG
2674 if (offPageInFile + cbPageInFile > offSectEnd)
2675 cbPageInFile = offSectEnd - offPageInFile;
2676#else
2677 if (iPage + 1 < cPages)
2678 {
2679 uint32_t offNextPage = RT_MAKE_U32_FROM_U8(pbHashTab[0 + 4 + cbHash], pbHashTab[1 + 4 + cbHash],
2680 pbHashTab[2 + 4 + cbHash], pbHashTab[3 + 4 + cbHash]);
2681 if (offNextPage - offPageInFile < cbPageInFile)
2682 cbPageInFile = offNextPage - offPageInFile;
2683 }
2684#endif
2685
2686 if (offPageInFile + cbPageInFile > SpecialPlaces.cbToHash)
2687 cbPageInFile = SpecialPlaces.cbToHash - offPageInFile;
2688
2689 /* Did we get a cache hit? */
2690 uint8_t *pbCur = (uint8_t *)pvScratch;
2691 if ( offPageInFile + cbPageInFile <= offScratchRead + cbScratchRead
2692 && offPageInFile >= offScratchRead)
2693 pbCur += offPageInFile - offScratchRead;
2694 /* Missed, read more. */
2695 else
2696 {
2697 offScratchRead = offPageInFile;
2698#ifdef COMPLICATED_AND_WRONG
2699 cbScratchRead = offSectEnd - offPageInFile;
2700#else
2701 cbScratchRead = SpecialPlaces.cbToHash - offPageInFile;
2702#endif
2703 if (cbScratchRead > cbScratchReadMax)
2704 cbScratchRead = cbScratchReadMax;
2705 rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pbCur, cbScratchRead, offScratchRead);
2706 if (RT_FAILURE(rc))
2707 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_READ_ERROR_HASH,
2708 "Page hash read error at %#x: %Rrc (cbScratchRead=%#zx)",
2709 offScratchRead, rc, cbScratchRead);
2710 }
2711
2712 /*
2713 * Hash it.
2714 */
2715 RTLDRPEHASHCTXUNION HashCtx;
2716 rc = rtLdrPE_HashInit(&HashCtx, enmDigest);
2717 AssertRCReturn(rc, rc);
2718
2719 /* Deal with special places. */
2720 uint32_t cbLeft = cbPageInFile;
2721 if (offPageInFile < SpecialPlaces.offEndSpecial)
2722 {
2723 uint32_t off = offPageInFile;
2724 if (off < SpecialPlaces.offCksum)
2725 {
2726 /* Hash everything up to the checksum. */
2727 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum - off, cbLeft);
2728 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2729 pbCur += cbChunk;
2730 cbLeft -= cbChunk;
2731 off += cbChunk;
2732 }
2733
2734 if (off < SpecialPlaces.offCksum + SpecialPlaces.cbCksum && off >= SpecialPlaces.offCksum)
2735 {
2736 /* Skip the checksum */
2737 uint32_t cbChunk = RT_MIN(SpecialPlaces.offCksum + SpecialPlaces.cbCksum - off, cbLeft);
2738 pbCur += cbChunk;
2739 cbLeft -= cbChunk;
2740 off += cbChunk;
2741 }
2742
2743 if (off < SpecialPlaces.offSecDir && off >= SpecialPlaces.offCksum + SpecialPlaces.cbCksum)
2744 {
2745 /* Hash everything between the checksum and the data dir entry. */
2746 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir - off, cbLeft);
2747 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbChunk);
2748 pbCur += cbChunk;
2749 cbLeft -= cbChunk;
2750 off += cbChunk;
2751 }
2752
2753 if (off < SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir && off >= SpecialPlaces.offSecDir)
2754 {
2755 /* Skip the security data directory entry. */
2756 uint32_t cbChunk = RT_MIN(SpecialPlaces.offSecDir + SpecialPlaces.cbSecDir - off, cbLeft);
2757 pbCur += cbChunk;
2758 cbLeft -= cbChunk;
2759 off += cbChunk;
2760 }
2761 }
2762
2763 rtLdrPE_HashUpdate(&HashCtx, enmDigest, pbCur, cbLeft);
2764 if (cbPageInFile < _4K)
2765 rtLdrPE_HashUpdate(&HashCtx, enmDigest, &g_abRTZero4K[cbPageInFile], _4K - cbPageInFile);
2766
2767 /*
2768 * Finish the hash calculation and compare the result.
2769 */
2770 RTLDRPEHASHRESUNION HashRes;
2771 rtLdrPE_HashFinalize(&HashCtx, enmDigest, &HashRes);
2772
2773 pbHashTab += 4;
2774 if (memcmp(pbHashTab, &HashRes, cbHash) != 0)
2775 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_MISMATCH,
2776 "Page hash failed for page #%u, @%#x, %#x bytes: %.*Rhxs != %.*Rhxs",
2777 iPage, offPageInFile, cbPageInFile, (size_t)cbHash, pbHashTab, (size_t)cbHash, &HashRes);
2778 pbHashTab += cbHash;
2779 offPrev = offPageInFile;
2780 }
2781
2782 /*
2783 * Check that the last table entry has a hash value of zero.
2784 */
2785 if (!ASMMemIsZero(pbHashTab + 4, cbHash))
2786 return RTErrInfoSetF(pErrInfo, VERR_LDRVI_PAGE_HASH_TAB_TOO_LONG,
2787 "Maltform final page hash table entry: #%u %#010x %.*Rhxs",
2788 cPages - 1, RT_MAKE_U32_FROM_U8(pbHashTab[0], pbHashTab[1], pbHashTab[2], pbHashTab[3]),
2789 (size_t)cbHash, pbHashTab + 4);
2790 return VINF_SUCCESS;
2791}
2792
2793
2794/**
2795 * Validates the image hash, including page hashes if present.
2796 *
2797 * @returns IPRT status code.
2798 * @param pModPe The PE module.
2799 * @param pSignature The decoded signature data.
2800 * @param pErrInfo Optional error info buffer.
2801 */
2802static int rtldrPE_VerifySignatureValidateHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
2803{
2804 AssertReturn(pSignature->enmDigest > RTDIGESTTYPE_INVALID && pSignature->enmDigest < RTDIGESTTYPE_END, VERR_INTERNAL_ERROR_4);
2805 AssertPtrReturn(pSignature->pIndData, VERR_INTERNAL_ERROR_5);
2806 AssertReturn(RTASN1CORE_IS_PRESENT(&pSignature->pIndData->DigestInfo.Digest.Asn1Core), VERR_INTERNAL_ERROR_5);
2807 AssertPtrReturn(pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, VERR_INTERNAL_ERROR_5);
2808
2809 uint32_t const cbHash = rtLdrPE_HashGetHashSize(pSignature->enmDigest);
2810 AssertReturn(pSignature->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash, VERR_INTERNAL_ERROR_5);
2811
2812 /*
2813 * Allocate a temporary memory buffer.
2814 * Note! The _4K that gets subtracted is to avoid that the 16-byte heap
2815 * block header in ring-0 (iprt) caused any unnecessary internal
2816 * heap fragmentation.
2817 */
2818#ifdef IN_RING0
2819 uint32_t cbScratch = _256K - _4K;
2820#else
2821 uint32_t cbScratch = _1M;
2822#endif
2823 void *pvScratch = RTMemTmpAlloc(cbScratch);
2824 if (!pvScratch)
2825 {
2826 cbScratch = _4K;
2827 pvScratch = RTMemTmpAlloc(cbScratch);
2828 if (!pvScratch)
2829 return RTErrInfoSet(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate 4KB of scratch space for hashing image.");
2830 }
2831
2832 /*
2833 * Calculate and compare the full image hash.
2834 */
2835 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, pSignature->enmDigest,
2836 &pSignature->HashCtx, &pSignature->HashRes, pErrInfo);
2837 if (RT_SUCCESS(rc))
2838 {
2839 if (!memcmp(&pSignature->HashRes, pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash))
2840 {
2841 /*
2842 * Compare the page hashes if present.
2843 *
2844 * Seems the difference between V1 and V2 page hash attributes is
2845 * that v1 uses SHA-1 while v2 uses SHA-256. The data structures
2846 * seems to be identical otherwise. Initially we assumed the digest
2847 * algorithm was supposed to be RTCRSPCINDIRECTDATACONTENT::DigestInfo,
2848 * i.e. the same as for the whole image hash. The initial approach
2849 * worked just fine, but this makes more sense.
2850 *
2851 * (See also comments in osslsigncode.c (google it).)
2852 */
2853 PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE pAttrib;
2854 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pSignature->pIndData,
2855 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2);
2856 if (pAttrib)
2857 rc = rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA256, pvScratch, cbScratch, pErrInfo);
2858 else
2859 {
2860 pAttrib = RTCrSpcIndirectDataContent_GetPeImageObjAttrib(pSignature->pIndData,
2861 RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1);
2862 if (pAttrib)
2863 rc = rtldrPE_VerifyAllPageHashes(pModPe, pAttrib, RTDIGESTTYPE_SHA1, pvScratch, cbScratch, pErrInfo);
2864 }
2865 }
2866 else
2867 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
2868 "Full image signature mismatch: %.*Rhxs, expected %.*Rhxs",
2869 cbHash, &pSignature->HashRes,
2870 cbHash, pSignature->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
2871 }
2872
2873 RTMemTmpFree(pvScratch);
2874 return rc;
2875}
2876
2877#endif /* !IPRT_WITHOUT_LDR_VERIFY */
2878
2879
2880/** @interface_method_impl{RTLDROPS,pfnVerifySignature} */
2881static DECLCALLBACK(int) rtldrPE_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser,
2882 PRTERRINFO pErrInfo)
2883{
2884#ifndef IPRT_WITHOUT_LDR_VERIFY
2885 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2886
2887 int rc = rtldrPE_VerifySignatureImagePrecoditions(pModPe, pErrInfo);
2888 if (RT_SUCCESS(rc))
2889 {
2890 PRTLDRPESIGNATURE pSignature = NULL;
2891 rc = rtldrPE_VerifySignatureRead(pModPe, &pSignature, pErrInfo);
2892 if (RT_SUCCESS(rc))
2893 {
2894 rc = rtldrPE_VerifySignatureDecode(pModPe, pSignature, pErrInfo);
2895 if (RT_SUCCESS(rc))
2896 rc = rtldrPE_VerifySignatureValidateHash(pModPe, pSignature, pErrInfo);
2897 if (RT_SUCCESS(rc))
2898 {
2899 rc = pfnCallback(&pModPe->Core, RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA,
2900 &pSignature->ContentInfo, sizeof(pSignature->ContentInfo),
2901 pErrInfo, pvUser);
2902 }
2903 rtldrPE_VerifySignatureDestroy(pModPe, pSignature);
2904 }
2905 }
2906 return rc;
2907#else
2908 RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
2909 return VERR_NOT_SUPPORTED;
2910#endif
2911}
2912
2913
2914
2915/**
2916 * @interface_method_impl{RTLDROPS,pfnHashImage}
2917 */
2918static DECLCALLBACK(int) rtldrPE_HashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, char *pszDigest, size_t cbDigest)
2919{
2920 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
2921
2922 /*
2923 * Allocate a temporary memory buffer.
2924 */
2925 uint32_t cbScratch = _16K;
2926 void *pvScratch = RTMemTmpAlloc(cbScratch);
2927 if (!pvScratch)
2928 {
2929 cbScratch = _4K;
2930 pvScratch = RTMemTmpAlloc(cbScratch);
2931 if (!pvScratch)
2932 return VERR_NO_TMP_MEMORY;
2933 }
2934
2935 /*
2936 * Do the hashing.
2937 */
2938 RTLDRPEHASHCTXUNION HashCtx;
2939 RTLDRPEHASHRESUNION HashRes;
2940 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, enmDigest, &HashCtx, &HashRes, NULL);
2941 if (RT_SUCCESS(rc))
2942 {
2943 /*
2944 * Format the digest into as human readable hash string.
2945 */
2946 switch (enmDigest)
2947 {
2948 case RTDIGESTTYPE_SHA512: rc = RTSha512ToString(HashRes.abSha512, pszDigest, cbDigest); break;
2949 case RTDIGESTTYPE_SHA256: rc = RTSha256ToString(HashRes.abSha256, pszDigest, cbDigest); break;
2950 case RTDIGESTTYPE_SHA1: rc = RTSha1ToString(HashRes.abSha1, pszDigest, cbDigest); break;
2951 case RTDIGESTTYPE_MD5: rc = RTMd5ToString(HashRes.abMd5, pszDigest, cbDigest); break;
2952 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
2953 }
2954 }
2955 return rc;
2956}
2957
2958
2959/**
2960 * Binary searches the lookup table.
2961 *
2962 * @returns RVA of unwind info on success, UINT32_MAX on failure.
2963 * @param paFunctions The table to lookup @a uRva in.
2964 * @param iEnd Size of the table.
2965 * @param uRva The RVA of the function we want.
2966 */
2967DECLINLINE(PCIMAGE_RUNTIME_FUNCTION_ENTRY)
2968rtldrPE_LookupRuntimeFunctionEntry(PCIMAGE_RUNTIME_FUNCTION_ENTRY paFunctions, size_t iEnd, uint32_t uRva)
2969{
2970 size_t iBegin = 0;
2971 while (iBegin < iEnd)
2972 {
2973 size_t const i = iBegin + (iEnd - iBegin) / 2;
2974 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = &paFunctions[i];
2975 if (uRva < pEntry->BeginAddress)
2976 iEnd = i;
2977 else if (uRva > pEntry->EndAddress)
2978 iBegin = i + 1;
2979 else
2980 return pEntry;
2981 }
2982 return NULL;
2983}
2984
2985
2986/**
2987 * Processes an IRET frame.
2988 *
2989 * @returns IPRT status code.
2990 * @param pState The unwind state being worked.
2991 * @param fErrCd Non-zero if there is an error code on the stack.
2992 */
2993static int rtldrPE_UnwindFrame_Amd64_IRet(PRTDBGUNWINDSTATE pState, uint8_t fErrCd)
2994{
2995 /* POP ErrCd (optional): */
2996 Assert(fErrCd <= 1);
2997 int rcRet;
2998 if (fErrCd)
2999 {
3000 pState->u.x86.uErrCd = 0;
3001 pState->u.x86.Loaded.s.fErrCd = 1;
3002 rcRet = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uErrCd);
3003 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3004 }
3005 else
3006 {
3007 pState->u.x86.Loaded.s.fErrCd = 0;
3008 rcRet = VINF_SUCCESS;
3009 }
3010
3011 /* Set return type and frame pointer. */
3012 pState->enmRetType = RTDBGRETURNTYPE_IRET64;
3013 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3014 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3015
3016 /* POP RIP: */
3017 int rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3018 if (RT_FAILURE(rc))
3019 rcRet = rc;
3020 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3021
3022 /* POP CS: */
3023 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_CS]);
3024 if (RT_FAILURE(rc))
3025 rcRet = rc;
3026 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3027
3028 /* POP RFLAGS: */
3029 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uRFlags);
3030 if (RT_FAILURE(rc))
3031 rcRet = rc;
3032 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3033
3034 /* POP RSP, part 1: */
3035 uint64_t uNewRsp = (pState->u.x86.auRegs[X86_GREG_xSP] - 8) & ~(uint64_t)15;
3036 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &uNewRsp);
3037 if (RT_FAILURE(rc))
3038 rcRet = rc;
3039 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3040
3041 /* POP SS: */
3042 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_SS]);
3043 if (RT_FAILURE(rc))
3044 rcRet = rc;
3045 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3046
3047 /* POP RSP, part 2: */
3048 pState->u.x86.auRegs[X86_GREG_xSP] = uNewRsp;
3049
3050 /* Set loaded indicators: */
3051 pState->u.x86.Loaded.s.fRegs |= RT_BIT(X86_GREG_xSP);
3052 pState->u.x86.Loaded.s.fSegs |= RT_BIT(X86_SREG_CS) | RT_BIT(X86_SREG_SS);
3053 pState->u.x86.Loaded.s.fPc = 1;
3054 pState->u.x86.Loaded.s.fFrameAddr = 1;
3055 pState->u.x86.Loaded.s.fRFlags = 1;
3056 return VINF_SUCCESS;
3057}
3058
3059
3060static int rtldrPE_UnwindFrame_Amd64(PRTLDRMODPE pThis, void const *pvBits, PRTDBGUNWINDSTATE pState, uint32_t uRvaPc,
3061 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry)
3062{
3063 /* Did we find any unwind information? */
3064 if (!pEntry)
3065 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3066
3067 /*
3068 * Do the unwinding.
3069 */
3070 IMAGE_RUNTIME_FUNCTION_ENTRY ChainedEntry;
3071 unsigned iFrameReg = ~0U;
3072 unsigned offFrameReg = 0;
3073
3074 int fInEpilog = -1; /* -1: not-determined-assume-false; 0: false; 1: true. */
3075 uint8_t cbEpilog = 0;
3076 uint8_t offEpilog = UINT8_MAX;
3077 int rcRet = VINF_SUCCESS;
3078 int rc;
3079 for (unsigned cChainLoops = 0; ; cChainLoops++)
3080 {
3081 /*
3082 * Get the info.
3083 */
3084 union
3085 {
3086 uint32_t uRva;
3087 uint8_t ab[ RT_OFFSETOF(IMAGE_UNWIND_INFO, aOpcodes)
3088 + sizeof(IMAGE_UNWIND_CODE) * 256
3089 + sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)];
3090 } uBuf;
3091 rc = rtldrPEReadPartByRvaInfoBuf(pThis, pvBits, pEntry->UnwindInfoAddress, sizeof(uBuf), &uBuf);
3092 if (RT_FAILURE(rc))
3093 return rc;
3094
3095 /*
3096 * Check the info.
3097 */
3098 ASMCompilerBarrier(); /* we're aliasing */
3099 PCIMAGE_UNWIND_INFO pInfo = (PCIMAGE_UNWIND_INFO)&uBuf;
3100
3101 if (pInfo->Version != 1 && pInfo->Version != 2)
3102 return VERR_DBG_MALFORMED_UNWIND_INFO;
3103
3104 /*
3105 * Execute the opcodes.
3106 */
3107 unsigned const cOpcodes = pInfo->CountOfCodes;
3108 unsigned iOpcode = 0;
3109
3110 /*
3111 * Check for epilog opcodes at the start and see if we're in an epilog.
3112 */
3113 if ( pInfo->Version >= 2
3114 && iOpcode < cOpcodes
3115 && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3116 {
3117 if (fInEpilog == -1)
3118 {
3119 cbEpilog = pInfo->aOpcodes[iOpcode].u.CodeOffset;
3120 Assert(cbEpilog > 0);
3121
3122 uint32_t uRvaEpilog = pEntry->EndAddress - cbEpilog;
3123 iOpcode++;
3124 if ( (pInfo->aOpcodes[iOpcode - 1].u.OpInfo & 1)
3125 && uRvaPc >= uRvaEpilog)
3126 {
3127 offEpilog = uRvaPc - uRvaEpilog;
3128 fInEpilog = 1;
3129 }
3130 else
3131 {
3132 fInEpilog = 0;
3133 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3134 {
3135 uRvaEpilog = pEntry->EndAddress
3136 - (pInfo->aOpcodes[iOpcode].u.CodeOffset + (pInfo->aOpcodes[iOpcode].u.OpInfo << 8));
3137 iOpcode++;
3138 if (uRvaPc - uRvaEpilog < cbEpilog)
3139 {
3140 offEpilog = uRvaPc - uRvaEpilog;
3141 fInEpilog = 1;
3142 break;
3143 }
3144 }
3145 }
3146 }
3147 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3148 iOpcode++;
3149 }
3150 if (fInEpilog != 1)
3151 {
3152 /*
3153 * Skip opcodes that doesn't apply to us if we're in the prolog.
3154 */
3155 uint32_t offPc = uRvaPc - pEntry->BeginAddress;
3156 if (offPc < pInfo->SizeOfProlog)
3157 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.CodeOffset > offPc)
3158 iOpcode++;
3159
3160 /*
3161 * Execute the opcodes.
3162 */
3163 if (pInfo->FrameRegister != 0)
3164 {
3165 iFrameReg = pInfo->FrameRegister;
3166 offFrameReg = pInfo->FrameOffset * 16;
3167 }
3168 while (iOpcode < cOpcodes)
3169 {
3170 Assert(pInfo->aOpcodes[iOpcode].u.CodeOffset <= offPc);
3171 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3172 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3173 switch (uUnwindOp)
3174 {
3175 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3176 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auRegs[uOpInfo]);
3177 if (RT_FAILURE(rc))
3178 rcRet = rc;
3179 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3180 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3181 iOpcode++;
3182 break;
3183
3184 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3185 if (uOpInfo == 0)
3186 {
3187 iOpcode += 2;
3188 AssertBreak(iOpcode <= cOpcodes);
3189 pState->u.x86.auRegs[X86_GREG_xSP] += pInfo->aOpcodes[iOpcode - 1].FrameOffset * 8;
3190 }
3191 else
3192 {
3193 iOpcode += 3;
3194 AssertBreak(iOpcode <= cOpcodes);
3195 pState->u.x86.auRegs[X86_GREG_xSP] += RT_MAKE_U32(pInfo->aOpcodes[iOpcode - 2].FrameOffset,
3196 pInfo->aOpcodes[iOpcode - 1].FrameOffset);
3197 }
3198 break;
3199
3200 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3201 AssertBreak(iOpcode <= cOpcodes);
3202 pState->u.x86.auRegs[X86_GREG_xSP] += uOpInfo * 8 + 8;
3203 iOpcode++;
3204 break;
3205
3206 case IMAGE_AMD64_UWOP_SET_FPREG:
3207 iFrameReg = uOpInfo;
3208 offFrameReg = pInfo->FrameOffset * 16;
3209 pState->u.x86.auRegs[X86_GREG_xSP] = pState->u.x86.auRegs[iFrameReg] - offFrameReg;
3210 iOpcode++;
3211 break;
3212
3213 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3214 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3215 {
3216 uint32_t off = 0;
3217 iOpcode++;
3218 if (iOpcode < cOpcodes)
3219 {
3220 off = pInfo->aOpcodes[iOpcode].FrameOffset;
3221 iOpcode++;
3222 if (uUnwindOp == IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR && iOpcode < cOpcodes)
3223 {
3224 off |= (uint32_t)pInfo->aOpcodes[iOpcode].FrameOffset << 16;
3225 iOpcode++;
3226 }
3227 }
3228 off *= 8;
3229 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP] + off,
3230 &pState->u.x86.auRegs[uOpInfo]);
3231 if (RT_FAILURE(rc))
3232 rcRet = rc;
3233 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3234 break;
3235 }
3236
3237 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3238 iOpcode += 2;
3239 break;
3240
3241 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3242 iOpcode += 3;
3243 break;
3244
3245 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME:
3246 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3247
3248 case IMAGE_AMD64_UWOP_EPILOG:
3249 iOpcode += 1;
3250 break;
3251
3252 case IMAGE_AMD64_UWOP_RESERVED_7:
3253 AssertFailedReturn(VERR_DBG_MALFORMED_UNWIND_INFO);
3254
3255 default:
3256 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3257 }
3258 }
3259 }
3260 else
3261 {
3262 /*
3263 * We're in the POP sequence of an epilog. The POP sequence should
3264 * mirror the PUSH sequence exactly.
3265 *
3266 * Note! We should only end up here for the initial frame (just consider
3267 * RSP, stack allocations, non-volatile register restores, ++).
3268 */
3269 while (iOpcode < cOpcodes)
3270 {
3271 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3272 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3273 switch (uUnwindOp)
3274 {
3275 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3276 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3277 if (offEpilog == 0)
3278 {
3279 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP],
3280 &pState->u.x86.auRegs[uOpInfo]);
3281 if (RT_FAILURE(rc))
3282 rcRet = rc;
3283 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3284 }
3285 else
3286 {
3287 /* Decrement offEpilog by estimated POP instruction length. */
3288 offEpilog -= 1;
3289 if (offEpilog > 0 && uOpInfo >= 8)
3290 offEpilog -= 1;
3291 }
3292 iOpcode++;
3293 break;
3294
3295 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME: /* Must terminate an epilog, so always execute this. */
3296 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3297
3298 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3299 case IMAGE_AMD64_UWOP_SET_FPREG:
3300 case IMAGE_AMD64_UWOP_EPILOG:
3301 iOpcode++;
3302 break;
3303 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3304 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3305 iOpcode += 2;
3306 break;
3307 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3308 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3309 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3310 iOpcode += 3;
3311 break;
3312
3313 default:
3314 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3315 }
3316 }
3317 }
3318
3319 /*
3320 * Chained stuff?
3321 */
3322 if (!(pInfo->Flags & IMAGE_UNW_FLAGS_CHAININFO))
3323 break;
3324 ChainedEntry = *(PCIMAGE_RUNTIME_FUNCTION_ENTRY)&pInfo->aOpcodes[(cOpcodes + 1) & ~1];
3325 pEntry = &ChainedEntry;
3326 AssertReturn(cChainLoops < 32, VERR_DBG_MALFORMED_UNWIND_INFO);
3327 }
3328
3329 /*
3330 * RSP should now give us the return address, so perform a RET.
3331 */
3332 pState->enmRetType = RTDBGRETURNTYPE_NEAR64;
3333
3334 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3335 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3336 pState->u.x86.Loaded.s.fFrameAddr = 1;
3337
3338 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3339 if (RT_FAILURE(rc))
3340 rcRet = rc;
3341 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3342 pState->u.x86.Loaded.s.fPc = 1;
3343 return rcRet;
3344}
3345
3346
3347/**
3348 * @interface_method_impl{RTLDROPS,pfnUnwindFrame}
3349 */
3350static DECLCALLBACK(int) rtldrPE_UnwindFrame(PRTLDRMODINTERNAL pMod, void const *pvBits,
3351 uint32_t iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
3352{
3353 PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
3354
3355 /*
3356 * Translate the segment + offset into an RVA.
3357 */
3358 RTLDRADDR uRvaPc = off;
3359 if (iSeg != UINT32_MAX)
3360 {
3361 int rc = rtldrPE_SegOffsetToRva(pMod, iSeg, off, &uRvaPc);
3362 if (RT_FAILURE(rc))
3363 return rc;
3364 }
3365
3366 /*
3367 * Check for unwind info and match the architecture.
3368 */
3369 if ( pThis->ExceptionDir.Size == 0
3370 || pThis->ExceptionDir.VirtualAddress < pThis->cbHeaders)
3371 return VERR_DBG_NO_UNWIND_INFO;
3372 if (pThis->Core.enmArch != pState->enmArch)
3373 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3374
3375 /* Currently only AMD64 unwinding is implemented, so head it off right away. */
3376 if (pThis->Core.enmArch != RTLDRARCH_AMD64)
3377 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3378
3379 /*
3380 * Make the lookup table available to us.
3381 */
3382 void const *pvTable = NULL;
3383 uint32_t const cbTable = pThis->ExceptionDir.Size;
3384 AssertReturn( cbTable < pThis->cbImage
3385 && pThis->ExceptionDir.VirtualAddress < pThis->cbImage
3386 && pThis->ExceptionDir.VirtualAddress + cbTable <= pThis->cbImage, VERR_INTERNAL_ERROR_3);
3387 int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, pThis->ExceptionDir.Size, &pvTable);
3388 if (RT_FAILURE(rc))
3389 return rc;
3390
3391 /*
3392 * The rest is architecture dependent.
3393 *
3394 * Note! On windows we try catch access violations so we can safely use
3395 * this code on mapped images during assertions.
3396 */
3397#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3398 __try
3399 {
3400#endif
3401 switch (pThis->Core.enmArch)
3402 {
3403 case RTLDRARCH_AMD64:
3404 rc = rtldrPE_UnwindFrame_Amd64(pThis, pvBits, pState, uRvaPc,
3405 rtldrPE_LookupRuntimeFunctionEntry((PCIMAGE_RUNTIME_FUNCTION_ENTRY)pvTable,
3406 cbTable / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY),
3407 (uint32_t)uRvaPc));
3408 break;
3409
3410 default:
3411 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3412 break;
3413 }
3414#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3415 }
3416 __except (1 /*EXCEPTION_EXECUTE_HANDLER*/)
3417 {
3418 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3419 }
3420#endif
3421 rtldrPEFreePart(pThis, pvBits, pvTable);
3422 return rc;
3423}
3424
3425
3426/** @interface_method_impl{RTLDROPS,pfnDone} */
3427static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
3428{
3429 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3430 if (pModPe->pvBits)
3431 {
3432 RTMemFree(pModPe->pvBits);
3433 pModPe->pvBits = NULL;
3434 }
3435 return VINF_SUCCESS;
3436}
3437
3438
3439/** @interface_method_impl{RTLDROPS,pfnClose} */
3440static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
3441{
3442 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3443 if (pModPe->paSections)
3444 {
3445 RTMemFree(pModPe->paSections);
3446 pModPe->paSections = NULL;
3447 }
3448 if (pModPe->pvBits)
3449 {
3450 RTMemFree(pModPe->pvBits);
3451 pModPe->pvBits = NULL;
3452 }
3453 return VINF_SUCCESS;
3454}
3455
3456
3457/**
3458 * Operations for a 32-bit PE module.
3459 */
3460static const RTLDROPSPE s_rtldrPE32Ops =
3461{
3462 {
3463 "pe32",
3464 rtldrPEClose,
3465 NULL,
3466 rtldrPEDone,
3467 rtldrPEEnumSymbols,
3468 /* ext */
3469 rtldrPEGetImageSize,
3470 rtldrPEGetBits,
3471 rtldrPERelocate,
3472 rtldrPEGetSymbolEx,
3473 rtldrPE_QueryForwarderInfo,
3474 rtldrPE_EnumDbgInfo,
3475 rtldrPE_EnumSegments,
3476 rtldrPE_LinkAddressToSegOffset,
3477 rtldrPE_LinkAddressToRva,
3478 rtldrPE_SegOffsetToRva,
3479 rtldrPE_RvaToSegOffset,
3480 NULL,
3481 rtldrPE_QueryProp,
3482 rtldrPE_VerifySignature,
3483 rtldrPE_HashImage,
3484 NULL /*pfnUnwindFrame*/,
3485 42
3486 },
3487 rtldrPEResolveImports32,
3488 42
3489};
3490
3491
3492/**
3493 * Operations for a 64-bit PE module.
3494 */
3495static const RTLDROPSPE s_rtldrPE64Ops =
3496{
3497 {
3498 "pe64",
3499 rtldrPEClose,
3500 NULL,
3501 rtldrPEDone,
3502 rtldrPEEnumSymbols,
3503 /* ext */
3504 rtldrPEGetImageSize,
3505 rtldrPEGetBits,
3506 rtldrPERelocate,
3507 rtldrPEGetSymbolEx,
3508 rtldrPE_QueryForwarderInfo,
3509 rtldrPE_EnumDbgInfo,
3510 rtldrPE_EnumSegments,
3511 rtldrPE_LinkAddressToSegOffset,
3512 rtldrPE_LinkAddressToRva,
3513 rtldrPE_SegOffsetToRva,
3514 rtldrPE_RvaToSegOffset,
3515 NULL,
3516 rtldrPE_QueryProp,
3517 rtldrPE_VerifySignature,
3518 rtldrPE_HashImage,
3519 rtldrPE_UnwindFrame,
3520 42
3521 },
3522 rtldrPEResolveImports64,
3523 42
3524};
3525
3526
3527/**
3528 * Converts the optional header from 32 bit to 64 bit.
3529 * This is a rather simple task, if you start from the right end.
3530 *
3531 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
3532 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
3533 */
3534static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
3535{
3536 /*
3537 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3538 */
3539 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
3540 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
3541
3542 /* from LoaderFlags and out the difference is 4 * 32-bits. */
3543 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
3544 Assert( RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
3545 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
3546 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3547 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3548 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
3549 while (pu32Src >= pu32SrcLast)
3550 *pu32Dst-- = *pu32Src--;
3551
3552 /* the previous 4 fields are 32/64 and needs special attention. */
3553 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
3554 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
3555 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
3556 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
3557 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
3558
3559 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
3560 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
3561 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
3562 */
3563 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
3564 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
3565 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
3566 uint32_t u32ImageBase = pOptHdr32->ImageBase;
3567 pOptHdr64->ImageBase = u32ImageBase;
3568}
3569
3570
3571/**
3572 * Converts the load config directory from 32 bit to 64 bit.
3573 * This is a rather simple task, if you start from the right end.
3574 *
3575 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
3576 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
3577 */
3578static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
3579{
3580 /*
3581 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3582 */
3583 IMAGE_LOAD_CONFIG_DIRECTORY32_V9 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32_V9 volatile *)pLoadCfg;
3584 IMAGE_LOAD_CONFIG_DIRECTORY64_V9 volatile *pLoadCfg64 = pLoadCfg;
3585
3586 pLoadCfg64->AddressOfSomeUnicodeString = pLoadCfg32->AddressOfSomeUnicodeString;
3587 pLoadCfg64->HotPatchTableOffset = pLoadCfg32->HotPatchTableOffset;
3588 pLoadCfg64->GuardRFVerifyStackPointerFunctionPointer = pLoadCfg32->GuardRFVerifyStackPointerFunctionPointer;
3589 pLoadCfg64->Reserved2 = pLoadCfg32->Reserved2;
3590 pLoadCfg64->DynamicValueRelocTableSection = pLoadCfg32->DynamicValueRelocTableSection;
3591 pLoadCfg64->DynamicValueRelocTableOffset = pLoadCfg32->DynamicValueRelocTableOffset;
3592 pLoadCfg64->GuardRFFailureRoutineFunctionPointer = pLoadCfg32->GuardRFFailureRoutineFunctionPointer;
3593 pLoadCfg64->GuardRFFailureRoutine = pLoadCfg32->GuardRFFailureRoutine;
3594 pLoadCfg64->CHPEMetadataPointer = pLoadCfg32->CHPEMetadataPointer;
3595 pLoadCfg64->DynamicValueRelocTable = pLoadCfg32->DynamicValueRelocTable;
3596 pLoadCfg64->GuardLongJumpTargetCount = pLoadCfg32->GuardLongJumpTargetCount;
3597 pLoadCfg64->GuardLongJumpTargetTable = pLoadCfg32->GuardLongJumpTargetTable;
3598 pLoadCfg64->GuardAddressTakenIatEntryCount = pLoadCfg32->GuardAddressTakenIatEntryCount;
3599 pLoadCfg64->GuardAddressTakenIatEntryTable = pLoadCfg32->GuardAddressTakenIatEntryTable;
3600 pLoadCfg64->CodeIntegrity.Reserved = pLoadCfg32->CodeIntegrity.Reserved;
3601 pLoadCfg64->CodeIntegrity.CatalogOffset = pLoadCfg32->CodeIntegrity.CatalogOffset;
3602 pLoadCfg64->CodeIntegrity.Catalog = pLoadCfg32->CodeIntegrity.Catalog;
3603 pLoadCfg64->CodeIntegrity.Flags = pLoadCfg32->CodeIntegrity.Flags;
3604 pLoadCfg64->GuardFlags = pLoadCfg32->GuardFlags;
3605 pLoadCfg64->GuardCFFunctionCount = pLoadCfg32->GuardCFFunctionCount;
3606 pLoadCfg64->GuardCFFunctionTable = pLoadCfg32->GuardCFFunctionTable;
3607 pLoadCfg64->GuardCFDispatchFunctionPointer = pLoadCfg32->GuardCFDispatchFunctionPointer;
3608 pLoadCfg64->GuardCFCCheckFunctionPointer = pLoadCfg32->GuardCFCCheckFunctionPointer;
3609 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
3610 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
3611 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
3612 pLoadCfg64->EditList = pLoadCfg32->EditList;
3613 pLoadCfg64->DependentLoadFlags = pLoadCfg32->DependentLoadFlags;
3614 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
3615 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
3616 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
3617 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
3618 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
3619 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
3620 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
3621 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
3622 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
3623 /* the rest is equal. */
3624 Assert( RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
3625 == RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
3626}
3627
3628
3629/**
3630 * Translate the PE/COFF machine name to a string.
3631 *
3632 * @returns Name string (read-only).
3633 * @param uMachine The PE/COFF machine.
3634 */
3635static const char *rtldrPEGetArchName(uint16_t uMachine)
3636{
3637 switch (uMachine)
3638 {
3639 case IMAGE_FILE_MACHINE_I386: return "X86_32";
3640 case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
3641
3642 case IMAGE_FILE_MACHINE_UNKNOWN: return "UNKNOWN";
3643 case IMAGE_FILE_MACHINE_AM33: return "AM33";
3644 case IMAGE_FILE_MACHINE_ARM: return "ARM";
3645 case IMAGE_FILE_MACHINE_THUMB: return "THUMB";
3646 case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT";
3647 case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
3648 case IMAGE_FILE_MACHINE_EBC: return "EBC";
3649 case IMAGE_FILE_MACHINE_IA64: return "IA64";
3650 case IMAGE_FILE_MACHINE_M32R: return "M32R";
3651 case IMAGE_FILE_MACHINE_MIPS16: return "MIPS16";
3652 case IMAGE_FILE_MACHINE_MIPSFPU: return "MIPSFPU";
3653 case IMAGE_FILE_MACHINE_MIPSFPU16: return "MIPSFPU16";
3654 case IMAGE_FILE_MACHINE_WCEMIPSV2: return "WCEMIPSV2";
3655 case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
3656 case IMAGE_FILE_MACHINE_POWERPCFP: return "POWERPCFP";
3657 case IMAGE_FILE_MACHINE_R4000: return "R4000";
3658 case IMAGE_FILE_MACHINE_SH3: return "SH3";
3659 case IMAGE_FILE_MACHINE_SH3DSP: return "SH3DSP";
3660 case IMAGE_FILE_MACHINE_SH4: return "SH4";
3661 case IMAGE_FILE_MACHINE_SH5: return "SH5";
3662 default: return "UnknownMachine";
3663 }
3664}
3665
3666
3667/**
3668 * Validates the file header.
3669 *
3670 * @returns iprt status code.
3671 * @param pFileHdr Pointer to the file header that needs validating.
3672 * @param fFlags Valid RTLDR_O_XXX combination.
3673 * @param pszLogName The log name to prefix the errors with.
3674 * @param penmArch Where to store the CPU architecture.
3675 * @param pErrInfo Where to return additional error information.
3676 */
3677static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName,
3678 PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
3679{
3680 RT_NOREF_PV(pszLogName);
3681
3682 size_t cbOptionalHeader;
3683 switch (pFileHdr->Machine)
3684 {
3685 case IMAGE_FILE_MACHINE_I386:
3686 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
3687 *penmArch = RTLDRARCH_X86_32;
3688 break;
3689 case IMAGE_FILE_MACHINE_AMD64:
3690 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
3691 *penmArch = RTLDRARCH_AMD64;
3692 break;
3693
3694 default:
3695 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n", pszLogName, pFileHdr->Machine));
3696 *penmArch = RTLDRARCH_INVALID;
3697 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unsupported Machine=%#x", pFileHdr->Machine);
3698 }
3699 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
3700 {
3701 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n", pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
3702 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfOptionalHeader=%#x expected %#x",
3703 pFileHdr->SizeOfOptionalHeader, cbOptionalHeader);
3704 }
3705 /* This restriction needs to be implemented elsewhere. */
3706 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
3707 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
3708 {
3709 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
3710 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "IMAGE_FILE_RELOCS_STRIPPED");
3711 }
3712 if (pFileHdr->NumberOfSections > 42)
3713 {
3714 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
3715 pszLogName, pFileHdr->NumberOfSections));
3716 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections=%d, implementation max is 42", pFileHdr->NumberOfSections);
3717 }
3718 if (pFileHdr->NumberOfSections < 1)
3719 {
3720 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
3721 pszLogName, pFileHdr->NumberOfSections));
3722 return RTERRINFO_LOG_SET(pErrInfo, VERR_BAD_EXE_FORMAT, "Image has no sections");
3723 }
3724 return VINF_SUCCESS;
3725}
3726
3727
3728/**
3729 * Validates the optional header (64/32-bit)
3730 *
3731 * @returns iprt status code.
3732 * @param pOptHdr Pointer to the optional header which needs validation.
3733 * @param pszLogName The log name to prefix the errors with.
3734 * @param offNtHdrs The offset of the NT headers from the start of the file.
3735 * @param pFileHdr Pointer to the file header (valid).
3736 * @param cbRawImage The raw image size.
3737 * @param fFlags Loader flags, RTLDR_O_XXX.
3738 * @param pErrInfo Where to return additional error information.
3739 */
3740static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
3741 const IMAGE_FILE_HEADER *pFileHdr, RTFOFF cbRawImage, uint32_t fFlags, PRTERRINFO pErrInfo)
3742{
3743 RT_NOREF_PV(pszLogName);
3744
3745 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
3746 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
3747 if (pOptHdr->Magic != CorrectMagic)
3748 {
3749 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
3750 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Magic=%#x, expected %#x", pOptHdr->Magic, CorrectMagic);
3751 }
3752 const uint32_t cbImage = pOptHdr->SizeOfImage;
3753 if (cbImage > _1G)
3754 {
3755 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
3756 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x - Our limit is 1GB (%#x)", cbImage, _1G);
3757 }
3758 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
3759 if (cbImage < cbMinImageSize)
3760 {
3761 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
3762 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x to small, minimum %#x", cbImage, cbMinImageSize);
3763 }
3764 if (pOptHdr->AddressOfEntryPoint >= cbImage)
3765 {
3766 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
3767 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
3768 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3769 "AddressOfEntryPoint=%#x - beyond image size (%#x)", pOptHdr->AddressOfEntryPoint, cbImage);
3770 }
3771 if (pOptHdr->BaseOfCode >= cbImage)
3772 {
3773 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
3774 pszLogName, pOptHdr->BaseOfCode, cbImage));
3775 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3776 "BaseOfCode=%#x - beyond image size (%#x)", pOptHdr->BaseOfCode, cbImage);
3777 }
3778#if 0/* only in 32-bit header */
3779 if (pOptHdr->BaseOfData >= cbImage)
3780 {
3781 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
3782 pszLogName, pOptHdr->BaseOfData, cbImage));
3783 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "BaseOfData=%#x - beyond image size (%#x)", pOptHdr->BaseOfData, cbImage);
3784 }
3785#endif
3786 if (pOptHdr->SizeOfHeaders >= cbImage)
3787 {
3788 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
3789 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
3790 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
3791 "SizeOfHeaders=%#x - beyond image size (%#x)", pOptHdr->SizeOfHeaders, cbImage);
3792 }
3793 /* don't know how to do the checksum, so ignore it. */
3794 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
3795 {
3796 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
3797 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Subsystem=%#x (unknown)", pOptHdr->Subsystem);
3798 }
3799 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
3800 {
3801 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
3802 pszLogName, pOptHdr->SizeOfHeaders,
3803 cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
3804 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
3805 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx",
3806 pOptHdr->SizeOfHeaders, cbMinImageSize,
3807 pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
3808 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) );
3809 }
3810 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
3811 {
3812 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
3813 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
3814 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x",
3815 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
3816 }
3817 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
3818 {
3819 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
3820 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
3821 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x\n",
3822 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
3823 }
3824
3825 /* DataDirectory */
3826 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
3827 {
3828 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
3829 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfRvaAndSizes=%d, expected %d",
3830 pOptHdr->NumberOfRvaAndSizes, RT_ELEMENTS(pOptHdr->DataDirectory));
3831 }
3832 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
3833 {
3834 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
3835 if (!pDir->Size)
3836 continue;
3837 size_t cb = cbImage;
3838 switch (i)
3839 {
3840 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
3841 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
3842 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
3843 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
3844 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
3845 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
3846 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
3847 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
3848 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
3849 break;
3850 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
3851 /* Delay inspection after section table is validated. */
3852 break;
3853
3854 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
3855 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3856 break;
3857 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3858 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3859 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_DELAY_IMPORT,
3860 "DELAY_IMPORT VirtualAddress=%#x Size=%#x: not supported", pDir->VirtualAddress, pDir->Size);
3861
3862 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
3863 /* The VirtualAddress is a PointerToRawData. */
3864 cb = (size_t)cbRawImage; Assert((RTFOFF)cb == cbRawImage);
3865 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3866 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3867 if (pDir->Size < sizeof(WIN_CERTIFICATE))
3868 {
3869 Log(("rtldrPEOpen: %s: Security directory #%u is too small: %#x bytes\n", pszLogName, i, pDir->Size));
3870 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3871 "Security directory is too small: %#x bytes", pDir->Size);
3872 }
3873 if (pDir->Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
3874 {
3875 Log(("rtldrPEOpen: %s: Security directory #%u is too large: %#x bytes\n", pszLogName, i, pDir->Size));
3876 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3877 "Security directory is too large: %#x bytes", pDir->Size);
3878 }
3879 if (pDir->VirtualAddress & 7)
3880 {
3881 Log(("rtldrPEOpen: %s: Security directory #%u is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
3882 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
3883 "Security directory is misaligned: %#x", pDir->VirtualAddress);
3884 }
3885 /* When using the in-memory reader with a debugger, we may get
3886 into trouble here since we might not have access to the whole
3887 physical file. So skip the tests below. Makes VBoxGuest.sys
3888 load and check out just fine, for instance. */
3889 if (fFlags & RTLDR_O_FOR_DEBUG)
3890 continue;
3891 break;
3892
3893 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
3894 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3895 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3896 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_GLOBALPTR, "GLOBALPTR VirtualAddress=%#x Size=%#x: not supported",
3897 pDir->VirtualAddress, pDir->Size);
3898
3899 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
3900 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3901 break;
3902 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3903 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3904 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_TLS, "TLS VirtualAddress=%#x Size=%#x: not supported",
3905 pDir->VirtualAddress, pDir->Size);
3906
3907 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
3908 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
3909 break;
3910 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
3911 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3912 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_COM_DESCRIPTOR,
3913 "COM_DESCRIPTOR VirtualAddress=%#x Size=%#x: not supported",
3914 pDir->VirtualAddress, pDir->Size);
3915
3916 default:
3917 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
3918 pszLogName, i, pDir->VirtualAddress, pDir->Size));
3919 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x Size=%#x is not supported",
3920 i, pDir->VirtualAddress, pDir->Size);
3921 }
3922 if (pDir->VirtualAddress >= cb)
3923 {
3924 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
3925 pszLogName, i, pDir->VirtualAddress, cb));
3926 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x is invalid (limit %#x)",
3927 i, pDir->VirtualAddress, cb);
3928 }
3929 if (pDir->Size > cb - pDir->VirtualAddress)
3930 {
3931 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
3932 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
3933 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)",
3934 i, pDir->Size, pDir->VirtualAddress, cb);
3935 }
3936 }
3937 return VINF_SUCCESS;
3938}
3939
3940
3941/**
3942 * Validates and touch up the section headers.
3943 *
3944 * The touching up is restricted to setting the VirtualSize field for old-style
3945 * linkers that sets it to zero.
3946 *
3947 * @returns iprt status code.
3948 * @param paSections Pointer to the array of sections that is to be validated.
3949 * @param cSections Number of sections in that array.
3950 * @param pszLogName The log name to prefix the errors with.
3951 * @param pOptHdr Pointer to the optional header (valid).
3952 * @param cbRawImage The raw image size.
3953 * @param fFlags Loader flags, RTLDR_O_XXX.
3954 * @param fNoCode Verify that the image contains no code.
3955 */
3956static int rtldrPEValidateAndTouchUpSectionHeaders(IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
3957 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, RTFOFF cbRawImage, uint32_t fFlags,
3958 bool fNoCode)
3959{
3960 RT_NOREF_PV(pszLogName);
3961
3962 /*
3963 * Do a quick pass to detect linker setting VirtualSize to zero.
3964 */
3965 bool fFixupVirtualSize = true;
3966 IMAGE_SECTION_HEADER *pSH = &paSections[0];
3967 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
3968 if ( pSH->Misc.VirtualSize != 0
3969 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
3970 {
3971 fFixupVirtualSize = false;
3972 break;
3973 }
3974
3975 /*
3976 * Actual pass.
3977 */
3978 const uint32_t cbImage = pOptHdr->SizeOfImage;
3979 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
3980 pSH = &paSections[0];
3981 Log3(("RTLdrPE: Section Headers:\n"));
3982 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
3983 {
3984 const unsigned iSH = (unsigned)(pSH - &paSections[0]); NOREF(iSH);
3985 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
3986 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
3987 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
3988 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
3989 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
3990 iSH, pSH->Name, pSH->Characteristics,
3991 pSH->VirtualAddress, pSH->Misc.VirtualSize,
3992 pSH->PointerToRawData, pSH->SizeOfRawData,
3993 pSH->PointerToRelocations, pSH->NumberOfRelocations,
3994 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
3995
3996 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
3997 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
3998 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
3999 {
4000 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
4001 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
4002 return VERR_BAD_EXE_FORMAT;
4003 }
4004
4005 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
4006 || pSH->SizeOfRawData > cbRawImage
4007 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
4008 {
4009 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#x) - section #%d '%.*s'!!!\n",
4010 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
4011 iSH, sizeof(pSH->Name), pSH->Name));
4012 return VERR_BAD_EXE_FORMAT;
4013 }
4014
4015 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
4016 {
4017 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4018 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4019 return VERR_BAD_EXE_FORMAT;
4020 }
4021
4022 if (!(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
4023 {
4024 /* Calc VirtualSize if necessary. This is for internal reasons. */
4025 if ( pSH->Misc.VirtualSize == 0
4026 && fFixupVirtualSize)
4027 {
4028 pSH->Misc.VirtualSize = cbImage - RT_MIN(pSH->VirtualAddress, cbImage);
4029 for (uint32_t i = 1; i < cSHdrsLeft; i++)
4030 if ( !(pSH[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
4031 && pSH[i].VirtualAddress >= pSH->VirtualAddress)
4032 {
4033 pSH->Misc.VirtualSize = RT_MIN(pSH[i].VirtualAddress - pSH->VirtualAddress, pSH->Misc.VirtualSize);
4034 break;
4035 }
4036 }
4037
4038 if (pSH->Misc.VirtualSize > 0)
4039 {
4040 if (pSH->VirtualAddress < uRvaPrev)
4041 {
4042 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
4043 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
4044 return VERR_BAD_EXE_FORMAT;
4045 }
4046 if (pSH->VirtualAddress > cbImage)
4047 {
4048 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
4049 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
4050 return VERR_BAD_EXE_FORMAT;
4051 }
4052
4053 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
4054 {
4055 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4056 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4057 return VERR_BAD_EXE_FORMAT;
4058 }
4059
4060#ifdef PE_FILE_OFFSET_EQUALS_RVA
4061 /* Our loader code assume rva matches the file offset. */
4062 if ( pSH->SizeOfRawData
4063 && pSH->PointerToRawData != pSH->VirtualAddress)
4064 {
4065 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
4066 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
4067 return VERR_BAD_EXE_FORMAT;
4068 }
4069#endif
4070
4071 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
4072 }
4073 }
4074
4075 /* ignore the relocations and linenumbers. */
4076 }
4077
4078 /*
4079 * Do a separate run if we need to validate the no-code claim from the
4080 * optional header.
4081 */
4082 if (fNoCode)
4083 {
4084 pSH = &paSections[0];
4085 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4086 if (pSH->Characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE))
4087 return VERR_LDR_ARCH_MISMATCH;
4088 }
4089
4090
4091 /** @todo r=bird: more sanity checks! */
4092 return VINF_SUCCESS;
4093}
4094
4095
4096/**
4097 * Reads image data by RVA using the section headers.
4098 *
4099 * @returns iprt status code.
4100 * @param pModPe The PE module instance.
4101 * @param pvBuf Where to store the bits.
4102 * @param cb Number of bytes to tread.
4103 * @param RVA Where to read from.
4104 */
4105static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
4106{
4107 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
4108 PRTLDRREADER pReader = pModPe->Core.pReader;
4109 uint32_t cbRead;
4110 int rc;
4111
4112 /*
4113 * Is it the headers, i.e. prior to the first section.
4114 */
4115 if (RVA < pModPe->cbHeaders)
4116 {
4117 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
4118 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
4119 if ( cbRead == cb
4120 || RT_FAILURE(rc))
4121 return rc;
4122 cb -= cbRead;
4123 RVA += cbRead;
4124 pvBuf = (uint8_t *)pvBuf + cbRead;
4125 }
4126
4127 /* In the zero space between headers and the first section? */
4128 if (RVA < pSH->VirtualAddress)
4129 {
4130 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
4131 memset(pvBuf, 0, cbRead);
4132 if (cbRead == cb)
4133 return VINF_SUCCESS;
4134 cb -= cbRead;
4135 RVA += cbRead;
4136 pvBuf = (uint8_t *)pvBuf + cbRead;
4137 }
4138
4139 /*
4140 * Iterate the sections.
4141 */
4142 for (unsigned cLeft = pModPe->cSections;
4143 cLeft > 0;
4144 cLeft--, pSH++)
4145 {
4146 uint32_t off = RVA - pSH->VirtualAddress;
4147 if (off < pSH->Misc.VirtualSize)
4148 {
4149 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
4150 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
4151 if ( cbRead == cb
4152 || RT_FAILURE(rc))
4153 return rc;
4154 cb -= cbRead;
4155 RVA += cbRead;
4156 pvBuf = (uint8_t *)pvBuf + cbRead;
4157 }
4158 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
4159 if (RVA < RVANext)
4160 {
4161 cbRead = RT_MIN(RVANext - RVA, cb);
4162 memset(pvBuf, 0, cbRead);
4163 if (cbRead == cb)
4164 return VINF_SUCCESS;
4165 cb -= cbRead;
4166 RVA += cbRead;
4167 pvBuf = (uint8_t *)pvBuf + cbRead;
4168 }
4169 }
4170
4171 AssertFailed();
4172 return VERR_INTERNAL_ERROR;
4173}
4174
4175
4176/**
4177 * Validates the data of some selected data directories entries and remember
4178 * important bits for later.
4179 *
4180 * This requires a valid section table and thus has to wait till after we've
4181 * read and validated it.
4182 *
4183 * @returns iprt status code.
4184 * @param pModPe The PE module instance.
4185 * @param pOptHdr Pointer to the optional header (valid).
4186 * @param fFlags Loader flags, RTLDR_O_XXX.
4187 * @param pErrInfo Where to return extended error information. Optional.
4188 */
4189static int rtldrPEValidateDirectoriesAndRememberStuff(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags,
4190 PRTERRINFO pErrInfo)
4191{
4192 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
4193 union /* combine stuff we're reading to help reduce stack usage. */
4194 {
4195 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
4196 uint8_t abZeros[sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64) * 4];
4197 } u;
4198
4199 /*
4200 * The load config entry may include lock prefix tables and whatnot which we don't implement.
4201 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
4202 * actual data before we can make up our mind about it all.
4203 */
4204 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
4205 if (Dir.Size)
4206 {
4207 const size_t cbExpectV9 = !pModPe->f64Bit
4208 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V9)
4209 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V9);
4210 const size_t cbExpectV8 = !pModPe->f64Bit
4211 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V8)
4212 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V8);
4213 const size_t cbExpectV7 = !pModPe->f64Bit
4214 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V7)
4215 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7);
4216 const size_t cbExpectV6 = !pModPe->f64Bit
4217 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V6)
4218 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V6);
4219 const size_t cbExpectV5 = !pModPe->f64Bit
4220 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V5)
4221 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V5);
4222 const size_t cbExpectV4 = !pModPe->f64Bit
4223 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V4)
4224 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V4);
4225 const size_t cbExpectV3 = !pModPe->f64Bit
4226 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V3)
4227 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V3);
4228 const size_t cbExpectV2 = !pModPe->f64Bit
4229 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V2)
4230 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2);
4231 const size_t cbExpectV1 = !pModPe->f64Bit
4232 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V1)
4233 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2) /*No V1*/;
4234 const size_t cbNewHack = cbExpectV5; /* Playing safe here since there might've been revisions between V5 and V6 we don't know about . */
4235 const size_t cbMaxKnown = cbExpectV9;
4236
4237 bool fNewerStructureHack = false;
4238 if ( Dir.Size != cbExpectV9
4239 && Dir.Size != cbExpectV8
4240 && Dir.Size != cbExpectV7
4241 && Dir.Size != cbExpectV6
4242 && Dir.Size != cbExpectV5
4243 && Dir.Size != cbExpectV4
4244 && Dir.Size != cbExpectV3
4245 && Dir.Size != cbExpectV2
4246 && Dir.Size != cbExpectV1)
4247 {
4248 fNewerStructureHack = Dir.Size > cbNewHack /* These structure changes are slowly getting to us! More futher down. */
4249 && Dir.Size <= sizeof(u);
4250 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",
4251 pszLogName, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1,
4252 fNewerStructureHack ? " Will try ignore extra bytes if all zero." : ""));
4253 if (!fNewerStructureHack)
4254 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4255 "Unexpected load config dir size of %u bytes; supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
4256 Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4257 }
4258
4259 /*
4260 * Read, check new stuff and convert to 64-bit.
4261 *
4262 * If we accepted a newer structures when loading for debug or validation,
4263 * otherwise we require the new bits to be all zero and hope that they are
4264 * insignificant where image loading is concerned (that's mostly been the
4265 * case even for non-zero bits, only hard exception is LockPrefixTable).
4266 */
4267 RT_ZERO(u.Cfg64);
4268 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4269 if (RT_FAILURE(rc))
4270 return rc;
4271 if ( fNewerStructureHack
4272 && Dir.Size > cbMaxKnown
4273 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4274 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4275 {
4276 Log(("rtldrPEOpen: %s: load cfg dir: Unexpected bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4277 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4278 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4279 "Grown load config (%u to %u bytes) includes non-zero bytes: %.*Rhxs",
4280 cbMaxKnown, Dir.Size, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4281 }
4282 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4283
4284 if (u.Cfg64.Size != Dir.Size)
4285 {
4286 /* Kludge #1: ntdll.dll from XP seen with Dir.Size=0x40 and Cfg64.Size=0x00. */
4287 if (Dir.Size == 0x40 && u.Cfg64.Size == 0x00 && !pModPe->f64Bit)
4288 {
4289 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the XP kludge.\n",
4290 pszLogName, u.Cfg64.Size, Dir.Size));
4291 u.Cfg64.Size = Dir.Size;
4292 }
4293 /* Kludge #2: This happens a lot. Structure changes, but the linker doesn't get
4294 updated and stores some old size in the directory. Use the header size. */
4295 else if ( u.Cfg64.Size == cbExpectV9
4296 || u.Cfg64.Size == cbExpectV8
4297 || u.Cfg64.Size == cbExpectV7
4298 || u.Cfg64.Size == cbExpectV6
4299 || u.Cfg64.Size == cbExpectV5
4300 || u.Cfg64.Size == cbExpectV4
4301 || u.Cfg64.Size == cbExpectV3
4302 || u.Cfg64.Size == cbExpectV2
4303 || u.Cfg64.Size == cbExpectV1
4304 || (fNewerStructureHack = (u.Cfg64.Size > cbNewHack && u.Cfg64.Size <= sizeof(u))) )
4305 {
4306 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the old linker kludge.\n",
4307 pszLogName, u.Cfg64.Size, Dir.Size));
4308
4309 Dir.Size = u.Cfg64.Size;
4310 uint32_t const uOrgDir = Dir.Size;
4311 RT_ZERO(u.Cfg64);
4312 rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4313 if (RT_FAILURE(rc))
4314 return rc;
4315 if ( fNewerStructureHack
4316 && Dir.Size > cbMaxKnown
4317 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4318 {
4319 Log(("rtldrPEOpen: %s: load cfg dir: Unknown bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4320 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4321 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4322 "Grown load config (%u to %u bytes, dir %u) includes non-zero bytes: %.*Rhxs",
4323 cbMaxKnown, Dir.Size, uOrgDir, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4324 }
4325 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4326 AssertReturn(u.Cfg64.Size == Dir.Size,
4327 RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE, "Data changed while reading! (%d vs %d)\n",
4328 u.Cfg64.Size, Dir.Size));
4329 }
4330 else
4331 {
4332 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",
4333 pszLogName, u.Cfg64.Size, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1));
4334 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4335 "Unexpected load config header size of %u bytes (dir %u); supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
4336 u.Cfg64.Size, Dir.Size, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4337 }
4338 }
4339 if (u.Cfg64.LockPrefixTable && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4340 {
4341 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
4342 pszLogName, u.Cfg64.LockPrefixTable));
4343 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOCK_PREFIX_TABLE,
4344 "Lock prefix table not supported: %RX64", u.Cfg64.LockPrefixTable);
4345 }
4346#if 0/* this seems to be safe to ignore. */
4347 if ( u.Cfg64.SEHandlerTable
4348 || u.Cfg64.SEHandlerCount)
4349 {
4350 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
4351 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
4352 return VERR_BAD_EXE_FORMAT;
4353 }
4354#endif
4355 if (u.Cfg64.EditList && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4356 {
4357 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
4358 pszLogName, u.Cfg64.EditList));
4359 return RTErrInfoSetF(pErrInfo, VERR_BAD_EXE_FORMAT, "Load config EditList=%RX64 is not supported", u.Cfg64.EditList);
4360 }
4361 /** @todo GuardCFC? Possibly related to:
4362 * http://research.microsoft.com/pubs/69217/ccs05-cfi.pdf
4363 * Not trusting something designed by bakas who don't know how to modify a
4364 * structure without messing up its natural alignment. */
4365 if ( ( u.Cfg64.GuardCFCCheckFunctionPointer
4366 || u.Cfg64.GuardCFDispatchFunctionPointer
4367 || u.Cfg64.GuardCFFunctionTable
4368 || u.Cfg64.GuardCFFunctionCount
4369 || u.Cfg64.GuardFlags
4370 || u.Cfg64.GuardAddressTakenIatEntryTable
4371 || u.Cfg64.GuardAddressTakenIatEntryCount
4372 || u.Cfg64.GuardLongJumpTargetTable
4373 || u.Cfg64.GuardLongJumpTargetCount)
4374 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) )
4375 {
4376 Log(("rtldrPEOpen: %s: load cfg dir: Guard stuff: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!\n",
4377 pszLogName, u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4378 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4379 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4380 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount ));
4381#if 0 /* ntdll 15002 uses this. */
4382 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_GUARD_CF_STUFF,
4383 "Guard bits in load config: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!",
4384 u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4385 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4386 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4387 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount);
4388#endif
4389 }
4390 }
4391
4392 /*
4393 * If the image is signed and we're not doing this for debug purposes,
4394 * take a look at the signature.
4395 */
4396 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4397 if (Dir.Size)
4398 {
4399 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
4400 if (!pFirst)
4401 return VERR_NO_TMP_MEMORY;
4402 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
4403 if (RT_SUCCESS(rc))
4404 {
4405 uint32_t off = 0;
4406 do
4407 {
4408 PWIN_CERTIFICATE pCur = (PWIN_CERTIFICATE)((uint8_t *)pFirst + off);
4409
4410 /* validate the members. */
4411 if ( pCur->dwLength < sizeof(WIN_CERTIFICATE)
4412 || pCur->dwLength + off > Dir.Size)
4413 {
4414 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
4415 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4416 "Cert at %#x LB %#x: Bad header length value: %#x", off, Dir.Size, pCur->dwLength);
4417 break;
4418 }
4419 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
4420 && pCur->wRevision != WIN_CERT_REVISION_1_0)
4421 {
4422 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
4423 if (pCur->wRevision >= WIN_CERT_REVISION_1_0)
4424 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
4425 "Cert at %#x LB %#x: Unsupported revision: %#x", off, Dir.Size, pCur->wRevision);
4426 else
4427 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4428 "Cert at %#x LB %#x: Malformed revision: %#x", off, Dir.Size, pCur->wRevision);
4429 break;
4430 }
4431 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
4432 && pCur->wCertificateType != WIN_CERT_TYPE_X509
4433 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
4434 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
4435 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
4436 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
4437 )
4438 {
4439 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wCertificateType=%#x\n", pszLogName, off, Dir.Size, pCur->wCertificateType));
4440 if (pCur->wCertificateType)
4441 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
4442 "Cert at %#x LB %#x: Unsupported certificate type: %#x",
4443 off, Dir.Size, pCur->wCertificateType);
4444 else
4445 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4446 "Cert at %#x LB %#x: Malformed certificate type: %#x",
4447 off, Dir.Size, pCur->wCertificateType);
4448 break;
4449 }
4450
4451 /* Remember the first signed data certificate. */
4452 if ( pCur->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA
4453 && pModPe->offPkcs7SignedData == 0)
4454 {
4455 pModPe->offPkcs7SignedData = Dir.VirtualAddress
4456 + (uint32_t)((uintptr_t)&pCur->bCertificate[0] - (uintptr_t)pFirst);
4457 pModPe->cbPkcs7SignedData = pCur->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
4458 }
4459
4460 /* next */
4461 off += RT_ALIGN(pCur->dwLength, WIN_CERTIFICATE_ALIGNMENT);
4462 } while (off < Dir.Size);
4463 }
4464 RTMemTmpFree(pFirst);
4465 if (RT_FAILURE(rc) && !(fFlags & RTLDR_O_FOR_DEBUG))
4466 return rc;
4467 }
4468
4469 return VINF_SUCCESS;
4470}
4471
4472
4473/**
4474 * Open a PE image.
4475 *
4476 * @returns iprt status code.
4477 * @param pReader The loader reader instance which will provide the raw image bits.
4478 * @param fFlags Loader flags, RTLDR_O_XXX.
4479 * @param enmArch Architecture specifier.
4480 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
4481 * @param phLdrMod Where to store the handle.
4482 * @param pErrInfo Where to return extended error information. Optional.
4483 */
4484DECLHIDDEN(int) rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs,
4485 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4486{
4487 /*
4488 * Read and validate the file header.
4489 */
4490 IMAGE_FILE_HEADER FileHdr;
4491 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
4492 if (RT_FAILURE(rc))
4493 return rc;
4494 RTLDRARCH enmArchImage;
4495 const char *pszLogName = pReader->pfnLogName(pReader);
4496 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage, pErrInfo);
4497 if (RT_FAILURE(rc))
4498 return rc;
4499
4500 /*
4501 * Match the CPU architecture.
4502 */
4503 bool fArchNoCodeCheckPending = false;
4504 if ( enmArch != enmArchImage
4505 && ( enmArch != RTLDRARCH_WHATEVER
4506 && !(fFlags & RTLDR_O_WHATEVER_ARCH)) )
4507 {
4508 if (!(fFlags & RTLDR_O_IGNORE_ARCH_IF_NO_CODE))
4509 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Image is for '%s', only accepting images for '%s'.",
4510 rtldrPEGetArchName(FileHdr.Machine), RTLdrArchName(enmArch));
4511 fArchNoCodeCheckPending = true;
4512 }
4513
4514 /*
4515 * Read and validate the "optional" header. Convert 32->64 if necessary.
4516 */
4517 IMAGE_OPTIONAL_HEADER64 OptHdr;
4518 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
4519 if (RT_FAILURE(rc))
4520 return rc;
4521 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
4522 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
4523 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags, pErrInfo);
4524 if (RT_FAILURE(rc))
4525 return rc;
4526 if (fArchNoCodeCheckPending && OptHdr.SizeOfCode != 0)
4527 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH,
4528 "Image is for '%s' and contains code (%#x), only accepting images for '%s' with code.",
4529 rtldrPEGetArchName(FileHdr.Machine), OptHdr.SizeOfCode, RTLdrArchName(enmArch));
4530
4531 /*
4532 * Read and validate section headers.
4533 */
4534 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
4535 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
4536 if (!paSections)
4537 return VERR_NO_MEMORY;
4538 rc = pReader->pfnRead(pReader, paSections, cbSections,
4539 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
4540 if (RT_SUCCESS(rc))
4541 {
4542 rc = rtldrPEValidateAndTouchUpSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
4543 &OptHdr, pReader->pfnSize(pReader), fFlags, fArchNoCodeCheckPending);
4544 if (RT_SUCCESS(rc))
4545 {
4546 /*
4547 * Allocate and initialize the PE module structure.
4548 */
4549 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
4550 if (pModPe)
4551 {
4552 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
4553 pModPe->Core.eState = LDR_STATE_OPENED;
4554 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
4555 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
4556 else
4557 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
4558 pModPe->Core.pReader = pReader;
4559 pModPe->Core.enmFormat= RTLDRFMT_PE;
4560 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
4561 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4562 ? RTLDRTYPE_EXECUTABLE_FIXED
4563 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
4564 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4565 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
4566 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
4567 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
4568 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
4569 ? RTLDRARCH_X86_32
4570 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
4571 ? RTLDRARCH_AMD64
4572 : RTLDRARCH_WHATEVER;
4573 pModPe->pvBits = NULL;
4574 pModPe->offNtHdrs = offNtHdrs;
4575 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
4576 pModPe->u16Machine = FileHdr.Machine;
4577 pModPe->fFile = FileHdr.Characteristics;
4578 pModPe->cSections = FileHdr.NumberOfSections;
4579 pModPe->paSections = paSections;
4580 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
4581 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
4582 pModPe->cbImage = OptHdr.SizeOfImage;
4583 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
4584 pModPe->uTimestamp = FileHdr.TimeDateStamp;
4585 pModPe->cImports = UINT32_MAX;
4586 pModPe->f64Bit = FileHdr.SizeOfOptionalHeader == sizeof(OptHdr);
4587 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
4588 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
4589 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
4590 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
4591 pModPe->SecurityDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4592 pModPe->ExceptionDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
4593 pModPe->fDllCharacteristics = OptHdr.DllCharacteristics;
4594
4595 /*
4596 * Perform validation of some selected data directories which requires
4597 * inspection of the actual data. This also saves some certificate
4598 * information.
4599 */
4600 rc = rtldrPEValidateDirectoriesAndRememberStuff(pModPe, &OptHdr, fFlags, pErrInfo);
4601 if (RT_SUCCESS(rc))
4602 {
4603 *phLdrMod = &pModPe->Core;
4604 return VINF_SUCCESS;
4605 }
4606 RTMemFree(pModPe);
4607 }
4608 else
4609 rc = VERR_NO_MEMORY;
4610 }
4611 }
4612 RTMemFree(paSections);
4613 return rc;
4614}
4615
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