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

149 lines
4.6 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;
}
/*
48:83EC 28 | sub rsp, 28 |
48:8BD9 | mov rbx,rcx | save to non-volatile register
48:8D4B 10 | lea rcx,qword ptr ds:[rbx+10] | offsetof(InjectDLLThreadData, dllPath)
FF13 | call qword ptr ds:[rbx] | offsetof(InjectDLLThreadData, loadLibraryA)
48:8BC8 | mov rcx,rax |
BA 01000000 | mov edx,1 |
FF53 08 | call qword ptr ds:[rbx+8] | offsetof(InjectDLLThreadData, getProcAddress)
FFD0 | call rax |
48:83C4 28 | add rsp, 28 |
C3 | ret |
*/
struct InjectDLLThreadData
{
InjectDLLThreadData(uintptr_t _loadLibraryA, uintptr_t _getProcAddress, const char * _dllPath)
{
memset(this, 0, sizeof(*this));
loadLibraryA = _loadLibraryA;
getProcAddress = _getProcAddress;
strcpy_s(dllPath, sizeof(dllPath), _dllPath);
static const UInt8 kCode[] =
{
0x48, 0x83, 0xEC, 0x28,
0x48, 0x8B, 0xD9,
0x48, 0x8D, 0x4B, 0x10,
0xFF, 0x13,
0x48, 0x8B, 0xC8,
0xBA, 0x01, 0x00, 0x00, 0x00,
0xFF, 0x53, 0x08,
0xFF, 0xD0,
0x48, 0x83, 0xC4, 0x28,
0xC3,
};
memcpy(code, kCode, sizeof(kCode));
}
uintptr_t loadLibraryA; // 00
uintptr_t getProcAddress; // 08
char dllPath[2048]; // 10
UInt8 code[128]; // 810
};
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");
uintptr_t getProcAddressAddr = (uintptr_t)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProcAddress");
_MESSAGE("hookBase = %016I64X", hookBase);
_MESSAGE("loadLibraryAAddr = %016I64X", loadLibraryAAddr);
_MESSAGE("getProcAddressAddr = %016I64X", getProcAddressAddr);
InjectDLLThreadData data(loadLibraryAAddr, getProcAddressAddr, dllPath);
size_t bytesWritten;
WriteProcessMemory(process, (LPVOID)hookBase, (void *)&data, sizeof(data), &bytesWritten);
auto * remoteData = (InjectDLLThreadData *)hookBase;
HANDLE thread = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)(&remoteData->code), (void *)remoteData, 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;
}