Skip to content

Commit

Permalink
Launcher/Loader: don't assume a base of 0x400000
Browse files Browse the repository at this point in the history
  • Loading branch information
32th-System committed Sep 5, 2023
1 parent 70e1a75 commit dd85655
Show file tree
Hide file tree
Showing 8 changed files with 301 additions and 31 deletions.
2 changes: 1 addition & 1 deletion thprac/src/thprac/thprac_init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ void RemoteInit()
{
if (((int)&__ImageBase) != ((int)GetModuleHandleW(nullptr))) {
ExeSig exeSig;
if (GetExeInfoEx((size_t)GetCurrentProcess(), exeSig)) {
if (GetExeInfoEx((size_t)GetCurrentProcess(), (uintptr_t)GetModuleHandleW(NULL), exeSig)) {
for (auto& gameDef : gGameDefs) {
if (gameDef.catagory != CAT_MAIN && gameDef.catagory != CAT_SPINOFF_STG) {
continue;
Expand Down
36 changes: 23 additions & 13 deletions thprac/src/thprac/thprac_launcher_games.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,22 @@ bool GetExeInfo(void* exeBuffer, size_t exeSize, ExeSig& exeSigOut)

return true;
}
bool GetExeInfoEx(size_t process, ExeSig& exeSigOut)
bool GetExeInfoEx(uintptr_t hProcess, uintptr_t base, ExeSig& exeSigOut)
{
HANDLE hProc = (HANDLE)process;
DWORD bytesRead;
HANDLE hProc = (HANDLE)hProcess;

IMAGE_DOS_HEADER dosHeader;
if (!ReadProcessMemory(hProc, (void*)0x400000, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead)) {
if (!ReadProcessMemory(hProc, (void*)base, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead)) {
return false;
}
IMAGE_NT_HEADERS ntHeader;
if (!ReadProcessMemory(hProc, (void*)(0x400000 + dosHeader.e_lfanew), &ntHeader, sizeof(IMAGE_NT_HEADERS), &bytesRead)) {
if (!ReadProcessMemory(hProc, (void*)(base + dosHeader.e_lfanew), &ntHeader, sizeof(IMAGE_NT_HEADERS), &bytesRead)) {
return false;
}

exeSigOut.timeStamp = ntHeader.FileHeader.TimeDateStamp;
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((ULONG_PTR)(0x400000 + dosHeader.e_lfanew) + ((LONG)(LONG_PTR) & (((IMAGE_NT_HEADERS*)0)->OptionalHeader)) + ntHeader.FileHeader.SizeOfOptionalHeader);
PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((ULONG_PTR)(base + dosHeader.e_lfanew) + ((LONG)(LONG_PTR) & (((IMAGE_NT_HEADERS*)0)->OptionalHeader)) + ntHeader.FileHeader.SizeOfOptionalHeader);
for (int i = 0; i < ntHeader.FileHeader.NumberOfSections; i++, pSection++) {
IMAGE_SECTION_HEADER section;
if (!ReadProcessMemory(hProc, (void*)(pSection), &section, sizeof(IMAGE_SECTION_HEADER), &bytesRead)) {
Expand Down Expand Up @@ -1524,7 +1524,7 @@ class THGameGui {

return THPrac::LoadSelf(process, extraData, extraSize);
}
static bool WINAPI CheckProcessOmni(PROCESSENTRY32W& proc)
static bool WINAPI CheckProcessOmni(PROCESSENTRY32W& proc, uintptr_t& base)
{
if (wcscmp(L"東方紅魔郷.exe", proc.szExeFile)) {
if (proc.szExeFile[0] != L't' || proc.szExeFile[1] != L'h')
Expand All @@ -1543,23 +1543,29 @@ class THGameGui {
if (!hProc)
return false;

base = GetGameModuleBase(hProc);
if (!base) {
return false;
}

// Check THPrac signature
DWORD sigAddr = 0;
DWORD sigCheck = 0;
DWORD bytesReadRPM;
ReadProcessMemory(hProc, (void*)0x40003c, &sigAddr, 4, &bytesReadRPM);

ReadProcessMemory(hProc, (void*)(base + 0x3c), &sigAddr, 4, &bytesReadRPM);
if (bytesReadRPM != 4 || !sigAddr) {
CloseHandle(hProc);
return false;
}
ReadProcessMemory(hProc, (void*)(0x400000 + sigAddr - 4), &sigCheck, 4, &bytesReadRPM);
ReadProcessMemory(hProc, (void*)(base + sigAddr - 4), &sigCheck, 4, &bytesReadRPM);
if (bytesReadRPM != 4 || sigCheck) {
CloseHandle(hProc);
return false;
}

ExeSig sig;
if (GetExeInfoEx((size_t)hProc, sig)) {
if (GetExeInfoEx((size_t)hProc, base, sig)) {
for (auto& gameDef : gGameDefs) {
if (gameDef.catagory != CAT_MAIN && gameDef.catagory != CAT_SPINOFF_STG) {
continue;
Expand All @@ -1573,7 +1579,7 @@ class THGameGui {
CloseHandle(hProc);
return false;
}
static DWORD WINAPI CheckProcess(DWORD process, std::wstring& exePath)
static DWORD WINAPI CheckProcess(DWORD process, std::wstring& exePath, uintptr_t& base)
{
// TODO: THPRAC SIG CHECK & EXE TIME STAMP CHECK
int result = 0;
Expand All @@ -1596,6 +1602,7 @@ class THGameGui {
modulePath = GetUnifiedPath(std::wstring(me32.szExePath));
if (exePathU16 == modulePath) {
result = 1;
base = (uintptr_t)me32.modBaseAddr;
break;
}
}
Expand Down Expand Up @@ -1646,14 +1653,15 @@ class THGameGui {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32FirstW(snapshot, &procEntry)) {
do {
bool test = isOmni ? CheckProcessOmni(procEntry) : CheckProcess(procEntry.th32ProcessID, exePath);
uintptr_t base;
bool test = isOmni ? CheckProcessOmni(procEntry, base) : CheckProcess(procEntry.th32ProcessID, exePath, base);
if (test) {
auto hProc = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE,
procEntry.th32ProcessID);
if (hProc) {
auto result = (WriteTHPracSig(hProc) && LocalApplyTHPrac(hProc));
auto result = (WriteTHPracSig(hProc, base) && LocalApplyTHPrac(hProc));
CloseHandle(hProc);
return result ? 1 : 0;
}
Expand Down Expand Up @@ -1721,6 +1729,8 @@ class THGameGui {
startup_info.cb = sizeof(STARTUPINFOW);
CreateProcessW(currentInstPath.c_str(), nullptr, nullptr, nullptr, false, CREATE_SUSPENDED, nullptr, currentInstDir.c_str(), &startup_info, &proc_info);

uintptr_t base = GetGameModuleBase(proc_info.hProcess);

if (currentInst.useVpatch) {
auto exeName = GetNameFromFullPath(currentInstPath);
if (exeName == L"東方紅魔郷.exe") {
Expand All @@ -1734,7 +1744,7 @@ class THGameGui {
}
}
if (currentInst.useTHPrac) {
result = (WriteTHPracSig(proc_info.hProcess) && LocalApplyTHPrac(proc_info.hProcess));
result = (WriteTHPracSig(proc_info.hProcess, base) && LocalApplyTHPrac(proc_info.hProcess));
}

if (!result) {
Expand Down
2 changes: 1 addition & 1 deletion thprac/src/thprac/thprac_launcher_games.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
namespace THPrac {

bool GetExeInfo(void* exeBuffer, size_t exeSize, ExeSig& exeSigOut);
bool GetExeInfoEx(size_t process, ExeSig& exeSigOut);
bool GetExeInfoEx(uintptr_t hProcess, uintptr_t base, ExeSig& exeSigOut);

bool LauncherGamesGuiUpd();
void LauncherGamesGuiSwitch(const char* idStr);
Expand Down
54 changes: 39 additions & 15 deletions thprac/src/thprac/thprac_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
#include "thprac_launcher_cfg.h"
#include "thprac_load_exe.h"
#include "thprac_utils.h"
#include "utils/wininternal.h"
#include <Windows.h>
#include <algorithm>
#include <metrohash128.h>
#include <psapi.h>
#include <string>
#include <tlhelp32.h>

#pragma comment(lib, "psapi.lib")


namespace THPrac {
enum thprac_prompt_t {
PR_FAILED,
Expand Down Expand Up @@ -103,7 +101,26 @@ bool PromptUser(thprac_prompt_t info, THGameSig* gameSig = nullptr)
return false;
}

THGameSig* CheckOngoingGame(PROCESSENTRY32W& proc)
uintptr_t GetGameModuleBase(HANDLE hProc) {
static decltype(NtQueryInformationProcess)* _NtQueryInformationProcess;

if (!_NtQueryInformationProcess) {
_NtQueryInformationProcess = (decltype(NtQueryInformationProcess)*)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtQueryInformationProcess");
}

PROCESS_BASIC_INFORMATION pbi;
_NtQueryInformationProcess(hProc, ProcessBasicInformation, &pbi, sizeof(pbi), nullptr);

LPVOID based = (LPVOID)((uintptr_t)pbi.PebBaseAddress + offsetof(PEB, ImageBaseAddress));

uintptr_t ret = 0;
DWORD byteRet;
ReadProcessMemory(hProc, based, &ret, sizeof(ret), &byteRet);

return ret;
}

THGameSig* CheckOngoingGame(PROCESSENTRY32W& proc, uintptr_t& base)
{
// Eliminate impossible process
if ( wcscmp(L"東方紅魔郷.exe", proc.szExeFile) && wcscmp(L"alcostg.exe", proc.szExeFile)) {
Expand Down Expand Up @@ -135,23 +152,28 @@ THGameSig* CheckOngoingGame(PROCESSENTRY32W& proc)
if (!hProc)
return nullptr;

base = GetGameModuleBase(hProc);
if (!base) {
return nullptr;
}

// Check THPrac signature
DWORD sigAddr = 0;
DWORD sigCheck = 0;
DWORD bytesReadRPM;
ReadProcessMemory(hProc, (void*)0x40003c, &sigAddr, 4, &bytesReadRPM);
ReadProcessMemory(hProc, (void*)(base + 0x3c), &sigAddr, 4, &bytesReadRPM);
if (bytesReadRPM != 4 || !sigAddr) {
CloseHandle(hProc);
return nullptr;
}
ReadProcessMemory(hProc, (void*)(0x400000 + sigAddr - 4), &sigCheck, 4, &bytesReadRPM);
ReadProcessMemory(hProc, (void*)(base + sigAddr - 4), &sigCheck, 4, &bytesReadRPM);
if (bytesReadRPM != 4 || sigCheck) {
CloseHandle(hProc);
return nullptr;
}

ExeSig sig;
if (GetExeInfoEx((size_t)hProc, sig)) {
if (GetExeInfoEx((size_t)hProc, base, sig)) {
for (auto& gameDef : gGameDefs) {
if (gameDef.catagory != CAT_MAIN && gameDef.catagory != CAT_SPINOFF_STG) {
continue;
Expand All @@ -166,14 +188,14 @@ THGameSig* CheckOngoingGame(PROCESSENTRY32W& proc)
return nullptr;
}

bool WriteTHPracSig(HANDLE hProc)
bool WriteTHPracSig(HANDLE hProc, uintptr_t base)
{
DWORD sigAddr = 0;
DWORD bytesReadRPM;
ReadProcessMemory(hProc, (void*)0x40003c, &sigAddr, 4, &bytesReadRPM);
ReadProcessMemory(hProc, (void*)(base + 0x3c), &sigAddr, 4, &bytesReadRPM);
if (bytesReadRPM != 4 || !sigAddr)
return false;
sigAddr += 0x400000;
sigAddr += base;
sigAddr -= 4;

constexpr DWORD thpracSig = 'CARP';
Expand All @@ -189,7 +211,7 @@ bool WriteTHPracSig(HANDLE hProc)
return true;
}

bool ApplyTHPracToProc(PROCESSENTRY32W& proc)
bool ApplyTHPracToProc(PROCESSENTRY32W& proc, uintptr_t base)
{
// Open the related process
auto hProc = OpenProcess(
Expand All @@ -200,7 +222,7 @@ bool ApplyTHPracToProc(PROCESSENTRY32W& proc)
if (!hProc)
return false;

auto result = (WriteTHPracSig(hProc) && THPrac::LoadSelf(hProc));
auto result = (WriteTHPracSig(hProc, base) && THPrac::LoadSelf(hProc));
CloseHandle(hProc);

return result;
Expand Down Expand Up @@ -257,6 +279,7 @@ bool RunGameWithTHPrac(THGameSig& gameSig, std::wstring& name)
memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
CreateProcessW(name.c_str(), nullptr, nullptr, nullptr, false, CREATE_SUSPENDED, nullptr, nullptr, &startup_info, &proc_info);
uintptr_t base = GetGameModuleBase(proc_info.hProcess);

if (isVpatchValid) {
auto vpNameLength = (wcslen(gameSig.vPatchStr) + 1) * sizeof(wchar_t);
Expand All @@ -269,7 +292,7 @@ bool RunGameWithTHPrac(THGameSig& gameSig, std::wstring& name)
}
}

auto result = (WriteTHPracSig(proc_info.hProcess) && THPrac::LoadSelf(proc_info.hProcess));
auto result = (WriteTHPracSig(proc_info.hProcess, base) && THPrac::LoadSelf(proc_info.hProcess));
if (!result)
TerminateThread(proc_info.hThread, ERROR_FUNCTION_FAILED);
else
Expand All @@ -291,11 +314,12 @@ bool FindOngoingGame(bool prompt)
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (Process32FirstW(snapshot, &entry)) {
do {
gameSig = CheckOngoingGame(entry);
uintptr_t base;
gameSig = CheckOngoingGame(entry, base);
if (gameSig) {
hasPrompted = true;
if (PromptUser(PR_ASK_IF_ATTACH, gameSig)) {
if (ApplyTHPracToProc(entry)) {
if (ApplyTHPracToProc(entry, base)) {
PromptUser(PR_INFO_ATTACHED);
CloseHandle(snapshot);
return true;
Expand Down
3 changes: 2 additions & 1 deletion thprac/src/thprac/thprac_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
#endif

namespace THPrac {
uintptr_t GetGameModuleBase(HANDLE hProc);
bool CheckIfAnyGame();
bool WriteTHPracSig(HANDLE hProc);
bool WriteTHPracSig(HANDLE hProc, uintptr_t base);
bool FindOngoingGame(bool prompt = false);
bool FindAndRunGame(bool prompt = false);
}
Loading

0 comments on commit dd85655

Please sign in to comment.