VirtualBox

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

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

SUP,IPRT: Extended RTLdrQueryPropEx with a pvBits parameter, RTLDRPROP_IMPORT_COUNT and RTLDRPROP_IMPORT_MODULE. Hook LdrLoadDll to validate DLLs before they get to NtCreateSection and the loader code/data can be messed up (windows 7 / 32-bit crash). Allow the kernel to buffer the log file, no real need that each write hits the disk.

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

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