Skip to content

Commit

Permalink
Removed MinHook dependency. Implemented IAT redirection as the hookin…
Browse files Browse the repository at this point in the history
…g method to alleviate issues with OBS, Fraps, etc.
  • Loading branch information
networkMe committed Sep 23, 2015
1 parent 79ad316 commit 686ccbf
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 18 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ endif()
set(CMAKE_SHARED_MODULE_PREFIX "")
set(DLL_SOURCE_FILES
src/dll/DllMain.cpp
src/dll/IATHook.h src/dll/IATHook.cpp
src/dll/GLStructs.h src/dll/GLStructs.cpp
src/dll/GDISwapBuffers.h src/dll/GDISwapBuffers.cpp

Expand All @@ -44,7 +45,6 @@ add_library(MissingHUD2Hook MODULE ${DLL_SOURCE_FILES})
set_target_properties(MissingHUD2Hook PROPERTIES LINK_FLAGS "-static")
target_compile_definitions(MissingHUD2Hook PRIVATE "-DGLEW_STATIC")
target_link_libraries(MissingHUD2Hook
MinHook
glew32
soil2
OpenGL32
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Missing HUD 2 is an Open-GL powered informational overlay for the Binding of Isa
The developers of Rebirth (Edmund McMillen, Nicalis) decided that one of their design decisions for the game would be to hide raw player statistics from the player as to not to overwhelm them.
This project attempts to give the player the choice to see their raw statistics if they choose to.

![Image of MissingHUD2](https://raw.githubusercontent.com/networkMe/missinghud2/master/doc/isaac-mhud2-example.jpg)
![Image of MissingHUD2](https://raw.githubusercontent.com/networkMe/missinghud2/master/doc/isaac-mhud2-example-dwd.jpg)

## Using
Missing HUD 2 aims to be nearly transparent to the user (and to Rebirth itself).
Expand All @@ -14,6 +14,9 @@ Note: The HUD only appears if you are in an active run.

If you wish to no longer see the HUD, just close the main executable and the HUD will disappear (the DLL will be unloaded).

The latest binary release can be found here:
https://github.com/networkMe/missinghud2/releases/latest

## Current features
* Works in fullscreen and windowed mode (as it's a direct OpenGL implementation)
* Show statistics HUD on the left of the Rebirth viewport
Expand All @@ -32,7 +35,6 @@ Missing HUD 2 has the below dependencies:
* [Qt5](http://www.qt.io/) (static version directory set manually in CMake)
* [EasyLogging++](https://github.com/easylogging/easyloggingpp)
2. Injected DLL
* [MinHook](https://github.com/TsudaKageyu/minhook)
* [GLEW](https://github.com/nigels-com/glew)
* [SOIL2](https://bitbucket.org/SpartanJ/soil2)

Expand Down
Binary file added doc/isaac-mhud2-example-dwd.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed doc/isaac-mhud2-example.jpg
Binary file not shown.
21 changes: 8 additions & 13 deletions src/dll/DllMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
#include <thread>

#include <windows.h>
#include <MinHook.h>

#include "IATHook.h"
#include "RebirthMemReader.h"
#include "GDISwapBuffers.h"
#include "ResourceLoader.h"
Expand All @@ -39,21 +39,17 @@ extern "C" DLL_PUBLIC void MHUD2_Start()
{
OutputDebugString("[MHUD2_Start] MissingHUD2 injected and starting.");

// Initialize MinHook library
if (MH_Initialize() != MH_OK)
FreeLibraryAndExitThread(dll_handle, EXIT_FAILURE);

// Initialize any static objects we require that don't involve an OpenGL context
ResourceLoader::Initialize(dll_handle);
RebirthMemReader::GetMemoryReader();

// Hook the OpenGL SwapBuffers function
// Hook the OpenGL SwapBuffers function via IAT redirection
GDISwapBuffers *gdi_swapbuffers = GDISwapBuffers::GetInstance();
if (MH_CreateHook(gdi_swapbuffers->GetGDI32HookAddr(), (LPVOID)&gdiSwapBuffersDetour, (LPVOID*)gdi_swapbuffers->GetEndpointAddr()) != MH_OK)
if (!IATHook::InitIATHook(GetModuleHandle(ISAAC_MODULE_NAME), "GDI32.dll", "SwapBuffers", (LPVOID)&gdiSwapBuffersDetour))
FreeLibraryAndExitThread(dll_handle, EXIT_FAILURE);

// Enable the hooks
if (MH_EnableHook(gdi_swapbuffers->GetGDI32HookAddr()) != MH_OK)
// Enable the IAT hooks
if (!IATHook::EnableIATHook("SwapBuffers", (LPVOID*)gdi_swapbuffers->GetEndpointAddr()))
FreeLibraryAndExitThread(dll_handle, EXIT_FAILURE);
}

Expand All @@ -65,8 +61,8 @@ extern "C" DLL_PUBLIC void MHUD2_Stop()
GDISwapBuffers *gdi_swapbuffers = GDISwapBuffers::GetInstance();
gdi_swapbuffers->WaitForCleanup();

// Disable MinHook hooks
MH_DisableHook(gdi_swapbuffers->GetGDI32HookAddr());
// Disable IAT hooks
IATHook::DisableIATHook("SwapBuffers");

// Wait for the hooks to be no longer active
// aka. for Rebirth to exit our detours for the last time
Expand All @@ -77,8 +73,7 @@ extern "C" DLL_PUBLIC void MHUD2_Stop()
ResourceLoader::Destroy();
RebirthMemReader::Destroy();

// Destroy MinHook and exit our DLL, Rebirth should be a lot less informative now!
MH_Uninitialize();
// Exit our DLL, Rebirth should be a lot less informative now!
FreeLibraryAndExitThread(dll_handle, EXIT_SUCCESS);
}

Expand Down
2 changes: 1 addition & 1 deletion src/dll/GDISwapBuffers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void GDISwapBuffers::WaitForCleanup()
{
std::unique_lock<std::mutex> uni_lock(cleanup_mutex);
cleanup_resources_ = true;
cleanup_complete_cv_.wait(uni_lock);
cleanup_complete_cv_.wait_for(uni_lock, std::chrono::seconds(5));
};

LPVOID GDISwapBuffers::GetGDI32HookAddr()
Expand Down
127 changes: 127 additions & 0 deletions src/dll/IATHook.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// Copyright 2015 Trevor Meehl
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "IATHook.h"

std::map<std::string, IATHookInfo> IATHook::iat_hooks_;

bool IATHook::InitIATHook(HMODULE app_module, std::string target_module, std::string proc_name, LPVOID detour_proc)
{
// Get necessary PE information
IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER*)app_module;

MEMORY_BASIC_INFORMATION module_mem = { 0 };
if (!VirtualQuery((LPVOID)app_module, &module_mem, sizeof(module_mem)))
return false;

IMAGE_NT_HEADERS *pe_header = (IMAGE_NT_HEADERS*)((DWORD)dos_header->e_lfanew + (DWORD)module_mem.AllocationBase);
if (dos_header->e_magic != IMAGE_DOS_SIGNATURE || pe_header->Signature != IMAGE_NT_SIGNATURE)
return false;

IMAGE_IMPORT_DESCRIPTOR *import_descriptor = (IMAGE_IMPORT_DESCRIPTOR *)((DWORD)pe_header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (DWORD)module_mem.AllocationBase);
if (!(import_descriptor->Name))
return false;

while (import_descriptor->Name)
{
const char* imported_dll_name = (const char*)((DWORD)import_descriptor->Name + (DWORD)module_mem.AllocationBase);
OutputDebugString(imported_dll_name);

if (std::string(imported_dll_name) == target_module)
{
IMAGE_THUNK_DATA *thunk = (IMAGE_THUNK_DATA*)((DWORD)import_descriptor->FirstThunk + (DWORD)module_mem.AllocationBase);
IMAGE_THUNK_DATA *orig_thunk = (IMAGE_THUNK_DATA*)((DWORD)import_descriptor->OriginalFirstThunk + (DWORD)module_mem.AllocationBase);

for(; orig_thunk->u1.Function != 0; ++orig_thunk, ++thunk)
{
if (orig_thunk->u1.Ordinal & IMAGE_ORDINAL_FLAG) // ignore ordinal imports
continue;

IMAGE_IMPORT_BY_NAME *import_via_name = (IMAGE_IMPORT_BY_NAME*)((DWORD)orig_thunk->u1.AddressOfData + (DWORD)module_mem.AllocationBase);
const char* import_proc_name = (const char*)(import_via_name->Name);
if (std::string(import_proc_name) == proc_name)
{
// Found the proc to hook
IATHookInfo iat_hook_info;
iat_hook_info.app_module = app_module;
iat_hook_info.target_module = target_module;
iat_hook_info.proc_name = proc_name;
iat_hook_info.detour_proc = detour_proc;
iat_hook_info.orig_proc_addr = (LPVOID)thunk->u1.Function;
iat_hook_info.thunk = thunk;
iat_hooks_.insert(std::make_pair(proc_name, iat_hook_info));

return true;
}
}

// We didn't find the proc to hook
return false;
}

++import_descriptor;
}

// We didn't find the DLL to hook :(
return false;
}

bool IATHook::EnableIATHook(std::string proc_name, LPVOID *orig_proc_addr)
{
if (iat_hooks_.count(proc_name) == 0)
return false;

IATHookInfo iathook_info = iat_hooks_[proc_name];

// Make the import memory page writable
MEMORY_BASIC_INFORMATION thunk_mem_info;
if (!VirtualQuery(iathook_info.thunk, &thunk_mem_info, sizeof(MEMORY_BASIC_INFORMATION)))
return false;
if (!VirtualProtect(thunk_mem_info.BaseAddress, thunk_mem_info.RegionSize, PAGE_READWRITE, &thunk_mem_info.Protect))
return false;

// Replace the IAT function pointer
*(orig_proc_addr) = iathook_info.orig_proc_addr;
iathook_info.thunk->u1.Function = (DWORD)iathook_info.detour_proc;

// Restore the import memory page permissions
DWORD dummy;
VirtualProtect(thunk_mem_info.BaseAddress, thunk_mem_info.RegionSize, thunk_mem_info.Protect, &dummy);

return true;
}

bool IATHook::DisableIATHook(std::string proc_name)
{
if (iat_hooks_.count(proc_name) == 0)
return false;

IATHookInfo iathook_info = iat_hooks_[proc_name];

// Make the import memory page writable
MEMORY_BASIC_INFORMATION thunk_mem_info;
if (!VirtualQuery(iathook_info.thunk, &thunk_mem_info, sizeof(MEMORY_BASIC_INFORMATION)))
return false;
if (!VirtualProtect(thunk_mem_info.BaseAddress, thunk_mem_info.RegionSize, PAGE_READWRITE, &thunk_mem_info.Protect))
return false;

// Replace the IAT function pointer with the old function
iathook_info.thunk->u1.Function = (DWORD)iathook_info.orig_proc_addr;

// Restore the import memory page permissions
DWORD dummy;
VirtualProtect(thunk_mem_info.BaseAddress, thunk_mem_info.RegionSize, thunk_mem_info.Protect, &dummy);

return true;
}
49 changes: 49 additions & 0 deletions src/dll/IATHook.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2015 Trevor Meehl
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MISSINGHUD2_IATHOOK_H
#define MISSINGHUD2_IATHOOK_H

#include <string>
#include <map>

#include <windows.h>
#include <winnt.h>

struct IATHookInfo
{
bool hook_enabled = false;

HMODULE app_module = 0;
std::string target_module;
std::string proc_name;
LPVOID detour_proc = nullptr;
LPVOID orig_proc_addr = nullptr;

IMAGE_THUNK_DATA *thunk = nullptr;
};

class IATHook
{
public:
static bool InitIATHook(HMODULE app_module, std::string target_module, std::string proc_name, LPVOID detour_proc);

static bool EnableIATHook(std::string proc_name, LPVOID *orig_proc_addr);
static bool DisableIATHook(std::string proc_name);

private:
static std::map<std::string, IATHookInfo> iat_hooks_;
};

#endif //MISSINGHUD2_IATHOOK_H
2 changes: 1 addition & 1 deletion src/dll/RebirthMemReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ float RebirthMemReader::GetDealWithDevilChance()
return 0.0f;

int current_floor = *((int*)player_manager_inst);
if (current_floor == 7 || current_floor > 8) // In-eligible for natural DWD on these floors (even with Goat Head)
if (current_floor == 1 || current_floor > 8) // In-eligible for natural DWD on these floors (even with Goat Head)
return 0.0f;

DWORD player = GetPlayerMemAddr();
Expand Down

0 comments on commit 686ccbf

Please sign in to comment.