Skip to content

Commit

Permalink
Implemented a more dynamic way to find the necessary memory addresses…
Browse files Browse the repository at this point in the history
… in Rebirth (memory scanning).

Percentages display is also now possible.
  • Loading branch information
networkMe committed Sep 23, 2015
1 parent 6af1fb5 commit 3a73448
Show file tree
Hide file tree
Showing 8 changed files with 231 additions and 37 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ set(DLL_SOURCE_FILES
src/dll/GLStructs.h src/dll/GLStructs.cpp
src/dll/GDISwapBuffers.h src/dll/GDISwapBuffers.cpp

src/dll/RebirthMemSignatures.h
src/dll/RebirthMemReader.h src/dll/RebirthMemReader.cpp
src/dll/HUDOverlay.h src/dll/HUDOverlay.cpp
src/dll/HUDStat.h src/dll/HUDStat.cpp
Expand Down
4 changes: 2 additions & 2 deletions src/BoIProcess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ bool BoIProcess::HookBoIProcess()
LoadLibraryIntoBoI(remote_mem, "MissingHUD2Hook.dll");

// Call "Start" method in the MissingHUD2Hook.dll
FARPROC start_addr = GetRemoteProcAddress(injected_dll_, "MissingHUD2Hook.dll", "HUD2_Start");
FARPROC start_addr = GetRemoteProcAddress(injected_dll_, "MissingHUD2Hook.dll", "MHUD2_Start");
HANDLE rThread = CreateRemoteThread(process_, NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, NULL, 0, NULL);
if (rThread == NULL)
throw std::runtime_error("[HookBoIProcess] Couldn't execute Start in our MissingHUD2Hook.dll.");
Expand Down Expand Up @@ -134,7 +134,7 @@ bool BoIProcess::UnhookBoIProcess()
if (IsRunning())
{
// Call the "Stop" function on the injected DLL, it should clean everything up inside Isaac by itself
FARPROC stop_addr = GetRemoteProcAddress(injected_dll_, "MissingHUD2Hook.dll", "HUD2_Stop");
FARPROC stop_addr = GetRemoteProcAddress(injected_dll_, "MissingHUD2Hook.dll", "MHUD2_Stop");
HANDLE rem_thread = CreateRemoteThread(process_, NULL, 0, (LPTHREAD_START_ROUTINE)stop_addr, NULL, 0, NULL);
if (rem_thread == NULL)
throw std::runtime_error("[HookBoIProcess] Couldn't execute Start in our MissingHUD2Hook.dll.");
Expand Down
8 changes: 4 additions & 4 deletions src/dll/DllMain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ int APIENTRY DllMain(HMODULE h_dll, DWORD reason, LPVOID reserved)
return TRUE;
}

extern "C" DLL_PUBLIC void HUD2_Start()
extern "C" DLL_PUBLIC void MHUD2_Start()
{
OutputDebugString("[HUD2_Start] MissingHUD2 injected and starting.");
OutputDebugString("[MHUD2_Start] MissingHUD2 injected and starting.");

// Initialize MinHook library
if (MH_Initialize() != MH_OK)
Expand All @@ -57,9 +57,9 @@ extern "C" DLL_PUBLIC void HUD2_Start()
FreeLibraryAndExitThread(dll_handle, EXIT_FAILURE);
}

extern "C" DLL_PUBLIC void HUD2_Stop()
extern "C" DLL_PUBLIC void MHUD2_Stop()
{
OutputDebugString("[HUD2_Stop] MissingHUD2 exiting.");
OutputDebugString("[MHUD2_Stop] MissingHUD2 exiting.");

// Tell our SwapBuffers detour to cleanup it's OpenGL stuff
GDISwapBuffers *gdi_swapbuffers = GDISwapBuffers::GetInstance();
Expand Down
47 changes: 40 additions & 7 deletions src/dll/HUDStat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,55 @@ HUDStat::~HUDStat()
{
}

void HUDStat::Draw(glm::vec2 position, float stat_value)
void HUDStat::Draw(glm::vec2 position, float stat_value, bool percentage)
{
// Draw icon sprite
glm::vec2 icon_size(0.08f, 0.13f);
SpriteSheet *icon_sprite = SpriteSheet::GetSpriteSheet(MHUD2_STAT_ICONS);
icon_sprite->DrawSprite(position, icon_size, MHUD2STAT_STRING[hud_stat_]);

// Get value as string
std::stringstream val_stream;
val_stream.setf(std::ios::fixed, std::ios::floatfield);
val_stream.precision(1);
val_stream << stat_value;
// Draw statistic value
position.x += 0.075f;
position.y -= 0.0375f;
TextRenderer *isaac_text = TextRenderer::GetRenderer(MHUD2_ISAAC_FONT_PNG, MHUD2_ISAAC_FONT_CHARMAP);
isaac_text->RenderText(position, NumToStr(stat_value, percentage));
}

void HUDStat::Draw(glm::vec2 position, int stat_value)
{
// Draw icon sprite
glm::vec2 icon_size(0.08f, 0.13f);
SpriteSheet *icon_sprite = SpriteSheet::GetSpriteSheet(MHUD2_STAT_ICONS);
icon_sprite->DrawSprite(position, icon_size, MHUD2STAT_STRING[hud_stat_]);

// Draw statistic value
position.x += 0.075f;
position.y -= 0.0375f;
TextRenderer *isaac_text = TextRenderer::GetRenderer(MHUD2_ISAAC_FONT_PNG, MHUD2_ISAAC_FONT_CHARMAP);
isaac_text->RenderText(position, val_stream.str());
isaac_text->RenderText(position, NumToStr(stat_value));
}

std::string HUDStat::NumToStr(float number, bool percentage)
{
std::stringstream ss;

if (percentage)
{
ss << (number * 100) << "%";
}
else
{
ss.setf(std::ios::fixed, std::ios::floatfield);
ss.precision(1);
ss << number;
}

return ss.str();
}

std::string HUDStat::NumToStr(int number)
{
std::stringstream ss;
ss << number;
return ss.str();
}
6 changes: 5 additions & 1 deletion src/dll/HUDStat.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ class HUDStat
HUDStat(MHUDSTAT mhud_stat);
~HUDStat();

void Draw(glm::vec2 position, float stat_value);
void Draw(glm::vec2 position, float stat_value, bool percentage = false);
void Draw(glm::vec2 position, int stat_value);

std::string NumToStr(float number, bool percentage = false);
std::string NumToStr(int number);

private:
MHUDSTAT hud_stat_;
Expand Down
117 changes: 97 additions & 20 deletions src/dll/RebirthMemReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ void RebirthMemReader::Destroy()

RebirthMemReader::RebirthMemReader()
{
// Get the base address of our Isaac module
base_address_ = (DWORD)GetModuleHandle(ISAAC_MODULE_NAME);
GetRebirthModuleInfo();
}

RebirthMemReader::~RebirthMemReader()
Expand All @@ -45,20 +44,20 @@ RebirthMemReader::~RebirthMemReader()

bool RebirthMemReader::IsRunActive()
{
// To check wether a run is currently active I check how many player managers there are
// To check wether a run is currently active I check how many players there are
// (just like Rebirth actually does)
// When there are 0 player managers, then we are not in a run
DWORD player_manager_class = GetPlayerManagerClassMemAddr();
if (player_manager_class == 0)
// When there are 0 players, then we are not in a run
DWORD player_list = GetPlayerListMemAddr();
if (player_list == 0)
return false;

int num_players = (int)((*(DWORD*)(player_manager_class + sizeof(DWORD))) - (*(DWORD*)player_manager_class));
int num_players = (int)((*(DWORD*)(player_list + sizeof(DWORD))) - (*(DWORD*) player_list));
return (num_players > 0);
}

float RebirthMemReader::GetPlayerStatf(RebirthPlayerStat player_stat)
{
DWORD player_class = GetPlayerClassMemAddr();
DWORD player_class = GetPlayerMemAddr();
if (player_class == 0)
return 0.0f;

Expand All @@ -74,32 +73,110 @@ float RebirthMemReader::GetPlayerStatf(RebirthPlayerStat player_stat)

int RebirthMemReader::GetPlayerStati(RebirthPlayerStat player_stat)
{
DWORD player_class = GetPlayerClassMemAddr();
DWORD player_class = GetPlayerMemAddr();
if (player_class == 0)
return 0;

int stat_val = *((int*)(player_class + player_stat));
return stat_val;
}

DWORD RebirthMemReader::GetPlayerManagerClassMemAddr()
DWORD RebirthMemReader::GetPlayerManagerMemAddr()
{
DWORD player_manager_p_class = (base_address_ + 0x21A1F4);
DWORD player_manager_data_addr = *((DWORD*)(player_manager_p_class));
if (player_manager_data_addr == 0)
return 0;
DWORD player_manager_inst = *((DWORD*)(player_manager_inst_p_addr_));
if (player_manager_inst == 0)
return 0; // Player manager hasn't been initialized by Rebirth yet
else
return player_manager_data_addr + 0x8A2C;
return player_manager_inst;
}


DWORD RebirthMemReader::GetPlayerListMemAddr()
{
DWORD player_manager_inst = GetPlayerManagerMemAddr();
if (player_manager_inst == 0)
return 0;

if (player_manager_player_list_offset_ == 0)
return 0;

return (player_manager_inst + player_manager_player_list_offset_);
}

DWORD RebirthMemReader::GetPlayerClassMemAddr()
DWORD RebirthMemReader::GetPlayerMemAddr()
{
// Here we follow the Rebirth memory address chain to get
// the current player class associated with the current run
DWORD player_manager_class = GetPlayerManagerClassMemAddr();
if (player_manager_class == 0)
DWORD player_list = GetPlayerListMemAddr();
if (player_list == 0)
return 0;

DWORD player_p_class = *((DWORD*)player_manager_class);
return *((DWORD*)player_p_class);
DWORD player_p = *((DWORD*)player_list);
return *((DWORD*)player_p);
}

void RebirthMemReader::GetRebirthModuleInfo()
{
// Get the base address of the Rebirth module
DWORD module_handle = (DWORD)GetModuleHandle(ISAAC_MODULE_NAME);
MEMORY_BASIC_INFORMATION rebirth_mem = { 0 };
if (VirtualQuery((LPVOID)module_handle, &rebirth_mem, sizeof(rebirth_mem)) == 0)
return;

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

module_address_ = (DWORD)rebirth_mem.AllocationBase;
module_size_ = pe_header->OptionalHeader.SizeOfImage;

// Find the static address pointer of the Rebirth PlayerManager instance
std::vector<unsigned char> player_manager_inst_address_p_bytes_ = SearchMemForVal(PlayerManagerInstAddr);
if (player_manager_inst_address_p_bytes_.size() < 4)
return;
player_manager_inst_p_addr_ = *((DWORD*)player_manager_inst_address_p_bytes_.data()) + 4;

// Find the offset of the Players list relative to the PlayerManager instance
*((DWORD*)(PlayerManagerPlayerListOffset.signature + 2)) = player_manager_inst_p_addr_;
std::vector<unsigned char> player_manager_player_list_offset_bytes_ = SearchMemForVal(PlayerManagerPlayerListOffset);
if (player_manager_player_list_offset_bytes_.size() < 2)
return;
player_manager_player_list_offset_ = *((WORD*)player_manager_player_list_offset_bytes_.data());
}

std::vector<unsigned char> RebirthMemReader::SearchMemForVal(MemSig mem_sig)
{
std::vector<unsigned char> val_bytes;
int sig_len = strlen(mem_sig.search_mask);
unsigned char* p_search = (unsigned char*)module_address_;
unsigned char* p_search_end = (unsigned char*)(module_address_ + module_size_ - sig_len);

while (p_search < p_search_end)
{
int matching_bytes = 0;
for (int i = 0; i < sig_len; ++i)
{
if (mem_sig.search_mask[i] != '?' && mem_sig.search_mask[i] != 'v'
&& p_search[i] != mem_sig.signature[i])
break;
++matching_bytes;
}

if (matching_bytes == sig_len)
{
// Found the signature, grab the return value bytes
for (int i = 0; i < sig_len; ++i)
{
if (mem_sig.search_mask[i] == 'v')
{
val_bytes.push_back(p_search[i]);
}
}
}

++p_search;
}

return val_bytes;
}
20 changes: 17 additions & 3 deletions src/dll/RebirthMemReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
#ifndef MISSINGHUD2_REBIRTHMEMREADER_H
#define MISSINGHUD2_REBIRTHMEMREADER_H

#include <vector>
#include <sstream>

#include <windows.h>

#include "RebirthMemSignatures.h"

#define ISAAC_MODULE_NAME "isaac-ng.exe"

// These values are the offsets of the specific statistic from the core Player memory address
enum RebirthPlayerStat {
kSpeed = 0xCB4,
kRange = 0xBF4,
Expand All @@ -45,13 +49,23 @@ class RebirthMemReader
RebirthMemReader();
~RebirthMemReader();

DWORD GetPlayerManagerClassMemAddr();
DWORD GetPlayerClassMemAddr();
void GetRebirthModuleInfo();

// Note this function always returns the first signature it finds, so make sure it's unique
std::vector<unsigned char> SearchMemForVal(MemSig mem_sig);

DWORD GetPlayerManagerMemAddr();
DWORD GetPlayerListMemAddr();
DWORD GetPlayerMemAddr();

private:
static RebirthMemReader* mem_reader_;

DWORD base_address_ = 0;
DWORD module_address_ = 0;
DWORD module_size_ = 0;

DWORD player_manager_inst_p_addr_ = 0;
DWORD player_manager_player_list_offset_ = 0;
};


Expand Down
65 changes: 65 additions & 0 deletions src/dll/RebirthMemSignatures.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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_REBIRTHMEMSIGNATURES_H
#define MISSINGHUD2_REBIRTHMEMSIGNATURES_H

struct MemSig
{
const unsigned char* signature; // As a byte array { 0x11, 0x22, 0x00, etc }
const char* search_mask; // As a string where "b" = actual byte to search for from the signature array,
// "?" = a wildcard byte,
// "v" = a value byte to return
};



// 8B 1D B4 C4 2D 01 | mov ebx,dword ptr ds:[<&timeGetTime>] |
// C7 05 6C E4 33 01 40 5D | mov dword ptr ds:[133E46C],isaac-ng.1275D40 |
// C7 05 70 E4 33 01 D0 5D | mov dword ptr ds:[133E470],isaac-ng.1275DD0 |
// C7 05 74 E4 33 01 10 5E | mov dword ptr ds:[133E474],isaac-ng.1275E10 |
// FF D3 | call ebx |
// A3 F0 A1 33 01 | mov dword ptr ds:[133A1F0],eax | <== 0x133A1F0 is the address required
const static unsigned char PlayerManagerInstAddrSig[] =
{
0x8B, 0x1D, 0x00, 0x00, 0x00, 0x00,
0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xD3,
0xA3, 0x00, 0x00, 0x00, 0x00
};
const static MemSig PlayerManagerInstAddr = {
PlayerManagerInstAddrSig,
"bb????bb????????bb????????bb????????bbbvvvv"
};



// 8B 35 F4 A1 33 01 | mov esi,dword ptr ds:[133A1F4] |
// 8B 86 30 8A 00 00 | mov eax,dword ptr ds:[esi+8A30] |
// 2B 86 2C 8A 00 00 | sub eax,dword ptr ds:[esi+8A2C] | <== 0x8A2C is the offset required
static unsigned char PlayerManagerPlayerListOffsetSig[] =
{
0x8B, 0x35, 0x00, 0x00, 0x00, 0x00,
0x8B, 0x86, 0x00, 0x00, 0x00, 0x00,
0x2B, 0x86, 0x00, 0x00, 0x00, 0x00
};
const static MemSig PlayerManagerPlayerListOffset = {
PlayerManagerPlayerListOffsetSig,
"bb????bb????bbvv??"
};


#endif //MISSINGHUD2_REBIRTHMEMSIGNATURES_H

0 comments on commit 3a73448

Please sign in to comment.