/* $Id: kLdrMod.c 2851 2006-11-02 03:21:54Z bird $ */ /** @file * * kLdr - The Module Interpreter for the Portable Executable (PE) Format. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include "kLdrHlp.h" #include "kLdrInternal.h" #include "kLdrModPE.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRMODPE_STRICT * Define KLDRMODPE_STRICT to enabled strict checks in KLDRMODPE. */ #define KLDRMOD_STRICT 1 /** @def KLDRMODPE_ASSERT * Assert that an expression is true when KLDR_STRICT is defined. */ #ifdef KLDRMODPE_STRICT # define KLDRMODPE_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRMODPE_ASSERT(expr) do {} while (0) #endif /** @def KLDRMODPE_RVA2TYPE * Converts a RVA to a pointer of the specified type. * @param pvBits The bits (image base). * @param uRVA The image relative virtual address. * @param type The type to cast to. */ #define KLDRMODPE_RVA2TYPE(pvBits, uRVA, type) \ ( (type) ((uintptr_t)(pvBits) + (uRVA)) ) /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Instance data for the PE module interpreter. */ typedef struct KLDRMODPE { /** Pointer to the module. (Follows the section table.) */ PKLDRMOD pMod; /** Pointer to the RDR mapping of the raw file bits. NULL if not mapped. */ const void *pvBits; /** Whether we've mapped the image or not. */ uint32_t fMapped : 1; /** Reserved flags. */ uint32_t f31Reserved; /** The number of imported modules. * If ~(uint32_t)0 this hasn't been determined yet. */ uint32_t cImportModules; /** The offset of the NT headers. */ off_t offHdrs; /** Copy of the NT headers. */ IMAGE_NT_HEADERS64 Hdrs; /** The section header table . */ IMAGE_SECTION_HEADER aShdrs[1]; } KLDRMODPE, *PKLDRMODPE; /******************************************************************************* * Internal Functions * *******************************************************************************/ static int kldrModPECreateInstance(PKLDRRDR pRdr, off_t offNewHdr, PKLDRMODPE *ppMod); static void kldrModPEConvertLoadConfig(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg); static int kLdrModPEValidateOptionalHeader(PKLDRMODPE pModPE); static int kLdrModPEValidateSectionHeaders(PKLDRMODPE pModPE); static void kldrModPEConvertOptionalHeader(PIMAGE_OPTIONAL_HEADER64 pOptionalHeader); static int kldrModPEQueryForwarder(PKLDRMODPE pModPE, const void *pvBits, const char *pszForwarder, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind); static int32_t kldrModPENumberOfImports(PKLDRMOD pMod, const void *pvBits); /** * Create a loader module instance interpreting the executable image found * in the specified file provider instance. * * @returns 0 on success and *ppMod pointing to a module instance. * On failure, a non-zero OS specific error code is returned. * @param pOps Pointer to the registered method table. * @param pRdr The file provider instance to use. * @param offNewHdr The offset of the new header in MZ files. -1 if not found. * @param ppMod Where to store the module instance pointer. */ static int kldrModPECreate(PCKLDRMODOPS pOps, PKLDRRDR pRdr, off_t offNewHdr, PPKLDRMOD ppMod) { PKLDRMODPE pModPE; int rc; /* * Create the instance data and do a minimal header validation. */ rc = kldrModPECreateInstance(pRdr, offNewHdr, &pModPE); if (!rc) { pModPE->pMod->pOps = pOps; pModPE->pMod->u32Magic = KLDRMOD_MAGIC; *ppMod = pModPE->pMod; return 0; } kldrHlpFree(pModPE); return rc; } /** * Separate function for reading creating the PE module instance to * simplify cleanup on failure. */ static int kldrModPECreateInstance(PKLDRRDR pRdr, off_t offNewHdr, PKLDRMODPE *ppModPE) { struct { uint32_t Signature; IMAGE_FILE_HEADER FileHdr; } s; PKLDRMODPE pModPE; PKLDRMOD pMod; size_t cb; size_t cchFilename; off_t off; uint32_t i; int rc; *ppModPE = NULL; /* * Read the signature and file header. */ rc = kLdrRdrRead(pRdr, &s, sizeof(s), offNewHdr > 0 ? offNewHdr : 0); if (rc) return rc; if (s.Signature != IMAGE_NT_SIGNATURE) return KLDR_ERR_UNKNOWN_FORMAT; /* sanity checks. */ if ( s.FileHdr.NumberOfSections > 1024*1024 || ( s.FileHdr.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER32) && s.FileHdr.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER64)) ) return KLDR_ERR_PE_BAD_FILE_HEADER; if ( s.FileHdr.Machine != IMAGE_FILE_MACHINE_I386 && s.FileHdr.Machine != IMAGE_FILE_MACHINE_AMD64 ) return KLDR_ERR_PE_UNSUPPORTED_MACHINE; /* * Calc the instance size, allocate and initialize it. */ cchFilename = kLdrHlpStrLen(kLdrRdrName(pRdr)); cb = sizeof(KLDRMOD) + s.FileHdr.NumberOfSections * sizeof(KLDRSEG) + sizeof(KLDRMODPE) - sizeof(IMAGE_SECTION_HEADER) + s.FileHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER) + cchFilename + 1; pModPE = (PKLDRMODPE)kldrHlpAlloc(cb); if (!pModPE) return KLDR_ERR_NO_MEMORY; /* KLDRMOD */ pMod = (PKLDRMOD)((uint8_t *)pModPE + sizeof(KLDRMOD) + (s.FileHdr.NumberOfSections + 1) * sizeof(KLDRSEG)); pMod->pvData = pModPE; pMod->pRdr = pRdr; pMod->pOps = NULL; /* set upon success. */ pMod->cSegments = s.FileHdr.NumberOfSections + 1; pMod->cchFilename = cchFilename; pMod->pszFilename = (char *)&pMod->aSegments[pMod->cSegments]; kLdrHlpMemCopy((char *)pMod->pszFilename, kLdrRdrName(pRdr), cchFilename + 1); pMod->pszName = kldrHlpGetFilename(pMod->pszFilename); pMod->cchName = cchFilename - (pMod->pszName - pMod->pszFilename); switch (s.FileHdr.Machine) { case IMAGE_FILE_MACHINE_I386: pMod->enmCpu = KLDRCPU_I386; pMod->enmArch = KLDRARCH_X86_32; pMod->enmEndian = KLDRENDIAN_LITTLE; break; case IMAGE_FILE_MACHINE_AMD64: pMod->enmCpu = KLDRCPU_K8; pMod->enmArch = KLDRARCH_AMD64; pMod->enmEndian = KLDRENDIAN_LITTLE; break; default: kldrHlpAssert(0); break; } pMod->enmFmt = KLDRFMT_PE; pMod->u32Magic = 0; /* set upon success. */ /* KLDRMODPE */ pModPE->pMod = pMod; pModPE->pvBits = NULL; pModPE->fMapped = 0; pModPE->f31Reserved = 0; pModPE->cImportModules = ~(uint32_t)0; pModPE->offHdrs = offNewHdr >= 0 ? offNewHdr : 0; pModPE->Hdrs.Signature = s.Signature; pModPE->Hdrs.FileHeader = s.FileHdr; *ppModPE = pModPE; /* * Read the optional header and the section table. */ off = pModPE->offHdrs + sizeof(pModPE->Hdrs.Signature) + sizeof(pModPE->Hdrs.FileHeader); rc = kLdrRdrRead(pRdr, &pModPE->Hdrs.OptionalHeader, pModPE->Hdrs.FileHeader.SizeOfOptionalHeader, off); if (rc) return rc; if (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader != sizeof(pModPE->Hdrs.OptionalHeader)) kldrModPEConvertOptionalHeader(&pModPE->Hdrs.OptionalHeader); off += pModPE->Hdrs.FileHeader.SizeOfOptionalHeader; rc = kLdrRdrRead(pRdr, &pModPE->aShdrs[0], sizeof(IMAGE_SECTION_HEADER) * pModPE->Hdrs.FileHeader.NumberOfSections, off); if (rc) return rc; /* * Validate the two. */ rc = kLdrModPEValidateOptionalHeader(pModPE); if (rc) return rc; for (i = 0; i < pModPE->Hdrs.FileHeader.NumberOfSections; i++) { rc = kLdrModPEValidateSectionHeaders(pModPE); if (rc) return rc; } /* * Setup the KLDRMOD segment array. */ /* The implied headers section. */ pMod->aSegments[0].pvUser = NULL; pMod->aSegments[0].pchName = "TheHeaders"; pMod->aSegments[0].cchName = sizeof("TheHeaders") - 1; pMod->aSegments[0].cb = pModPE->Hdrs.OptionalHeader.SizeOfHeaders; pMod->aSegments[0].LinkAddress = pModPE->Hdrs.OptionalHeader.ImageBase; pMod->aSegments[0].MapAddress = NIL_KLDRADDR; pMod->aSegments[0].enmProt = KLDRPROT_READONLY; /* The section headers. */ for (i = 0; i < pModPE->Hdrs.FileHeader.NumberOfSections; i++) { char *pch; pMod->aSegments[i + 1].pvUser = NULL; pMod->aSegments[i + 1].pchName = pch = &pModPE->aShdrs[i].Name[0]; cb = IMAGE_SIZEOF_SHORT_NAME; while ( cb > 0 && (pch[cb - 1] == ' ' || pch[cb - 1] == '\0')) cb--; pMod->aSegments[i + 1].cchName = cb; if (!(pModPE->aShdrs[i].Characteristics & IMAGE_SCN_TYPE_NOLOAD)) { pMod->aSegments[i + 1].cb = pModPE->aShdrs[i].Misc.VirtualSize; pMod->aSegments[i + 1].LinkAddress = pModPE->aShdrs[i].VirtualAddress; } else { pMod->aSegments[i + 1].cb = 0; pMod->aSegments[i + 1].LinkAddress = NIL_KLDRADDR; } pMod->aSegments[i + 1].MapAddress = NIL_KLDRADDR; switch ( pModPE->aShdrs[i].Characteristics & (IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE)) { case 0: case IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KLDRPROT_NOACCESS; break; case IMAGE_SCN_MEM_READ: case IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KLDRPROT_READONLY; break; case IMAGE_SCN_MEM_WRITE: case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KLDRPROT_WRITECOPY; break; case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED: case IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KLDRPROT_READWRITE; break; case IMAGE_SCN_MEM_EXECUTE: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KLDRPROT_EXECUTE; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_SHARED: pMod->aSegments[i + 1].enmProt = KLDRPROT_EXECUTE_READ; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KLDRPROT_EXECUTE_WRITECOPY; break; case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED: case IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_SHARED | IMAGE_SCN_MEM_READ: pMod->aSegments[i + 1].enmProt = KLDRPROT_EXECUTE_READWRITE; break; } switch (pModPE->aShdrs[i].Characteristics & IMAGE_SCN_ALIGN_MASK) { default: kldrHlpAssert(0); case 0: pMod->aSegments[i + 1].Alignment = 0; break; case IMAGE_SCN_ALIGN_1BYTES: pMod->aSegments[i + 1].Alignment = 1; break; case IMAGE_SCN_ALIGN_2BYTES: pMod->aSegments[i + 1].Alignment = 2; break; case IMAGE_SCN_ALIGN_4BYTES: pMod->aSegments[i + 1].Alignment = 4; break; case IMAGE_SCN_ALIGN_8BYTES: pMod->aSegments[i + 1].Alignment = 8; break; case IMAGE_SCN_ALIGN_16BYTES: pMod->aSegments[i + 1].Alignment = 16; break; case IMAGE_SCN_ALIGN_32BYTES: pMod->aSegments[i + 1].Alignment = 32; break; case IMAGE_SCN_ALIGN_64BYTES: pMod->aSegments[i + 1].Alignment = 64; break; case IMAGE_SCN_ALIGN_128BYTES: pMod->aSegments[i + 1].Alignment = 128; break; case IMAGE_SCN_ALIGN_256BYTES: pMod->aSegments[i + 1].Alignment = 256; break; case IMAGE_SCN_ALIGN_512BYTES: pMod->aSegments[i + 1].Alignment = 512; break; case IMAGE_SCN_ALIGN_1024BYTES: pMod->aSegments[i + 1].Alignment = 1024; break; case IMAGE_SCN_ALIGN_2048BYTES: pMod->aSegments[i + 1].Alignment = 2048; break; case IMAGE_SCN_ALIGN_4096BYTES: pMod->aSegments[i + 1].Alignment = 4096; break; case IMAGE_SCN_ALIGN_8192BYTES: pMod->aSegments[i + 1].Alignment = 8192; break; } } /* * We're done. */ *ppModPE = pModPE; return 0; } /** * Internal worker which validates the section headers. */ static int kLdrModPEValidateOptionalHeader(PKLDRMODPE pModPE) { const unsigned fIs32Bit = pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32); /* the magic */ if ( pModPE->Hdrs.OptionalHeader.Magic != (fIs32Bit ? IMAGE_NT_OPTIONAL_HDR32_MAGIC : IMAGE_NT_OPTIONAL_HDR64_MAGIC)) return KLDR_ERR_PE_BAD_OPTIONAL_HEADER; /** @todo validate more */ return 0; } /** * Internal worker which validates the section headers. */ static int kLdrModPEValidateSectionHeaders(PKLDRMODPE pModPE) { /** @todo validate shdrs */ return 0; } /** * Converts a 32-bit optional header to a 64-bit one * * @param pOptHdr The optional header to convert. */ static void kldrModPEConvertOptionalHeader(PIMAGE_OPTIONAL_HEADER64 pOptHdr) { /* volatile everywhere! */ IMAGE_OPTIONAL_HEADER32 volatile *pOptHdr32 = (IMAGE_OPTIONAL_HEADER32 volatile *)pOptHdr; IMAGE_OPTIONAL_HEADER64 volatile *pOptHdr64 = pOptHdr; uint32_t volatile *pu32Dst; uint32_t volatile *pu32Src; uint32_t volatile *pu32SrcLast; uint32_t u32; /* From LoaderFlags and out the difference is 4 * 32-bits. */ pu32Dst = (uint32_t *)&pOptHdr64->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1; pu32Src = (uint32_t *)&pOptHdr32->DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES] - 1; pu32SrcLast = (uint32_t *)&pOptHdr32->LoaderFlags; while (pu32Src >= pu32SrcLast) *pu32Dst-- = *pu32Src--; /* The previous 4 fields are 32/64 and needs special attention. */ pOptHdr64->SizeOfHeapCommit = pOptHdr32->SizeOfHeapCommit; pOptHdr64->SizeOfHeapReserve = pOptHdr32->SizeOfHeapReserve; pOptHdr64->SizeOfStackCommit = pOptHdr32->SizeOfStackCommit; u32 = pOptHdr32->SizeOfStackReserve; pOptHdr64->SizeOfStackReserve = u32; /* * The rest matches except for BaseOfData which has been merged into ImageBase in the 64-bit version. * Thus, ImageBase needs some special treatement. It will probably work fine assigning one to the * other since this is all declared volatile, but taking now chances, we'll use a temp variable. */ u32 = pOptHdr32->ImageBase; pOptHdr64->ImageBase = u32; } /** * Converts a 32-bit load config directory to a 64 bit one. * * @param pOptHdr The load config to convert. */ static void kldrModPEConvertLoadConfig(PIMAGE_LOAD_CONFIG_DIRECTORY64 pLoadCfg) { /* volatile everywhere! */ IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *pLoadCfg32 = (IMAGE_LOAD_CONFIG_DIRECTORY32 volatile *)pLoadCfg; IMAGE_LOAD_CONFIG_DIRECTORY64 volatile *pLoadCfg64 = pLoadCfg; uint32_t u32; pLoadCfg64->SEHandlerCount = pLoadCfg32->SEHandlerCount; pLoadCfg64->SEHandlerTable = pLoadCfg32->SEHandlerTable; pLoadCfg64->SecurityCookie = pLoadCfg32->SecurityCookie; pLoadCfg64->EditList = pLoadCfg32->EditList; pLoadCfg64->Reserved1 = pLoadCfg32->Reserved1; pLoadCfg64->CSDVersion = pLoadCfg32->CSDVersion; /* (ProcessHeapFlags switched place with ProcessAffinityMask, but we're * more than 16 byte off by now so it doesn't matter.) */ pLoadCfg64->ProcessHeapFlags = pLoadCfg32->ProcessHeapFlags; pLoadCfg64->ProcessAffinityMask = pLoadCfg32->ProcessAffinityMask; pLoadCfg64->VirtualMemoryThreshold = pLoadCfg32->VirtualMemoryThreshold; pLoadCfg64->MaximumAllocationSize = pLoadCfg32->MaximumAllocationSize; pLoadCfg64->LockPrefixTable = pLoadCfg32->LockPrefixTable; pLoadCfg64->DeCommitTotalFreeThreshold = pLoadCfg32->DeCommitTotalFreeThreshold; u32 = pLoadCfg32->DeCommitFreeBlockThreshold; pLoadCfg64->DeCommitFreeBlockThreshold = u32; /* the remainder matches. */ } /** @copydoc KLDRMODOPS::pfnDestroy */ static int kldrModPEDestroy(PKLDRMOD pMod) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; int rc = 0; KLDRMODPE_ASSERT(pModPE->fMapped); if (pMod->pRdr) { rc = kLdrRdrClose(pMod->pRdr); pMod->pRdr = NULL; } pMod->u32Magic = 0; pMod->pOps = NULL; kldrHlpFree(pModPE); return rc; } /** * Gets usable bits and the right base address. * * @returns 0 on success. * @returns A non-zero status code if the BaseAddress isn't right or some problem is encountered * featch in a temp mapping the bits. * @param pModPE The interpreter module instance * @param ppvBits The bits address, IN & OUT. * @param pBaseAddress The base address, IN & OUT. Optional. */ static int kldrModPEBitsAndBaseAddress(PKLDRMODPE pModPE, const void **ppvBits, PKLDRADDR pBaseAddress) { int rc = 0; /* * Correct the base address. * * We don't use the base address for interpreting the bits in this * interpreter, which makes things relativly simple. */ if (pBaseAddress) { if (*pBaseAddress == KLDRMOD_BASEADDRESS_MAP) *pBaseAddress = pModPE->pMod->aSegments[0].MapAddress; else if (*pBaseAddress == KLDRMOD_BASEADDRESS_LINK) *pBaseAddress = pModPE->Hdrs.OptionalHeader.ImageBase; } /* * Get bits. */ if (!*ppvBits) { if (pModPE->fMapped) *ppvBits = (void *)(uintptr_t)pModPE->pMod->aSegments[0].MapAddress; else if (pModPE->pvBits) *ppvBits = pModPE->pvBits; else { /** @todo do an internal mapping. */ rc = -1; } } return 0; } /** @copydoc kLdrModQuerySymbol */ static int kldrModPEQuerySymbol(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, uint32_t uSymbol, const char *pszSymbol, PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; const uint32_t *paExportRVAs; const IMAGE_EXPORT_DIRECTORY *pExpDir; uint32_t iExpOrd; uint32_t uRVA; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, &BaseAddress); if (rc) return rc; if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < sizeof(IMAGE_EXPORT_DIRECTORY)) return KLDR_ERR_SYMBOL_NOT_FOUND; pExpDir = KLDRMODPE_RVA2TYPE(pvBits, pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, PIMAGE_EXPORT_DIRECTORY); if (!pszSymbol) { /* * Simple, calculate the unbased ordinal and bounds check it. */ iExpOrd = uSymbol - pExpDir->Base; if (iExpOrd >= KLDR_MAX(pExpDir->NumberOfNames, pExpDir->NumberOfFunctions)) return KLDR_ERR_SYMBOL_NOT_FOUND; } else { /* * Do a binary search for the name. * (The name table is sorted in ascending ordered by the linker.) */ const uint32_t *paRVANames = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const uint32_t *); const uint16_t *paOrdinals = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const uint16_t *); int32_t iStart = 1; /* one based binary searching is simpler. */ int32_t iEnd = pExpDir->NumberOfNames; for (;;) { int32_t i; int diff; const char *pszName; /* done? */ if (iStart > iEnd) { #ifdef KLDRMODPE_STRICT /* Make sure the linker and we both did our job right. */ for (i = 0; i < pExpDir->NumberOfNames; i++) { pszName = KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i], const char *); KLDRMODPE_ASSERT(kLdrHlpStrComp(pszName, pszSymbol)); KLDRMODPE_ASSERT(i == 0 || kLdrHlpStrComp(pszName, KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *))); } #endif return KLDR_ERR_SYMBOL_NOT_FOUND; } i = (iEnd - iStart) / 2 + iStart; pszName = KLDRMODPE_RVA2TYPE(pvBits, paRVANames[i - 1], const char *); diff = kLdrHlpStrComp(pszName, pszSymbol); if (diff < 0) iStart = i + 1; /* The symbol must be after the current name. */ else if (diff) iEnd = i - 1; /* The symbol must be before the current name. */ else { iExpOrd = paOrdinals[i - 1]; /* match! */ break; } } } /* * Lookup the address in the 'symbol' table. */ paExportRVAs = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const uint32_t *); uRVA = paExportRVAs[iExpOrd]; if ( uRVA - pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) return kldrModPEQueryForwarder(pModPE, pvBits, KLDRMODPE_RVA2TYPE(pvBits, uRVA, const char *), pfnGetForwarder, pvUser, puValue, pfKind); /* * Set the return value. */ if (puValue) *puValue = BaseAddress + uRVA; if (pfKind) *pfKind = (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; return 0; } /** * Deal with a forwarder entry. * * We do this seprately from kldrModPEQuerySymbol because the code is clumsy (as is all PE code * thanks to the descriptive field names), and because it uses quite a bit more stack and we're * trying to avoid allocating stack unless we have to. * * @returns See kLdrModQuerySymbol. * @param pModPE The PE module interpreter instance. * @param pvBits Where to read the image from. * @param pszForwarder The forwarder entry name. * @param pfnGetForwarder The callback for resolving forwarder symbols. (optional) * @param pvUser The user argument for the callback. * @param puValue Where to put the value. (optional) * @param pfKind Where to put the symbol kind. (optional) */ static int kldrModPEQueryForwarder(PKLDRMODPE pModPE, const void *pvBits, const char *pszForwarder, PFNKLDRMODGETIMPORT pfnGetForwarder, void *pvUser, PKLDRADDR puValue, uint32_t *pfKind) { const IMAGE_IMPORT_DESCRIPTOR *paImpDir; uint32_t iImpModule; uint32_t cchImpModule; const char *pszSymbol; uint32_t uSymbol; int rc; if (!pfnGetForwarder) return KLDR_ERR_FORWARDER_SYMBOL; /* * Separate the name into a module name and a symbol name or ordinal. * * The module name ends at the first dot ('.'). * After the dot follows either a symbol name or a hash ('#') + ordinal. */ pszSymbol = pszForwarder; while (*pszSymbol != '.') pszSymbol++; if (!*pszSymbol) return KLDR_ERR_PE_BAD_FORWARDER; cchImpModule = pszSymbol - pszForwarder; pszSymbol++; /* skip the dot */ if (!*pszSymbol) return KLDR_ERR_PE_BAD_FORWARDER; if (*pszSymbol == '#') { unsigned uBase; pszSymbol++; /* skip the hash */ /* base detection */ uBase = 10; if (pszSymbol[0] == '0' && (pszSymbol[1] == 'x' || pszSymbol[1] == 'X')) { uBase = 16; pszSymbol += 2; } /* ascii to integer */ uSymbol = 0; for (;;) { /* convert char to digit. */ unsigned uDigit = *pszSymbol++; if (uDigit >= '0' && uDigit <= '9') uDigit -= '0'; else if (uDigit >= 'a' && uDigit <= 'z') uDigit -= 'a' + 10; else if (uDigit >= 'A' && uDigit <= 'Z') uDigit -= 'A' + 10; else if (!uDigit) break; else return KLDR_ERR_PE_BAD_FORWARDER; if (uDigit >= uBase) return KLDR_ERR_PE_BAD_FORWARDER; /* insert the digit */ uSymbol *= uBase; uSymbol += uDigit; } pszSymbol = NULL; /* no symbol name. */ } else uSymbol = NIL_KLDRMOD_SYM_ORDINAL; /* no ordinal number. */ /* * Find the import module name. * * We ASSUME the linker will make sure there is an import * entry for the module... not sure if this is right though. */ if ( !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size || !pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) return KLDR_ERR_PE_FORWARDER_IMPORT_NOT_FOUND; paImpDir = KLDRMODPE_RVA2TYPE(pvBits, pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, const IMAGE_IMPORT_DESCRIPTOR *); kldrModPENumberOfImports(pModPE->pMod, pvBits); for (iImpModule = 0; iImpModule < pModPE->cImportModules; iImpModule++) { const char *pszName = KLDRMODPE_RVA2TYPE(pvBits, paImpDir[iImpModule].Name, const char *); size_t cchName = kLdrHlpStrLen(pszName); if ( ( cchName == cchImpModule || ( cchName > cchImpModule && pszName[cchImpModule] == '.' && (pszName[cchImpModule + 1] == 'd' || pszName[cchImpModule + 1] == 'D') && (pszName[cchImpModule + 2] == 'l' || pszName[cchImpModule + 2] == 'L') && (pszName[cchImpModule + 3] == 'l' || pszName[cchImpModule + 3] == 'L')) ) && kLdrHlpMemIComp(pszName, pszForwarder, cchImpModule) ) { /* * Now the rest is up to the callback (almost). */ rc = pfnGetForwarder(pModPE->pMod, iImpModule, uSymbol, pszSymbol, puValue, pfKind, pvUser); if (!rc && pfKind) *pfKind |= KLDRSYMKIND_FORWARDER; return rc; } } return KLDR_ERR_PE_FORWARDER_IMPORT_NOT_FOUND; } /** @copydoc kLdrModEnumSymbols */ static int kldrModPEEnumSymbols(PKLDRMOD pMod, uint32_t fFlags, const void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODENUMSYMS pfnCallback, void *pvUser) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; const uint32_t *paFunctions; const IMAGE_EXPORT_DIRECTORY *pExpDir; const uint32_t *paRVANames; const uint16_t *paOrdinals; uint32_t iFunction; uint32_t cFunctions; uint32_t cNames; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, &BaseAddress); if (rc) return rc; if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size < sizeof(IMAGE_EXPORT_DIRECTORY)) return KLDR_ERR_SYMBOL_NOT_FOUND; pExpDir = KLDRMODPE_RVA2TYPE(pvBits, pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, PIMAGE_EXPORT_DIRECTORY); /* * Enumerate the ordinal exports. */ paRVANames = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNames, const uint32_t *); paOrdinals = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfNameOrdinals, const uint16_t *); paFunctions = KLDRMODPE_RVA2TYPE(pvBits, pExpDir->AddressOfFunctions, const uint32_t *); cFunctions = pExpDir->NumberOfFunctions; cNames = pExpDir->NumberOfNames; for (iFunction = 0; iFunction < cFunctions; iFunction++) { unsigned fFoundName; uint32_t iName; const uint32_t uRVA = paFunctions[iFunction]; const KLDRADDR uValue = BaseAddress + uRVA; uint32_t fKind = (pModPE->Hdrs.FileHeader.SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER32) ? KLDRSYMKIND_32BIT : KLDRSYMKIND_64BIT) | KLDRSYMKIND_NO_TYPE; if ( uRVA - pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress < pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) fKind |= KLDRSYMKIND_FORWARDER; /* * Any symbol names? */ fFoundName = 0; for (iName = 0; iName < cNames; iName++) { if (!paOrdinals[iName] != iFunction) continue; fFoundName = 1; rc = pfnCallback(pMod, iName + pExpDir->Base, KLDRMODPE_RVA2TYPE(pvBits, paRVANames[iName], const char *), uValue, fKind, pvUser); if (rc) return rc; } /* * If no names, call once with the ordinal only. */ if (!fFoundName) { rc = pfnCallback(pMod, iName + pExpDir->Base, NULL, uValue, fKind, pvUser); if (rc) return rc; } } return 0; } /** @copydoc kLdrModGetImport */ static int kldrModPEGetImport(PKLDRMOD pMod, void *pvBits, uint32_t iImport, char *pszName, size_t cchName) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; const char *pszImportName; size_t cchImportName; int rc; /* * Make sure we've got mapped bits and resolve any base address aliases. */ rc = kldrModPEBitsAndBaseAddress(pModPE, &pvBits, NULL); if (rc) return rc; /* * Simple bounds check. */ if (iImport >= (uint32_t)kldrModPENumberOfImports(pMod, pvBits)) return KLDR_ERR_IMPORT_ORDINAL_OUT_OF_BOUNDS; /* * Get the name. */ pImpDesc = KLDRMODPE_RVA2TYPE(pvBits, pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + sizeof(IMAGE_IMPORT_DESCRIPTOR) * iImport, const IMAGE_IMPORT_DESCRIPTOR *); pszImportName = KLDRMODPE_RVA2TYPE(pvBits, pImpDesc->Name, const char *); cchImportName = strlen(pszImportName); if (cchImportName < cchName) { kLdrHlpMemCopy(pszName, pszImportName, cchImportName + 1); rc = 0; } else { kLdrHlpMemCopy(pszName, pszImportName, cchName); if (cchName) pszName[cchName - 1] = '\0'; rc = KLDR_ERR_BUFFER_OVERFLOW; } return rc; } /** @copydoc kLdrModNumberOfImports */ static int32_t kldrModPENumberOfImports(PKLDRMOD pMod, const void *pvBits) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; if (pModPE->cImportModules == ~(uint32_t)0) { /* * We'll have to walk the import descriptors to figure out their number. * First, make sure we've got mapped bits and resolve any base address aliases. */ if (kldrModPEBitsAndBaseAddress(pModPE, &pvBits, NULL)) return -1; pModPE->cImportModules = 0; if ( pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size && pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) { const IMAGE_IMPORT_DESCRIPTOR *pImpDesc; pImpDesc = KLDRMODPE_RVA2TYPE(pvBits, pModPE->Hdrs.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress, const IMAGE_IMPORT_DESCRIPTOR *); while (pImpDesc->Name && pImpDesc->FirstThunk) { pModPE->cImportModules = 0; pImpDesc++; } } } return pModPE->cImportModules; } /** @copydoc kLdrModGetStackInfo */ static int kldrModPEGetStackInfo(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRSTACKINFO pStackInfo) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; pStackInfo->Address = NIL_KLDRADDR; pStackInfo->LinkAddress = NIL_KLDRADDR; pStackInfo->cbStack = pStackInfo->cbStackThread = pModPE->Hdrs.OptionalHeader.SizeOfStackReserve; return 0; } /** @copydoc kLdrModQueryMainEntrypoint */ static int kldrModPEQueryMainEntrypoint(PKLDRMOD pMod, const void *pvBits, KLDRADDR BaseAddress, PKLDRADDR pMainEPAddress) { PKLDRMODPE pModPE = (PKLDRMODPE)pMod->pvData; int rc; rc = kldrModPEBitsAndBaseAddress(pModPE, NULL, &BaseAddress); if (rc) return rc; *pMainEPAddress = pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint ? BaseAddress + pModPE->Hdrs.OptionalHeader.AddressOfEntryPoint : NIL_KLDRADDR; return 0; } /** @copydoc kLdrModEnumDbgInfo */ int (* pfnEnumDbgInfo)(PKLDRMOD pMod, const void *pvBits, PFNKLDRENUMDBG pfnCallback, void *pvUser); /** @copydoc kLdrModHasDbgInfo */ int (* pfnHasDbgInfo)(PKLDRMOD pMod, const void *pvBits); /** @copydoc kLdrModMap */ int (* pfnMap)(PKLDRMOD pMod); /** @copydoc kLdrModUnmap */ int (* pfnUnmap)(PKLDRMOD pMod); /** @copydoc kLdrModAllocTLS */ int (* pfnAllocTLS)(PKLDRMOD pMod); /** @copydoc kLdrModFreeTLS */ void (* pfnFreeTLS)(PKLDRMOD pMod); /** @copydoc kLdrModReload */ int (* pfnReload)(PKLDRMOD pMod); /** @copydoc kLdrModFixupMapping */ int (* pfnFixupMapping)(PKLDRMOD pMod, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); /** @copydoc kLdrModCallInit */ int (* pfnCallInit)(PKLDRMOD pMod); /** @copydoc kLdrModCallTerm */ int (* pfnCallTerm)(PKLDRMOD pMod); /** @copydoc kLdrModCallThread */ int (* pfnCallThread)(PKLDRMOD pMod, unsigned fAttachingOrDetaching); /** @copydoc kLdrModSize */ size_t (* pfnSize)(PKLDRMOD pMod); /** @copydoc kLdrModGetBits */ int (* pfnGetBits)(PKLDRMOD pMod, void *pvBits, KLDRADDR BaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser); /** @copydoc kLdrModRelocateBits */ int (* pfnRelocateBits)(PKLDRMOD pMod, void *pvBits, KLDRADDR NewBaseAddress, KLDRADDR OldBaseAddress, PFNKLDRMODGETIMPORT pfnGetImport, void *pvUser);