enderalse/source/fs.dll/skse64/skse64_loader_common/Inject.cpp

363 lines
7.7 KiB
C++

#include "LoaderError.h"
#include "IdentifyEXE.h"
#include "common/IFileStream.h"
// remote thread creation
static bool DoInjectDLLThread(PROCESS_INFORMATION * info, const char * dllPath, bool sync, bool noTimeout);
bool InjectDLLThread(PROCESS_INFORMATION * info, const char * dllPath, bool sync, bool noTimeout)
{
bool result = false;
// wrap DLL injection in SEH, if it crashes print a message
__try {
result = DoInjectDLLThread(info, dllPath, sync, noTimeout);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
PrintLoaderError("DLL injection failed. In most cases, this is caused by an overly paranoid software firewall or antivirus package. Disabling either of these may solve the problem.");
result = false;
}
return result;
}
static bool DoInjectDLLThread(PROCESS_INFORMATION * info, const char * dllPath, bool sync, bool noTimeout)
{
bool result = false;
// make sure the dll exists
IFileStream fileCheck;
if(!fileCheck.Open(dllPath))
{
PrintLoaderError("Couldn't find %s.", dllPath);
return false;
}
fileCheck.Close();
HANDLE process = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, info->dwProcessId);
if(process)
{
uintptr_t hookBase = (uintptr_t)VirtualAllocEx(process, NULL, 8192, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(hookBase)
{
// safe because kernel32 is loaded at the same address in all processes
// (can change across restarts)
uintptr_t loadLibraryAAddr = (uintptr_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
_MESSAGE("hookBase = %016I64X", hookBase);
_MESSAGE("loadLibraryAAddr = %016I64X", loadLibraryAAddr);
size_t bytesWritten;
WriteProcessMemory(process, (LPVOID)hookBase, dllPath, strlen(dllPath) + 1, &bytesWritten);
HANDLE thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibraryAAddr, (void *)hookBase, 0, NULL);
if(thread)
{
if(sync)
{
switch(WaitForSingleObject(thread, noTimeout ? INFINITE : 1000 * 60)) // timeout = one minute
{
case WAIT_OBJECT_0:
_MESSAGE("hook thread complete");
result = true;
break;
case WAIT_ABANDONED:
_ERROR("Process::InstallHook: waiting for thread = WAIT_ABANDONED");
break;
case WAIT_TIMEOUT:
_ERROR("Process::InstallHook: waiting for thread = WAIT_TIMEOUT");
break;
}
}
else
result = true;
CloseHandle(thread);
}
else
_ERROR("CreateRemoteThread failed (%d)", GetLastError());
VirtualFreeEx(process, (LPVOID)hookBase, 0, MEM_RELEASE);
}
else
_ERROR("Process::InstallHook: couldn't allocate memory in target process");
CloseHandle(process);
}
else
_ERROR("Process::InstallHook: couldn't get process handle");
return result;
}
// main hook
#if 0
#pragma pack (push, 1)
struct HookLayout
{
enum
{
kNumLibs = 16,
kMaxLibNameLen = MAX_PATH
};
struct DoLoadLibrary
{
UInt8 push; // 68
UInt32 strAddr; // address
UInt8 indCall1; // FF
UInt8 indCall2; // 15
UInt32 callAddr; // address
void Clear(void)
{
// nops
push = 0x90;
strAddr = 0x90909090;
indCall1 = 0x90;
indCall2 = 0x90;
callAddr = 0x90909090;
}
void Setup(UInt32 _strAddr, UInt32 _callAddr)
{
push = 0x68;
strAddr = _strAddr;
indCall1 = 0xFF;
indCall2 = 0x15;
callAddr = _callAddr;
}
};
// code (entry point)
UInt8 infLoop1; // EB
UInt8 infLoop2; // FF
DoLoadLibrary loadLib[kNumLibs];
UInt8 callMain1; // FF
UInt8 callMain2; // 25
UInt32 callMainAddr; // address
// data
char libNames[kMaxLibNameLen * kNumLibs];
UInt32 mainAddr;
void Init(ProcHookInfo * hookInfo)
{
#if 0
infLoop1 = 0xEB;
infLoop2 = 0xFE;
#else
infLoop1 = 0x90;
infLoop2 = 0x90;
#endif
for(UInt32 i = 0; i < kNumLibs; i++)
loadLib[i].Clear();
callMain1 = 0xFF;
callMain2 = 0x25;
callMainAddr = 0;
memset(libNames, 0, sizeof(libNames));
mainAddr = 0;
}
};
#pragma pack (pop, 1)
struct HookSetup
{
HookLayout m_data;
HANDLE m_proc;
UInt32 m_base;
UInt32 m_loadLib;
UInt32 m_libIdx;
UInt32 m_strOffset;
bool m_isInit;
HookSetup()
{
m_proc = NULL;
m_base = 0;
m_loadLib = 0;
m_libIdx = 0;
m_strOffset = 0;
m_isInit = false;
}
bool Init(PROCESS_INFORMATION * info, ProcHookInfo * hookInfo)
{
bool result = false;
if(m_isInit) return true;
m_loadLib = hookInfo->loadLibAddr;
UInt32 hookBaseAddr = hookInfo->hookCallAddr;
m_data.Init(hookInfo);
m_proc = OpenProcess(
PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, FALSE, info->dwProcessId);
if(m_proc)
{
m_base = (UInt32)VirtualAllocEx(m_proc, NULL, sizeof(m_data), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(m_base)
{
UInt32 hookBaseCallAddr;
size_t bytesTransferred = 0;
_MESSAGE("remote memory = %08X", m_base);
// update the call
if( ReadProcessMemory(m_proc, (void *)(hookBaseAddr + 1), &hookBaseCallAddr, sizeof(hookBaseCallAddr), &bytesTransferred) &&
(bytesTransferred == sizeof(hookBaseCallAddr)))
{
// adjust for relcall
hookBaseCallAddr += 5 + hookBaseAddr;
_MESSAGE("old winmain = %08X", hookBaseCallAddr);
m_data.mainAddr = hookBaseCallAddr;
m_data.callMainAddr = GetRemoteOffset(&m_data.mainAddr);
UInt32 newHookDst = m_base - hookBaseAddr - 5;
if( WriteProcessMemory(m_proc, (void *)(hookBaseAddr + 1), &newHookDst, sizeof(newHookDst), &bytesTransferred) &&
(bytesTransferred == sizeof(newHookDst)))
{
m_isInit = true;
result = true;
}
else
{
_ERROR("couldn't write memory (update winmain)");
}
}
else
{
_ERROR("couldn't read memory (update winmain)");
}
}
else
{
_ERROR("couldn't allocate memory in remote process");
}
}
else
{
_ERROR("couldn't open process");
}
return result;
}
bool AddLoadLibrary(const char * dllPath)
{
bool result = false;
if(m_libIdx < HookLayout::kNumLibs)
{
HookLayout::DoLoadLibrary * lib = &m_data.loadLib[m_libIdx];
char * strDst = &m_data.libNames[m_strOffset];
m_libIdx++;
#pragma warning (push)
#pragma warning (disable : 4996)
strcpy(strDst, dllPath);
#pragma warning (pop)
m_strOffset += strlen(dllPath) + 1;
lib->Setup(
GetRemoteOffset(strDst),
m_loadLib);
if(UpdateRemoteProc())
{
result = true;
}
}
return result;
}
UInt32 GetRemoteOffset(void * data)
{
return m_base + ((UInt32)data) - ((UInt32)&m_data);
}
bool UpdateRemoteProc(void)
{
size_t bytesTransferred;
return WriteProcessMemory(m_proc, (void *)m_base, &m_data, sizeof(m_data), &bytesTransferred) &&
(bytesTransferred == sizeof(m_data));
}
};
HookSetup g_hookData;
static bool DoInjectDLL(PROCESS_INFORMATION * info, const char * dllPath, ProcHookInfo * hookInfo)
{
bool result = false;
if(g_hookData.Init(info, hookInfo))
{
if(g_hookData.AddLoadLibrary(dllPath))
{
result = true;
}
else
{
_ERROR("couldn't add library to list");
}
}
else
{
_ERROR("couldn't init hook");
}
return result;
}
bool InjectDLL(PROCESS_INFORMATION * info, const char * dllPath, ProcHookInfo * hookInfo)
{
bool result = false;
// wrap DLL injection in SEH, if it crashes print a message
__try {
result = DoInjectDLL(info, dllPath, hookInfo);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
PrintLoaderError("DLL injection failed. In most cases, this is caused by an overly paranoid software firewall or antivirus package. Disabling either of these may solve the problem.");
result = false;
}
return result;
}
#else
bool InjectDLL(PROCESS_INFORMATION * info, const char * dllPath, ProcHookInfo * hookInfo)
{
// ### this needs to be updated for x64
return false;
}
#endif