Skip to content

Commit e27ca1b

Browse files
committed
Add implementation
1 parent d3b2e2f commit e27ca1b

3 files changed

Lines changed: 365 additions & 0 deletions

File tree

BetterRandom.bat

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
@echo off
2+
3+
setlocal enabledelayedexpansion
4+
5+
REM BG EE: Baldur.exe
6+
REM BG2 EE: Baldur.exe
7+
REM IWD EE: Icewind.exe
8+
REM Torment EE: Torment.exe
9+
REM BG : BGMain2.exe
10+
REM BG2 : BGMain.exe
11+
REM IWD: IDMain.exe
12+
REM Torment: Torment.exe
13+
14+
REM Check for original executables (BGMain2.exe is for BG and BGMain.exe is for BG2)
15+
if exist "BGMain2.exe" (set "TARGET_EXE=BGMain2.exe" & goto :next)
16+
if exist "BGMain.exe" (set "TARGET_EXE=BGMain.exe" & goto :next)
17+
if exist "IDMain.exe" (set "TARGET_EXE=IDMain.exe" & goto :next)
18+
if exist "Torment.exe" (set "TARGET_EXE=Torment.exe" & goto :next)
19+
20+
REM Check for EE executables
21+
if exist "Baldur.exe" (set "TARGET_EXE=Baldur.exe" & goto :next)
22+
if exist "Baldur2.exe" (set "TARGET_EXE=Baldur2.exe" & goto :next)
23+
if exist "Icewind.exe" (set "TARGET_EXE=Icewind.exe" & goto :next)
24+
if exist "Torment.exe" (set "TARGET_EXE=Torment.exe" & goto :next)
25+
26+
echo No matching executable found. Exiting...
27+
exit /b 1
28+
29+
:next
30+
31+
REM Check if the executable is 32-bit
32+
for /f %%G in ('powershell -NoProfile -Command "(get-content '%TARGET_EXE%' -totalcount 50) | select-string -Pattern 'PE..L' -Quiet"') do ( set "IS32EXE=%%G" )
33+
REM Check if the executable is 64-bit
34+
for /f %%G in ('powershell -NoProfile -Command "(get-content '%TARGET_EXE%' -totalcount 50) | select-string -Pattern 'PE..d' -Quiet"') do ( set "IS64EXE=%%G" )
35+
36+
REM Check for 32-bit target executable
37+
if "%IS32EXE%"=="True" (
38+
set "DLL_NAME=BetterRandom_x86.dll"
39+
set "SETDLL=setdll_x86.exe"
40+
REM Additional actions for 32-bit executable...
41+
)
42+
43+
REM Check for 64-bit target executable
44+
if "%IS64EXE%"=="True" (
45+
set "DLL_NAME=BetterRandom_x64.dll"
46+
set "SETDLL=setdll_x64.exe"
47+
REM Additional actions for 64-bit executable...
48+
)
49+
50+
51+
REM Prompt user for action
52+
echo 1. Add better random library to %TARGET_EXE%
53+
echo 2. Remove better random library from %TARGET_EXE%
54+
set /p "ACTION=Enter action number (1 or 2): "
55+
56+
57+
REM Process user's choice
58+
if "!ACTION!"=="1" (
59+
REM Inject the BetterRandom library using setdll.exe
60+
%SETDLL% /d:%DLL_NAME% %TARGET_EXE%
61+
) else if "!ACTION!"=="2" (
62+
REM Remove the BetterRandom libary using setdll.exe
63+
echo %SETDLL%
64+
%SETDLL% /r %TARGET_EXE%
65+
) else (
66+
echo Invalid action number. Exiting...
67+
)
68+
69+
endlocal

BetterRandom.cpp

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include <Windows.h>
2+
#include <detours.h>
3+
#include <random>
4+
#include <psapi.h>
5+
#include <winnt.h>
6+
#include <dbghelp.h>
7+
8+
// Original function pointer
9+
typedef int(WINAPI *OriginalRandFunc)();
10+
11+
// Pointer to the original rand() function
12+
OriginalRandFunc pOriginalRand = NULL;
13+
14+
std::random_device rd; // Non-deterministic random number generator
15+
std::uniform_int_distribution<> dist(0, RAND_MAX); // Distribute results between 0 and RAND_MAX
16+
std::mt19937 gen(rd()); // Seed Mersenne Twister generator
17+
18+
// Typedef for the original function
19+
typedef int (*OriginalFunc)(int);
20+
21+
// Pointer to the original function
22+
OriginalFunc pOriginalFunction = nullptr;
23+
24+
// Hooked function for rand()
25+
__declspec(dllexport) int WINAPI HookedRand()
26+
{
27+
// Call the custom implementation instead of the original function
28+
return dist(gen);
29+
}
30+
31+
bool MatchPattern(const unsigned char* data, const unsigned char* pattern, int length)
32+
{
33+
for (int i = 0; i < length; ++i)
34+
{
35+
// Skip certain characters in the pattern
36+
if (pattern[i] == '.')
37+
continue;
38+
39+
// If the characters don't match, return false
40+
if (data[i] != pattern[i])
41+
return false;
42+
}
43+
44+
return true;
45+
}
46+
47+
// Find the offset of a function within a module using a pattern
48+
LPBYTE FindFunctionOffset(HMODULE hModule, const unsigned char *pattern, int length)
49+
{
50+
// Get the base address of the module
51+
auto moduleBase = reinterpret_cast<DWORD_PTR>(hModule);
52+
53+
// Get the module's PE header
54+
auto ntHeader = ImageNtHeader(hModule);
55+
if (!ntHeader)
56+
return 0; // Invalid PE header
57+
58+
// Get the image section header
59+
auto sectionHeader = IMAGE_FIRST_SECTION(ntHeader);
60+
if (!sectionHeader)
61+
return 0; // Invalid section header
62+
63+
// Search within the module's code section
64+
IMAGE_SECTION_HEADER *codeSectionHeader = nullptr;
65+
for (int i = 0; i < ntHeader->FileHeader.NumberOfSections; ++i)
66+
{
67+
if (sectionHeader->Characteristics & IMAGE_SCN_MEM_EXECUTE)
68+
{
69+
codeSectionHeader = sectionHeader;
70+
break;
71+
}
72+
sectionHeader++;
73+
}
74+
75+
if (!codeSectionHeader)
76+
return 0x0; // Code section not found
77+
78+
// Search for the pattern within the code section
79+
auto codeSectionMemory = reinterpret_cast<const unsigned char *>(moduleBase + codeSectionHeader->VirtualAddress);
80+
auto endAddress = codeSectionMemory + codeSectionHeader->Misc.VirtualSize - 2;
81+
82+
for (; codeSectionMemory < endAddress; codeSectionMemory++)
83+
{
84+
if (MatchPattern(codeSectionMemory, pattern, length))
85+
// if (memcmp(codeSectionMemory, pattern, length) == 0)
86+
return (LPBYTE)codeSectionMemory;
87+
}
88+
89+
// Pattern not found
90+
return 0x0;
91+
}
92+
93+
// Function to initialize the hook
94+
void InitHook()
95+
{
96+
// Get the address of the target function
97+
auto hModule = GetModuleHandle(nullptr);
98+
99+
LPBYTE pFunctionAddress = 0x0;
100+
101+
// Find the offset of the target function using a specific pattern
102+
103+
// Baldur's Gate EE, Baldur's Gate 2 EE, Icewind Dale EE,
104+
unsigned char pattern1[] = "\x48\x83..\xE8.......\xFD\x43\x03\x00";
105+
if (pFunctionAddress == 0x0)
106+
pFunctionAddress = FindFunctionOffset(hModule, pattern1, sizeof(pattern1) - 1);
107+
108+
// Planescape EE
109+
unsigned char pattern2[] = "\xE8.......\xFD\x43\x03\x00";
110+
if (pFunctionAddress == 0x0)
111+
pFunctionAddress = FindFunctionOffset(hModule, pattern2, sizeof(pattern2) - 1);
112+
113+
// Baldur's Gate, Baldur's Gate 2, Icewind Dale, Planescape Torment
114+
unsigned char pattern3[] = "\xE8....\x8B....\xFD\x43\x03\x00";
115+
if (pFunctionAddress == 0x0)
116+
pFunctionAddress = FindFunctionOffset(hModule, pattern3, sizeof(pattern3) - 1);
117+
118+
if (pFunctionAddress == 0x0)
119+
{
120+
MessageBoxA(NULL, "original rand() method not found!", "betterrand.dll Error", MB_OK);
121+
throw("Error: original rand() method not found!");
122+
}
123+
124+
SYSTEM_INFO systemInfo;
125+
GetSystemInfo(&systemInfo);
126+
127+
auto baseAddress = systemInfo.lpMinimumApplicationAddress;
128+
auto scanSize = reinterpret_cast<SIZE_T>(systemInfo.lpMaximumApplicationAddress) - reinterpret_cast<SIZE_T>(systemInfo.lpMinimumApplicationAddress);
129+
130+
// Get module info
131+
MODULEINFO modinfo = {NULL};
132+
GetModuleInformation(GetCurrentProcess(), hModule, &modinfo, sizeof(modinfo));
133+
134+
// Create a trampoline for the original function
135+
auto pOriginalFunction = reinterpret_cast<OriginalRandFunc>(pFunctionAddress);
136+
137+
// Detour the function with the custom implementation
138+
DetourTransactionBegin();
139+
DetourUpdateThread(GetCurrentThread());
140+
DetourAttach(&(PVOID &)pOriginalFunction, HookedRand);
141+
DetourTransactionCommit();
142+
}
143+
144+
// Function to remove the hook
145+
void RemoveHook()
146+
{
147+
// Detach the hook
148+
DetourTransactionBegin();
149+
DetourUpdateThread(GetCurrentThread());
150+
DetourDetach(&(PVOID &)pOriginalRand, HookedRand);
151+
DetourTransactionCommit();
152+
}
153+
154+
// Entry point of the DLL
155+
BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
156+
{
157+
if (DetourIsHelperProcess())
158+
{
159+
return TRUE;
160+
}
161+
162+
if (dwReason == DLL_PROCESS_ATTACH)
163+
{
164+
// Initialize the hook when the DLL is loaded
165+
InitHook();
166+
}
167+
else if (dwReason == DLL_PROCESS_DETACH)
168+
{
169+
// Remove the hook when the DLL is unloaded
170+
RemoveHook();
171+
}
172+
173+
return TRUE;
174+
}

build.bat

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
@echo off
2+
3+
REM ===================================================================================================================
4+
5+
REM Define the path to vswhere
6+
set "VSWHERE=C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
7+
8+
REM Find the path to Visual Studio
9+
for /f "usebackq delims=" %%i in (`"%VSWHERE%" -latest -property installationPath`) do (
10+
set "VS_PATH=%%i"
11+
goto :SetEnvironment
12+
)
13+
14+
:SetEnvironment
15+
REM Check if Visual Studio was found
16+
if "%VS_PATH%"=="" (
17+
echo Visual Studio not found.
18+
exit /b
19+
)
20+
21+
REM Notify user
22+
echo Visual Studio found at %VS_PATH%
23+
24+
REM ===================================================================================================================
25+
26+
SETLOCAL
27+
28+
REM Run the script to set up the environment variables
29+
call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
30+
31+
REM Build detours
32+
cd Detours
33+
SET DETOURS_TARGET_PROCESSOR=X64
34+
nmake clean
35+
nmake
36+
cd ..
37+
38+
rem Set compilation variables
39+
set INCLUDE_PATH="Detours\include"
40+
set LIB_PATH="Detours\lib.X64"
41+
set OUTPUT_FILE="BetterRandomom_x64.dll"
42+
set INPUT_FILE="BetterRandom.cpp"
43+
set LIB_FILES="detours.lib Dbghelp.lib user32.lib"
44+
45+
rem Compile the code using cl.exe
46+
cl.exe /O2 /EHsc /LD /I "%INCLUDE_PATH%" /Fe:"%OUTPUT_FILE%" "%INPUT_FILE%" /link /LIBPATH:"%LIB_PATH%" "%LIB_FILES%"
47+
48+
ENDLOCAl
49+
50+
REM ===================================================================================================================
51+
52+
SETLOCAL
53+
54+
REM Run the script to set up the environment variables
55+
call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x86
56+
57+
REM Build detours
58+
cd Detours
59+
SET DETOURS_TARGET_PROCESSOR=X86
60+
nmake clean
61+
nmake
62+
cd ..
63+
64+
rem Set compilation variables
65+
set INCLUDE_PATH="Detours\include"
66+
set LIB_PATH="Detours\lib.X86"
67+
set OUTPUT_FILE="BetterRandom_x86.dll"
68+
set INPUT_FILE="BetterRandom.cpp"
69+
set LIB_FILES="detours.lib Dbghelp.lib user32.lib"
70+
71+
rem Compile the code using cl.exe
72+
cl.exe /O2 /EHsc /LD /I "%INCLUDE_PATH%" /Fe:"%OUTPUT_FILE%" "%INPUT_FILE%" /link /LIBPATH:"%LIB_PATH%" "%LIB_FILES%"
73+
74+
ENDLOCAL
75+
76+
REM ===================================================================================================================
77+
78+
REM Clear and create build directory
79+
rmdir /S /Q build
80+
mkdir build
81+
82+
REM ===================================================================================================================
83+
84+
REM Move final files into build directory
85+
cp Detours\LICENSE.md build\LICENSE.detours
86+
cp LICENSE build\LICENSE.BetterRandom
87+
cp README.md build
88+
cp BetterRandom.bat build
89+
cp Detours\bin.X64\setdll.exe build\setdll_x64.exe
90+
cp Detours\bin.X86\setdll.exe build\setdll_x86.exe
91+
mv BetterRandom_x64.dll build
92+
mv BetterRandom_x86.dll build
93+
94+
REM ===================================================================================================================
95+
96+
REM cleanup build intermediate files
97+
del BetterRandom_x64.exp
98+
del BetterRandom_x86.exp
99+
del BetterRandom_x64.lib
100+
del BetterRandom_x86.lib
101+
del BetterRandom.obj
102+
103+
REM ===================================================================================================================
104+
105+
REM Clean up Detours
106+
rmdir /S /Q Detours\bin.X64
107+
rmdir /S /Q Detours\bin.X86
108+
rmdir /S /Q Detours\lib.X64
109+
rmdir /S /Q Detours\bin.X86
110+
rmdir /S /Q Detours\include
111+
cd Detours
112+
SETLOCAL
113+
SET DETOURS_TARGET_PROCESSOR=X64
114+
call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x64
115+
nmake clean
116+
ENDLOCAl
117+
SETLOCAL
118+
SET DETOURS_TARGET_PROCESSOR=X86
119+
call "%VS_PATH%\VC\Auxiliary\Build\vcvarsall.bat" x86
120+
nmake clean
121+
ENDLOCAl
122+
cd ..

0 commit comments

Comments
 (0)