VirtualBox

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

Last change on this file since 86550 was 86550, 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.3 KB
Line 
1/* $Id: ldrPE.cpp 86550 2020-10-13 00:01:19Z 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 for (uint32_t i = 0; i < pSignature->cNested; i++)
3010 {
3011 pInfo = &pSignature->paNested[i]; /* Note! pInfo changes! */
3012 if ( !pInfo->fValidatedImageHash
3013 && pInfo->enmDigest == pInfo->enmDigest
3014 /* paranoia from the top of this function: */
3015 && pInfo->pIndData
3016 && RTASN1CORE_IS_PRESENT(&pInfo->pIndData->DigestInfo.Digest.Asn1Core)
3017 && pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv
3018 && pInfo->pIndData->DigestInfo.Digest.Asn1Core.cb == cbHash)
3019 {
3020 pInfo->fValidatedImageHash = true;
3021 if (memcmp(pHashRes, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv, cbHash) != 0)
3022 {
3023 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
3024 "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
3025 cbHash, pHashRes,
3026 cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
3027 break;
3028 }
3029 }
3030 }
3031 }
3032 else
3033 rc = RTErrInfoSetF(pErrInfo, VERR_LDRVI_IMAGE_HASH_MISMATCH,
3034 "Full image signature #%u mismatch: %.*Rhxs, expected %.*Rhxs", pInfo->iSignature + 1,
3035 cbHash, &pInfo->HashRes,
3036 cbHash, pInfo->pIndData->DigestInfo.Digest.Asn1Core.uData.pv);
3037 }
3038 return rc;
3039}
3040
3041
3042/**
3043 * Validates the image hash, including page hashes if present.
3044 *
3045 * @returns IPRT status code.
3046 * @param pModPe The PE module.
3047 * @param pSignature The decoded signature data.
3048 * @param pErrInfo Optional error info buffer.
3049 */
3050static int rtldrPE_VerifySignatureValidateHash(PRTLDRMODPE pModPe, PRTLDRPESIGNATURE pSignature, PRTERRINFO pErrInfo)
3051{
3052 /*
3053 * Allocate a temporary memory buffer.
3054 * Note! The _4K that gets subtracted is to avoid that the 16-byte heap
3055 * block header in ring-0 (iprt) caused any unnecessary internal
3056 * heap fragmentation.
3057 */
3058# ifdef IN_RING0
3059 uint32_t cbScratch = _256K - _4K;
3060# else
3061 uint32_t cbScratch = _1M;
3062# endif
3063 void *pvScratch = RTMemTmpAlloc(cbScratch);
3064 if (!pvScratch)
3065 {
3066 cbScratch = _4K;
3067 pvScratch = RTMemTmpAlloc(cbScratch);
3068 if (!pvScratch)
3069 return RTErrInfoSet(pErrInfo, VERR_NO_TMP_MEMORY, "Failed to allocate 4KB of scratch space for hashing image.");
3070 }
3071
3072 /*
3073 * Verify signatures.
3074 */
3075 /* Image hashes: */
3076 int rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->Primary,
3077 pvScratch, cbScratch, pErrInfo);
3078 for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
3079 rc = rtldrPE_VerifySignatureValidateOneImageHash(pModPe, pSignature, &pSignature->paNested[i],
3080 pvScratch, cbScratch, pErrInfo);
3081
3082 /* Page hashes: */
3083 if (RT_SUCCESS(rc))
3084 {
3085 rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->Primary, pvScratch, cbScratch, pErrInfo);
3086 for (unsigned i = 0; i < pSignature->cNested && RT_SUCCESS(rc); i++)
3087 rc = rtldrPE_VerifySignatureValidateOnePageHashes(pModPe, &pSignature->paNested[i], pvScratch, cbScratch, pErrInfo);
3088 }
3089
3090 /*
3091 * Ditch the scratch buffer.
3092 */
3093 RTMemTmpFree(pvScratch);
3094 return rc;
3095}
3096
3097#endif /* !IPRT_WITHOUT_LDR_VERIFY */
3098
3099
3100/** @interface_method_impl{RTLDROPS,pfnVerifySignature} */
3101static DECLCALLBACK(int) rtldrPE_VerifySignature(PRTLDRMODINTERNAL pMod, PFNRTLDRVALIDATESIGNEDDATA pfnCallback, void *pvUser,
3102 PRTERRINFO pErrInfo)
3103{
3104#ifndef IPRT_WITHOUT_LDR_VERIFY
3105 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3106
3107 int rc = rtldrPE_VerifySignatureImagePrecoditions(pModPe, pErrInfo);
3108 if (RT_SUCCESS(rc))
3109 {
3110 PRTLDRPESIGNATURE pSignature = NULL;
3111 rc = rtldrPE_VerifySignatureRead(pModPe, &pSignature, pErrInfo);
3112 if (RT_SUCCESS(rc))
3113 {
3114 rc = rtldrPE_VerifySignatureDecode(pModPe, pSignature, pErrInfo);
3115 if (RT_SUCCESS(rc))
3116 rc = rtldrPE_VerifySignatureValidateHash(pModPe, pSignature, pErrInfo);
3117 if (RT_SUCCESS(rc))
3118 {
3119 /*
3120 * Work the callback.
3121 */
3122 /* The primary signature: */
3123 RTLDRSIGNATUREINFO Info;
3124 Info.iSignature = 0;
3125 Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
3126 Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
3127 Info.pvSignature = pSignature->Primary.pContentInfo;
3128 Info.cbSignature = sizeof(*pSignature->Primary.pContentInfo);
3129 Info.pvExternalData = NULL;
3130 Info.cbExternalData = 0;
3131 rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
3132
3133 /* The nested signatures: */
3134 for (uint32_t iNested = 0; iNested < pSignature->cNested && rc == VINF_SUCCESS; iNested++)
3135 {
3136 Info.iSignature = (uint16_t)(1 + iNested);
3137 Info.cSignatures = (uint16_t)(1 + pSignature->cNested);
3138 Info.enmType = RTLDRSIGNATURETYPE_PKCS7_SIGNED_DATA;
3139 Info.pvSignature = pSignature->paNested[iNested].pContentInfo;
3140 Info.cbSignature = sizeof(*pSignature->paNested[iNested].pContentInfo);
3141 Info.pvExternalData = NULL;
3142 Info.cbExternalData = 0;
3143 rc = pfnCallback(&pModPe->Core, &Info, pErrInfo, pvUser);
3144 }
3145 }
3146 rtldrPE_VerifySignatureDestroy(pModPe, pSignature);
3147 }
3148 }
3149 return rc;
3150#else
3151 RT_NOREF_PV(pMod); RT_NOREF_PV(pfnCallback); RT_NOREF_PV(pvUser); RT_NOREF_PV(pErrInfo);
3152 return VERR_NOT_SUPPORTED;
3153#endif
3154}
3155
3156
3157
3158/**
3159 * @interface_method_impl{RTLDROPS,pfnHashImage}
3160 */
3161static DECLCALLBACK(int) rtldrPE_HashImage(PRTLDRMODINTERNAL pMod, RTDIGESTTYPE enmDigest, char *pszDigest, size_t cbDigest)
3162{
3163 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3164
3165 /*
3166 * Allocate a temporary memory buffer.
3167 */
3168 uint32_t cbScratch = _16K;
3169 void *pvScratch = RTMemTmpAlloc(cbScratch);
3170 if (!pvScratch)
3171 {
3172 cbScratch = _4K;
3173 pvScratch = RTMemTmpAlloc(cbScratch);
3174 if (!pvScratch)
3175 return VERR_NO_TMP_MEMORY;
3176 }
3177
3178 /*
3179 * Do the hashing.
3180 */
3181 RTLDRPEHASHCTXUNION HashCtx;
3182 RTLDRPEHASHRESUNION HashRes;
3183 int rc = rtldrPE_HashImageCommon(pModPe, pvScratch, cbScratch, enmDigest, &HashCtx, &HashRes, NULL);
3184 if (RT_SUCCESS(rc))
3185 {
3186 /*
3187 * Format the digest into as human readable hash string.
3188 */
3189 switch (enmDigest)
3190 {
3191 case RTDIGESTTYPE_SHA512: rc = RTSha512ToString(HashRes.abSha512, pszDigest, cbDigest); break;
3192 case RTDIGESTTYPE_SHA256: rc = RTSha256ToString(HashRes.abSha256, pszDigest, cbDigest); break;
3193 case RTDIGESTTYPE_SHA1: rc = RTSha1ToString(HashRes.abSha1, pszDigest, cbDigest); break;
3194 case RTDIGESTTYPE_MD5: rc = RTMd5ToString(HashRes.abMd5, pszDigest, cbDigest); break;
3195 default: AssertFailedReturn(VERR_INTERNAL_ERROR_3);
3196 }
3197 }
3198 return rc;
3199}
3200
3201
3202/**
3203 * Binary searches the lookup table.
3204 *
3205 * @returns RVA of unwind info on success, UINT32_MAX on failure.
3206 * @param paFunctions The table to lookup @a uRva in.
3207 * @param iEnd Size of the table.
3208 * @param uRva The RVA of the function we want.
3209 */
3210DECLINLINE(PCIMAGE_RUNTIME_FUNCTION_ENTRY)
3211rtldrPE_LookupRuntimeFunctionEntry(PCIMAGE_RUNTIME_FUNCTION_ENTRY paFunctions, size_t iEnd, uint32_t uRva)
3212{
3213 size_t iBegin = 0;
3214 while (iBegin < iEnd)
3215 {
3216 size_t const i = iBegin + (iEnd - iBegin) / 2;
3217 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry = &paFunctions[i];
3218 if (uRva < pEntry->BeginAddress)
3219 iEnd = i;
3220 else if (uRva > pEntry->EndAddress)
3221 iBegin = i + 1;
3222 else
3223 return pEntry;
3224 }
3225 return NULL;
3226}
3227
3228
3229/**
3230 * Processes an IRET frame.
3231 *
3232 * @returns IPRT status code.
3233 * @param pState The unwind state being worked.
3234 * @param fErrCd Non-zero if there is an error code on the stack.
3235 */
3236static int rtldrPE_UnwindFrame_Amd64_IRet(PRTDBGUNWINDSTATE pState, uint8_t fErrCd)
3237{
3238 /* POP ErrCd (optional): */
3239 Assert(fErrCd <= 1);
3240 int rcRet;
3241 if (fErrCd)
3242 {
3243 pState->u.x86.uErrCd = 0;
3244 pState->u.x86.Loaded.s.fErrCd = 1;
3245 rcRet = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uErrCd);
3246 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3247 }
3248 else
3249 {
3250 pState->u.x86.Loaded.s.fErrCd = 0;
3251 rcRet = VINF_SUCCESS;
3252 }
3253
3254 /* Set return type and frame pointer. */
3255 pState->enmRetType = RTDBGRETURNTYPE_IRET64;
3256 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3257 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3258
3259 /* POP RIP: */
3260 int rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3261 if (RT_FAILURE(rc))
3262 rcRet = rc;
3263 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3264
3265 /* POP CS: */
3266 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_CS]);
3267 if (RT_FAILURE(rc))
3268 rcRet = rc;
3269 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3270
3271 /* POP RFLAGS: */
3272 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.uRFlags);
3273 if (RT_FAILURE(rc))
3274 rcRet = rc;
3275 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3276
3277 /* POP RSP, part 1: */
3278 uint64_t uNewRsp = (pState->u.x86.auRegs[X86_GREG_xSP] - 8) & ~(uint64_t)15;
3279 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &uNewRsp);
3280 if (RT_FAILURE(rc))
3281 rcRet = rc;
3282 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3283
3284 /* POP SS: */
3285 rc = RTDbgUnwindLoadStackU16(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auSegs[X86_SREG_SS]);
3286 if (RT_FAILURE(rc))
3287 rcRet = rc;
3288 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3289
3290 /* POP RSP, part 2: */
3291 pState->u.x86.auRegs[X86_GREG_xSP] = uNewRsp;
3292
3293 /* Set loaded indicators: */
3294 pState->u.x86.Loaded.s.fRegs |= RT_BIT(X86_GREG_xSP);
3295 pState->u.x86.Loaded.s.fSegs |= RT_BIT(X86_SREG_CS) | RT_BIT(X86_SREG_SS);
3296 pState->u.x86.Loaded.s.fPc = 1;
3297 pState->u.x86.Loaded.s.fFrameAddr = 1;
3298 pState->u.x86.Loaded.s.fRFlags = 1;
3299 return VINF_SUCCESS;
3300}
3301
3302
3303static int rtldrPE_UnwindFrame_Amd64(PRTLDRMODPE pThis, void const *pvBits, PRTDBGUNWINDSTATE pState, uint32_t uRvaPc,
3304 PCIMAGE_RUNTIME_FUNCTION_ENTRY pEntry)
3305{
3306 /* Did we find any unwind information? */
3307 if (!pEntry)
3308 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3309
3310 /*
3311 * Do the unwinding.
3312 */
3313 IMAGE_RUNTIME_FUNCTION_ENTRY ChainedEntry;
3314 unsigned iFrameReg = ~0U;
3315 unsigned offFrameReg = 0;
3316
3317 int fInEpilog = -1; /* -1: not-determined-assume-false; 0: false; 1: true. */
3318 uint8_t cbEpilog = 0;
3319 uint8_t offEpilog = UINT8_MAX;
3320 int rcRet = VINF_SUCCESS;
3321 int rc;
3322 for (unsigned cChainLoops = 0; ; cChainLoops++)
3323 {
3324 /*
3325 * Get the info.
3326 */
3327 union
3328 {
3329 uint32_t uRva;
3330 uint8_t ab[ RT_OFFSETOF(IMAGE_UNWIND_INFO, aOpcodes)
3331 + sizeof(IMAGE_UNWIND_CODE) * 256
3332 + sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY)];
3333 } uBuf;
3334 rc = rtldrPEReadPartByRvaInfoBuf(pThis, pvBits, pEntry->UnwindInfoAddress, sizeof(uBuf), &uBuf);
3335 if (RT_FAILURE(rc))
3336 return rc;
3337
3338 /*
3339 * Check the info.
3340 */
3341 ASMCompilerBarrier(); /* we're aliasing */
3342 PCIMAGE_UNWIND_INFO pInfo = (PCIMAGE_UNWIND_INFO)&uBuf;
3343
3344 if (pInfo->Version != 1 && pInfo->Version != 2)
3345 return VERR_DBG_MALFORMED_UNWIND_INFO;
3346
3347 /*
3348 * Execute the opcodes.
3349 */
3350 unsigned const cOpcodes = pInfo->CountOfCodes;
3351 unsigned iOpcode = 0;
3352
3353 /*
3354 * Check for epilog opcodes at the start and see if we're in an epilog.
3355 */
3356 if ( pInfo->Version >= 2
3357 && iOpcode < cOpcodes
3358 && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3359 {
3360 if (fInEpilog == -1)
3361 {
3362 cbEpilog = pInfo->aOpcodes[iOpcode].u.CodeOffset;
3363 Assert(cbEpilog > 0);
3364
3365 uint32_t uRvaEpilog = pEntry->EndAddress - cbEpilog;
3366 iOpcode++;
3367 if ( (pInfo->aOpcodes[iOpcode - 1].u.OpInfo & 1)
3368 && uRvaPc >= uRvaEpilog)
3369 {
3370 offEpilog = uRvaPc - uRvaEpilog;
3371 fInEpilog = 1;
3372 }
3373 else
3374 {
3375 fInEpilog = 0;
3376 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3377 {
3378 uRvaEpilog = pEntry->EndAddress
3379 - (pInfo->aOpcodes[iOpcode].u.CodeOffset + (pInfo->aOpcodes[iOpcode].u.OpInfo << 8));
3380 iOpcode++;
3381 if (uRvaPc - uRvaEpilog < cbEpilog)
3382 {
3383 offEpilog = uRvaPc - uRvaEpilog;
3384 fInEpilog = 1;
3385 break;
3386 }
3387 }
3388 }
3389 }
3390 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.UnwindOp == IMAGE_AMD64_UWOP_EPILOG)
3391 iOpcode++;
3392 }
3393 if (fInEpilog != 1)
3394 {
3395 /*
3396 * Skip opcodes that doesn't apply to us if we're in the prolog.
3397 */
3398 uint32_t offPc = uRvaPc - pEntry->BeginAddress;
3399 if (offPc < pInfo->SizeOfProlog)
3400 while (iOpcode < cOpcodes && pInfo->aOpcodes[iOpcode].u.CodeOffset > offPc)
3401 iOpcode++;
3402
3403 /*
3404 * Execute the opcodes.
3405 */
3406 if (pInfo->FrameRegister != 0)
3407 {
3408 iFrameReg = pInfo->FrameRegister;
3409 offFrameReg = pInfo->FrameOffset * 16;
3410 }
3411 while (iOpcode < cOpcodes)
3412 {
3413 Assert(pInfo->aOpcodes[iOpcode].u.CodeOffset <= offPc);
3414 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3415 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3416 switch (uUnwindOp)
3417 {
3418 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3419 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->u.x86.auRegs[uOpInfo]);
3420 if (RT_FAILURE(rc))
3421 rcRet = rc;
3422 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3423 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3424 iOpcode++;
3425 break;
3426
3427 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3428 if (uOpInfo == 0)
3429 {
3430 iOpcode += 2;
3431 AssertBreak(iOpcode <= cOpcodes);
3432 pState->u.x86.auRegs[X86_GREG_xSP] += pInfo->aOpcodes[iOpcode - 1].FrameOffset * 8;
3433 }
3434 else
3435 {
3436 iOpcode += 3;
3437 AssertBreak(iOpcode <= cOpcodes);
3438 pState->u.x86.auRegs[X86_GREG_xSP] += RT_MAKE_U32(pInfo->aOpcodes[iOpcode - 2].FrameOffset,
3439 pInfo->aOpcodes[iOpcode - 1].FrameOffset);
3440 }
3441 break;
3442
3443 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3444 AssertBreak(iOpcode <= cOpcodes);
3445 pState->u.x86.auRegs[X86_GREG_xSP] += uOpInfo * 8 + 8;
3446 iOpcode++;
3447 break;
3448
3449 case IMAGE_AMD64_UWOP_SET_FPREG:
3450 iFrameReg = uOpInfo;
3451 offFrameReg = pInfo->FrameOffset * 16;
3452 pState->u.x86.auRegs[X86_GREG_xSP] = pState->u.x86.auRegs[iFrameReg] - offFrameReg;
3453 iOpcode++;
3454 break;
3455
3456 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3457 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3458 {
3459 uint32_t off = 0;
3460 iOpcode++;
3461 if (iOpcode < cOpcodes)
3462 {
3463 off = pInfo->aOpcodes[iOpcode].FrameOffset;
3464 iOpcode++;
3465 if (uUnwindOp == IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR && iOpcode < cOpcodes)
3466 {
3467 off |= (uint32_t)pInfo->aOpcodes[iOpcode].FrameOffset << 16;
3468 iOpcode++;
3469 }
3470 }
3471 off *= 8;
3472 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP] + off,
3473 &pState->u.x86.auRegs[uOpInfo]);
3474 if (RT_FAILURE(rc))
3475 rcRet = rc;
3476 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3477 break;
3478 }
3479
3480 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3481 iOpcode += 2;
3482 break;
3483
3484 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3485 iOpcode += 3;
3486 break;
3487
3488 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME:
3489 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3490
3491 case IMAGE_AMD64_UWOP_EPILOG:
3492 iOpcode += 1;
3493 break;
3494
3495 case IMAGE_AMD64_UWOP_RESERVED_7:
3496 AssertFailedReturn(VERR_DBG_MALFORMED_UNWIND_INFO);
3497
3498 default:
3499 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3500 }
3501 }
3502 }
3503 else
3504 {
3505 /*
3506 * We're in the POP sequence of an epilog. The POP sequence should
3507 * mirror the PUSH sequence exactly.
3508 *
3509 * Note! We should only end up here for the initial frame (just consider
3510 * RSP, stack allocations, non-volatile register restores, ++).
3511 */
3512 while (iOpcode < cOpcodes)
3513 {
3514 uint8_t const uOpInfo = pInfo->aOpcodes[iOpcode].u.OpInfo;
3515 uint8_t const uUnwindOp = pInfo->aOpcodes[iOpcode].u.UnwindOp;
3516 switch (uUnwindOp)
3517 {
3518 case IMAGE_AMD64_UWOP_PUSH_NONVOL:
3519 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3520 if (offEpilog == 0)
3521 {
3522 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP],
3523 &pState->u.x86.auRegs[uOpInfo]);
3524 if (RT_FAILURE(rc))
3525 rcRet = rc;
3526 pState->u.x86.Loaded.s.fRegs |= RT_BIT(uOpInfo);
3527 }
3528 else
3529 {
3530 /* Decrement offEpilog by estimated POP instruction length. */
3531 offEpilog -= 1;
3532 if (offEpilog > 0 && uOpInfo >= 8)
3533 offEpilog -= 1;
3534 }
3535 iOpcode++;
3536 break;
3537
3538 case IMAGE_AMD64_UWOP_PUSH_MACHFRAME: /* Must terminate an epilog, so always execute this. */
3539 return rtldrPE_UnwindFrame_Amd64_IRet(pState, uOpInfo);
3540
3541 case IMAGE_AMD64_UWOP_ALLOC_SMALL:
3542 case IMAGE_AMD64_UWOP_SET_FPREG:
3543 case IMAGE_AMD64_UWOP_EPILOG:
3544 iOpcode++;
3545 break;
3546 case IMAGE_AMD64_UWOP_SAVE_NONVOL:
3547 case IMAGE_AMD64_UWOP_SAVE_XMM128:
3548 iOpcode += 2;
3549 break;
3550 case IMAGE_AMD64_UWOP_ALLOC_LARGE:
3551 case IMAGE_AMD64_UWOP_SAVE_NONVOL_FAR:
3552 case IMAGE_AMD64_UWOP_SAVE_XMM128_FAR:
3553 iOpcode += 3;
3554 break;
3555
3556 default:
3557 AssertMsgFailedReturn(("%u\n", uUnwindOp), VERR_DBG_MALFORMED_UNWIND_INFO);
3558 }
3559 }
3560 }
3561
3562 /*
3563 * Chained stuff?
3564 */
3565 if (!(pInfo->Flags & IMAGE_UNW_FLAGS_CHAININFO))
3566 break;
3567 ChainedEntry = *(PCIMAGE_RUNTIME_FUNCTION_ENTRY)&pInfo->aOpcodes[(cOpcodes + 1) & ~1];
3568 pEntry = &ChainedEntry;
3569 AssertReturn(cChainLoops < 32, VERR_DBG_MALFORMED_UNWIND_INFO);
3570 }
3571
3572 /*
3573 * RSP should now give us the return address, so perform a RET.
3574 */
3575 pState->enmRetType = RTDBGRETURNTYPE_NEAR64;
3576
3577 pState->u.x86.FrameAddr.off = pState->u.x86.auRegs[X86_GREG_xSP] - /* pretend rbp is pushed on the stack */ 8;
3578 pState->u.x86.FrameAddr.sel = pState->u.x86.auSegs[X86_SREG_SS];
3579 pState->u.x86.Loaded.s.fFrameAddr = 1;
3580
3581 rc = RTDbgUnwindLoadStackU64(pState, pState->u.x86.auRegs[X86_GREG_xSP], &pState->uPc);
3582 if (RT_FAILURE(rc))
3583 rcRet = rc;
3584 pState->u.x86.auRegs[X86_GREG_xSP] += 8;
3585 pState->u.x86.Loaded.s.fPc = 1;
3586 return rcRet;
3587}
3588
3589
3590/**
3591 * @interface_method_impl{RTLDROPS,pfnUnwindFrame}
3592 */
3593static DECLCALLBACK(int) rtldrPE_UnwindFrame(PRTLDRMODINTERNAL pMod, void const *pvBits,
3594 uint32_t iSeg, RTUINTPTR off, PRTDBGUNWINDSTATE pState)
3595{
3596 PRTLDRMODPE pThis = (PRTLDRMODPE)pMod;
3597
3598 /*
3599 * Translate the segment + offset into an RVA.
3600 */
3601 RTLDRADDR uRvaPc = off;
3602 if (iSeg != UINT32_MAX)
3603 {
3604 int rc = rtldrPE_SegOffsetToRva(pMod, iSeg, off, &uRvaPc);
3605 if (RT_FAILURE(rc))
3606 return rc;
3607 }
3608
3609 /*
3610 * Check for unwind info and match the architecture.
3611 */
3612 if ( pThis->ExceptionDir.Size == 0
3613 || pThis->ExceptionDir.VirtualAddress < pThis->cbHeaders)
3614 return VERR_DBG_NO_UNWIND_INFO;
3615 if (pThis->Core.enmArch != pState->enmArch)
3616 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3617
3618 /* Currently only AMD64 unwinding is implemented, so head it off right away. */
3619 if (pThis->Core.enmArch != RTLDRARCH_AMD64)
3620 return VERR_DBG_UNWIND_INFO_NOT_FOUND;
3621
3622 /*
3623 * Make the lookup table available to us.
3624 */
3625 void const *pvTable = NULL;
3626 uint32_t const cbTable = pThis->ExceptionDir.Size;
3627 AssertReturn( cbTable < pThis->cbImage
3628 && pThis->ExceptionDir.VirtualAddress < pThis->cbImage
3629 && pThis->ExceptionDir.VirtualAddress + cbTable <= pThis->cbImage, VERR_INTERNAL_ERROR_3);
3630 int rc = rtldrPEReadPartByRva(pThis, pvBits, pThis->ExceptionDir.VirtualAddress, pThis->ExceptionDir.Size, &pvTable);
3631 if (RT_FAILURE(rc))
3632 return rc;
3633
3634 /*
3635 * The rest is architecture dependent.
3636 *
3637 * Note! On windows we try catch access violations so we can safely use
3638 * this code on mapped images during assertions.
3639 */
3640#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3641 __try
3642 {
3643#endif
3644 switch (pThis->Core.enmArch)
3645 {
3646 case RTLDRARCH_AMD64:
3647 rc = rtldrPE_UnwindFrame_Amd64(pThis, pvBits, pState, uRvaPc,
3648 rtldrPE_LookupRuntimeFunctionEntry((PCIMAGE_RUNTIME_FUNCTION_ENTRY)pvTable,
3649 cbTable / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY),
3650 (uint32_t)uRvaPc));
3651 break;
3652
3653 default:
3654 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3655 break;
3656 }
3657#if defined(_MSC_VER) && defined(IN_RING3) && !defined(IN_SUP_HARDENED_R3)
3658 }
3659 __except (1 /*EXCEPTION_EXECUTE_HANDLER*/)
3660 {
3661 rc = VERR_DBG_UNWIND_INFO_NOT_FOUND;
3662 }
3663#endif
3664 rtldrPEFreePart(pThis, pvBits, pvTable);
3665 return rc;
3666}
3667
3668
3669/** @interface_method_impl{RTLDROPS,pfnDone} */
3670static DECLCALLBACK(int) rtldrPEDone(PRTLDRMODINTERNAL pMod)
3671{
3672 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3673 if (pModPe->pvBits)
3674 {
3675 RTMemFree(pModPe->pvBits);
3676 pModPe->pvBits = NULL;
3677 }
3678 return VINF_SUCCESS;
3679}
3680
3681
3682/** @interface_method_impl{RTLDROPS,pfnClose} */
3683static DECLCALLBACK(int) rtldrPEClose(PRTLDRMODINTERNAL pMod)
3684{
3685 PRTLDRMODPE pModPe = (PRTLDRMODPE)pMod;
3686 if (pModPe->paSections)
3687 {
3688 RTMemFree(pModPe->paSections);
3689 pModPe->paSections = NULL;
3690 }
3691 if (pModPe->pvBits)
3692 {
3693 RTMemFree(pModPe->pvBits);
3694 pModPe->pvBits = NULL;
3695 }
3696 return VINF_SUCCESS;
3697}
3698
3699
3700/**
3701 * Operations for a 32-bit PE module.
3702 */
3703static const RTLDROPSPE s_rtldrPE32Ops =
3704{
3705 {
3706 "pe32",
3707 rtldrPEClose,
3708 NULL,
3709 rtldrPEDone,
3710 rtldrPEEnumSymbols,
3711 /* ext */
3712 rtldrPEGetImageSize,
3713 rtldrPEGetBits,
3714 rtldrPERelocate,
3715 rtldrPEGetSymbolEx,
3716 rtldrPE_QueryForwarderInfo,
3717 rtldrPE_EnumDbgInfo,
3718 rtldrPE_EnumSegments,
3719 rtldrPE_LinkAddressToSegOffset,
3720 rtldrPE_LinkAddressToRva,
3721 rtldrPE_SegOffsetToRva,
3722 rtldrPE_RvaToSegOffset,
3723 NULL,
3724 rtldrPE_QueryProp,
3725 rtldrPE_VerifySignature,
3726 rtldrPE_HashImage,
3727 NULL /*pfnUnwindFrame*/,
3728 42
3729 },
3730 rtldrPEResolveImports32,
3731 42
3732};
3733
3734
3735/**
3736 * Operations for a 64-bit PE module.
3737 */
3738static const RTLDROPSPE s_rtldrPE64Ops =
3739{
3740 {
3741 "pe64",
3742 rtldrPEClose,
3743 NULL,
3744 rtldrPEDone,
3745 rtldrPEEnumSymbols,
3746 /* ext */
3747 rtldrPEGetImageSize,
3748 rtldrPEGetBits,
3749 rtldrPERelocate,
3750 rtldrPEGetSymbolEx,
3751 rtldrPE_QueryForwarderInfo,
3752 rtldrPE_EnumDbgInfo,
3753 rtldrPE_EnumSegments,
3754 rtldrPE_LinkAddressToSegOffset,
3755 rtldrPE_LinkAddressToRva,
3756 rtldrPE_SegOffsetToRva,
3757 rtldrPE_RvaToSegOffset,
3758 NULL,
3759 rtldrPE_QueryProp,
3760 rtldrPE_VerifySignature,
3761 rtldrPE_HashImage,
3762 rtldrPE_UnwindFrame,
3763 42
3764 },
3765 rtldrPEResolveImports64,
3766 42
3767};
3768
3769
3770/**
3771 * Converts the optional header from 32 bit to 64 bit.
3772 * This is a rather simple task, if you start from the right end.
3773 *
3774 * @param pOptHdr On input this is a PIMAGE_OPTIONAL_HEADER32.
3775 * On output this will be a PIMAGE_OPTIONAL_HEADER64.
3776 */
3777static void rtldrPEConvert32BitOptionalHeaderTo64Bit(PIMAGE_OPTIONAL_HEADER64 pOptHdr)
3778{
3779 /*
3780 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3781 */
3782 IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr;
3783 IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr;
3784
3785 /* from LoaderFlags and out the difference is 4 * 32-bits. */
3786 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, LoaderFlags) + 16 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, LoaderFlags));
3787 Assert( RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]) + 16
3788 == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]));
3789 uint32_t volatile *pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3790 const uint32_t volatile *pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1;
3791 const uint32_t volatile *pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags;
3792 while (pu32Src >= pu32SrcLast)
3793 *pu32Dst-- = *pu32Src--;
3794
3795 /* the previous 4 fields are 32/64 and needs special attention. */
3796 pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit;
3797 pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve;
3798 pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit;
3799 uint32_t u32SizeOfStackReserve = pOptHdr32->SizeOfStackReserve;
3800 pOptHdr64->SizeOfStackReserve = u32SizeOfStackReserve;
3801
3802 /* The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version..
3803 * Thus, ImageBase needs some special treatment. It will probably work fine assigning one to the
3804 * other since this is all declared volatile, but taking now chances, we'll use a temp variable.
3805 */
3806 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SizeOfStackReserve) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SizeOfStackReserve));
3807 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, BaseOfData) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, ImageBase));
3808 Assert(RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER32, SectionAlignment) == RT_UOFFSETOF(IMAGE_OPTIONAL_HEADER64, SectionAlignment));
3809 uint32_t u32ImageBase = pOptHdr32->ImageBase;
3810 pOptHdr64->ImageBase = u32ImageBase;
3811}
3812
3813
3814/**
3815 * Converts the load config directory from 32 bit to 64 bit.
3816 * This is a rather simple task, if you start from the right end.
3817 *
3818 * @param pLoadCfg On input this is a PIMAGE_LOAD_CONFIG_DIRECTORY32.
3819 * On output this will be a PIMAGE_LOAD_CONFIG_DIRECTORY64.
3820 */
3821static void rtldrPEConvert32BitLoadConfigTo64Bit(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg)
3822{
3823 /*
3824 * volatile everywhere! Trying to prevent the compiler being a smarta$$ and reorder stuff.
3825 */
3826 IMAGE_LOAD_CONFIG_DIRECTORY32_V12 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32_V12 volatile *)pLoadCfg;
3827 IMAGE_LOAD_CONFIG_DIRECTORY64_V12 volatile *pLoadCfg64 = pLoadCfg;
3828
3829 pLoadCfg64->GuardXFGTableDispatchFunctionPointer = pLoadCfg32->GuardXFGTableDispatchFunctionPointer;
3830 pLoadCfg64->GuardXFGDispatchFunctionPointer = pLoadCfg32->GuardXFGDispatchFunctionPointer;
3831 pLoadCfg64->GuardXFGCheckFunctionPointer = pLoadCfg32->GuardXFGCheckFunctionPointer;
3832 pLoadCfg64->GuardEHContinuationCount = pLoadCfg32->GuardEHContinuationCount;
3833 pLoadCfg64->GuardEHContinuationTable = pLoadCfg32->GuardEHContinuationTable;
3834 pLoadCfg64->VolatileMetadataPointer = pLoadCfg32->VolatileMetadataPointer;
3835 pLoadCfg64->EnclaveConfigurationPointer = pLoadCfg32->EnclaveConfigurationPointer;
3836 pLoadCfg64->Reserved3 = pLoadCfg32->Reserved3;
3837 pLoadCfg64->HotPatchTableOffset = pLoadCfg32->HotPatchTableOffset;
3838 pLoadCfg64->GuardRFVerifyStackPointerFunctionPointer = pLoadCfg32->GuardRFVerifyStackPointerFunctionPointer;
3839 pLoadCfg64->Reserved2 = pLoadCfg32->Reserved2;
3840 pLoadCfg64->DynamicValueRelocTableSection = pLoadCfg32->DynamicValueRelocTableSection;
3841 pLoadCfg64->DynamicValueRelocTableOffset = pLoadCfg32->DynamicValueRelocTableOffset;
3842 pLoadCfg64->GuardRFFailureRoutineFunctionPointer = pLoadCfg32->GuardRFFailureRoutineFunctionPointer;
3843 pLoadCfg64->GuardRFFailureRoutine = pLoadCfg32->GuardRFFailureRoutine;
3844 pLoadCfg64->CHPEMetadataPointer = pLoadCfg32->CHPEMetadataPointer;
3845 pLoadCfg64->DynamicValueRelocTable = pLoadCfg32->DynamicValueRelocTable;
3846 pLoadCfg64->GuardLongJumpTargetCount = pLoadCfg32->GuardLongJumpTargetCount;
3847 pLoadCfg64->GuardLongJumpTargetTable = pLoadCfg32->GuardLongJumpTargetTable;
3848 pLoadCfg64->GuardAddressTakenIatEntryCount = pLoadCfg32->GuardAddressTakenIatEntryCount;
3849 pLoadCfg64->GuardAddressTakenIatEntryTable = pLoadCfg32->GuardAddressTakenIatEntryTable;
3850 pLoadCfg64->CodeIntegrity.Reserved = pLoadCfg32->CodeIntegrity.Reserved;
3851 pLoadCfg64->CodeIntegrity.CatalogOffset = pLoadCfg32->CodeIntegrity.CatalogOffset;
3852 pLoadCfg64->CodeIntegrity.Catalog = pLoadCfg32->CodeIntegrity.Catalog;
3853 pLoadCfg64->CodeIntegrity.Flags = pLoadCfg32->CodeIntegrity.Flags;
3854 pLoadCfg64->GuardFlags = pLoadCfg32->GuardFlags;
3855 pLoadCfg64->GuardCFFunctionCount = pLoadCfg32->GuardCFFunctionCount;
3856 pLoadCfg64->GuardCFFunctionTable = pLoadCfg32->GuardCFFunctionTable;
3857 pLoadCfg64->GuardCFDispatchFunctionPointer = pLoadCfg32->GuardCFDispatchFunctionPointer;
3858 pLoadCfg64->GuardCFCCheckFunctionPointer = pLoadCfg32->GuardCFCCheckFunctionPointer;
3859 pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount;
3860 pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable;
3861 pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie;
3862 pLoadCfg64->EditList = pLoadCfg32->EditList;
3863 pLoadCfg64->DependentLoadFlags = pLoadCfg32->DependentLoadFlags;
3864 pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion;
3865 pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; /* switched place with ProcessAffinityMask, but we're more than 16 byte off by now so it doesn't matter. */
3866 pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask;
3867 pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold;
3868 pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize;
3869 pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable;
3870 pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold;
3871 uint32_t u32DeCommitFreeBlockThreshold = pLoadCfg32->DeCommitFreeBlockThreshold;
3872 pLoadCfg64->DeCommitFreeBlockThreshold = u32DeCommitFreeBlockThreshold;
3873 /* the rest is equal. */
3874 Assert( RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY32, DeCommitFreeBlockThreshold)
3875 == RT_UOFFSETOF(IMAGE_LOAD_CONFIG_DIRECTORY64, DeCommitFreeBlockThreshold));
3876}
3877
3878
3879/**
3880 * Translate the PE/COFF machine name to a string.
3881 *
3882 * @returns Name string (read-only).
3883 * @param uMachine The PE/COFF machine.
3884 */
3885static const char *rtldrPEGetArchName(uint16_t uMachine)
3886{
3887 switch (uMachine)
3888 {
3889 case IMAGE_FILE_MACHINE_I386: return "X86_32";
3890 case IMAGE_FILE_MACHINE_AMD64: return "AMD64";
3891
3892 case IMAGE_FILE_MACHINE_UNKNOWN: return "UNKNOWN";
3893 case IMAGE_FILE_MACHINE_AM33: return "AM33";
3894 case IMAGE_FILE_MACHINE_ARM: return "ARM";
3895 case IMAGE_FILE_MACHINE_THUMB: return "THUMB";
3896 case IMAGE_FILE_MACHINE_ARMNT: return "ARMNT";
3897 case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
3898 case IMAGE_FILE_MACHINE_EBC: return "EBC";
3899 case IMAGE_FILE_MACHINE_IA64: return "IA64";
3900 case IMAGE_FILE_MACHINE_M32R: return "M32R";
3901 case IMAGE_FILE_MACHINE_MIPS16: return "MIPS16";
3902 case IMAGE_FILE_MACHINE_MIPSFPU: return "MIPSFPU";
3903 case IMAGE_FILE_MACHINE_MIPSFPU16: return "MIPSFPU16";
3904 case IMAGE_FILE_MACHINE_WCEMIPSV2: return "WCEMIPSV2";
3905 case IMAGE_FILE_MACHINE_POWERPC: return "POWERPC";
3906 case IMAGE_FILE_MACHINE_POWERPCFP: return "POWERPCFP";
3907 case IMAGE_FILE_MACHINE_R4000: return "R4000";
3908 case IMAGE_FILE_MACHINE_SH3: return "SH3";
3909 case IMAGE_FILE_MACHINE_SH3DSP: return "SH3DSP";
3910 case IMAGE_FILE_MACHINE_SH4: return "SH4";
3911 case IMAGE_FILE_MACHINE_SH5: return "SH5";
3912 default: return "UnknownMachine";
3913 }
3914}
3915
3916
3917/**
3918 * Validates the file header.
3919 *
3920 * @returns iprt status code.
3921 * @param pFileHdr Pointer to the file header that needs validating.
3922 * @param fFlags Valid RTLDR_O_XXX combination.
3923 * @param pszLogName The log name to prefix the errors with.
3924 * @param penmArch Where to store the CPU architecture.
3925 * @param pErrInfo Where to return additional error information.
3926 */
3927static int rtldrPEValidateFileHeader(PIMAGE_FILE_HEADER pFileHdr, uint32_t fFlags, const char *pszLogName,
3928 PRTLDRARCH penmArch, PRTERRINFO pErrInfo)
3929{
3930 RT_NOREF_PV(pszLogName);
3931
3932 size_t cbOptionalHeader;
3933 switch (pFileHdr->Machine)
3934 {
3935 case IMAGE_FILE_MACHINE_I386:
3936 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER32);
3937 *penmArch = RTLDRARCH_X86_32;
3938 break;
3939 case IMAGE_FILE_MACHINE_AMD64:
3940 cbOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64);
3941 *penmArch = RTLDRARCH_AMD64;
3942 break;
3943
3944 default:
3945 Log(("rtldrPEOpen: %s: Unsupported Machine=%#x\n", pszLogName, pFileHdr->Machine));
3946 *penmArch = RTLDRARCH_INVALID;
3947 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Unsupported Machine=%#x", pFileHdr->Machine);
3948 }
3949 if (pFileHdr->SizeOfOptionalHeader != cbOptionalHeader)
3950 {
3951 Log(("rtldrPEOpen: %s: SizeOfOptionalHeader=%#x expected %#x\n", pszLogName, pFileHdr->SizeOfOptionalHeader, cbOptionalHeader));
3952 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfOptionalHeader=%#x expected %#x",
3953 pFileHdr->SizeOfOptionalHeader, cbOptionalHeader);
3954 }
3955 /* This restriction needs to be implemented elsewhere. */
3956 if ( (pFileHdr->Characteristics & IMAGE_FILE_RELOCS_STRIPPED)
3957 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
3958 {
3959 Log(("rtldrPEOpen: %s: IMAGE_FILE_RELOCS_STRIPPED\n", pszLogName));
3960 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "IMAGE_FILE_RELOCS_STRIPPED");
3961 }
3962 if (pFileHdr->NumberOfSections > 42)
3963 {
3964 Log(("rtldrPEOpen: %s: NumberOfSections=%d - our limit is 42, please raise it if the binary makes sense.(!!!)\n",
3965 pszLogName, pFileHdr->NumberOfSections));
3966 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfSections=%d, implementation max is 42", pFileHdr->NumberOfSections);
3967 }
3968 if (pFileHdr->NumberOfSections < 1)
3969 {
3970 Log(("rtldrPEOpen: %s: NumberOfSections=%d - we can't have an image without sections (!!!)\n",
3971 pszLogName, pFileHdr->NumberOfSections));
3972 return RTERRINFO_LOG_SET(pErrInfo, VERR_BAD_EXE_FORMAT, "Image has no sections");
3973 }
3974 return VINF_SUCCESS;
3975}
3976
3977
3978/**
3979 * Validates the optional header (64/32-bit)
3980 *
3981 * @returns iprt status code.
3982 * @param pOptHdr Pointer to the optional header which needs validation.
3983 * @param pszLogName The log name to prefix the errors with.
3984 * @param offNtHdrs The offset of the NT headers from the start of the file.
3985 * @param pFileHdr Pointer to the file header (valid).
3986 * @param cbRawImage The raw image size.
3987 * @param fFlags Loader flags, RTLDR_O_XXX.
3988 * @param pErrInfo Where to return additional error information.
3989 */
3990static int rtldrPEValidateOptionalHeader(const IMAGE_OPTIONAL_HEADER64 *pOptHdr, const char *pszLogName, RTFOFF offNtHdrs,
3991 const IMAGE_FILE_HEADER *pFileHdr, uint64_t cbRawImage, uint32_t fFlags, PRTERRINFO pErrInfo)
3992{
3993 RT_NOREF_PV(pszLogName);
3994
3995 const uint16_t CorrectMagic = pFileHdr->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32)
3996 ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC;
3997 if (pOptHdr->Magic != CorrectMagic)
3998 {
3999 Log(("rtldrPEOpen: %s: Magic=%#x - expected %#x!!!\n", pszLogName, pOptHdr->Magic, CorrectMagic));
4000 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Magic=%#x, expected %#x", pOptHdr->Magic, CorrectMagic);
4001 }
4002 const uint32_t cbImage = pOptHdr->SizeOfImage;
4003 if (cbImage > _1G)
4004 {
4005 Log(("rtldrPEOpen: %s: SizeOfImage=%#x - Our limit is 1GB (%#x)!!!\n", pszLogName, cbImage, _1G));
4006 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x - Our limit is 1GB (%#x)", cbImage, _1G);
4007 }
4008 const uint32_t cbMinImageSize = pFileHdr->SizeOfOptionalHeader + sizeof(*pFileHdr) + 4 + (uint32_t)offNtHdrs;
4009 if (cbImage < cbMinImageSize)
4010 {
4011 Log(("rtldrPEOpen: %s: SizeOfImage=%#x to small, minimum %#x!!!\n", pszLogName, cbImage, cbMinImageSize));
4012 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfImage=%#x to small, minimum %#x", cbImage, cbMinImageSize);
4013 }
4014 if (pOptHdr->AddressOfEntryPoint >= cbImage)
4015 {
4016 Log(("rtldrPEOpen: %s: AddressOfEntryPoint=%#x - beyond image size (%#x)!!!\n",
4017 pszLogName, pOptHdr->AddressOfEntryPoint, cbImage));
4018 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4019 "AddressOfEntryPoint=%#x - beyond image size (%#x)", pOptHdr->AddressOfEntryPoint, cbImage);
4020 }
4021 if (pOptHdr->BaseOfCode >= cbImage)
4022 {
4023 Log(("rtldrPEOpen: %s: BaseOfCode=%#x - beyond image size (%#x)!!!\n",
4024 pszLogName, pOptHdr->BaseOfCode, cbImage));
4025 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4026 "BaseOfCode=%#x - beyond image size (%#x)", pOptHdr->BaseOfCode, cbImage);
4027 }
4028#if 0/* only in 32-bit header */
4029 if (pOptHdr->BaseOfData >= cbImage)
4030 {
4031 Log(("rtldrPEOpen: %s: BaseOfData=%#x - beyond image size (%#x)!!!\n",
4032 pszLogName, pOptHdr->BaseOfData, cbImage));
4033 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "BaseOfData=%#x - beyond image size (%#x)", pOptHdr->BaseOfData, cbImage);
4034 }
4035#endif
4036 if (pOptHdr->SizeOfHeaders >= cbImage)
4037 {
4038 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - beyond image size (%#x)!!!\n",
4039 pszLogName, pOptHdr->SizeOfHeaders, cbImage));
4040 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT,
4041 "SizeOfHeaders=%#x - beyond image size (%#x)", pOptHdr->SizeOfHeaders, cbImage);
4042 }
4043 /* don't know how to do the checksum, so ignore it. */
4044 if (pOptHdr->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
4045 {
4046 Log(("rtldrPEOpen: %s: Subsystem=%#x (unknown)!!!\n", pszLogName, pOptHdr->Subsystem));
4047 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "Subsystem=%#x (unknown)", pOptHdr->Subsystem);
4048 }
4049 if (pOptHdr->SizeOfHeaders < cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER))
4050 {
4051 Log(("rtldrPEOpen: %s: SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx!!!\n",
4052 pszLogName, pOptHdr->SizeOfHeaders,
4053 cbMinImageSize, pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
4054 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER)));
4055 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfHeaders=%#x - cbMinImageSize %#x + sections %#x = %#llx",
4056 pOptHdr->SizeOfHeaders, cbMinImageSize,
4057 pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER),
4058 cbMinImageSize + pFileHdr->NumberOfSections * sizeof(IMAGE_SECTION_HEADER) );
4059 }
4060 if (pOptHdr->SizeOfStackReserve < pOptHdr->SizeOfStackCommit)
4061 {
4062 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
4063 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
4064 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x",
4065 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
4066 }
4067 if (pOptHdr->SizeOfHeapReserve < pOptHdr->SizeOfHeapCommit)
4068 {
4069 Log(("rtldrPEOpen: %s: SizeOfStackReserve %#x < SizeOfStackCommit %#x!!!\n",
4070 pszLogName, pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit));
4071 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "SizeOfStackReserve %#x < SizeOfStackCommit %#x\n",
4072 pOptHdr->SizeOfStackReserve, pOptHdr->SizeOfStackCommit);
4073 }
4074
4075 /* DataDirectory */
4076 if (pOptHdr->NumberOfRvaAndSizes != RT_ELEMENTS(pOptHdr->DataDirectory))
4077 {
4078 Log(("rtldrPEOpen: %s: NumberOfRvaAndSizes=%d!!!\n", pszLogName, pOptHdr->NumberOfRvaAndSizes));
4079 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "NumberOfRvaAndSizes=%d, expected %d",
4080 pOptHdr->NumberOfRvaAndSizes, RT_ELEMENTS(pOptHdr->DataDirectory));
4081 }
4082 for (unsigned i = 0; i < RT_ELEMENTS(pOptHdr->DataDirectory); i++)
4083 {
4084 IMAGE_DATA_DIRECTORY const *pDir = &pOptHdr->DataDirectory[i];
4085 if (!pDir->Size)
4086 continue;
4087 size_t cb = cbImage;
4088 switch (i)
4089 {
4090 case IMAGE_DIRECTORY_ENTRY_EXPORT: // 0
4091 case IMAGE_DIRECTORY_ENTRY_IMPORT: // 1
4092 case IMAGE_DIRECTORY_ENTRY_RESOURCE: // 2
4093 case IMAGE_DIRECTORY_ENTRY_EXCEPTION: // 3
4094 case IMAGE_DIRECTORY_ENTRY_BASERELOC: // 5
4095 case IMAGE_DIRECTORY_ENTRY_DEBUG: // 6
4096 case IMAGE_DIRECTORY_ENTRY_COPYRIGHT: // 7
4097 case IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT: // 11
4098 case IMAGE_DIRECTORY_ENTRY_IAT: // 12 /* Import Address Table */
4099 break;
4100 case IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG: // 10 - need to check for lock prefixes.
4101 /* Delay inspection after section table is validated. */
4102 break;
4103
4104 case IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT: // 13
4105 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4106 break;
4107 Log(("rtldrPEOpen: %s: dir no. %d (DELAY_IMPORT) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4108 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4109 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_DELAY_IMPORT,
4110 "DELAY_IMPORT VirtualAddress=%#x Size=%#x: not supported", pDir->VirtualAddress, pDir->Size);
4111
4112 case IMAGE_DIRECTORY_ENTRY_SECURITY: // 4
4113 /* The VirtualAddress is a PointerToRawData. */
4114 cb = (size_t)cbRawImage; Assert((uint64_t)cb == cbRawImage);
4115 Log(("rtldrPEOpen: %s: dir no. %d (SECURITY) VirtualAddress=%#x Size=%#x\n", pszLogName, i, pDir->VirtualAddress, pDir->Size));
4116 if (pDir->Size < sizeof(WIN_CERTIFICATE))
4117 {
4118 Log(("rtldrPEOpen: %s: Security directory #%u is too small: %#x bytes\n", pszLogName, i, pDir->Size));
4119 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4120 "Security directory is too small: %#x bytes", pDir->Size);
4121 }
4122 if (pDir->Size >= RTLDRMODPE_MAX_SECURITY_DIR_SIZE)
4123 {
4124 Log(("rtldrPEOpen: %s: Security directory #%u is too large: %#x bytes\n", pszLogName, i, pDir->Size));
4125 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4126 "Security directory is too large: %#x bytes", pDir->Size);
4127 }
4128 if (pDir->VirtualAddress & 7)
4129 {
4130 Log(("rtldrPEOpen: %s: Security directory #%u is misaligned: %#x\n", pszLogName, i, pDir->VirtualAddress));
4131 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4132 "Security directory is misaligned: %#x", pDir->VirtualAddress);
4133 }
4134 /* When using the in-memory reader with a debugger, we may get
4135 into trouble here since we might not have access to the whole
4136 physical file. So skip the tests below. Makes VBoxGuest.sys
4137 load and check out just fine, for instance. */
4138 if (fFlags & RTLDR_O_FOR_DEBUG)
4139 continue;
4140 break;
4141
4142 case IMAGE_DIRECTORY_ENTRY_GLOBALPTR: // 8 /* (MIPS GP) */
4143 Log(("rtldrPEOpen: %s: dir no. %d (GLOBALPTR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4144 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4145 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_GLOBALPTR, "GLOBALPTR VirtualAddress=%#x Size=%#x: not supported",
4146 pDir->VirtualAddress, pDir->Size);
4147
4148 case IMAGE_DIRECTORY_ENTRY_TLS: // 9
4149 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4150 break;
4151 Log(("rtldrPEOpen: %s: dir no. %d (TLS) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4152 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4153 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_TLS, "TLS VirtualAddress=%#x Size=%#x: not supported",
4154 pDir->VirtualAddress, pDir->Size);
4155
4156 case IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR:// 14
4157 if (fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4158 break;
4159 Log(("rtldrPEOpen: %s: dir no. %d (COM_DESCRIPTOR) VirtualAddress=%#x Size=%#x is not supported!!!\n",
4160 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4161 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDRPE_COM_DESCRIPTOR,
4162 "COM_DESCRIPTOR VirtualAddress=%#x Size=%#x: not supported",
4163 pDir->VirtualAddress, pDir->Size);
4164
4165 default:
4166 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x Size=%#x is not supported!!!\n",
4167 pszLogName, i, pDir->VirtualAddress, pDir->Size));
4168 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x Size=%#x is not supported",
4169 i, pDir->VirtualAddress, pDir->Size);
4170 }
4171 if (pDir->VirtualAddress >= cb)
4172 {
4173 Log(("rtldrPEOpen: %s: dir no. %d VirtualAddress=%#x is invalid (limit %#x)!!!\n",
4174 pszLogName, i, pDir->VirtualAddress, cb));
4175 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d VirtualAddress=%#x is invalid (limit %#x)",
4176 i, pDir->VirtualAddress, cb);
4177 }
4178 if (pDir->Size > cb - pDir->VirtualAddress)
4179 {
4180 Log(("rtldrPEOpen: %s: dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)!!!\n",
4181 pszLogName, i, pDir->Size, pDir->VirtualAddress, cb));
4182 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_BAD_EXE_FORMAT, "dir no. %d Size=%#x is invalid (rva=%#x, limit=%#x)",
4183 i, pDir->Size, pDir->VirtualAddress, cb);
4184 }
4185 }
4186 return VINF_SUCCESS;
4187}
4188
4189
4190/**
4191 * Validates and touch up the section headers.
4192 *
4193 * The touching up is restricted to setting the VirtualSize field for old-style
4194 * linkers that sets it to zero.
4195 *
4196 * @returns iprt status code.
4197 * @param paSections Pointer to the array of sections that is to be validated.
4198 * @param cSections Number of sections in that array.
4199 * @param pszLogName The log name to prefix the errors with.
4200 * @param pOptHdr Pointer to the optional header (valid).
4201 * @param cbRawImage The raw image size.
4202 * @param fFlags Loader flags, RTLDR_O_XXX.
4203 * @param fNoCode Verify that the image contains no code.
4204 */
4205static int rtldrPEValidateAndTouchUpSectionHeaders(IMAGE_SECTION_HEADER *paSections, unsigned cSections, const char *pszLogName,
4206 const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint64_t cbRawImage, uint32_t fFlags,
4207 bool fNoCode)
4208{
4209 RT_NOREF_PV(pszLogName);
4210
4211 /*
4212 * Do a quick pass to detect linker setting VirtualSize to zero.
4213 */
4214 bool fFixupVirtualSize = true;
4215 IMAGE_SECTION_HEADER *pSH = &paSections[0];
4216 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4217 if ( pSH->Misc.VirtualSize != 0
4218 && !(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD))
4219 {
4220 fFixupVirtualSize = false;
4221 break;
4222 }
4223
4224 /*
4225 * Actual pass.
4226 */
4227 const uint32_t cbImage = pOptHdr->SizeOfImage;
4228 uint32_t uRvaPrev = pOptHdr->SizeOfHeaders;
4229 pSH = &paSections[0];
4230 Log3(("RTLdrPE: Section Headers:\n"));
4231 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4232 {
4233 const unsigned iSH = (unsigned)(pSH - &paSections[0]); NOREF(iSH);
4234 Log3(("RTLdrPE: #%d '%-8.8s' Characteristics: %08RX32\n"
4235 "RTLdrPE: VirtAddr: %08RX32 VirtSize: %08RX32\n"
4236 "RTLdrPE: FileOff: %08RX32 FileSize: %08RX32\n"
4237 "RTLdrPE: RelocOff: %08RX32 #Relocs: %08RX32\n"
4238 "RTLdrPE: LineOff: %08RX32 #Lines: %08RX32\n",
4239 iSH, pSH->Name, pSH->Characteristics,
4240 pSH->VirtualAddress, pSH->Misc.VirtualSize,
4241 pSH->PointerToRawData, pSH->SizeOfRawData,
4242 pSH->PointerToRelocations, pSH->NumberOfRelocations,
4243 pSH->PointerToLinenumbers, pSH->NumberOfLinenumbers));
4244
4245 AssertCompile(IMAGE_SCN_MEM_16BIT == IMAGE_SCN_MEM_PURGEABLE);
4246 if ( ( pSH->Characteristics & (IMAGE_SCN_MEM_PURGEABLE | IMAGE_SCN_MEM_PRELOAD | IMAGE_SCN_MEM_FARDATA) )
4247 && !(fFlags & RTLDR_O_FOR_DEBUG)) /* purgable/16-bit seen on w2ksp0 hal.dll, ignore the bunch. */
4248 {
4249 Log(("rtldrPEOpen: %s: Unsupported section flag(s) %#x section #%d '%.*s'!!!\n",
4250 pszLogName, pSH->Characteristics, iSH, sizeof(pSH->Name), pSH->Name));
4251 return VERR_BAD_EXE_FORMAT;
4252 }
4253
4254 if ( pSH->PointerToRawData > cbRawImage /// @todo pSH->PointerToRawData >= cbRawImage ?
4255 || pSH->SizeOfRawData > cbRawImage
4256 || pSH->PointerToRawData + pSH->SizeOfRawData > cbRawImage)
4257 {
4258 Log(("rtldrPEOpen: %s: PointerToRawData=%#x SizeOfRawData=%#x - beyond end of file (%#llx) - section #%d '%.*s'!!!\n",
4259 pszLogName, pSH->PointerToRawData, pSH->SizeOfRawData, cbRawImage,
4260 iSH, sizeof(pSH->Name), pSH->Name));
4261 return VERR_BAD_EXE_FORMAT;
4262 }
4263
4264 if (pSH->PointerToRawData & (pOptHdr->FileAlignment - 1)) //ASSUMES power of 2 alignment.
4265 {
4266 Log(("rtldrPEOpen: %s: PointerToRawData=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4267 pszLogName, pSH->PointerToRawData, pOptHdr->FileAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4268 return VERR_BAD_EXE_FORMAT;
4269 }
4270
4271 if (!(pSH->Characteristics & IMAGE_SCN_TYPE_NOLOAD)) /* binutils uses this for '.stab' even if it's reserved/obsoleted by MS. */
4272 {
4273 /* Calc VirtualSize if necessary. This is for internal reasons. */
4274 if ( pSH->Misc.VirtualSize == 0
4275 && fFixupVirtualSize)
4276 {
4277 pSH->Misc.VirtualSize = cbImage - RT_MIN(pSH->VirtualAddress, cbImage);
4278 for (uint32_t i = 1; i < cSHdrsLeft; i++)
4279 if ( !(pSH[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)
4280 && pSH[i].VirtualAddress >= pSH->VirtualAddress)
4281 {
4282 pSH->Misc.VirtualSize = RT_MIN(pSH[i].VirtualAddress - pSH->VirtualAddress, pSH->Misc.VirtualSize);
4283 break;
4284 }
4285 }
4286
4287 if (pSH->Misc.VirtualSize > 0)
4288 {
4289 if (pSH->VirtualAddress < uRvaPrev)
4290 {
4291 Log(("rtldrPEOpen: %s: Overlaps previous section or sections aren't in ascending order, VirtualAddress=%#x uRvaPrev=%#x - section #%d '%.*s'!!!\n",
4292 pszLogName, pSH->VirtualAddress, uRvaPrev, iSH, sizeof(pSH->Name), pSH->Name));
4293 return VERR_BAD_EXE_FORMAT;
4294 }
4295 if (pSH->VirtualAddress > cbImage)
4296 {
4297 Log(("rtldrPEOpen: %s: VirtualAddress=%#x - beyond image size (%#x) - section #%d '%.*s'!!!\n",
4298 pszLogName, pSH->VirtualAddress, cbImage, iSH, sizeof(pSH->Name), pSH->Name));
4299 return VERR_BAD_EXE_FORMAT;
4300 }
4301
4302 if (pSH->VirtualAddress & (pOptHdr->SectionAlignment - 1)) //ASSUMES power of 2 alignment.
4303 {
4304 Log(("rtldrPEOpen: %s: VirtualAddress=%#x misaligned (%#x) - section #%d '%.*s'!!!\n",
4305 pszLogName, pSH->VirtualAddress, pOptHdr->SectionAlignment, iSH, sizeof(pSH->Name), pSH->Name));
4306 return VERR_BAD_EXE_FORMAT;
4307 }
4308
4309#ifdef PE_FILE_OFFSET_EQUALS_RVA
4310 /* Our loader code assume rva matches the file offset. */
4311 if ( pSH->SizeOfRawData
4312 && pSH->PointerToRawData != pSH->VirtualAddress)
4313 {
4314 Log(("rtldrPEOpen: %s: ASSUMPTION FAILED: file offset %#x != RVA %#x - section #%d '%.*s'!!!\n",
4315 pszLogName, pSH->PointerToRawData, pSH->VirtualAddress, iSH, sizeof(pSH->Name), pSH->Name));
4316 return VERR_BAD_EXE_FORMAT;
4317 }
4318#endif
4319
4320 uRvaPrev = pSH->VirtualAddress + pSH->Misc.VirtualSize;
4321 }
4322 }
4323
4324 /* ignore the relocations and linenumbers. */
4325 }
4326
4327 /*
4328 * Do a separate run if we need to validate the no-code claim from the
4329 * optional header.
4330 */
4331 if (fNoCode)
4332 {
4333 pSH = &paSections[0];
4334 for (unsigned cSHdrsLeft = cSections; cSHdrsLeft > 0; cSHdrsLeft--, pSH++)
4335 if (pSH->Characteristics & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE))
4336 return VERR_LDR_ARCH_MISMATCH;
4337 }
4338
4339
4340 /** @todo r=bird: more sanity checks! */
4341 return VINF_SUCCESS;
4342}
4343
4344
4345/**
4346 * Reads image data by RVA using the section headers.
4347 *
4348 * @returns iprt status code.
4349 * @param pModPe The PE module instance.
4350 * @param pvBuf Where to store the bits.
4351 * @param cb Number of bytes to tread.
4352 * @param RVA Where to read from.
4353 */
4354static int rtldrPEReadRVA(PRTLDRMODPE pModPe, void *pvBuf, uint32_t cb, uint32_t RVA)
4355{
4356 const IMAGE_SECTION_HEADER *pSH = pModPe->paSections;
4357 PRTLDRREADER pReader = pModPe->Core.pReader;
4358 uint32_t cbRead;
4359 int rc;
4360
4361 /*
4362 * Is it the headers, i.e. prior to the first section.
4363 */
4364 if (RVA < pModPe->cbHeaders)
4365 {
4366 cbRead = RT_MIN(pModPe->cbHeaders - RVA, cb);
4367 rc = pReader->pfnRead(pReader, pvBuf, cbRead, RVA);
4368 if ( cbRead == cb
4369 || RT_FAILURE(rc))
4370 return rc;
4371 cb -= cbRead;
4372 RVA += cbRead;
4373 pvBuf = (uint8_t *)pvBuf + cbRead;
4374 }
4375
4376 /* In the zero space between headers and the first section? */
4377 if (RVA < pSH->VirtualAddress)
4378 {
4379 cbRead = RT_MIN(pSH->VirtualAddress - RVA, cb);
4380 memset(pvBuf, 0, cbRead);
4381 if (cbRead == cb)
4382 return VINF_SUCCESS;
4383 cb -= cbRead;
4384 RVA += cbRead;
4385 pvBuf = (uint8_t *)pvBuf + cbRead;
4386 }
4387
4388 /*
4389 * Iterate the sections.
4390 */
4391 for (unsigned cLeft = pModPe->cSections;
4392 cLeft > 0;
4393 cLeft--, pSH++)
4394 {
4395 uint32_t off = RVA - pSH->VirtualAddress;
4396 if (off < pSH->Misc.VirtualSize)
4397 {
4398 cbRead = RT_MIN(pSH->Misc.VirtualSize - off, cb);
4399 rc = pReader->pfnRead(pReader, pvBuf, cbRead, pSH->PointerToRawData + off);
4400 if ( cbRead == cb
4401 || RT_FAILURE(rc))
4402 return rc;
4403 cb -= cbRead;
4404 RVA += cbRead;
4405 pvBuf = (uint8_t *)pvBuf + cbRead;
4406 }
4407 uint32_t RVANext = cLeft ? pSH[1].VirtualAddress : pModPe->cbImage;
4408 if (RVA < RVANext)
4409 {
4410 cbRead = RT_MIN(RVANext - RVA, cb);
4411 memset(pvBuf, 0, cbRead);
4412 if (cbRead == cb)
4413 return VINF_SUCCESS;
4414 cb -= cbRead;
4415 RVA += cbRead;
4416 pvBuf = (uint8_t *)pvBuf + cbRead;
4417 }
4418 }
4419
4420 AssertFailed();
4421 return VERR_INTERNAL_ERROR;
4422}
4423
4424
4425/**
4426 * Validates the data of some selected data directories entries and remember
4427 * important bits for later.
4428 *
4429 * This requires a valid section table and thus has to wait till after we've
4430 * read and validated it.
4431 *
4432 * @returns iprt status code.
4433 * @param pModPe The PE module instance.
4434 * @param pOptHdr Pointer to the optional header (valid).
4435 * @param fFlags Loader flags, RTLDR_O_XXX.
4436 * @param pErrInfo Where to return extended error information. Optional.
4437 */
4438static int rtldrPEValidateDirectoriesAndRememberStuff(PRTLDRMODPE pModPe, const IMAGE_OPTIONAL_HEADER64 *pOptHdr, uint32_t fFlags,
4439 PRTERRINFO pErrInfo)
4440{
4441 const char *pszLogName = pModPe->Core.pReader->pfnLogName(pModPe->Core.pReader); NOREF(pszLogName);
4442 union /* combine stuff we're reading to help reduce stack usage. */
4443 {
4444 IMAGE_LOAD_CONFIG_DIRECTORY64 Cfg64;
4445 uint8_t abZeros[sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64) * 4];
4446 } u;
4447
4448 /*
4449 * The load config entry may include lock prefix tables and whatnot which we don't implement.
4450 * It does also include a lot of stuff which we can ignore, so we'll have to inspect the
4451 * actual data before we can make up our mind about it all.
4452 */
4453 IMAGE_DATA_DIRECTORY Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG];
4454 if (Dir.Size)
4455 {
4456 const size_t cbExpectV12 = !pModPe->f64Bit
4457 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V12)
4458 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V12);
4459 const size_t cbExpectV11 = !pModPe->f64Bit
4460 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V11)
4461 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V11);
4462 const size_t cbExpectV10 = !pModPe->f64Bit
4463 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V10)
4464 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V10);
4465 const size_t cbExpectV9 = !pModPe->f64Bit
4466 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V9)
4467 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V9);
4468 const size_t cbExpectV8 = !pModPe->f64Bit
4469 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V8)
4470 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V8);
4471 const size_t cbExpectV7 = !pModPe->f64Bit
4472 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V7)
4473 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V7);
4474 const size_t cbExpectV6 = !pModPe->f64Bit
4475 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V6)
4476 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V6);
4477 const size_t cbExpectV5 = !pModPe->f64Bit
4478 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V5)
4479 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V5);
4480 const size_t cbExpectV4 = !pModPe->f64Bit
4481 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V4)
4482 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V4);
4483 const size_t cbExpectV3 = !pModPe->f64Bit
4484 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V3)
4485 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V3);
4486 const size_t cbExpectV2 = !pModPe->f64Bit
4487 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V2)
4488 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2);
4489 const size_t cbExpectV1 = !pModPe->f64Bit
4490 ? sizeof(IMAGE_LOAD_CONFIG_DIRECTORY32_V1)
4491 : sizeof(IMAGE_LOAD_CONFIG_DIRECTORY64_V2) /*No V1*/;
4492 const size_t cbNewHack = cbExpectV5; /* Playing safe here since there might've been revisions between V5 and V6 we don't know about . */
4493 const size_t cbMaxKnown = cbExpectV12;
4494
4495 bool fNewerStructureHack = false;
4496 if ( Dir.Size != cbExpectV12
4497 && Dir.Size != cbExpectV11
4498 && Dir.Size != cbExpectV10
4499 && Dir.Size != cbExpectV9
4500 && Dir.Size != cbExpectV8
4501 && Dir.Size != cbExpectV7
4502 && Dir.Size != cbExpectV6
4503 && Dir.Size != cbExpectV5
4504 && Dir.Size != cbExpectV4
4505 && Dir.Size != cbExpectV3
4506 && Dir.Size != cbExpectV2
4507 && Dir.Size != cbExpectV1)
4508 {
4509 fNewerStructureHack = Dir.Size > cbNewHack /* These structure changes are slowly getting to us! More futher down. */
4510 && Dir.Size <= sizeof(u);
4511 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",
4512 pszLogName, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1,
4513 fNewerStructureHack ? " Will try ignore extra bytes if all zero." : ""));
4514 if (!fNewerStructureHack)
4515 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4516 "Unexpected load config dir size of %u bytes; supported sized: %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, %zu, or %zu",
4517 Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4518 }
4519
4520 /*
4521 * Read, check new stuff and convert to 64-bit.
4522 *
4523 * If we accepted a newer structures when loading for debug or validation,
4524 * otherwise we require the new bits to be all zero and hope that they are
4525 * insignificant where image loading is concerned (that's mostly been the
4526 * case even for non-zero bits, only hard exception is LockPrefixTable).
4527 */
4528 RT_ZERO(u.Cfg64);
4529 int rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4530 if (RT_FAILURE(rc))
4531 return rc;
4532 if ( fNewerStructureHack
4533 && Dir.Size > cbMaxKnown
4534 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4535 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4536 {
4537 Log(("rtldrPEOpen: %s: load cfg dir: Unexpected bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4538 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4539 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4540 "Grown load config (%u to %u bytes) includes non-zero bytes: %.*Rhxs",
4541 cbMaxKnown, Dir.Size, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4542 }
4543 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4544
4545 if (u.Cfg64.Size != Dir.Size)
4546 {
4547 /* Kludge #1: ntdll.dll from XP seen with Dir.Size=0x40 and Cfg64.Size=0x00. */
4548 if (Dir.Size == 0x40 && u.Cfg64.Size == 0x00 && !pModPe->f64Bit)
4549 {
4550 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the XP kludge.\n",
4551 pszLogName, u.Cfg64.Size, Dir.Size));
4552 u.Cfg64.Size = Dir.Size;
4553 }
4554 /* Kludge #2: This happens a lot. Structure changes, but the linker doesn't get
4555 updated and stores some old size in the directory. Use the header size. */
4556 else if ( u.Cfg64.Size == cbExpectV12
4557 || u.Cfg64.Size == cbExpectV11
4558 || u.Cfg64.Size == cbExpectV10
4559 || u.Cfg64.Size == cbExpectV9
4560 || u.Cfg64.Size == cbExpectV8
4561 || u.Cfg64.Size == cbExpectV7
4562 || u.Cfg64.Size == cbExpectV6
4563 || u.Cfg64.Size == cbExpectV5
4564 || u.Cfg64.Size == cbExpectV4
4565 || u.Cfg64.Size == cbExpectV3
4566 || u.Cfg64.Size == cbExpectV2
4567 || u.Cfg64.Size == cbExpectV1
4568 || (fNewerStructureHack = (u.Cfg64.Size > cbNewHack && u.Cfg64.Size <= sizeof(u))) )
4569 {
4570 Log(("rtldrPEOpen: %s: load cfg dir: Header (%d) and directory (%d) size mismatch, applying the old linker kludge.\n",
4571 pszLogName, u.Cfg64.Size, Dir.Size));
4572
4573 uint32_t const uOrgDir = Dir.Size;
4574 Dir.Size = u.Cfg64.Size;
4575 RT_ZERO(u.Cfg64);
4576 rc = rtldrPEReadRVA(pModPe, &u.Cfg64, Dir.Size, Dir.VirtualAddress);
4577 if (RT_FAILURE(rc))
4578 return rc;
4579 if ( fNewerStructureHack
4580 && Dir.Size > cbMaxKnown
4581 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION))
4582 && !ASMMemIsZero(&u.abZeros[cbMaxKnown], Dir.Size - cbMaxKnown))
4583 {
4584 Log(("rtldrPEOpen: %s: load cfg dir: Unknown bytes are non-zero (%u bytes of which %u expected to be zero): %.*Rhxs\n",
4585 pszLogName, Dir.Size, Dir.Size - cbMaxKnown, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]));
4586 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4587 "Grown load config (%u to %u bytes, dir %u) includes non-zero bytes: %.*Rhxs",
4588 cbMaxKnown, Dir.Size, uOrgDir, Dir.Size - cbMaxKnown, &u.abZeros[cbMaxKnown]);
4589 }
4590 rtldrPEConvert32BitLoadConfigTo64Bit(&u.Cfg64);
4591 AssertReturn(u.Cfg64.Size == Dir.Size,
4592 RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE, "Data changed while reading! (%d vs %d)\n",
4593 u.Cfg64.Size, Dir.Size));
4594 }
4595 else
4596 {
4597 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",
4598 pszLogName, u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1));
4599 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOAD_CONFIG_SIZE,
4600 "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",
4601 u.Cfg64.Size, Dir.Size, cbExpectV12, cbExpectV11, cbExpectV10, cbExpectV9, cbExpectV8, cbExpectV7, cbExpectV6, cbExpectV5, cbExpectV4, cbExpectV3, cbExpectV2, cbExpectV1);
4602 }
4603 }
4604 if (u.Cfg64.LockPrefixTable && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4605 {
4606 Log(("rtldrPEOpen: %s: load cfg dir: lock prefix table at %RX64. We don't support lock prefix tables!\n",
4607 pszLogName, u.Cfg64.LockPrefixTable));
4608 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_LOCK_PREFIX_TABLE,
4609 "Lock prefix table not supported: %RX64", u.Cfg64.LockPrefixTable);
4610 }
4611#if 0/* this seems to be safe to ignore. */
4612 if ( u.Cfg64.SEHandlerTable
4613 || u.Cfg64.SEHandlerCount)
4614 {
4615 Log(("rtldrPEOpen: %s: load cfg dir: SEHandlerTable=%RX64 and SEHandlerCount=%RX64 are unsupported!\n",
4616 pszLogName, u.Cfg64.SEHandlerTable, u.Cfg64.SEHandlerCount));
4617 return VERR_BAD_EXE_FORMAT;
4618 }
4619#endif
4620 if (u.Cfg64.EditList && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)))
4621 {
4622 Log(("rtldrPEOpen: %s: load cfg dir: EditList=%RX64 is unsupported!\n",
4623 pszLogName, u.Cfg64.EditList));
4624 return RTErrInfoSetF(pErrInfo, VERR_BAD_EXE_FORMAT, "Load config EditList=%RX64 is not supported", u.Cfg64.EditList);
4625 }
4626 /** @todo GuardCFC? Possibly related to:
4627 * http://research.microsoft.com/pubs/69217/ccs05-cfi.pdf
4628 * Not trusting something designed by bakas who don't know how to modify a
4629 * structure without messing up its natural alignment. */
4630 if ( ( u.Cfg64.GuardCFCCheckFunctionPointer
4631 || u.Cfg64.GuardCFDispatchFunctionPointer
4632 || u.Cfg64.GuardCFFunctionTable
4633 || u.Cfg64.GuardCFFunctionCount
4634 || u.Cfg64.GuardFlags
4635 || u.Cfg64.GuardAddressTakenIatEntryTable
4636 || u.Cfg64.GuardAddressTakenIatEntryCount
4637 || u.Cfg64.GuardLongJumpTargetTable
4638 || u.Cfg64.GuardLongJumpTargetCount)
4639 && !(fFlags & (RTLDR_O_FOR_DEBUG | RTLDR_O_FOR_VALIDATION)) )
4640 {
4641 Log(("rtldrPEOpen: %s: load cfg dir: Guard stuff: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!\n",
4642 pszLogName, u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4643 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4644 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4645 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount ));
4646#if 0 /* ntdll 15002 uses this. */
4647 return RTErrInfoSetF(pErrInfo, VERR_LDRPE_GUARD_CF_STUFF,
4648 "Guard bits in load config: %RX64,%RX64,%RX64,%RX64,%RX32,%RX64,%RX64,%RX64,%RX64!",
4649 u.Cfg64.GuardCFCCheckFunctionPointer, u.Cfg64.GuardCFDispatchFunctionPointer,
4650 u.Cfg64.GuardCFFunctionTable, u.Cfg64.GuardCFFunctionCount, u.Cfg64.GuardFlags,
4651 u.Cfg64.GuardAddressTakenIatEntryTable, u.Cfg64.GuardAddressTakenIatEntryCount,
4652 u.Cfg64.GuardLongJumpTargetTable, u.Cfg64.GuardLongJumpTargetCount);
4653#endif
4654 }
4655 }
4656
4657 /*
4658 * If the image is signed and we're not doing this for debug purposes,
4659 * take a look at the signature.
4660 */
4661 Dir = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4662 if (Dir.Size)
4663 {
4664 PWIN_CERTIFICATE pFirst = (PWIN_CERTIFICATE)RTMemTmpAlloc(Dir.Size);
4665 if (!pFirst)
4666 return VERR_NO_TMP_MEMORY;
4667 int rc = pModPe->Core.pReader->pfnRead(pModPe->Core.pReader, pFirst, Dir.Size, Dir.VirtualAddress);
4668 if (RT_SUCCESS(rc))
4669 {
4670 uint32_t off = 0;
4671 do
4672 {
4673 PWIN_CERTIFICATE pCur = (PWIN_CERTIFICATE)((uint8_t *)pFirst + off);
4674
4675 /* validate the members. */
4676 if ( pCur->dwLength < sizeof(WIN_CERTIFICATE)
4677 || pCur->dwLength + off > Dir.Size)
4678 {
4679 Log(("rtldrPEOpen: %s: cert at %#x/%#x: dwLength=%#x\n", pszLogName, off, Dir.Size, pCur->dwLength));
4680 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4681 "Cert at %#x LB %#x: Bad header length value: %#x", off, Dir.Size, pCur->dwLength);
4682 break;
4683 }
4684 if ( pCur->wRevision != WIN_CERT_REVISION_2_0
4685 && pCur->wRevision != WIN_CERT_REVISION_1_0)
4686 {
4687 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wRevision=%#x\n", pszLogName, off, Dir.Size, pCur->wRevision));
4688 if (pCur->wRevision >= WIN_CERT_REVISION_1_0)
4689 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
4690 "Cert at %#x LB %#x: Unsupported revision: %#x", off, Dir.Size, pCur->wRevision);
4691 else
4692 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4693 "Cert at %#x LB %#x: Malformed revision: %#x", off, Dir.Size, pCur->wRevision);
4694 break;
4695 }
4696 if ( pCur->wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA
4697 && pCur->wCertificateType != WIN_CERT_TYPE_X509
4698 /*&& pCur->wCertificateType != WIN_CERT_TYPE_RESERVED_1*/
4699 /*&& pCur->wCertificateType != WIN_CERT_TYPE_TS_STACK_SIGNED*/
4700 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_PKCS115
4701 && pCur->wCertificateType != WIN_CERT_TYPE_EFI_GUID
4702 )
4703 {
4704 Log(("rtldrPEOpen: %s: cert at %#x/%#x: wCertificateType=%#x\n", pszLogName, off, Dir.Size, pCur->wCertificateType));
4705 if (pCur->wCertificateType)
4706 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_UNSUPPORTED,
4707 "Cert at %#x LB %#x: Unsupported certificate type: %#x",
4708 off, Dir.Size, pCur->wCertificateType);
4709 else
4710 rc = RTErrInfoSetF(pErrInfo, VERR_LDRPE_CERT_MALFORMED,
4711 "Cert at %#x LB %#x: Malformed certificate type: %#x",
4712 off, Dir.Size, pCur->wCertificateType);
4713 break;
4714 }
4715
4716 /* Remember the first signed data certificate. */
4717 if ( pCur->wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA
4718 && pModPe->offPkcs7SignedData == 0)
4719 {
4720 pModPe->offPkcs7SignedData = Dir.VirtualAddress
4721 + (uint32_t)((uintptr_t)&pCur->bCertificate[0] - (uintptr_t)pFirst);
4722 pModPe->cbPkcs7SignedData = pCur->dwLength - RT_UOFFSETOF(WIN_CERTIFICATE, bCertificate);
4723 }
4724
4725 /* next */
4726 off += RT_ALIGN(pCur->dwLength, WIN_CERTIFICATE_ALIGNMENT);
4727 } while (off < Dir.Size);
4728 }
4729 RTMemTmpFree(pFirst);
4730 if (RT_FAILURE(rc) && !(fFlags & RTLDR_O_FOR_DEBUG))
4731 return rc;
4732 }
4733
4734 return VINF_SUCCESS;
4735}
4736
4737
4738/**
4739 * Open a PE image.
4740 *
4741 * @returns iprt status code.
4742 * @param pReader The loader reader instance which will provide the raw image bits.
4743 * @param fFlags Loader flags, RTLDR_O_XXX.
4744 * @param enmArch Architecture specifier.
4745 * @param offNtHdrs The offset of the NT headers (where you find "PE\0\0").
4746 * @param phLdrMod Where to store the handle.
4747 * @param pErrInfo Where to return extended error information. Optional.
4748 */
4749DECLHIDDEN(int) rtldrPEOpen(PRTLDRREADER pReader, uint32_t fFlags, RTLDRARCH enmArch, RTFOFF offNtHdrs,
4750 PRTLDRMOD phLdrMod, PRTERRINFO pErrInfo)
4751{
4752 /*
4753 * Read and validate the file header.
4754 */
4755 IMAGE_FILE_HEADER FileHdr;
4756 int rc = pReader->pfnRead(pReader, &FileHdr, sizeof(FileHdr), offNtHdrs + 4);
4757 if (RT_FAILURE(rc))
4758 return rc;
4759 RTLDRARCH enmArchImage;
4760 const char *pszLogName = pReader->pfnLogName(pReader);
4761 rc = rtldrPEValidateFileHeader(&FileHdr, fFlags, pszLogName, &enmArchImage, pErrInfo);
4762 if (RT_FAILURE(rc))
4763 return rc;
4764
4765 /*
4766 * Match the CPU architecture.
4767 */
4768 bool fArchNoCodeCheckPending = false;
4769 if ( enmArch != enmArchImage
4770 && ( enmArch != RTLDRARCH_WHATEVER
4771 && !(fFlags & RTLDR_O_WHATEVER_ARCH)) )
4772 {
4773 if (!(fFlags & RTLDR_O_IGNORE_ARCH_IF_NO_CODE))
4774 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH, "Image is for '%s', only accepting images for '%s'.",
4775 rtldrPEGetArchName(FileHdr.Machine), RTLdrArchName(enmArch));
4776 fArchNoCodeCheckPending = true;
4777 }
4778
4779 /*
4780 * Read and validate the "optional" header. Convert 32->64 if necessary.
4781 */
4782 IMAGE_OPTIONAL_HEADER64 OptHdr;
4783 rc = pReader->pfnRead(pReader, &OptHdr, FileHdr.SizeOfOptionalHeader, offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER));
4784 if (RT_FAILURE(rc))
4785 return rc;
4786 if (FileHdr.SizeOfOptionalHeader != sizeof(OptHdr))
4787 rtldrPEConvert32BitOptionalHeaderTo64Bit(&OptHdr);
4788 rc = rtldrPEValidateOptionalHeader(&OptHdr, pszLogName, offNtHdrs, &FileHdr, pReader->pfnSize(pReader), fFlags, pErrInfo);
4789 if (RT_FAILURE(rc))
4790 return rc;
4791 if (fArchNoCodeCheckPending && OptHdr.SizeOfCode != 0)
4792 return RTERRINFO_LOG_SET_F(pErrInfo, VERR_LDR_ARCH_MISMATCH,
4793 "Image is for '%s' and contains code (%#x), only accepting images for '%s' with code.",
4794 rtldrPEGetArchName(FileHdr.Machine), OptHdr.SizeOfCode, RTLdrArchName(enmArch));
4795
4796 /*
4797 * Read and validate section headers.
4798 */
4799 const size_t cbSections = sizeof(IMAGE_SECTION_HEADER) * FileHdr.NumberOfSections;
4800 PIMAGE_SECTION_HEADER paSections = (PIMAGE_SECTION_HEADER)RTMemAlloc(cbSections);
4801 if (!paSections)
4802 return VERR_NO_MEMORY;
4803 rc = pReader->pfnRead(pReader, paSections, cbSections,
4804 offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader);
4805 if (RT_SUCCESS(rc))
4806 {
4807 rc = rtldrPEValidateAndTouchUpSectionHeaders(paSections, FileHdr.NumberOfSections, pszLogName,
4808 &OptHdr, pReader->pfnSize(pReader), fFlags, fArchNoCodeCheckPending);
4809 if (RT_SUCCESS(rc))
4810 {
4811 /*
4812 * Allocate and initialize the PE module structure.
4813 */
4814 PRTLDRMODPE pModPe = (PRTLDRMODPE)RTMemAllocZ(sizeof(*pModPe));
4815 if (pModPe)
4816 {
4817 pModPe->Core.u32Magic = RTLDRMOD_MAGIC;
4818 pModPe->Core.eState = LDR_STATE_OPENED;
4819 if (FileHdr.SizeOfOptionalHeader == sizeof(OptHdr))
4820 pModPe->Core.pOps = &s_rtldrPE64Ops.Core;
4821 else
4822 pModPe->Core.pOps = &s_rtldrPE32Ops.Core;
4823 pModPe->Core.pReader = pReader;
4824 pModPe->Core.enmFormat= RTLDRFMT_PE;
4825 pModPe->Core.enmType = FileHdr.Characteristics & IMAGE_FILE_DLL
4826 ? FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4827 ? RTLDRTYPE_EXECUTABLE_FIXED
4828 : RTLDRTYPE_EXECUTABLE_RELOCATABLE
4829 : FileHdr.Characteristics & IMAGE_FILE_RELOCS_STRIPPED
4830 ? RTLDRTYPE_SHARED_LIBRARY_FIXED
4831 : RTLDRTYPE_SHARED_LIBRARY_RELOCATABLE;
4832 pModPe->Core.enmEndian= RTLDRENDIAN_LITTLE;
4833 pModPe->Core.enmArch = FileHdr.Machine == IMAGE_FILE_MACHINE_I386
4834 ? RTLDRARCH_X86_32
4835 : FileHdr.Machine == IMAGE_FILE_MACHINE_AMD64
4836 ? RTLDRARCH_AMD64
4837 : RTLDRARCH_WHATEVER;
4838 pModPe->pvBits = NULL;
4839 pModPe->offNtHdrs = offNtHdrs;
4840 pModPe->offEndOfHdrs = offNtHdrs + 4 + sizeof(IMAGE_FILE_HEADER) + FileHdr.SizeOfOptionalHeader + cbSections;
4841 pModPe->u16Machine = FileHdr.Machine;
4842 pModPe->fFile = FileHdr.Characteristics;
4843 pModPe->cSections = FileHdr.NumberOfSections;
4844 pModPe->paSections = paSections;
4845 pModPe->uEntryPointRVA= OptHdr.AddressOfEntryPoint;
4846 pModPe->uImageBase = (RTUINTPTR)OptHdr.ImageBase;
4847 pModPe->cbImage = OptHdr.SizeOfImage;
4848 pModPe->cbHeaders = OptHdr.SizeOfHeaders;
4849 pModPe->uTimestamp = FileHdr.TimeDateStamp;
4850 pModPe->cImports = UINT32_MAX;
4851 pModPe->f64Bit = FileHdr.SizeOfOptionalHeader == sizeof(OptHdr);
4852 pModPe->ImportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
4853 pModPe->RelocDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
4854 pModPe->ExportDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
4855 pModPe->DebugDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
4856 pModPe->SecurityDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
4857 pModPe->ExceptionDir = OptHdr.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
4858 pModPe->fDllCharacteristics = OptHdr.DllCharacteristics;
4859
4860 /*
4861 * Perform validation of some selected data directories which requires
4862 * inspection of the actual data. This also saves some certificate
4863 * information.
4864 */
4865 rc = rtldrPEValidateDirectoriesAndRememberStuff(pModPe, &OptHdr, fFlags, pErrInfo);
4866 if (RT_SUCCESS(rc))
4867 {
4868 *phLdrMod = &pModPe->Core;
4869 return VINF_SUCCESS;
4870 }
4871 RTMemFree(pModPe);
4872 }
4873 else
4874 rc = VERR_NO_MEMORY;
4875 }
4876 }
4877 RTMemFree(paSections);
4878 return rc;
4879}
4880
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