mirror of
https://gitlab.com/Mr_Goldberg/goldberg_emulator.git
synced 2024-11-27 14:08:39 +01:00
1783 lines
63 KiB
C++
1783 lines
63 KiB
C++
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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 <stddef.h>
|
|
|
|
#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.
|