VirtualBox

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

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

RTLdrQueryPropEx: RTLDRPROP_UNWIND_TABLE and RTLDRPROP_UNWIND_INFO for getting PE unwind info.

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

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