VirtualBox

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

Last change on this file since 64891 was 64891, checked in by vboxsync, 8 years ago

RTLdrQueryProp: Added RTLDRPROP_FILE_OFF_HEADER, implementing it for PE.

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