////////////////////////////////////////////////////////////////////////////// // // Module Enumeration Functions (modules.cpp of detours.lib) // // Microsoft Research Detours Package, Version 2.1. // // Copyright (c) Microsoft Corporation. All rights reserved. // // Module enumeration functions. // #include #if (_MSC_VER < 1310) #else #include #endif //#define DETOUR_DEBUG 1 #define DETOURS_INTERNAL #include "detours.h" ////////////////////////////////////////////////////////////////////////////// // #ifndef _STRSAFE_H_INCLUDED_ static inline HRESULT StringCchLengthA(const char* psz, size_t cchMax, size_t* pcch) { HRESULT hr = S_OK; size_t cchMaxPrev = cchMax; if (cchMax > 2147483647) { return ERROR_INVALID_PARAMETER; } while (cchMax && (*psz != '\0')) { psz++; cchMax--; } if (cchMax == 0) { // the string is longer than cchMax hr = ERROR_INVALID_PARAMETER; } if (SUCCEEDED(hr) && pcch) { *pcch = cchMaxPrev - cchMax; } return hr; } static inline HRESULT StringCchCopyA(char* pszDest, size_t cchDest, const char* pszSrc) { HRESULT hr = S_OK; if (cchDest == 0) { // can not null terminate a zero-byte dest buffer hr = ERROR_INVALID_PARAMETER; } else { while (cchDest && (*pszSrc != '\0')) { *pszDest++ = *pszSrc++; cchDest--; } if (cchDest == 0) { // we are going to truncate pszDest pszDest--; hr = ERROR_INVALID_PARAMETER; } *pszDest= '\0'; } return hr; } static inline HRESULT StringCchCatA(char* pszDest, size_t cchDest, const char* pszSrc) { HRESULT hr; size_t cchDestCurrent; if (cchDest > 2147483647) { return ERROR_INVALID_PARAMETER; } hr = StringCchLengthA(pszDest, cchDest, &cchDestCurrent); if (SUCCEEDED(hr)) { hr = StringCchCopyA(pszDest + cchDestCurrent, cchDest - cchDestCurrent, pszSrc); } return hr; } #endif ////////////////////////////////////////////////////////////////////////////// // const GUID DETOUR_EXE_RESTORE_GUID = { 0x2ed7a3ff, 0x3339, 0x4a8d, { 0x80, 0x5c, 0xd4, 0x98, 0x15, 0x3f, 0xc2, 0x8f }}; ////////////////////////////////////////////////////////////////////////////// // PDETOUR_SYM_INFO DetourLoadImageHlp(VOID) { static DETOUR_SYM_INFO symInfo; static PDETOUR_SYM_INFO pSymInfo = NULL; static BOOL failed = false; if (failed) { return NULL; } if (pSymInfo != NULL) { return pSymInfo; } ZeroMemory(&symInfo, sizeof(symInfo)); // Create a real handle to the process. #if 0 DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), GetCurrentProcess(), &symInfo.hProcess, 0, FALSE, DUPLICATE_SAME_ACCESS); #else symInfo.hProcess = GetCurrentProcess(); #endif symInfo.hDbgHelp = LoadLibraryA("dbghelp.dll"); if (symInfo.hDbgHelp == NULL) { abort: failed = true; if (symInfo.hDbgHelp != NULL) { FreeLibrary(symInfo.hDbgHelp); } symInfo.pfImagehlpApiVersionEx = NULL; symInfo.pfSymInitialize = NULL; symInfo.pfSymSetOptions = NULL; symInfo.pfSymGetOptions = NULL; symInfo.pfSymLoadModule64 = NULL; symInfo.pfSymGetModuleInfo64 = NULL; symInfo.pfSymFromName = NULL; return NULL; } symInfo.pfImagehlpApiVersionEx = (PF_ImagehlpApiVersionEx)GetProcAddress(symInfo.hDbgHelp, "ImagehlpApiVersionEx"); symInfo.pfSymInitialize = (PF_SymInitialize)GetProcAddress(symInfo.hDbgHelp, "SymInitialize"); symInfo.pfSymSetOptions = (PF_SymSetOptions)GetProcAddress(symInfo.hDbgHelp, "SymSetOptions"); symInfo.pfSymGetOptions = (PF_SymGetOptions)GetProcAddress(symInfo.hDbgHelp, "SymGetOptions"); symInfo.pfSymLoadModule64 = (PF_SymLoadModule64)GetProcAddress(symInfo.hDbgHelp, "SymLoadModule64"); symInfo.pfSymGetModuleInfo64 = (PF_SymGetModuleInfo64)GetProcAddress(symInfo.hDbgHelp, "SymGetModuleInfo64"); symInfo.pfSymFromName = (PF_SymFromName)GetProcAddress(symInfo.hDbgHelp, "SymFromName"); API_VERSION av; ZeroMemory(&av, sizeof(av)); av.MajorVersion = API_VERSION_NUMBER; if (symInfo.pfImagehlpApiVersionEx == NULL || symInfo.pfSymInitialize == NULL || symInfo.pfSymLoadModule64 == NULL || symInfo.pfSymGetModuleInfo64 == NULL || symInfo.pfSymFromName == NULL) { goto abort; } symInfo.pfImagehlpApiVersionEx(&av); if (av.MajorVersion < API_VERSION_NUMBER) { goto abort; } if (!symInfo.pfSymInitialize(symInfo.hProcess, NULL, FALSE)) { // We won't retry the initialize if it fails. goto abort; } if (symInfo.pfSymGetOptions != NULL && symInfo.pfSymSetOptions != NULL) { DWORD dw = symInfo.pfSymGetOptions(); dw &= ~(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | 0); dw |= ( #if defined(SYMOPT_EXACT_SYMBOLS) SYMOPT_EXACT_SYMBOLS | #endif #if defined(SYMOPT_NO_UNQUALIFIED_LOADS) SYMOPT_NO_UNQUALIFIED_LOADS | #endif SYMOPT_DEFERRED_LOADS | #if defined(SYMOPT_FAIL_CRITICAL_ERRORS) SYMOPT_FAIL_CRITICAL_ERRORS | #endif #if defined(SYMOPT_INCLUDE_32BIT_MODULES) SYMOPT_INCLUDE_32BIT_MODULES | #endif 0); symInfo.pfSymSetOptions(dw); } pSymInfo = &symInfo; return pSymInfo; } PVOID WINAPI DetourFindFunction(PCSTR pszModule, PCSTR pszFunction) { /////////////////////////////////////////////// First, try GetProcAddress. // HMODULE hModule = LoadLibraryA(pszModule); if (hModule == NULL) { return NULL; } PBYTE pbCode = (PBYTE)GetProcAddress(hModule, pszFunction); if (pbCode) { return pbCode; } ////////////////////////////////////////////////////// Then try ImageHelp. // DETOUR_TRACE(("DetourFindFunction(%s, %s)\n", pszModule, pszFunction)); PDETOUR_SYM_INFO pSymInfo = DetourLoadImageHlp(); if (pSymInfo == NULL) { DETOUR_TRACE(("DetourLoadImageHlp failed: %d\n", GetLastError())); return NULL; } if (pSymInfo->pfSymLoadModule64(pSymInfo->hProcess, NULL, (PCHAR)pszModule, NULL, (DWORD64)hModule, 0) == 0) { DETOUR_TRACE(("SymLoadModule64(%p) failed: %d\n", pSymInfo->hProcess, GetLastError())); // We don't stop because some version of dbghelp fail secondary calls. //return NULL; } HRESULT hrRet; CHAR szFullName[512]; IMAGEHLP_MODULE64 modinfo; ZeroMemory(&modinfo, sizeof(modinfo)); modinfo.SizeOfStruct = sizeof(modinfo); if (!pSymInfo->pfSymGetModuleInfo64(pSymInfo->hProcess, (DWORD64)hModule, &modinfo)) { DETOUR_TRACE(("SymGetModuleInfo64(%p, %p) failed: %d\n", pSymInfo->hProcess, hModule, GetLastError())); return NULL; } hrRet = StringCchCopyA(szFullName, sizeof(szFullName)/sizeof(CHAR), modinfo.ModuleName); if (FAILED(hrRet)) { DETOUR_TRACE(("StringCchCopyA failed: %08x\n", hrRet)); return NULL; } hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), "!"); if (FAILED(hrRet)) { DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet)); return NULL; } hrRet = StringCchCatA(szFullName, sizeof(szFullName)/sizeof(CHAR), pszFunction); if (FAILED(hrRet)) { DETOUR_TRACE(("StringCchCatA failed: %08x\n", hrRet)); return NULL; } struct CFullSymbol : SYMBOL_INFO { CHAR szRestOfName[512]; } symbol; ZeroMemory(&symbol, sizeof(symbol)); //symbol.ModBase = (ULONG64)hModule; symbol.SizeOfStruct = sizeof(SYMBOL_INFO); #ifdef DBHLPAPI symbol.MaxNameLen = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]); #else symbol.MaxNameLength = sizeof(symbol.szRestOfName)/sizeof(symbol.szRestOfName[0]); #endif if (!pSymInfo->pfSymFromName(pSymInfo->hProcess, szFullName, &symbol)) { DETOUR_TRACE(("SymFromName(%s) failed: %d\n", szFullName, GetLastError())); return NULL; } #ifdef DETOURS_IA64 // On the IA64, we get a raw code pointer from the symbol engine // and have to convert it to a wrapped [code pointer, global pointer]. // PPLABEL_DESCRIPTOR pldEntry = (PPLABEL_DESCRIPTOR)DetourGetEntryPoint(hModule); PPLABEL_DESCRIPTOR pldSymbol = new PLABEL_DESCRIPTOR; pldSymbol->EntryPoint = symbol.Address; pldSymbol->GlobalPointer = pldEntry->GlobalPointer; return (PBYTE)pldSymbol; #else return (PBYTE)symbol.Address; #endif } //////////////////////////////////////////////////// Module Image Functions. // HMODULE WINAPI DetourEnumerateModules(HMODULE hModuleLast) { PBYTE pbLast; if (hModuleLast == NULL) { pbLast = (PBYTE)0x10000; } else { pbLast = (PBYTE)hModuleLast + 0x10000; } MEMORY_BASIC_INFORMATION mbi; ZeroMemory(&mbi, sizeof(mbi)); // Find the next memory region that contains a mapped PE image. // for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { if (VirtualQuery((PVOID)pbLast, &mbi, sizeof(mbi)) <= 0) { break; } // Skip uncommitted regions and guard pages. // if ((mbi.State != MEM_COMMIT) || (mbi.Protect & PAGE_GUARD)) { continue; } __try { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbLast; if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { continue; } PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { continue; } return (HMODULE)pDosHeader; } __except(EXCEPTION_EXECUTE_HANDLER) { return NULL; } } return NULL; } PVOID WINAPI DetourGetEntryPoint(HMODULE hModule) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; if (hModule == NULL) { pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); } __try { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { SetLastError(ERROR_INVALID_EXE_SIGNATURE); return NULL; } if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } SetLastError(NO_ERROR); return ((PBYTE)pDosHeader) + pNtHeader->OptionalHeader.AddressOfEntryPoint; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } } ULONG WINAPI DetourGetModuleSize(HMODULE hModule) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; if (hModule == NULL) { pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); } __try { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { SetLastError(ERROR_INVALID_EXE_SIGNATURE); return NULL; } if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } SetLastError(NO_ERROR); return (pNtHeader->OptionalHeader.SizeOfImage); } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } } static inline PBYTE RvaAdjust(PIMAGE_DOS_HEADER pDosHeader, DWORD raddr) { if (raddr != NULL) { return ((PBYTE)pDosHeader) + raddr; } return NULL; } BOOL WINAPI DetourEnumerateExports(HMODULE hModule, PVOID pContext, PF_DETOUR_ENUMERATE_EXPORT_CALLBACK pfExport) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; if (hModule == NULL) { pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); } __try { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { SetLastError(ERROR_INVALID_EXE_SIGNATURE); return FALSE; } if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { SetLastError(ERROR_EXE_MARKED_INVALID); return FALSE; } PIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY) RvaAdjust(pDosHeader, pNtHeader->OptionalHeader .DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (pExportDir == NULL) { SetLastError(ERROR_EXE_MARKED_INVALID); return FALSE; } PDWORD pdwFunctions = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfFunctions); PDWORD pdwNames = (PDWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNames); PWORD pwOrdinals = (PWORD)RvaAdjust(pDosHeader, pExportDir->AddressOfNameOrdinals); for (DWORD nFunc = 0; nFunc < pExportDir->NumberOfFunctions; nFunc++) { PBYTE pbCode = (pdwFunctions != NULL) ? (PBYTE)RvaAdjust(pDosHeader, pdwFunctions[nFunc]) : NULL; PCHAR pszName = NULL; for (DWORD n = 0; n < pExportDir->NumberOfNames; n++) { if (pwOrdinals[n] == nFunc) { pszName = (pdwNames != NULL) ? (PCHAR)RvaAdjust(pDosHeader, pdwNames[n]) : NULL; break; } } ULONG nOrdinal = pExportDir->Base + nFunc; if (!pfExport(pContext, nOrdinal, pszName, pbCode)) { break; } } SetLastError(NO_ERROR); return TRUE; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } } static PDETOUR_LOADED_BINARY WINAPI GetPayloadSectionFromModule(HMODULE hModule) { PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; if (hModule == NULL) { pDosHeader = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL); } __try { if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((PBYTE)pDosHeader + pDosHeader->e_lfanew); if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { SetLastError(ERROR_INVALID_EXE_SIGNATURE); return NULL; } if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } PIMAGE_SECTION_HEADER pSectionHeaders = (PIMAGE_SECTION_HEADER)((PBYTE)pNtHeader + sizeof(pNtHeader->Signature) + sizeof(pNtHeader->FileHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader); for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; n++) { if (strcmp((PCHAR)pSectionHeaders[n].Name, ".detour") == 0) { if (pSectionHeaders[n].VirtualAddress == 0 || pSectionHeaders[n].SizeOfRawData == 0) { break; } PBYTE pbData = (PBYTE)pDosHeader + pSectionHeaders[n].VirtualAddress; DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pbData; if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { break; } if (pHeader->nDataOffset == 0) { pHeader->nDataOffset = pHeader->cbHeaderSize; } SetLastError(NO_ERROR); return (PBYTE)pHeader; } } SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } } DWORD WINAPI DetourGetSizeOfPayloads(HMODULE hModule) { PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule); if (pBinary == NULL) { // Error set by GetPayloadSectonFromModule. return 0; } __try { DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary; if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { SetLastError(ERROR_INVALID_HANDLE); return 0; } SetLastError(NO_ERROR); return pHeader->cbDataSize; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_HANDLE); return 0; } } PVOID WINAPI DetourFindPayload(HMODULE hModule, REFGUID rguid, DWORD * pcbData) { PBYTE pbData = NULL; if (pcbData) { *pcbData = 0; } PDETOUR_LOADED_BINARY pBinary = GetPayloadSectionFromModule(hModule); if (pBinary == NULL) { // Error set by GetPayloadSectonFromModule. return NULL; } __try { DETOUR_SECTION_HEADER *pHeader = (DETOUR_SECTION_HEADER *)pBinary; if (pHeader->cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || pHeader->nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { SetLastError(ERROR_INVALID_EXE_SIGNATURE); return NULL; } PBYTE pbBeg = ((PBYTE)pHeader) + pHeader->nDataOffset; PBYTE pbEnd = ((PBYTE)pHeader) + pHeader->cbDataSize; for (pbData = pbBeg; pbData < pbEnd;) { DETOUR_SECTION_RECORD *pSection = (DETOUR_SECTION_RECORD *)pbData; if (pSection->guid.Data1 == rguid.Data1 && pSection->guid.Data2 == rguid.Data2 && pSection->guid.Data3 == rguid.Data3 && pSection->guid.Data4[0] == rguid.Data4[0] && pSection->guid.Data4[1] == rguid.Data4[1] && pSection->guid.Data4[2] == rguid.Data4[2] && pSection->guid.Data4[3] == rguid.Data4[3] && pSection->guid.Data4[4] == rguid.Data4[4] && pSection->guid.Data4[5] == rguid.Data4[5] && pSection->guid.Data4[6] == rguid.Data4[6] && pSection->guid.Data4[7] == rguid.Data4[7]) { if (pcbData) { *pcbData = pSection->cbBytes - sizeof(*pSection); SetLastError(NO_ERROR); return (PBYTE)(pSection + 1); } } pbData = (PBYTE)pSection + pSection->cbBytes; } SetLastError(ERROR_INVALID_HANDLE); return NULL; } __except(EXCEPTION_EXECUTE_HANDLER) { SetLastError(ERROR_INVALID_HANDLE); return NULL; } } BOOL WINAPI DetourRestoreAfterWithEx(PVOID pvData, DWORD cbData) { PDETOUR_EXE_RESTORE pder = (PDETOUR_EXE_RESTORE)pvData; if (pder->cb != sizeof(*pder) || pder->cb > cbData) { SetLastError(ERROR_BAD_EXE_FORMAT); return FALSE; } DWORD dwPermIdh; DWORD dwPermInh; DWORD dwPermClr; DWORD dwOld; BOOL fSucceeded = FALSE; if (!VirtualProtect(pder->pidh, sizeof(pder->idh), PAGE_EXECUTE_READWRITE, &dwPermIdh)) { goto end0; } if (!VirtualProtect(pder->pinh, sizeof(pder->inh), PAGE_EXECUTE_READWRITE, &dwPermInh)) { goto end1; } if (pder->pclrFlags != NULL) { if (!VirtualProtect(pder->pclrFlags, sizeof(pder->clrFlags), PAGE_EXECUTE_READWRITE, &dwPermClr)) { goto end2; } } CopyMemory(pder->pidh, &pder->idh, sizeof(pder->idh)); CopyMemory(pder->pinh, &pder->inh, sizeof(pder->inh)); if (pder->pclrFlags != NULL) { CopyMemory(pder->pclrFlags, &pder->clrFlags, sizeof(pder->clrFlags)); } fSucceeded = TRUE; if (pder->pclrFlags != NULL) { VirtualProtect(pder->pclrFlags, sizeof(pder->clrFlags), dwPermIdh, &dwOld); } end2: VirtualProtect(pder->pinh, sizeof(pder->inh), dwPermInh, &dwOld); end1: VirtualProtect(pder->pidh, sizeof(pder->idh), dwPermIdh, &dwOld); end0: return fSucceeded; } BOOL WINAPI DetourRestoreAfterWith() { for (HMODULE hMod = NULL; (hMod = DetourEnumerateModules(hMod)) != NULL;) { PVOID pvData; DWORD cbData; pvData = DetourFindPayload(hMod, DETOUR_EXE_RESTORE_GUID, &cbData); if (pvData == NULL || cbData == 0) { continue; } return DetourRestoreAfterWithEx(pvData, cbData); } SetLastError(ERROR_MOD_NOT_FOUND); return FALSE; } // End of File