////////////////////////////////////////////////////////////////////////////// // // Create a process with a DLL (creatwth.cpp of detours.lib) // // Microsoft Research Detours Package, Version 3.0 Build_316. // // Copyright (c) Microsoft Corporation. All rights reserved. // #include #include #if (_MSC_VER < 1299) typedef DWORD DWORD_PTR; #endif #if (_MSC_VER < 1310) #else #include #endif // #define DETOUR_DEBUG 1 // #define IGNORE_CHECKSUMS 1 #define DETOURS_INTERNAL #include "detours.h" #define IMPORT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] #define BOUND_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT] #define CLR_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR] #define IAT_DIRECTORY OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT] ////////////////////////////////////////////////////////////////////////////// // #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 ////////////////////////////////////////////////////////////////////////////// // #if IGNORE_CHECKSUMS static WORD detour_sum_minus(WORD wSum, WORD wMinus) { wSum = (WORD)(wSum - ((wSum < wMinus) ? 1 : 0)); wSum = (WORD)(wSum - wMinus); return wSum; } static WORD detour_sum_done(DWORD PartialSum) { // Fold final carry into a single word result and return the resultant value. return (WORD)(((PartialSum >> 16) + PartialSum) & 0xffff); } static WORD detour_sum_data(DWORD dwSum, PBYTE pbData, DWORD cbData) { while (cbData > 0) { dwSum += *((PWORD&)pbData)++; dwSum = (dwSum >> 16) + (dwSum & 0xffff); cbData -= sizeof(WORD); } return detour_sum_done(dwSum); } static WORD detour_sum_final(WORD wSum, PIMAGE_NT_HEADERS pinh) { DETOUR_TRACE((".... : %08x (value: %08x)\n", wSum, pinh->OptionalHeader.CheckSum)); // Subtract the two checksum words in the optional header from the computed. wSum = detour_sum_minus(wSum, ((PWORD)(&pinh->OptionalHeader.CheckSum))[0]); wSum = detour_sum_minus(wSum, ((PWORD)(&pinh->OptionalHeader.CheckSum))[1]); return wSum; } static WORD ChkSumRange(WORD wSum, HANDLE hProcess, PBYTE pbBeg, PBYTE pbEnd) { BYTE rbPage[4096]; while (pbBeg < pbEnd) { if (!ReadProcessMemory(hProcess, pbBeg, rbPage, sizeof(rbPage), NULL)) { DETOUR_TRACE(("ReadProcessMemory(chk@%p..%p) failed: %d\n", pbBeg, pbEnd, GetLastError())); break; } wSum = detour_sum_data(wSum, rbPage, sizeof(rbPage)); pbBeg += sizeof(rbPage); } return wSum; } static WORD ComputeChkSum(HANDLE hProcess, PBYTE pbModule, PIMAGE_NT_HEADERS pinh) { // See LdrVerifyMappedImageMatchesChecksum. MEMORY_BASIC_INFORMATION mbi; ZeroMemory(&mbi, sizeof(mbi)); WORD wSum = 0; PBYTE pbLast = pbModule; for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { ZeroMemory(&mbi, sizeof(mbi)); if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { if (GetLastError() == ERROR_INVALID_PARAMETER) { break; } DETOUR_TRACE(("VirtualQueryEx(%p) failed: %d\n", pbLast, GetLastError())); break; } if (mbi.AllocationBase != pbModule) { break; } wSum = ChkSumRange(wSum, hProcess, (PBYTE)mbi.BaseAddress, (PBYTE)mbi.BaseAddress + mbi.RegionSize); DETOUR_TRACE(("[%p..%p] : %04x\n", (PBYTE)mbi.BaseAddress, (PBYTE)mbi.BaseAddress + mbi.RegionSize, wSum)); } return detour_sum_final(wSum, pinh); } #endif // IGNORE_CHECKSUMS ////////////////////////////////////////////////////////////////////////////// // // Enumate through modules in the target process. // static HMODULE WINAPI EnumerateModulesInProcess(HANDLE hProcess, HMODULE hModuleLast, PIMAGE_NT_HEADERS32 pNtHeader) { 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 (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { break; } if ((mbi.RegionSize & 0xfff) == 0xfff) { break; } if (((PBYTE)mbi.BaseAddress + mbi.RegionSize) < pbLast) { break; } // Skip uncommitted regions and guard pages. // if ((mbi.State != MEM_COMMIT) || ((mbi.Protect & 0xff) == PAGE_NOACCESS) || (mbi.Protect & PAGE_GUARD)) { continue; } __try { IMAGE_DOS_HEADER idh; if (!ReadProcessMemory(hProcess, pbLast, &idh, sizeof(idh), NULL)) { DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", pbLast, pbLast + sizeof(idh), GetLastError())); continue; } if (idh.e_magic != IMAGE_DOS_SIGNATURE || (DWORD)idh.e_lfanew > mbi.RegionSize || (DWORD)idh.e_lfanew < sizeof(idh)) { continue; } if (!ReadProcessMemory(hProcess, pbLast + idh.e_lfanew, pNtHeader, sizeof(*pNtHeader), NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %d\n", pbLast + idh.e_lfanew, pbLast + idh.e_lfanew + sizeof(*pNtHeader), pbLast, GetLastError())); continue; } if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { continue; } return (HMODULE)pbLast; } __except(EXCEPTION_EXECUTE_HANDLER) { continue; } } return NULL; } ////////////////////////////////////////////////////////////////////////////// // // Find a region of memory in which we can create a replacement import table. // static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbBase, DWORD cbAlloc) { MEMORY_BASIC_INFORMATION mbi; ZeroMemory(&mbi, sizeof(mbi)); PBYTE pbLast = pbBase; for (;; pbLast = (PBYTE)mbi.BaseAddress + mbi.RegionSize) { ZeroMemory(&mbi, sizeof(mbi)); if (VirtualQueryEx(hProcess, (PVOID)pbLast, &mbi, sizeof(mbi)) == 0) { if (GetLastError() == ERROR_INVALID_PARAMETER) { break; } DETOUR_TRACE(("VirtualQueryEx(%p) failed: %d\n", pbLast, GetLastError())); break; } if ((mbi.RegionSize & 0xfff) == 0xfff) { break; } // Skip anything other than a pure free region. // if (mbi.State != MEM_FREE) { continue; } PBYTE pbAddress = (PBYTE)(((DWORD_PTR)mbi.BaseAddress + 0xffff) & ~(DWORD_PTR)0xffff); DETOUR_TRACE(("Free region %p..%p\n", mbi.BaseAddress, (PBYTE)mbi.BaseAddress + mbi.RegionSize)); for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += 0x10000) { PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, MEM_RESERVE, PAGE_READWRITE); if (pbAlloc == NULL) { DETOUR_TRACE(("VirtualAllocEx(%p) failed: %d\n", pbAddress, GetLastError())); continue; } pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, MEM_COMMIT, PAGE_READWRITE); if (pbAlloc == NULL) { DETOUR_TRACE(("VirtualAllocEx(%p) failed: %d\n", pbAddress, GetLastError())); continue; } DETOUR_TRACE(("[%p..%p] Allocated for import table.\n", pbAlloc, pbAlloc + cbAlloc)); return pbAlloc; } } return NULL; } static inline DWORD PadToDword(DWORD dw) { return (dw + 3) & ~3u; } static inline DWORD PadToDwordPtr(DWORD dw) { return (dw + 7) & ~7u; } static inline HRESULT ReplaceOptionalSizeA(char* pszDest, size_t cchDest, const char* pszSize) { if (cchDest == 0 || pszDest == NULL || pszSize == NULL || pszSize[0] == '\0' || pszSize[1] == '\0' || pszSize[2] != '\0') { // can not write into empty buffer or with string other than two chars. return ERROR_INVALID_PARAMETER; } else { for (; cchDest >= 2; cchDest--, pszDest++) { if (pszDest[0] == '?' && pszDest[1] == '?') { pszDest[0] = pszSize[0]; pszDest[1] = pszSize[1]; break; } } } return S_OK; } ////////////////////////////////////////////////////////////////////////////// // #if DETOURS_32BIT #define DWORD_XX DWORD32 #define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS32 #define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR32_MAGIC #define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG32 #define UPDATE_IMPORTS_XX UpdateImports32 #define DETOURS_BITS_XX 32 #include "uimports.cpp" #undef DETOUR_EXE_RESTORE_FIELD_XX #undef DWORD_XX #undef IMAGE_NT_HEADERS_XX #undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX #undef IMAGE_ORDINAL_FLAG_XX #undef UPDATE_IMPORTS_XX #endif // DETOURS_32BIT #if DETOURS_64BIT #define DWORD_XX DWORD64 #define IMAGE_NT_HEADERS_XX IMAGE_NT_HEADERS64 #define IMAGE_NT_OPTIONAL_HDR_MAGIC_XX IMAGE_NT_OPTIONAL_HDR64_MAGIC #define IMAGE_ORDINAL_FLAG_XX IMAGE_ORDINAL_FLAG64 #define UPDATE_IMPORTS_XX UpdateImports64 #define DETOURS_BITS_XX 64 #include "uimports.cpp" #undef DETOUR_EXE_RESTORE_FIELD_XX #undef DWORD_XX #undef IMAGE_NT_HEADERS_XX #undef IMAGE_NT_OPTIONAL_HDR_MAGIC_XX #undef IMAGE_ORDINAL_FLAG_XX #undef UPDATE_IMPORTS_XX #endif // DETOURS_64BIT ////////////////////////////////////////////////////////////////////////////// // #if DETOURS_64BIT C_ASSERT(sizeof(IMAGE_NT_HEADERS64) == sizeof(IMAGE_NT_HEADERS32) + 16); static BOOL UpdateFrom32To64(HANDLE hProcess, HANDLE hModule, WORD machine) { IMAGE_DOS_HEADER idh; IMAGE_NT_HEADERS32 inh32; IMAGE_NT_HEADERS64 inh64; IMAGE_SECTION_HEADER sects[32]; PBYTE pbModule = (PBYTE)hModule; DWORD n; ZeroMemory(&inh32, sizeof(inh32)); ZeroMemory(&inh64, sizeof(inh64)); ZeroMemory(sects, sizeof(sects)); DETOUR_TRACE(("UpdateFrom32To64(%04x)\n", machine)); //////////////////////////////////////////////////////// Read old headers. // if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", pbModule, pbModule + sizeof(idh), GetLastError())); return FALSE; } DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p)\n", pbModule, pbModule + sizeof(idh))); PBYTE pnh = pbModule + idh.e_lfanew; if (!ReadProcessMemory(hProcess, pnh, &inh32, sizeof(inh32), NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", pnh, pnh + sizeof(inh32), GetLastError())); return FALSE; } DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh32))); if (inh32.FileHeader.NumberOfSections > (sizeof(sects)/sizeof(sects[0]))) { return FALSE; } PBYTE psects = pnh + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + inh32.FileHeader.SizeOfOptionalHeader; ULONG cb = inh32.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); if (!ReadProcessMemory(hProcess, psects, §s, cb, NULL)) { DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %d\n", psects, psects + cb, GetLastError())); return FALSE; } DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p)\n", psects, psects + cb)); ////////////////////////////////////////////////////////// Convert header. // inh64.Signature = inh32.Signature; inh64.FileHeader = inh32.FileHeader; inh64.FileHeader.Machine = machine; inh64.FileHeader.SizeOfOptionalHeader = sizeof(IMAGE_OPTIONAL_HEADER64); inh64.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC; inh64.OptionalHeader.MajorLinkerVersion = inh32.OptionalHeader.MajorLinkerVersion; inh64.OptionalHeader.MinorLinkerVersion = inh32.OptionalHeader.MinorLinkerVersion; inh64.OptionalHeader.SizeOfCode = inh32.OptionalHeader.SizeOfCode; inh64.OptionalHeader.SizeOfInitializedData = inh32.OptionalHeader.SizeOfInitializedData; inh64.OptionalHeader.SizeOfUninitializedData = inh32.OptionalHeader.SizeOfUninitializedData; inh64.OptionalHeader.AddressOfEntryPoint = inh32.OptionalHeader.AddressOfEntryPoint; inh64.OptionalHeader.BaseOfCode = inh32.OptionalHeader.BaseOfCode; inh64.OptionalHeader.ImageBase = inh32.OptionalHeader.ImageBase; inh64.OptionalHeader.SectionAlignment = inh32.OptionalHeader.SectionAlignment; inh64.OptionalHeader.FileAlignment = inh32.OptionalHeader.FileAlignment; inh64.OptionalHeader.MajorOperatingSystemVersion = inh32.OptionalHeader.MajorOperatingSystemVersion; inh64.OptionalHeader.MinorOperatingSystemVersion = inh32.OptionalHeader.MinorOperatingSystemVersion; inh64.OptionalHeader.MajorImageVersion = inh32.OptionalHeader.MajorImageVersion; inh64.OptionalHeader.MinorImageVersion = inh32.OptionalHeader.MinorImageVersion; inh64.OptionalHeader.MajorSubsystemVersion = inh32.OptionalHeader.MajorSubsystemVersion; inh64.OptionalHeader.MinorSubsystemVersion = inh32.OptionalHeader.MinorSubsystemVersion; inh64.OptionalHeader.Win32VersionValue = inh32.OptionalHeader.Win32VersionValue; inh64.OptionalHeader.SizeOfImage = inh32.OptionalHeader.SizeOfImage; inh64.OptionalHeader.SizeOfHeaders = inh32.OptionalHeader.SizeOfHeaders; inh64.OptionalHeader.CheckSum = inh32.OptionalHeader.CheckSum; inh64.OptionalHeader.Subsystem = inh32.OptionalHeader.Subsystem; inh64.OptionalHeader.DllCharacteristics = inh32.OptionalHeader.DllCharacteristics; inh64.OptionalHeader.SizeOfStackReserve = inh32.OptionalHeader.SizeOfStackReserve; inh64.OptionalHeader.SizeOfStackCommit = inh32.OptionalHeader.SizeOfStackCommit; inh64.OptionalHeader.SizeOfHeapReserve = inh32.OptionalHeader.SizeOfHeapReserve; inh64.OptionalHeader.SizeOfHeapCommit = inh32.OptionalHeader.SizeOfHeapCommit; inh64.OptionalHeader.LoaderFlags = inh32.OptionalHeader.LoaderFlags; inh64.OptionalHeader.NumberOfRvaAndSizes = inh32.OptionalHeader.NumberOfRvaAndSizes; for (n = 0; n < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; n++) { inh64.OptionalHeader.DataDirectory[n] = inh32.OptionalHeader.DataDirectory[n]; } inh64.IMPORT_DIRECTORY.VirtualAddress = 0; inh64.IMPORT_DIRECTORY.Size = 0; /////////////////////////////////////////////////////// Write new headers. // DWORD dwProtect = 0; if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, PAGE_EXECUTE_READWRITE, &dwProtect)) { return FALSE; } if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %d\n", pnh, pnh + sizeof(inh64), GetLastError())); return FALSE; } DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p)\n", pnh, pnh + sizeof(inh64))); psects = pnh + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + inh64.FileHeader.SizeOfOptionalHeader; cb = inh64.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER); if (!WriteProcessMemory(hProcess, psects, §s, cb, NULL)) { DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p) failed: %d\n", psects, psects + cb, GetLastError())); return FALSE; } DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb)); DWORD dwOld = 0; if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, dwProtect, &dwOld)) { return FALSE; } return TRUE; } #endif // DETOURS_64BIT ////////////////////////////////////////////////////////////////////////////// // BOOL WINAPI DetourUpdateProcessWithDll(HANDLE hProcess, LPCSTR *plpDlls, DWORD nDlls) { // Find the next memory region that contains a mapped PE image. // WORD mach32Bit = 0; WORD mach64Bit = 0; WORD exe32Bit = 0; HMODULE hModule = NULL; HMODULE hLast = NULL; for (;;) { IMAGE_NT_HEADERS32 inh; if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh)) == NULL) { break; } DETOUR_TRACE(("%p machine=%04x magic=%04x\n", hLast, inh.FileHeader.Machine, inh.OptionalHeader.Magic)); if ((inh.FileHeader.Characteristics & IMAGE_FILE_DLL) == 0) { hModule = hLast; if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { exe32Bit = inh.FileHeader.Machine; } DETOUR_TRACE(("%p Found EXE\n", hLast)); } else { if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { mach32Bit = inh.FileHeader.Machine; } else if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { mach64Bit = inh.FileHeader.Machine; } } } DETOUR_TRACE((" mach32Bit=%04x mach64Bit=%04x\n", mach32Bit, mach64Bit)); if (hModule == NULL) { SetLastError(ERROR_INVALID_OPERATION); return FALSE; } // Save the various headers for DetourRestoreAfterWith. // DETOUR_EXE_RESTORE der; ZeroMemory(&der, sizeof(der)); der.cb = sizeof(der); der.pidh = (PBYTE)hModule; der.cbidh = sizeof(der.idh); if (!ReadProcessMemory(hProcess, der.pidh, &der.idh, sizeof(der.idh), NULL)) { DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %d\n", der.pidh, der.pidh + der.cbidh, GetLastError())); return FALSE; } DETOUR_TRACE(("IDH: %p..%p\n", der.pidh, der.pidh + der.cbidh)); // We read the NT header in two passes to get the full size. // First we read just the Signature and FileHeader. der.pinh = der.pidh + der.idh.e_lfanew; der.cbinh = FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader); if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", der.pinh, der.pinh + der.cbinh, GetLastError())); return FALSE; } // Second we read the OptionalHeader and Section headers. der.cbinh = (FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader) + der.inh.FileHeader.SizeOfOptionalHeader + der.inh.FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)); #if DETOURS_64BIT if (exe32Bit && !mach32Bit) { // Include the Save the extra 16-bytes that will be overwritten with 64-bit header. der.cbinh += sizeof(IMAGE_NT_HEADERS64) - sizeof(IMAGE_NT_HEADERS32); } #endif // DETOURS_64BIT if (der.cbinh > sizeof(der.raw)) { return FALSE; } if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %d\n", der.pinh, der.pinh + der.cbinh, GetLastError())); return FALSE; } DETOUR_TRACE(("INH: %p..%p\n", der.pinh, der.pinh + der.cbinh)); // Third, we read the CLR header if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) { if (der.inh32.CLR_DIRECTORY.VirtualAddress != 0 && der.inh32.CLR_DIRECTORY.Size != 0) { } DETOUR_TRACE(("CLR32.VirtAddr=%x, CLR.Size=%x\n", der.inh32.CLR_DIRECTORY.VirtualAddress, der.inh32.CLR_DIRECTORY.Size)); der.pclr = ((PBYTE)hModule) + der.inh32.CLR_DIRECTORY.VirtualAddress; } else if (der.inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { if (der.inh64.CLR_DIRECTORY.VirtualAddress != 0 && der.inh64.CLR_DIRECTORY.Size != 0) { } DETOUR_TRACE(("CLR64.VirtAddr=%x, CLR.Size=%x\n", der.inh64.CLR_DIRECTORY.VirtualAddress, der.inh64.CLR_DIRECTORY.Size)); der.pclr = ((PBYTE)hModule) + der.inh64.CLR_DIRECTORY.VirtualAddress; } if (der.pclr != 0) { der.cbclr = sizeof(der.clr); if (!ReadProcessMemory(hProcess, der.pclr, &der.clr, der.cbclr, NULL)) { DETOUR_TRACE(("ReadProcessMemory(clr@%p..%p) failed: %d\n", der.pclr, der.pclr + der.cbclr, GetLastError())); return FALSE; } DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); } // Fourth, adjust for a 32-bit WOW64 process. if (exe32Bit && mach64Bit) { if (!der.pclr // Native binary || (der.clr.Flags & 1) == 0 // Or mixed-mode MSIL || (der.clr.Flags & 2) != 0) { // Or 32BIT Required MSIL mach64Bit = 0; if (mach32Bit == 0) { mach32Bit = exe32Bit; } } } // Now decide if we can insert the detour. #if DETOURS_32BIT if (!mach32Bit && mach64Bit) { // 64-bit native or 64-bit managed process. // // Can't detour a 64-bit process with 32-bit code. // Note: This happens for 32-bit PE binaries containing only // manage code that have been marked as 64-bit ready. // SetLastError(ERROR_INVALID_HANDLE); return FALSE; } else if (mach32Bit) { // 32-bit native or 32-bit managed process on any platform. if (!UpdateImports32(hProcess, hModule, plpDlls, nDlls)) { return FALSE; } } else { // Who knows!? SetLastError(ERROR_INVALID_HANDLE); return FALSE; } #endif // DETOURS_32BIT #if DETOURS_64BIT if (mach32Bit) { // Can't detour a 32-bit process with 64-bit code. SetLastError(ERROR_INVALID_HANDLE); return FALSE; } else if (exe32Bit && !mach32Bit) { // Try to convert the 32-bit managed binary to a 64-bit managed binary. if (!UpdateFrom32To64(hProcess, hModule, mach64Bit)) { return FALSE; } // 64-bit process from 32-bit managed binary. if (!UpdateImports64(hProcess, hModule, plpDlls, nDlls)) { return FALSE; } } else if (mach64Bit) { // 64-bit native or 64-bit managed process on any platform. if (!UpdateImports64(hProcess, hModule, plpDlls, nDlls)) { return FALSE; } } else { // Who knows!? SetLastError(ERROR_INVALID_HANDLE); return FALSE; } #endif // DETOURS_64BIT /////////////////////////////////////////////////// Update the CLR header. // if (der.pclr != NULL) { DETOUR_CLR_HEADER clr; CopyMemory(&clr, &der.clr, sizeof(clr)); clr.Flags &= 0xfffffffe; // Clear the IL_ONLY flag. DWORD dwProtect; if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) { DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %d\n", GetLastError())); return FALSE; } if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) { DETOUR_TRACE(("WriteProcessMemory(clr) failed: %d\n", GetLastError())); return FALSE; } if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) { DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %d\n", GetLastError())); return FALSE; } DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); #if DETOURS_64BIT if (der.clr.Flags & 0x2) { // Is the 32BIT Required Flag set? // X64 never gets here because the process appears as a WOW64 process. // However, on IA64, it doesn't appear to be a WOW process. DETOUR_TRACE(("CLR Requires 32-bit\n", der.pclr, der.pclr + der.cbclr)); SetLastError(ERROR_INVALID_HANDLE); return FALSE; } #endif // DETOURS_64BIT } //////////////////////////////// Save the undo data to the target process. // if (!DetourCopyPayloadToProcess(hProcess, DETOUR_EXE_RESTORE_GUID, &der, sizeof(der))) { DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////////////// // BOOL WINAPI DetourCreateProcessWithDllA(LPCSTR lpApplicationName, __in_z LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); PROCESS_INFORMATION pi; if (pfCreateProcessA == NULL) { pfCreateProcessA = CreateProcessA; } if (!pfCreateProcessA(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwMyCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, &pi)) { return FALSE; } LPCSTR rlpDlls[2]; DWORD nDlls = 0; if (lpDllName != NULL) { rlpDlls[nDlls++] = lpDllName; } if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { TerminateProcess(pi.hProcess, ~0u); return FALSE; } if (lpProcessInformation) { CopyMemory(lpProcessInformation, &pi, sizeof(pi)); } if (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(pi.hThread); } return TRUE; } BOOL WINAPI DetourCreateProcessWithDllW(LPCWSTR lpApplicationName, __in_z LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); PROCESS_INFORMATION pi; if (pfCreateProcessW == NULL) { pfCreateProcessW = CreateProcessW; } if (!pfCreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwMyCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, &pi)) { return FALSE; } LPCSTR rlpDlls[2]; DWORD nDlls = 0; if (lpDllName != NULL) { rlpDlls[nDlls++] = lpDllName; } if (!DetourUpdateProcessWithDll(pi.hProcess, rlpDlls, nDlls)) { TerminateProcess(pi.hProcess, ~0u); return FALSE; } if (lpProcessInformation) { CopyMemory(lpProcessInformation, &pi, sizeof(pi)); } if (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(pi.hThread); } return TRUE; } BOOL WINAPI DetourCopyPayloadToProcess(HANDLE hProcess, REFGUID rguid, PVOID pData, DWORD cbData) { DWORD cbTotal = (sizeof(IMAGE_DOS_HEADER) + sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER) + sizeof(DETOUR_SECTION_HEADER) + sizeof(DETOUR_SECTION_RECORD) + cbData); PBYTE pbBase = (PBYTE)VirtualAllocEx(hProcess, NULL, cbTotal, MEM_COMMIT, PAGE_READWRITE); if (pbBase == NULL) { DETOUR_TRACE(("VirtualAllocEx(%d) failed: %d\n", cbTotal, GetLastError())); return FALSE; } PBYTE pbTarget = pbBase; IMAGE_DOS_HEADER idh; IMAGE_NT_HEADERS inh; IMAGE_SECTION_HEADER ish; DETOUR_SECTION_HEADER dsh; DETOUR_SECTION_RECORD dsr; SIZE_T cbWrote = 0; ZeroMemory(&idh, sizeof(idh)); idh.e_magic = IMAGE_DOS_SIGNATURE; idh.e_lfanew = sizeof(idh); if (!WriteProcessMemory(hProcess, pbTarget, &idh, sizeof(idh), &cbWrote) || cbWrote != sizeof(idh)) { DETOUR_TRACE(("WriteProcessMemory(idh) failed: %d\n", GetLastError())); return FALSE; } pbTarget += sizeof(idh); ZeroMemory(&inh, sizeof(inh)); inh.Signature = IMAGE_NT_SIGNATURE; inh.FileHeader.SizeOfOptionalHeader = sizeof(inh.OptionalHeader); inh.FileHeader.Characteristics = IMAGE_FILE_DLL; inh.FileHeader.NumberOfSections = 1; inh.OptionalHeader.Magic = IMAGE_NT_OPTIONAL_HDR_MAGIC; if (!WriteProcessMemory(hProcess, pbTarget, &inh, sizeof(inh), &cbWrote) || cbWrote != sizeof(inh)) { return FALSE; } pbTarget += sizeof(inh); ZeroMemory(&ish, sizeof(ish)); memcpy(ish.Name, ".detour", sizeof(ish.Name)); ish.VirtualAddress = (DWORD)((pbTarget + sizeof(ish)) - pbBase); ish.SizeOfRawData = (sizeof(DETOUR_SECTION_HEADER) + sizeof(DETOUR_SECTION_RECORD) + cbData); if (!WriteProcessMemory(hProcess, pbTarget, &ish, sizeof(ish), &cbWrote) || cbWrote != sizeof(ish)) { return FALSE; } pbTarget += sizeof(ish); ZeroMemory(&dsh, sizeof(dsh)); dsh.cbHeaderSize = sizeof(dsh); dsh.nSignature = DETOUR_SECTION_HEADER_SIGNATURE; dsh.nDataOffset = sizeof(DETOUR_SECTION_HEADER); dsh.cbDataSize = (sizeof(DETOUR_SECTION_HEADER) + sizeof(DETOUR_SECTION_RECORD) + cbData); if (!WriteProcessMemory(hProcess, pbTarget, &dsh, sizeof(dsh), &cbWrote) || cbWrote != sizeof(dsh)) { return FALSE; } pbTarget += sizeof(dsh); ZeroMemory(&dsr, sizeof(dsr)); dsr.cbBytes = cbData + sizeof(DETOUR_SECTION_RECORD); dsr.nReserved = 0; dsr.guid = rguid; if (!WriteProcessMemory(hProcess, pbTarget, &dsr, sizeof(dsr), &cbWrote) || cbWrote != sizeof(dsr)) { return FALSE; } pbTarget += sizeof(dsr); if (!WriteProcessMemory(hProcess, pbTarget, pData, cbData, &cbWrote) || cbWrote != cbData) { return FALSE; } pbTarget += cbData; DETOUR_TRACE(("Copied %d byte payload into target process at %p\n", cbTotal, pbTarget - cbTotal)); return TRUE; } static BOOL fSearchedForHelper = FALSE; static PDETOUR_EXE_HELPER pHelper = NULL; VOID CALLBACK DetourFinishHelperProcess(HWND, HINSTANCE, LPSTR, INT) { HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pHelper->pid); if (hProcess == NULL) { DETOUR_TRACE(("OpenProcess(pid=%d) failed: %d\n", pHelper->pid, GetLastError())); ExitProcess(9901); } PCSTR pszModule = pHelper->DllName; if (!DetourUpdateProcessWithDll(hProcess, &pszModule, 1)) { DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%d) failed: %d\n", pHelper->pid, GetLastError())); ExitProcess(9902); } } BOOL WINAPI DetourIsHelperProcess(VOID) { PVOID pvData; DWORD cbData; if (fSearchedForHelper) { return (pHelper != NULL); } fSearchedForHelper = TRUE; pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData); if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) { return FALSE; } pHelper = (PDETOUR_EXE_HELPER)pvData; if (pHelper->cb < sizeof(*pHelper)) { pHelper = NULL; return FALSE; } return TRUE; } BOOL WINAPI DetourProcessViaHelperA(DWORD dwTargetPid, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { PROCESS_INFORMATION pi; STARTUPINFOA si; CHAR szExe[MAX_PATH]; CHAR szCommand[MAX_PATH]; DETOUR_EXE_HELPER helper; ZeroMemory(&helper, sizeof(helper)); helper.cb = sizeof(helper); helper.pid = dwTargetPid; strcpy_s(helper.DllName, ARRAYSIZE(helper.DllName), lpDllName); DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe)); if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { return FALSE; } #if DETOURS_OPTION_BITS #if DETOURS_32BIT strcat_s(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe"); #else // !DETOURS_32BIT strcat_s(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe"); #endif // !DETOURS_32BIT PCHAR pszDll; if ((pszDll = strrchr(helper.DllName, '\\')) != NULL) { pszDll++; } else if ((pszDll = strrchr(helper.DllName, ':')) != NULL) { pszDll++; } else { pszDll = helper.DllName; } // Replace "32." with "64." or "64." with "32." for (; *pszDll; pszDll++) { #if DETOURS_32BIT if (pszDll[0] == '3' && pszDll[1] == '2' && pszDll[2] == '.') { pszDll[0] = '6'; pszDll[1] = '4'; break; } #else if (pszDll[0] == '6' && pszDll[1] == '4' && pszDll[2] == '.') { pszDll[0] = '3'; pszDll[1] = '2'; break; } #endif } #else // DETOURS_OPTIONS_BITS strcat_s(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe"); #endif // DETOURS_OPTIONS_BITS sprintf_s(szCommand, ARRAYSIZE(szCommand), "rundll32.exe \"%hs\",#1", helper.DllName); ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { if (!DetourCopyPayloadToProcess(pi.hProcess, DETOUR_EXE_HELPER_GUID, &helper, sizeof(helper))) { DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); TerminateProcess(pi.hProcess, ~0u); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return FALSE; } ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); DWORD dwResult = 500; GetExitCodeProcess(pi.hProcess, &dwResult); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (dwResult != 0) { DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); return FALSE; } return TRUE; } else { DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); return FALSE; } } BOOL WINAPI DetourProcessViaHelperW(DWORD dwTargetPid, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { PROCESS_INFORMATION pi; STARTUPINFOW si; WCHAR szExe[MAX_PATH]; WCHAR szCommand[MAX_PATH]; DETOUR_EXE_HELPER helper; ZeroMemory(&helper, sizeof(helper)); helper.cb = sizeof(helper); helper.pid = dwTargetPid; strcpy_s(helper.DllName, ARRAYSIZE(helper.DllName), lpDllName); DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe)); if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { return FALSE; } #if DETOURS_OPTION_BITS #if DETOURS_32BIT wcscat_s(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe"); #else // !DETOURS_32BIT wcscat_s(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe"); #endif // !DETOURS_32BIT PCHAR pszDll; if ((pszDll = strrchr(helper.DllName, '\\')) != NULL) { pszDll++; } else if ((pszDll = strrchr(helper.DllName, ':')) != NULL) { pszDll++; } else { pszDll = helper.DllName; } // Replace "32." with "64." or "64." with "32." for (; *pszDll; pszDll++) { #if DETOURS_32BIT if (pszDll[0] == '3' && pszDll[1] == '2' && pszDll[2] == '.') { pszDll[0] = '6'; pszDll[1] = '4'; break; } #else if (pszDll[0] == '6' && pszDll[1] == '4' && pszDll[2] == '.') { pszDll[0] = '3'; pszDll[1] = '2'; break; } #endif } #else // DETOURS_OPTIONS_BITS wcscat_s(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe"); #endif // DETOURS_OPTIONS_BITS swprintf_s(szCommand, ARRAYSIZE(szCommand), L"rundll32.exe \"%hs\",#1", helper.DllName); ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { if (!DetourCopyPayloadToProcess(pi.hProcess, DETOUR_EXE_HELPER_GUID, &helper, sizeof(helper))) { DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %d\n", GetLastError())); TerminateProcess(pi.hProcess, ~0u); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); return FALSE; } ResumeThread(pi.hThread); ResumeThread(pi.hThread); WaitForSingleObject(pi.hProcess, INFINITE); DWORD dwResult = 500; GetExitCodeProcess(pi.hProcess, &dwResult); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); if (dwResult != 0) { DETOUR_TRACE(("Rundll32.exe failed: result=%d\n", dwResult)); return FALSE; } return TRUE; } else { DETOUR_TRACE(("CreateProcess failed: %d\n", GetLastError())); return FALSE; } } BOOL WINAPI DetourCreateProcessWithDllExA(LPCSTR lpApplicationName, __in_z LPSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCSTR lpCurrentDirectory, LPSTARTUPINFOA lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { if (pfCreateProcessA == NULL) { pfCreateProcessA = CreateProcessA; } PROCESS_INFORMATION backup; if (lpProcessInformation == NULL) { lpProcessInformation = &backup; ZeroMemory(&backup, sizeof(backup)); } if (!pfCreateProcessA(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | CREATE_SUSPENDED, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)) { return FALSE; } LPCSTR szDll = lpDllName; if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &szDll, 1) && !DetourProcessViaHelperA(lpProcessInformation->dwProcessId, lpDllName, pfCreateProcessA)) { TerminateProcess(lpProcessInformation->hProcess, ~0u); CloseHandle(lpProcessInformation->hProcess); CloseHandle(lpProcessInformation->hThread); return FALSE; } if (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(lpProcessInformation->hThread); } if (lpProcessInformation == &backup) { CloseHandle(lpProcessInformation->hProcess); CloseHandle(lpProcessInformation->hThread); } return TRUE; } BOOL WINAPI DetourCreateProcessWithDllExW(LPCWSTR lpApplicationName, __in_z LPWSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation, LPCSTR lpDllName, PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { if (pfCreateProcessW == NULL) { pfCreateProcessW = CreateProcessW; } PROCESS_INFORMATION backup; if (lpProcessInformation == NULL) { lpProcessInformation = &backup; ZeroMemory(&backup, sizeof(backup)); } if (!pfCreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags | CREATE_SUSPENDED, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation)) { return FALSE; } LPCSTR sz = lpDllName; if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, &sz, 1) && !DetourProcessViaHelperW(lpProcessInformation->dwProcessId, lpDllName, pfCreateProcessW)) { TerminateProcess(lpProcessInformation->hProcess, ~0u); CloseHandle(lpProcessInformation->hProcess); CloseHandle(lpProcessInformation->hThread); return FALSE; } if (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(lpProcessInformation->hThread); } if (lpProcessInformation == &backup) { CloseHandle(lpProcessInformation->hProcess); CloseHandle(lpProcessInformation->hThread); } return TRUE; } // ///////////////////////////////////////////////////////////////// End of File.