VirtualBox

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

Last change on this file since 57974 was 57974, checked in by vboxsync, 9 years ago

IPRT: Some more doxygen fixes.

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

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