VirtualBox

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

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

iprt/string.h: Dropped including utf16.h and let those who need it include it themselves. bugref:9344

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

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