VirtualBox

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

Last change on this file since 51914 was 51914, checked in by vboxsync, 11 years ago

hack

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