VirtualBox

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

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

SUP,LDR: Changed RTLdrGetBits to allow not resolving imports. Combined the memory and image purification code with the process validation code, adding a validation kind/mode parameter. The process verfication code now checks that code sections are unmodified. Had to add a self purification run before hooking NtCreateSection to undo a weird kernel32 change that avast made (making GetBinaryTypeW specify write thru when opening a file). So, VM startup is now even slower thanks to avast.

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