////////////////////////////////////////////////////////////////////////////// // // Create a process with a DLL (creatwth.cpp of detours.lib) // // Microsoft Research Detours Package, Version 4.0.1 // // Copyright (c) Microsoft Corporation. All rights reserved. // // #define DETOUR_DEBUG 1 #define DETOURS_INTERNAL #include "detours.h" #include #if DETOURS_VERSION != 0x4c0c1 // 0xMAJORcMINORcPATCH #error detours.h version mismatch #endif #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] ////////////////////////////////////////////////////////////////////////////// // const GUID DETOUR_EXE_HELPER_GUID = { /* ea0251b9-5cde-41b5-98d0-2af4a26b0fee */ 0xea0251b9, 0x5cde, 0x41b5, { 0x98, 0xd0, 0x2a, 0xf4, 0xa2, 0x6b, 0x0f, 0xee }}; ////////////////////////////////////////////////////////////////////////////// // // Enumerate through modules in the target process. // static PVOID LoadNtHeaderFromProcess(_In_ HANDLE hProcess, _In_ HMODULE hModule, _Out_ PIMAGE_NT_HEADERS32 pNtHeader) { ZeroMemory(pNtHeader, sizeof(*pNtHeader)); PBYTE pbModule = (PBYTE)hModule; if (pbModule == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return NULL; } MEMORY_BASIC_INFORMATION mbi; ZeroMemory(&mbi, sizeof(mbi)); if (VirtualQueryEx(hProcess, hModule, &mbi, sizeof(mbi)) == 0) { return NULL; } IMAGE_DOS_HEADER idh; if (!ReadProcessMemory(hProcess, pbModule, &idh, sizeof(idh), NULL)) { DETOUR_TRACE(("ReadProcessMemory(idh@%p..%p) failed: %lu\n", pbModule, pbModule + sizeof(idh), GetLastError())); return NULL; } if (idh.e_magic != IMAGE_DOS_SIGNATURE || (DWORD)idh.e_lfanew > mbi.RegionSize || (DWORD)idh.e_lfanew < sizeof(idh)) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } if (!ReadProcessMemory(hProcess, pbModule + idh.e_lfanew, pNtHeader, sizeof(*pNtHeader), NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p:%p) failed: %lu\n", pbModule + idh.e_lfanew, pbModule + idh.e_lfanew + sizeof(*pNtHeader), pbModule, GetLastError())); return NULL; } if (pNtHeader->Signature != IMAGE_NT_SIGNATURE) { SetLastError(ERROR_BAD_EXE_FORMAT); return NULL; } return pbModule + idh.e_lfanew; } static HMODULE EnumerateModulesInProcess(_In_ HANDLE hProcess, _In_opt_ HMODULE hModuleLast, _Out_ PIMAGE_NT_HEADERS32 pNtHeader, _Out_opt_ PVOID *pRemoteNtHeader) { ZeroMemory(pNtHeader, sizeof(*pNtHeader)); if (pRemoteNtHeader) { *pRemoteNtHeader = NULL; } PBYTE pbLast = (PBYTE)hModuleLast + MM_ALLOCATION_GRANULARITY; 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; } // Usermode address space has such an unaligned region size always at the // end and only at the end. // 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; } PVOID remoteHeader = LoadNtHeaderFromProcess(hProcess, (HMODULE)pbLast, pNtHeader); if (remoteHeader) { if (pRemoteNtHeader) { *pRemoteNtHeader = remoteHeader; } return (HMODULE)pbLast; } } return NULL; } ////////////////////////////////////////////////////////////////////////////// // // Find payloads in target process. // static PVOID FindDetourSectionInRemoteModule(_In_ HANDLE hProcess, _In_ HMODULE hModule, _In_ const IMAGE_NT_HEADERS32 *pNtHeader, _In_ PVOID pRemoteNtHeader) { if (pNtHeader->FileHeader.SizeOfOptionalHeader == 0) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } PIMAGE_SECTION_HEADER pRemoteSectionHeaders = (PIMAGE_SECTION_HEADER)((PBYTE)pRemoteNtHeader + sizeof(pNtHeader->Signature) + sizeof(pNtHeader->FileHeader) + pNtHeader->FileHeader.SizeOfOptionalHeader); IMAGE_SECTION_HEADER header; for (DWORD n = 0; n < pNtHeader->FileHeader.NumberOfSections; ++n) { if (!ReadProcessMemory(hProcess, pRemoteSectionHeaders + n, &header, sizeof(header), NULL)) { DETOUR_TRACE(("ReadProcessMemory(ish@%p..%p) failed: %lu\n", pRemoteSectionHeaders + n, (PBYTE)(pRemoteSectionHeaders + n) + sizeof(header), GetLastError())); return NULL; } if (strcmp((PCHAR)header.Name, ".detour") == 0) { if (header.VirtualAddress == 0 || header.SizeOfRawData == 0) { break; } SetLastError(NO_ERROR); return (PBYTE)hModule + header.VirtualAddress; } } SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } static PVOID FindPayloadInRemoteDetourSection(_In_ HANDLE hProcess, _In_ REFGUID rguid, _Out_opt_ DWORD *pcbData, _In_ PVOID pvRemoteDetoursSection) { if (pcbData) { *pcbData = 0; } PBYTE pbData = (PBYTE)pvRemoteDetoursSection; DETOUR_SECTION_HEADER header; if (!ReadProcessMemory(hProcess, pbData, &header, sizeof(header), NULL)) { DETOUR_TRACE(("ReadProcessMemory(dsh@%p..%p) failed: %lu\n", pbData, pbData + sizeof(header), GetLastError())); return NULL; } if (header.cbHeaderSize < sizeof(DETOUR_SECTION_HEADER) || header.nSignature != DETOUR_SECTION_HEADER_SIGNATURE) { SetLastError(ERROR_EXE_MARKED_INVALID); return NULL; } if (header.nDataOffset == 0) { header.nDataOffset = header.cbHeaderSize; } for (PVOID pvSection = pbData + header.nDataOffset; pvSection < pbData + header.cbDataSize;) { DETOUR_SECTION_RECORD section; if (!ReadProcessMemory(hProcess, pvSection, §ion, sizeof(section), NULL)) { DETOUR_TRACE(("ReadProcessMemory(dsr@%p..%p) failed: %lu\n", pvSection, (PBYTE)pvSection + sizeof(section), GetLastError())); return NULL; } if (DetourAreSameGuid(section.guid, rguid)) { if (pcbData) { *pcbData = section.cbBytes - sizeof(section); } SetLastError(NO_ERROR); return (DETOUR_SECTION_RECORD *)pvSection + 1; } pvSection = (PBYTE)pvSection + section.cbBytes; } return NULL; } _Success_(return != NULL) PVOID WINAPI DetourFindRemotePayload(_In_ HANDLE hProcess, _In_ REFGUID rguid, _Out_opt_ DWORD *pcbData) { if (hProcess == NULL) { SetLastError(ERROR_INVALID_HANDLE); return NULL; } IMAGE_NT_HEADERS32 header; PVOID pvRemoteHeader; for (HMODULE hMod = NULL; (hMod = EnumerateModulesInProcess(hProcess, hMod, &header, &pvRemoteHeader)) != NULL;) { PVOID pvData = FindDetourSectionInRemoteModule(hProcess, hMod, &header, pvRemoteHeader); if (pvData != NULL) { pvData = FindPayloadInRemoteDetourSection(hProcess, rguid, pcbData, pvData); if (pvData != NULL) { return pvData; } } } SetLastError(ERROR_MOD_NOT_FOUND); return NULL; } ////////////////////////////////////////////////////////////////////////////// // // Find a region of memory in which we can create a replacement import table. // static PBYTE FindAndAllocateNearBase(HANDLE hProcess, PBYTE pbModule, 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: %lu\n", pbLast, GetLastError())); break; } // Usermode address space has such an unaligned region size always at the // end and only at the end. // if ((mbi.RegionSize & 0xfff) == 0xfff) { break; } // Skip anything other than a pure free region. // if (mbi.State != MEM_FREE) { continue; } // Use the max of mbi.BaseAddress and pbBase, in case mbi.BaseAddress < pbBase. PBYTE pbAddress = (PBYTE)mbi.BaseAddress > pbBase ? (PBYTE)mbi.BaseAddress : pbBase; // Round pbAddress up to the nearest MM allocation boundary. const DWORD_PTR mmGranularityMinusOne = (DWORD_PTR)(MM_ALLOCATION_GRANULARITY -1); pbAddress = (PBYTE)(((DWORD_PTR)pbAddress + mmGranularityMinusOne) & ~mmGranularityMinusOne); #ifdef _WIN64 // The offset from pbModule to any replacement import must fit into 32 bits. // For simplicity, we check that the offset to the last byte fits into 32 bits, // instead of the largest offset we'll actually use. The values are very similar. const size_t GB4 = ((((size_t)1) << 32) - 1); if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { DETOUR_TRACE(("FindAndAllocateNearBase(1) failing due to distance >4GB %p\n", pbAddress)); return NULL; } #else UNREFERENCED_PARAMETER(pbModule); #endif DETOUR_TRACE(("Free region %p..%p\n", mbi.BaseAddress, (PBYTE)mbi.BaseAddress + mbi.RegionSize)); for (; pbAddress < (PBYTE)mbi.BaseAddress + mbi.RegionSize; pbAddress += MM_ALLOCATION_GRANULARITY) { PBYTE pbAlloc = (PBYTE)VirtualAllocEx(hProcess, pbAddress, cbAlloc, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (pbAlloc == NULL) { DETOUR_TRACE(("VirtualAllocEx(%p) failed: %lu\n", pbAddress, GetLastError())); continue; } #ifdef _WIN64 // The offset from pbModule to any replacement import must fit into 32 bits. if ((size_t)(pbAddress + cbAlloc - 1 - pbModule) > GB4) { DETOUR_TRACE(("FindAndAllocateNearBase(2) failing due to distance >4GB %p\n", pbAddress)); return NULL; } #endif 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(_Inout_z_count_(cchDest) LPSTR pszDest, _In_ size_t cchDest, _In_z_ LPCSTR 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; } for (; cchDest >= 2; cchDest--, pszDest++) { if (pszDest[0] == '?' && pszDest[1] == '?') { pszDest[0] = pszSize[0]; pszDest[1] = pszSize[1]; break; } } return S_OK; } static BOOL RecordExeRestore(HANDLE hProcess, HMODULE hModule, DETOUR_EXE_RESTORE& der) { // Save the various headers for DetourRestoreAfterWith. 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: %lu\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: %lu\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 (der.cbinh > sizeof(der.raw)) { return FALSE; } if (!ReadProcessMemory(hProcess, der.pinh, &der.inh, der.cbinh, NULL)) { DETOUR_TRACE(("ReadProcessMemory(inh@%p..%p) failed: %lu\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=%08lx, CLR.Size=%lu\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=%08lx, CLR.Size=%lu\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: %lu\n", der.pclr, der.pclr + der.cbclr, GetLastError())); return FALSE; } DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); } return TRUE; } ////////////////////////////////////////////////////////////////////////////// // #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 IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA32 #define UPDATE_IMPORTS_XX UpdateImports32 #define DETOURS_BITS_XX 32 #include "uimports.cc" #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 IMAGE_THUNK_DATAXX IMAGE_THUNK_DATA64 #define UPDATE_IMPORTS_XX UpdateImports64 #define DETOURS_BITS_XX 64 #include "uimports.cc" #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, HMODULE hModule, WORD machine, DETOUR_EXE_RESTORE& der) { 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: %lu\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: %lu\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: %lu\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]; } /////////////////////////////////////////////////////// Write new headers. // DWORD dwProtect = 0; if (!DetourVirtualProtectSameExecuteEx(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: %lu\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: %lu\n", psects, psects + cb, GetLastError())); return FALSE; } DETOUR_TRACE(("WriteProcessMemory(ish@%p..%p)\n", psects, psects + cb)); // Record the updated headers. if (!RecordExeRestore(hProcess, hModule, der)) { return FALSE; } // Remove the import table. if (der.pclr != NULL && (der.clr.Flags & COMIMAGE_FLAGS_ILONLY)) { inh64.IMPORT_DIRECTORY.VirtualAddress = 0; inh64.IMPORT_DIRECTORY.Size = 0; if (!WriteProcessMemory(hProcess, pnh, &inh64, sizeof(inh64), NULL)) { DETOUR_TRACE(("WriteProcessMemory(inh@%p..%p) failed: %lu\n", pnh, pnh + sizeof(inh64), GetLastError())); return FALSE; } } DWORD dwOld = 0; if (!VirtualProtectEx(hProcess, pbModule, inh64.OptionalHeader.SizeOfHeaders, dwProtect, &dwOld)) { return FALSE; } return TRUE; } #endif // DETOURS_64BIT typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); static BOOL IsWow64ProcessHelper(HANDLE hProcess, PBOOL Wow64Process) { #ifdef _X86_ if (Wow64Process == NULL) { return FALSE; } // IsWow64Process is not available on all supported versions of Windows. // HMODULE hKernel32 = LoadLibraryW(L"KERNEL32.DLL"); if (hKernel32 == NULL) { DETOUR_TRACE(("LoadLibraryW failed: %lu\n", GetLastError())); return FALSE; } LPFN_ISWOW64PROCESS pfnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress( hKernel32, "IsWow64Process"); if (pfnIsWow64Process == NULL) { DETOUR_TRACE(("GetProcAddress failed: %lu\n", GetLastError())); return FALSE; } return pfnIsWow64Process(hProcess, Wow64Process); #else return IsWow64Process(hProcess, Wow64Process); #endif } ////////////////////////////////////////////////////////////////////////////// // BOOL WINAPI DetourUpdateProcessWithDll(_In_ HANDLE hProcess, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_ DWORD nDlls) { // Find the next memory region that contains a mapped PE image. // BOOL bIs32BitProcess; BOOL bIs64BitOS = FALSE; HMODULE hModule = NULL; HMODULE hLast = NULL; DETOUR_TRACE(("DetourUpdateProcessWithDll(%p,dlls=%lu)\n", hProcess, nDlls)); for (;;) { IMAGE_NT_HEADERS32 inh; if ((hLast = EnumerateModulesInProcess(hProcess, hLast, &inh, NULL)) == 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; DETOUR_TRACE(("%p Found EXE\n", hLast)); } } if (hModule == NULL) { SetLastError(ERROR_INVALID_OPERATION); return FALSE; } // Determine if the target process is 32bit or 64bit. This is a two-stop process: // // 1. First, determine if we're running on a 64bit operating system. // - If we're running 64bit code (i.e. _WIN64 is defined), this is trivially true. // - If we're running 32bit code (i.e. _WIN64 is not defined), test if // we're running under Wow64. If so, it implies that the operating system // is 64bit. // #ifdef _WIN64 bIs64BitOS = TRUE; #else if (!IsWow64ProcessHelper(GetCurrentProcess(), &bIs64BitOS)) { return FALSE; } #endif // 2. With the operating system bitness known, we can now consider the target process: // - If we're running on a 64bit OS, the target process is 32bit in case // it is running under Wow64. Otherwise, it's 64bit, running natively // (without Wow64). // - If we're running on a 32bit OS, the target process must be 32bit, too. // if (bIs64BitOS) { if (!IsWow64ProcessHelper(hProcess, &bIs32BitProcess)) { return FALSE; } } else { bIs32BitProcess = TRUE; } DETOUR_TRACE((" 32BitProcess=%d\n", bIs32BitProcess)); return DetourUpdateProcessWithDllEx(hProcess, hModule, bIs32BitProcess, rlpDlls, nDlls); } BOOL WINAPI DetourUpdateProcessWithDllEx(_In_ HANDLE hProcess, _In_ HMODULE hModule, _In_ BOOL bIs32BitProcess, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_ DWORD nDlls) { // Find the next memory region that contains a mapped PE image. // BOOL bIs32BitExe = FALSE; DETOUR_TRACE(("DetourUpdateProcessWithDllEx(%p,%p,dlls=%lu)\n", hProcess, hModule, nDlls)); IMAGE_NT_HEADERS32 inh; if (hModule == NULL || !LoadNtHeaderFromProcess(hProcess, hModule, &inh)) { SetLastError(ERROR_INVALID_OPERATION); return FALSE; } if (inh.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC && inh.FileHeader.Machine != 0) { bIs32BitExe = TRUE; } DETOUR_TRACE((" 32BitExe=%d\n", bIs32BitExe)); if (hModule == NULL) { SetLastError(ERROR_INVALID_OPERATION); return FALSE; } // Save the various headers for DetourRestoreAfterWith. // DETOUR_EXE_RESTORE der; if (!RecordExeRestore(hProcess, hModule, der)) { return FALSE; } #if defined(DETOURS_64BIT) // Try to convert a neutral 32-bit managed binary to a 64-bit managed binary. if (bIs32BitExe && !bIs32BitProcess) { if (!der.pclr // Native binary || (der.clr.Flags & COMIMAGE_FLAGS_ILONLY) == 0 // Or mixed-mode MSIL || (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) != 0) { // Or 32BIT Required MSIL SetLastError(ERROR_INVALID_HANDLE); return FALSE; } if (!UpdateFrom32To64(hProcess, hModule, #if defined(DETOURS_X64) IMAGE_FILE_MACHINE_AMD64, #elif defined(DETOURS_IA64) IMAGE_FILE_MACHINE_IA64, #elif defined(DETOURS_ARM64) IMAGE_FILE_MACHINE_ARM64, #else #error Must define one of DETOURS_X64 or DETOURS_IA64 or DETOURS_ARM64 on 64-bit. #endif der)) { return FALSE; } bIs32BitExe = FALSE; } #endif // DETOURS_64BIT // Now decide if we can insert the detour. #if defined(DETOURS_32BIT) if (bIs32BitProcess) { // 32-bit native or 32-bit managed process on any platform. if (!UpdateImports32(hProcess, hModule, rlpDlls, nDlls)) { return FALSE; } } else { // 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; } #elif defined(DETOURS_64BIT) if (bIs32BitProcess || bIs32BitExe) { // Can't detour a 32-bit process with 64-bit code. SetLastError(ERROR_INVALID_HANDLE); return FALSE; } else { // 64-bit native or 64-bit managed process on any platform. if (!UpdateImports64(hProcess, hModule, rlpDlls, nDlls)) { return FALSE; } } #else #pragma Must define one of DETOURS_32BIT or DETOURS_64BIT. #endif // DETOURS_64BIT /////////////////////////////////////////////////// Update the CLR header. // if (der.pclr != NULL) { DETOUR_CLR_HEADER clr; CopyMemory(&clr, &der.clr, sizeof(clr)); clr.Flags &= ~COMIMAGE_FLAGS_ILONLY; // Clear the IL_ONLY flag. DWORD dwProtect; if (!DetourVirtualProtectSameExecuteEx(hProcess, der.pclr, sizeof(clr), PAGE_READWRITE, &dwProtect)) { DETOUR_TRACE(("VirtualProtectEx(clr) write failed: %lu\n", GetLastError())); return FALSE; } if (!WriteProcessMemory(hProcess, der.pclr, &clr, sizeof(clr), NULL)) { DETOUR_TRACE(("WriteProcessMemory(clr) failed: %lu\n", GetLastError())); return FALSE; } if (!VirtualProtectEx(hProcess, der.pclr, sizeof(clr), dwProtect, &dwProtect)) { DETOUR_TRACE(("VirtualProtectEx(clr) restore failed: %lu\n", GetLastError())); return FALSE; } DETOUR_TRACE(("CLR: %p..%p\n", der.pclr, der.pclr + der.cbclr)); #if DETOURS_64BIT if (der.clr.Flags & COMIMAGE_FLAGS_32BITREQUIRED) { // 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")); 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: %lu\n", GetLastError())); return FALSE; } return TRUE; } ////////////////////////////////////////////////////////////////////////////// // BOOL WINAPI DetourCreateProcessWithDllA(_In_opt_ LPCSTR lpApplicationName, _Inout_opt_ LPSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCSTR lpCurrentDirectory, _In_ LPSTARTUPINFOA lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ LPCSTR lpDllName, _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); PROCESS_INFORMATION pi; BOOL fResult = FALSE; if (pfCreateProcessA == NULL) { pfCreateProcessA = CreateProcessA; } fResult = pfCreateProcessA(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwMyCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, &pi); if (lpProcessInformation != NULL) { CopyMemory(lpProcessInformation, &pi, sizeof(pi)); } if (!fResult) { 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 (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(pi.hThread); } return TRUE; } BOOL WINAPI DetourCreateProcessWithDllW(_In_opt_ LPCWSTR lpApplicationName, _Inout_opt_ LPWSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCWSTR lpCurrentDirectory, _In_ LPSTARTUPINFOW lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ LPCSTR lpDllName, _In_opt_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { DWORD dwMyCreationFlags = (dwCreationFlags | CREATE_SUSPENDED); PROCESS_INFORMATION pi; if (pfCreateProcessW == NULL) { pfCreateProcessW = CreateProcessW; } BOOL fResult = pfCreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwMyCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, &pi); if (lpProcessInformation) { CopyMemory(lpProcessInformation, &pi, sizeof(pi)); } if (!fResult) { 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 (!(dwCreationFlags & CREATE_SUSPENDED)) { ResumeThread(pi.hThread); } return TRUE; } BOOL WINAPI DetourCopyPayloadToProcess(_In_ HANDLE hProcess, _In_ REFGUID rguid, _In_reads_bytes_(cbData) LPCVOID pvData, _In_ DWORD cbData) { return DetourCopyPayloadToProcessEx(hProcess, rguid, pvData, cbData) != NULL; } _Success_(return != NULL) PVOID WINAPI DetourCopyPayloadToProcessEx(_In_ HANDLE hProcess, _In_ REFGUID rguid, _In_reads_bytes_(cbData) LPCVOID pvData, _In_ DWORD cbData) { if (hProcess == NULL) { SetLastError(ERROR_INVALID_HANDLE); return NULL; } 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(%lu) failed: %lu\n", cbTotal, GetLastError())); return NULL; } // As you can see in the following code, // the memory layout of the payload range "[pbBase, pbBase+cbTotal]" is a PE executable file, // so DetourFreePayload can use "DetourGetContainingModule(Payload pointer)" to get the above "pbBase" pointer, // pbBase: the memory block allocated by VirtualAllocEx will be released in DetourFreePayload by VirtualFree. 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: %lu\n", GetLastError())); return NULL; } 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 NULL; } 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 NULL; } 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 NULL; } 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 NULL; } pbTarget += sizeof(dsr); if (!WriteProcessMemory(hProcess, pbTarget, pvData, cbData, &cbWrote) || cbWrote != cbData) { return NULL; } DETOUR_TRACE(("Copied %lu byte payload into target process at %p\n", cbData, pbTarget)); SetLastError(NO_ERROR); return pbTarget; } static BOOL s_fSearchedForHelper = FALSE; static PDETOUR_EXE_HELPER s_pHelper = NULL; VOID CALLBACK DetourFinishHelperProcess(_In_ HWND, _In_ HINSTANCE, _In_ LPSTR, _In_ INT) { LPCSTR * rlpDlls = NULL; DWORD Result = 9900; DWORD cOffset = 0; DWORD cSize = 0; HANDLE hProcess = NULL; if (s_pHelper == NULL) { DETOUR_TRACE(("DetourFinishHelperProcess called with s_pHelper = NULL.\n")); Result = 9905; goto Cleanup; } hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, s_pHelper->pid); if (hProcess == NULL) { DETOUR_TRACE(("OpenProcess(pid=%lu) failed: %lu\n", s_pHelper->pid, GetLastError())); Result = 9901; goto Cleanup; } rlpDlls = new NOTHROW LPCSTR [s_pHelper->nDlls]; cSize = s_pHelper->cb - sizeof(DETOUR_EXE_HELPER); for (DWORD n = 0; n < s_pHelper->nDlls; n++) { size_t cchDest = 0; HRESULT hr = StringCchLengthA(&s_pHelper->rDlls[cOffset], cSize - cOffset, &cchDest); if (!SUCCEEDED(hr)) { Result = 9902; goto Cleanup; } rlpDlls[n] = &s_pHelper->rDlls[cOffset]; cOffset += (DWORD)cchDest + 1; } if (!DetourUpdateProcessWithDll(hProcess, rlpDlls, s_pHelper->nDlls)) { DETOUR_TRACE(("DetourUpdateProcessWithDll(pid=%lu) failed: %lu\n", s_pHelper->pid, GetLastError())); Result = 9903; goto Cleanup; } Result = 0; Cleanup: if (rlpDlls != NULL) { delete[] rlpDlls; rlpDlls = NULL; } // Note: s_pHelper is allocated as part of injecting the payload in DetourCopyPayloadToProcess(..), // it's a fake section and not data allocated by the system PE loader. // Delete the payload after execution to release the memory occupied by it if (s_pHelper != NULL) { DetourFreePayload(s_pHelper); s_pHelper = NULL; } ExitProcess(Result); } BOOL WINAPI DetourIsHelperProcess(VOID) { PVOID pvData; DWORD cbData; if (s_fSearchedForHelper) { return (s_pHelper != NULL); } s_fSearchedForHelper = TRUE; pvData = DetourFindPayloadEx(DETOUR_EXE_HELPER_GUID, &cbData); if (pvData == NULL || cbData < sizeof(DETOUR_EXE_HELPER)) { return FALSE; } s_pHelper = (PDETOUR_EXE_HELPER)pvData; if (s_pHelper->cb < sizeof(*s_pHelper)) { s_pHelper = NULL; return FALSE; } return TRUE; } static BOOL WINAPI AllocExeHelper(_Out_ PDETOUR_EXE_HELPER *pHelper, _In_ DWORD dwTargetPid, _In_ DWORD nDlls, _In_reads_(nDlls) LPCSTR *rlpDlls) { PDETOUR_EXE_HELPER Helper = NULL; BOOL Result = FALSE; _Field_range_(0, cSize - 4) DWORD cOffset = 0; DWORD cSize = 4; if (pHelper == NULL) { goto Cleanup; } *pHelper = NULL; if (nDlls < 1 || nDlls > 4096) { SetLastError(ERROR_INVALID_PARAMETER); goto Cleanup; } for (DWORD n = 0; n < nDlls; n++) { HRESULT hr; size_t cchDest = 0; hr = StringCchLengthA(rlpDlls[n], 4096, &cchDest); if (!SUCCEEDED(hr)) { goto Cleanup; } cSize += (DWORD)cchDest + 1; } Helper = (PDETOUR_EXE_HELPER) new NOTHROW BYTE[sizeof(DETOUR_EXE_HELPER) + cSize]; if (Helper == NULL) { goto Cleanup; } Helper->cb = sizeof(DETOUR_EXE_HELPER) + cSize; Helper->pid = dwTargetPid; Helper->nDlls = nDlls; for (DWORD n = 0; n < nDlls; n++) { HRESULT hr; size_t cchDest = 0; if (cOffset > 0x10000 || cSize > 0x10000 || cOffset + 2 >= cSize) { goto Cleanup; } if (cOffset + 2 >= cSize || cOffset + 65536 < cSize) { goto Cleanup; } _Analysis_assume_(cOffset + 1 < cSize); _Analysis_assume_(cOffset < 0x10000); _Analysis_assume_(cSize < 0x10000); PCHAR psz = &Helper->rDlls[cOffset]; hr = StringCchCopyA(psz, cSize - cOffset, rlpDlls[n]); if (!SUCCEEDED(hr)) { goto Cleanup; } // REVIEW 28020 The expression '1<=_Param_(2)& &_Param_(2)<=2147483647' is not true at this call. // REVIEW 28313 Analysis will not proceed past this point because of annotation evaluation. The annotation expression *_Param_(3)<_Param_(2)&&*_Param_(3)<=stringLength$(_Param_(1)) cannot be true under any assumptions at this point in the program. #pragma warning(suppress:28020 28313) hr = StringCchLengthA(psz, cSize - cOffset, &cchDest); if (!SUCCEEDED(hr)) { goto Cleanup; } // Replace "32." with "64." or "64." with "32." for (DWORD c = (DWORD)cchDest + 1; c > 3; c--) { #if DETOURS_32BIT if (psz[c - 3] == '3' && psz[c - 2] == '2' && psz[c - 1] == '.') { psz[c - 3] = '6'; psz[c - 2] = '4'; break; } #else if (psz[c - 3] == '6' && psz[c - 2] == '4' && psz[c - 1] == '.') { psz[c - 3] = '3'; psz[c - 2] = '2'; break; } #endif } cOffset += (DWORD)cchDest + 1; } *pHelper = Helper; Helper = NULL; Result = TRUE; Cleanup: if (Helper != NULL) { delete[] (PBYTE)Helper; Helper = NULL; } return Result; } static VOID WINAPI FreeExeHelper(PDETOUR_EXE_HELPER *pHelper) { if (*pHelper != NULL) { delete[] (PBYTE)*pHelper; *pHelper = NULL; } } BOOL WINAPI DetourProcessViaHelperA(_In_ DWORD dwTargetPid, _In_ LPCSTR lpDllName, _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { return DetourProcessViaHelperDllsA(dwTargetPid, 1, &lpDllName, pfCreateProcessA); } BOOL WINAPI DetourProcessViaHelperDllsA(_In_ DWORD dwTargetPid, _In_ DWORD nDlls, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_ PDETOUR_CREATE_PROCESS_ROUTINEA pfCreateProcessA) { BOOL Result = FALSE; PROCESS_INFORMATION pi; STARTUPINFOA si; CHAR szExe[MAX_PATH]; CHAR szCommand[MAX_PATH]; PDETOUR_EXE_HELPER helper = NULL; HRESULT hr; DWORD nLen = GetEnvironmentVariableA("WINDIR", szExe, ARRAYSIZE(szExe)); DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls)); if (nDlls < 1 || nDlls > 4096) { SetLastError(ERROR_INVALID_PARAMETER); goto Cleanup; } if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { goto Cleanup; } if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { goto Cleanup; } #if DETOURS_OPTION_BITS #if DETOURS_32BIT hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\sysnative\\rundll32.exe"); #else // !DETOURS_32BIT hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\syswow64\\rundll32.exe"); #endif // !DETOURS_32BIT #else // DETOURS_OPTIONS_BITS hr = StringCchCatA(szExe, ARRAYSIZE(szExe), "\\system32\\rundll32.exe"); #endif // DETOURS_OPTIONS_BITS if (!SUCCEEDED(hr)) { goto Cleanup; } //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP")); //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before hr = StringCchPrintfA(szCommand, ARRAYSIZE(szCommand), "rundll32.exe \"%s\",#1", &helper->rDlls[0]); if (!SUCCEEDED(hr)) { goto Cleanup; } ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%hs\", \"%hs\")\n", szExe, szCommand)); if (pfCreateProcessA(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { if (!DetourCopyPayloadToProcess(pi.hProcess, DETOUR_EXE_HELPER_GUID, helper, helper->cb)) { DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError())); TerminateProcess(pi.hProcess, ~0u); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); goto Cleanup; } 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=%lu\n", dwResult)); goto Cleanup; } Result = TRUE; } else { DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError())); goto Cleanup; } Cleanup: FreeExeHelper(&helper); return Result; } BOOL WINAPI DetourProcessViaHelperW(_In_ DWORD dwTargetPid, _In_ LPCSTR lpDllName, _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { return DetourProcessViaHelperDllsW(dwTargetPid, 1, &lpDllName, pfCreateProcessW); } BOOL WINAPI DetourProcessViaHelperDllsW(_In_ DWORD dwTargetPid, _In_ DWORD nDlls, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_ PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW) { BOOL Result = FALSE; PROCESS_INFORMATION pi; STARTUPINFOW si; WCHAR szExe[MAX_PATH]; WCHAR szCommand[MAX_PATH]; PDETOUR_EXE_HELPER helper = NULL; HRESULT hr; WCHAR szDllName[MAX_PATH]; int cchWrittenWideChar; DWORD nLen = GetEnvironmentVariableW(L"WINDIR", szExe, ARRAYSIZE(szExe)); DETOUR_TRACE(("DetourProcessViaHelperDlls(pid=%lu,dlls=%lu)\n", dwTargetPid, nDlls)); if (nDlls < 1 || nDlls > 4096) { SetLastError(ERROR_INVALID_PARAMETER); goto Cleanup; } if (!AllocExeHelper(&helper, dwTargetPid, nDlls, rlpDlls)) { goto Cleanup; } if (nLen == 0 || nLen >= ARRAYSIZE(szExe)) { goto Cleanup; } #if DETOURS_OPTION_BITS #if DETOURS_32BIT hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\sysnative\\rundll32.exe"); #else // !DETOURS_32BIT hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\syswow64\\rundll32.exe"); #endif // !DETOURS_32BIT #else // DETOURS_OPTIONS_BITS hr = StringCchCatW(szExe, ARRAYSIZE(szExe), L"\\system32\\rundll32.exe"); #endif // DETOURS_OPTIONS_BITS if (!SUCCEEDED(hr)) { goto Cleanup; } //for East Asia languages and so on, like Chinese, print format with "%hs" can not work fine before user call _tsetlocale(LC_ALL,_T(".ACP")); //so we can't use "%hs" in format string, because the dll that contain this code would inject to any process, even not call _tsetlocale(LC_ALL,_T(".ACP")) before cchWrittenWideChar = MultiByteToWideChar(CP_ACP, 0, &helper->rDlls[0], -1, szDllName, ARRAYSIZE(szDllName)); if (cchWrittenWideChar >= ARRAYSIZE(szDllName) || cchWrittenWideChar <= 0) { goto Cleanup; } hr = StringCchPrintfW(szCommand, ARRAYSIZE(szCommand), L"rundll32.exe \"%s\",#1", szDllName); if (!SUCCEEDED(hr)) { goto Cleanup; } ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); DETOUR_TRACE(("DetourProcessViaHelperDlls(\"%ls\", \"%ls\")\n", szExe, szCommand)); if (pfCreateProcessW(szExe, szCommand, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi)) { if (!DetourCopyPayloadToProcess(pi.hProcess, DETOUR_EXE_HELPER_GUID, helper, helper->cb)) { DETOUR_TRACE(("DetourCopyPayloadToProcess failed: %lu\n", GetLastError())); TerminateProcess(pi.hProcess, ~0u); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); goto Cleanup; } 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=%lu\n", dwResult)); goto Cleanup; } Result = TRUE; } else { DETOUR_TRACE(("CreateProcess failed: %lu\n", GetLastError())); goto Cleanup; } Cleanup: FreeExeHelper(&helper); return Result; } BOOL WINAPI DetourCreateProcessWithDllExA(_In_opt_ LPCSTR lpApplicationName, _Inout_opt_ LPSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCSTR lpCurrentDirectory, _In_ LPSTARTUPINFOA lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ LPCSTR lpDllName, _In_opt_ 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(_In_opt_ LPCWSTR lpApplicationName, _Inout_opt_ LPWSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCWSTR lpCurrentDirectory, _In_ LPSTARTUPINFOW lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ LPCSTR lpDllName, _In_opt_ 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; } BOOL WINAPI DetourCreateProcessWithDllsA(_In_opt_ LPCSTR lpApplicationName, _Inout_opt_ LPSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCSTR lpCurrentDirectory, _In_ LPSTARTUPINFOA lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ DWORD nDlls, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_opt_ 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; } if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && !DetourProcessViaHelperDllsA(lpProcessInformation->dwProcessId, nDlls, rlpDlls, 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 DetourCreateProcessWithDllsW(_In_opt_ LPCWSTR lpApplicationName, _Inout_opt_ LPWSTR lpCommandLine, _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, _In_ BOOL bInheritHandles, _In_ DWORD dwCreationFlags, _In_opt_ LPVOID lpEnvironment, _In_opt_ LPCWSTR lpCurrentDirectory, _In_ LPSTARTUPINFOW lpStartupInfo, _Out_ LPPROCESS_INFORMATION lpProcessInformation, _In_ DWORD nDlls, _In_reads_(nDlls) LPCSTR *rlpDlls, _In_opt_ 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; } if (!DetourUpdateProcessWithDll(lpProcessInformation->hProcess, rlpDlls, nDlls) && !DetourProcessViaHelperDllsW(lpProcessInformation->dwProcessId, nDlls, rlpDlls, 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.