Skip to content

Commit

Permalink
fix(loader): handle unexpected loading order
Browse files Browse the repository at this point in the history
  • Loading branch information
Andoryuuta committed Nov 27, 2022
1 parent 8a40994 commit dcd82c6
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 16 deletions.
6 changes: 4 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required (VERSION 3.8)

project(MHS2Loader VERSION "0.0.1")
project(MHS2Loader VERSION "4.0.0")

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Expand All @@ -11,7 +11,7 @@ option(LOADER_NEXUS_CHECK "A check only for the nexus version to future-proof ag

add_library(dinput8 SHARED
"src/dinput_proxy/dinput8_proxy.cpp"
)
)

add_library(LoaderCore SHARED
"src/loader/entry.cpp"
Expand All @@ -25,6 +25,8 @@ add_library(LoaderCore SHARED
"src/loader/Plugin.hpp"
"src/loader/PluginConfig.hpp"
"src/loader/PluginConfig.cpp"
#"src/loader/Win32Internals.hpp"
#"src/loader/Win32Internals.cpp"
)
target_include_directories (LoaderCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_include_directories (LoaderCore PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/extern)
Expand Down
1 change: 1 addition & 0 deletions src/dinput_proxy/dinput8_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ extern "C" {

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
//MessageBoxA(NULL, "Paused pre-graphics initialization.\nInject RenderDoc then press OK.", "MHS2Loader renderdoc pause", MB_OK);
LoadLibraryA("LoaderCore.dll");
}

Expand Down
41 changes: 41 additions & 0 deletions src/loader/Win32Internals.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "Win32Internals.hpp"

#define ThreadQuerySetWin32StartAddress 9
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)

typedef NTSTATUS(WINAPI* pNtQueryInformationThread)(
HANDLE ThreadHandle,
LONG ThreadInformationClass,
PVOID ThreadInformation,
ULONG ThreadInformationLength,
PULONG ReturnLength
);

namespace Win32Internals {
uint64_t GetThreadStartAddress(uint64_t thread_id) {
// Resolve the internal NtQueryInformationThread function (non-public).
pNtQueryInformationThread NtQueryInformationThread = (pNtQueryInformationThread)GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationThread");
if (NtQueryInformationThread == NULL) {
return 0;
}

// Get a duplicate thread query handle.
HANDLE hCurrentProcess = GetCurrentProcess();
HANDLE hThreadQueryHandle = (HANDLE)0;
if (!DuplicateHandle(hCurrentProcess, (HANDLE)thread_id, hCurrentProcess, &hThreadQueryHandle, THREAD_QUERY_INFORMATION, FALSE, 0)) {
SetLastError(ERROR_ACCESS_DENIED);
return 0;
}

// Query for the thread entry point address (ThreadQuerySetWin32StartAddress).
DWORD_PTR dwStartAddress;
NTSTATUS ntStatus = NtQueryInformationThread(hThreadQueryHandle, ThreadQuerySetWin32StartAddress, &dwStartAddress, sizeof(DWORD_PTR), NULL);
CloseHandle(hThreadQueryHandle);

if (ntStatus != STATUS_SUCCESS) {
return 0;
}

return (uint64_t)dwStartAddress;
}
}
7 changes: 7 additions & 0 deletions src/loader/Win32Internals.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once
#include <cstdint>
#include <Windows.h>

namespace Win32Internals {
uint64_t GetThreadStartAddress(uint64_t thread_id);
}
78 changes: 64 additions & 14 deletions src/loader/entry.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <intrin.h>
#include <psapi.h>
#include <iostream>
#include <cstdio>
#include <fstream>
Expand All @@ -17,12 +19,17 @@
#include "Log.hpp"
#include "PluginManager.hpp"
#include "SigScan.hpp"
#include "Win32Internals.hpp"

#define VERSION_MESSAGE "MHS2Loader v3.0.0"
#pragma intrinsic(_ReturnAddress)

#define VERSION_MESSAGE "MHS2Loader v4.0.0"

std::mutex g_initialized_mutex;
bool g_initialized;

typedef void(*GetSystemTimeAsFileTime_t)(LPFILETIME lpSystemTimeAsFileTime);
GetSystemTimeAsFileTime_t fpGetSystemTimeAsFileTime = NULL;
typedef int(*main_t)(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd);
main_t fpMain = NULL;
typedef int64_t(*mainCRTStartup_t)();
Expand Down Expand Up @@ -132,7 +139,7 @@ int EnableCoreHooks() {
return 1;
}

void* mainStartAddr = (void*)SigScan::Scan(image_base, "48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B E9 41 8B F9 B9 0B 00 00 00");
void* mainStartAddr = (void*)SigScan::Scan(image_base, "48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 57 B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B E9 41 8B F9 B9 ?? ?? ?? ??");
if (mainStartAddr == nullptr) {
spdlog::get("PreLoader")->critical("Failed to scan for mainStartAddr");
return 1;
Expand Down Expand Up @@ -179,12 +186,6 @@ int EnableCoreHooks() {
void* sObserverManagerNoteRandAddr = (void*)0x140995990;
*/

if (MH_Initialize() != MH_OK)
{
spdlog::get("PreLoader")->error("Failed to initialize minhook!");
return 1;
}

if (MH_CreateHook(anticheatDispatchAddr, &hookedAnticheatDispatch, reinterpret_cast<LPVOID*>(&fpAnticheatDispatch)) != MH_OK) {
spdlog::get("PreLoader")->error("Failed to create anticheatDispatch hook");
return 1;
Expand Down Expand Up @@ -244,23 +245,72 @@ int EnableCoreHooks() {
return 1;
}

return 0;
}

// GetSystemTimeAsFileTime gets called pre-WinMain in the MSVCRT setup.
void hookedGetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
// Get return address of this hooked call.
uint64_t retAddress = (uint64_t)_ReturnAddress();

return 0;
// Get the start and end of the base process module (the main .exe).
MODULEINFO moduleInfo;
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &moduleInfo, sizeof(MODULEINFO));
uint64_t baseModStart = (uint64_t)moduleInfo.lpBaseOfDll;
uint64_t baseModEnd = (uint64_t)moduleInfo.lpBaseOfDll + (uint64_t)moduleInfo.lpBaseOfDll;

// If we are being called from within the module memory (e.g. not a random library),
// then we can assume this is likely the real code post-unpack.
if (retAddress >= baseModStart && retAddress <= baseModEnd) {
spdlog::get("PreLoader")->info("Got GetSystemTimeAsFileTime call with valid return address. Base module start: {0:x}, Base module end: {1:x}, GetSystemTimeAsFileTime Return address: {2:x}",
baseModStart,
baseModEnd,
retAddress);

// Enable our hooks and, if successful, disable the GetSystemTimeAsFileTime as we will not need it anymore.
if (EnableCoreHooks()) {
spdlog::get("PreLoader")->error("Failed to enable core hooks!");
return;
}

else {
spdlog::get("PreLoader")->info("Enabled core hooks");

if (MH_DisableHook(GetSystemTimeAsFileTime) != MH_OK) {
spdlog::get("PreLoader")->error("Failed to disable GetSystemTimeAsFileTime hook");
return;
}
}
}

fpGetSystemTimeAsFileTime(lpSystemTimeAsFileTime);
}

// This function is called from the loader-locked DllMain,
// does the bare-minimum to get control flow in the main thread
// by hooking the CRT startup and bypassing capcom's anti-tamper code.
// by hooking a function called in the CRT startup (GetSystemTimeAsFileTime) to
// bypass capcom's anti-tamper code early before any C++ static initalizers are called.
int LoaderLockedInitialize() {
Log::InitializePreLogger();

spdlog::get("PreLoader")->info("Begin enabling core hooks on thread: {0}", GetCurrentThreadId());
if (EnableCoreHooks()) {
spdlog::get("PreLoader")->error("Failed to enable core hooks!");
spdlog::get("PreLoader")->info("Begin enabling post-unpack hooks on thread: {0}", GetCurrentThreadId());

if (MH_Initialize() != MH_OK)
{
spdlog::get("PreLoader")->error("Failed to initialize minhook!");
return 1;
}

// Hook GetSystemTimeAsFileTime in order to get control of execution after the .exe is unpacked in memory.
if (MH_CreateHook(GetSystemTimeAsFileTime, &hookedGetSystemTimeAsFileTime, reinterpret_cast<LPVOID*>(&fpGetSystemTimeAsFileTime)) != MH_OK) {
spdlog::get("PreLoader")->error("Failed to create GetSystemTimeAsFileTime hook");
return 1;
}
spdlog::get("PreLoader")->info("Enabled core hooks");
if (MH_EnableHook(GetSystemTimeAsFileTime) != MH_OK) {
spdlog::get("PreLoader")->error("Failed to enable GetSystemTimeAsFileTime hook");
return 1;
}

return 0;
}

Expand Down

0 comments on commit dcd82c6

Please sign in to comment.