VirtualBox

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

Last change on this file since 87110 was 86551, checked in by vboxsync, 4 years ago

SUPHardNt,IPRT: If there are nested signatures (i.e. more than one signature), don't get grumpy if there are time or cert path issues with some of them, as long as one or more checks out perfectly. (Mind, all the signature data must check out, it's just the cert path or signing time we're relaxing here.) [build fix] ticketref:19743 bugref:3103

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