VirtualBox

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

Last change on this file since 75085 was 75085, checked in by vboxsync, 6 years ago

IPRT/ldrPE: Must save Dir.Size to uOrgDir before updating it. duh. ticketref:17977

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