initial
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.*
|
||||||
|
!.gitignore
|
||||||
BIN
Blockland.exe
Normal file
BIN
Blockland.exe
Normal file
Binary file not shown.
BIN
RedBlocklandLoader.dll
Normal file
BIN
RedBlocklandLoader.dll
Normal file
Binary file not shown.
12
compile.bat
Normal file
12
compile.bat
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
@echo off
|
||||||
|
cd /d %~dp0
|
||||||
|
|
||||||
|
set buildargs=-Wall -Werror -m32 -shared -Isrc -static -lpsapi -lshlwapi -static-libgcc -static-libstdc++
|
||||||
|
|
||||||
|
echo on
|
||||||
|
g++ src/RedBlocklandLoader.cpp %buildargs% -o RedBlocklandLoader.dll
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
rem objdump -d RedBlocklandLoader.dll > RedBlocklandLoader.dll.dump.txt
|
||||||
|
|
||||||
|
pause
|
||||||
12
readme.md
Normal file
12
readme.md
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
# RedBlocklandLoader
|
||||||
|
|
||||||
|
DLL mod loader for Blockland
|
||||||
|
Tested working with Blockland v21 r2033
|
||||||
|
|
||||||
|
## How to Install
|
||||||
|
- Copy `RedBlocklandLoader.dll` into your Blockland install folder, next to `Blockland.exe`
|
||||||
|
- Copy `Blockland.exe` into your Blockland folder, replacing the old `Blockland.exe`
|
||||||
|
- Make `Blockland.exe` read-only (rightclick -> Properties -> Attributes -> Read-only) so the launcher can't overwrite it
|
||||||
|
- Create a folder called `modules` the Blockland folder. This is where you will install DLL mods.
|
||||||
|
If all goes well, you should see "RedBlocklandLoader Init" in the console near the beginning of loading, followed by the loading of any installed DLL mods.
|
||||||
127
src/RedBlocklandLoader.cpp
Normal file
127
src/RedBlocklandLoader.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "RedoBlHooks.cpp"
|
||||||
|
|
||||||
|
char moduleDir[MAX_PATH];
|
||||||
|
|
||||||
|
std::map<std::string, HMODULE> moduleTable;
|
||||||
|
|
||||||
|
bool rblDetachModule(std::string name){
|
||||||
|
HMODULE module = moduleTable[name];
|
||||||
|
if(!module) return false;
|
||||||
|
|
||||||
|
BlPrintf(" " "Detaching module \"%s\"", name.c_str());
|
||||||
|
|
||||||
|
if(!FreeLibrary(module)){
|
||||||
|
BlPrintf(" \x03" "FreeLibrary failed to detach module. Error: %i", GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
moduleTable[name] = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
HMODULE rblAttachModule(std::string name){
|
||||||
|
rblDetachModule(name);
|
||||||
|
|
||||||
|
BlPrintf(" " "Attaching module \"%s\"", name.c_str());
|
||||||
|
|
||||||
|
char filename[MAX_PATH];
|
||||||
|
strcpy_s(filename, moduleDir);
|
||||||
|
PathAppend(filename, name.c_str());
|
||||||
|
|
||||||
|
HMODULE module = LoadLibraryA(filename);
|
||||||
|
if (module == NULL)
|
||||||
|
BlPrintf(" \x03" "LoadLibrary failed to attach module. Error: %i", GetLastError());
|
||||||
|
else
|
||||||
|
moduleTable[name] = module;
|
||||||
|
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rblLoad(){
|
||||||
|
BlPrintf("RedBlocklandLoader Init:");
|
||||||
|
|
||||||
|
if(!GetModuleFileNameA(NULL, moduleDir, MAX_PATH)){
|
||||||
|
BlPrintf(" \x03" "Failed to determine game file location. Error: %i", GetLastError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PathRemoveFileSpec(moduleDir);
|
||||||
|
PathAppend(moduleDir, "modules");
|
||||||
|
|
||||||
|
bool foundAny = false;
|
||||||
|
|
||||||
|
char moduleSearch[MAX_PATH];
|
||||||
|
strcpy_s(moduleSearch, moduleDir);
|
||||||
|
PathAppend(moduleSearch, "*.dll");
|
||||||
|
|
||||||
|
BlPrintf(" " "Searching for modules in: \"%s\"", moduleSearch);
|
||||||
|
|
||||||
|
WIN32_FIND_DATAA finddata;
|
||||||
|
HANDLE hFind = FindFirstFileA(moduleSearch, &finddata);
|
||||||
|
|
||||||
|
if(hFind!=INVALID_HANDLE_VALUE){
|
||||||
|
do{
|
||||||
|
if(finddata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foundAny = true;
|
||||||
|
rblAttachModule(finddata.cFileName);
|
||||||
|
|
||||||
|
} while(FindNextFileA(hFind, &finddata));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!foundAny){
|
||||||
|
BlPrintf(" \x03" "No modules found");
|
||||||
|
}
|
||||||
|
|
||||||
|
BlPrintf(" ");
|
||||||
|
}
|
||||||
|
|
||||||
|
BlFunctionDef(void, __cdecl, InitGame);
|
||||||
|
void __cdecl InitGameHook();
|
||||||
|
BlFunctionHookDef(InitGame);
|
||||||
|
|
||||||
|
void __cdecl InitGameHook(){
|
||||||
|
rblLoad();
|
||||||
|
|
||||||
|
InitGameHookOff();
|
||||||
|
InitGame();
|
||||||
|
InitGameHookOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rblInit(){
|
||||||
|
BlInit;
|
||||||
|
|
||||||
|
BlScanFunctionHex(InitGame, "55 8B EC 6A FF 68 ? ? ? ? 64 A1 ? ? ? ? 50 83 EC 08 56 57 A1 ? ? ? ? 33 C5 50 8D 45 F4 64 A3 ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? C7 05 ? ? ? ? ? ? ? ? E8 ? ? ? ? B9 ? ? ? ? E8 ? ? ? ? 8B F0 89 75 F0 85 F6 74 29 8B CE E8 ? ? ? ? 8D 46 34 C7 06 ? ? ? ? 89 45 F0 C7 40 ? ? ? ? ? C7 00 ? ? ? ? C7 40 ? ? ? ? ? EB 02");
|
||||||
|
|
||||||
|
InitGameHookOn();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rblDenit(){
|
||||||
|
InitGameHookOff();
|
||||||
|
BlPrintf("RedBlocklandLoader: Successfully removed");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool __stdcall DllMain(HINSTANCE hinstance, unsigned int reason, void* reserved){
|
||||||
|
switch(reason){
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
return rblInit();
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
return rblDenit();
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" void __declspec(dllexport) __cdecl RedBlocklandLoader(){}
|
||||||
169
src/RedoBlHooks.cpp
Normal file
169
src/RedoBlHooks.cpp
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// RedoBlHooks Version 2.2
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// Includes
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <psapi.h>
|
||||||
|
|
||||||
|
#include "RedoBlHooks.hpp"
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// Sig Scanning
|
||||||
|
|
||||||
|
ADDR ImageBase;
|
||||||
|
ADDR ImageSize;
|
||||||
|
|
||||||
|
void InitScanner(){
|
||||||
|
HMODULE module = GetModuleHandle(NULL);
|
||||||
|
if (module){
|
||||||
|
MODULEINFO info;
|
||||||
|
GetModuleInformation(GetCurrentProcess(), module, &info, sizeof(MODULEINFO));
|
||||||
|
ImageBase = (ADDR)info.lpBaseOfDll;
|
||||||
|
ImageSize = info.SizeOfImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompareData(BYTE *data, BYTE *pattern, char* mask){
|
||||||
|
for (; *mask; ++data, ++pattern, ++mask){
|
||||||
|
if (*mask=='x' && *data!=*pattern)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (*mask)==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADDR FindPattern(ADDR imageBase, ADDR imageSize, BYTE *pattern, char *mask){
|
||||||
|
for (ADDR i=imageBase; i < imageBase+imageSize; i++){
|
||||||
|
if(CompareData((PBYTE)i, pattern, mask)){
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public
|
||||||
|
|
||||||
|
ADDR rbh_ScanFunctionCode(char* pattern, char* mask){
|
||||||
|
return FindPattern(ImageBase, ImageSize-strlen(mask), (BYTE*)pattern, mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
ADDR rbh_ScanFunctionHex(char* text){
|
||||||
|
unsigned int len = strlen(text);
|
||||||
|
char* patt = (char*)malloc(len);
|
||||||
|
char* mask = (char*)malloc(len);
|
||||||
|
|
||||||
|
int outidx = 0;
|
||||||
|
int val = 0;
|
||||||
|
bool uk = false;
|
||||||
|
for(unsigned int i=0; i<len; i++){
|
||||||
|
char c = text[i];
|
||||||
|
if(c=='?'){
|
||||||
|
uk = true;
|
||||||
|
}else if(c>='0' && c<='9'){
|
||||||
|
val = (val<<4) + (c-'0');
|
||||||
|
}else if(c>='A' && c<='F'){
|
||||||
|
val = (val<<4) + (c-'A'+10);
|
||||||
|
}else if(c>='a' && c<='f'){
|
||||||
|
val = (val<<4) + (c-'a'+10);
|
||||||
|
}else if(c==' '){
|
||||||
|
patt[outidx] = uk ? 0 : val;
|
||||||
|
mask[outidx] = uk ? '?' : 'x';
|
||||||
|
val = 0;
|
||||||
|
uk = false;
|
||||||
|
outidx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
patt[outidx] = uk ? 0 : val;
|
||||||
|
mask[outidx] = uk ? '?' : 'x';
|
||||||
|
outidx++;
|
||||||
|
patt[outidx] = 0;
|
||||||
|
mask[outidx] = 0;
|
||||||
|
|
||||||
|
ADDR res = rbh_ScanFunctionCode(patt, mask);
|
||||||
|
|
||||||
|
free(patt);
|
||||||
|
free(mask);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// Call Patching and Hooking
|
||||||
|
|
||||||
|
void rbh_PatchByte(ADDR location, BYTE value){
|
||||||
|
DWORD oldProtection;
|
||||||
|
VirtualProtect((void*)location, 1, PAGE_EXECUTE_READWRITE, &oldProtection);
|
||||||
|
*((BYTE*)location) = value;
|
||||||
|
VirtualProtect((void*)location, 1, oldProtection, &oldProtection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rbh_PatchBytes(unsigned int len, ADDR location, BYTE* repl){
|
||||||
|
for(unsigned int i=0; i<len; i++){
|
||||||
|
rbh_PatchByte(location+i, repl[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rbh_PatchInt(ADDR addr, int rval){
|
||||||
|
for(unsigned int i=0; i<4; i++){
|
||||||
|
BYTE repl=(rval >> (i*8)) & 0xFF;
|
||||||
|
rbh_PatchByte(addr+i,repl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CallOffset(ADDR instr, ADDR func){
|
||||||
|
return func - (instr+4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatchCall(ADDR instr, ADDR target){
|
||||||
|
rbh_PatchInt(instr, CallOffset(instr, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
void PatchCopy(ADDR dest, ADDR src, unsigned int len){
|
||||||
|
for(unsigned int i=0; i<len; i++){
|
||||||
|
rbh_PatchByte(dest+i, *((BYTE*)(src+i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rbh_HookFunction(ADDR victim, ADDR detour, BYTE* origbytes){
|
||||||
|
memcpy(origbytes, (BYTE*)victim, 6); //save old data
|
||||||
|
|
||||||
|
rbh_PatchByte(victim, 0xE9); //call
|
||||||
|
PatchCall(victim+1, detour); //detour func with call offset
|
||||||
|
rbh_PatchByte(victim+5, 0xC3); //retn
|
||||||
|
}
|
||||||
|
|
||||||
|
void rbh_UnhookFunction(ADDR victim, BYTE* origbytes){
|
||||||
|
PatchCopy(victim, (ADDR)origbytes, 6); //restore old data
|
||||||
|
}
|
||||||
|
|
||||||
|
int rbh_PatchAllMatches(unsigned int len, char* pattern, char* mask, char* replace, bool debugprint){
|
||||||
|
int numpatched = 0;
|
||||||
|
for(ADDR i=ImageBase; i<ImageBase+ImageSize-len; i++){
|
||||||
|
if(CompareData((BYTE*)i, (BYTE*)pattern, mask)){
|
||||||
|
if(debugprint) BlPrintf("RedoBlHooks: Patching call at %08x", i);
|
||||||
|
|
||||||
|
numpatched++;
|
||||||
|
for(ADDR c=0; c<len; c++){
|
||||||
|
rbh_PatchByte(i+c, replace[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return numpatched;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// Init
|
||||||
|
|
||||||
|
BlFunctionDefIntern(rbh_BlPrintf);
|
||||||
|
|
||||||
|
bool rbh_InitInternal(){
|
||||||
|
InitScanner();
|
||||||
|
|
||||||
|
rbh_BlPrintf = (rbh_rbh_BlPrintfFnT)rbh_ScanFunctionHex((char*)"8D 44 24 08 33 D2 50 FF 74 24 08 33 C9 E8 ? ? ? ? 83 C4 08 C3");
|
||||||
|
if(!rbh_BlPrintf) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
89
src/RedoBlHooks.hpp
Normal file
89
src/RedoBlHooks.hpp
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
//////////////////////////////////////////////////
|
||||||
|
// RedoBlHooks Version 2.2
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef unsigned char BYTE;
|
||||||
|
typedef unsigned int ADDR;
|
||||||
|
|
||||||
|
#ifndef RBH_DEBUGPRINT
|
||||||
|
#define RBH_DEBUGPRINT true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BlFunctionDef(returnType, convention, name, ...) \
|
||||||
|
typedef returnType (convention *rbh_##name##FnT)(__VA_ARGS__); \
|
||||||
|
rbh_##name##FnT name;
|
||||||
|
|
||||||
|
#define BlFunctionDefExtern(returnType, convention, name, ...) \
|
||||||
|
typedef returnType (convention *rbh_##name##FnT)(__VA_ARGS__); \
|
||||||
|
extern rbh_##name##FnT name;
|
||||||
|
|
||||||
|
#define BlFunctionDefIntern(name) \
|
||||||
|
rbh_##name##FnT name;
|
||||||
|
|
||||||
|
#define BlScanFunctionCode(target, pattern, mask) \
|
||||||
|
target = (rbh_##target##FnT)rbh_ScanFunctionCode((char*)pattern, (char*)mask); \
|
||||||
|
if(!target){ \
|
||||||
|
rbh_BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
|
||||||
|
return false; \
|
||||||
|
}else{ \
|
||||||
|
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BlScanFunctionHex(target, text) \
|
||||||
|
target = (rbh_##target##FnT)rbh_ScanFunctionHex((char*)text); \
|
||||||
|
if(!target){ \
|
||||||
|
rbh_BlPrintf("RedoBlHooks | Cannot find function "#target"!"); \
|
||||||
|
return false; \
|
||||||
|
}else{ \
|
||||||
|
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found function "#target" at %08x", (int)target); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BlScanCode(target, pattern, mask) \
|
||||||
|
target = rbh_ScanFunctionCode((char*)pattern, (char*)mask); \
|
||||||
|
if(!target){ \
|
||||||
|
rbh_BlPrintf("RedoBlHooks | Cannot find pattern "#target"!"); \
|
||||||
|
return false; \
|
||||||
|
}else{ \
|
||||||
|
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BlScanHex(target, text) \
|
||||||
|
target = rbh_ScanFunctionHex((char*)text); \
|
||||||
|
if(!target){ \
|
||||||
|
rbh_BlPrintf("RedoBlHooks | Cannot find "#target"!"); \
|
||||||
|
return false; \
|
||||||
|
}else{ \
|
||||||
|
if(RBH_DEBUGPRINT) rbh_BlPrintf("RedoBlHooks | Found "#target" at %08x", (int)target); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BlFunctionHookDef(func) \
|
||||||
|
BYTE rbh_BlFunctionHook##func##Data[6]; \
|
||||||
|
void func##HookOn(){ rbh_HookFunction((ADDR)func, (ADDR)func##Hook, rbh_BlFunctionHook##func##Data); } \
|
||||||
|
void func##HookOff(){ rbh_UnhookFunction((ADDR)func, rbh_BlFunctionHook##func##Data); }
|
||||||
|
|
||||||
|
#define BlPatchAllMatches(len, patt, mask, repl) \
|
||||||
|
rbh_PatchAllMatches(len, (char*)patt, (char*)mask, (char*)repl, RBH_DEBUGPRINT);
|
||||||
|
|
||||||
|
#define BlPatchByte(addr, byte) \
|
||||||
|
rbh_PatchByte((ADDR)addr, (BYTE)byte);
|
||||||
|
|
||||||
|
#define BlPatchBytes(len, addr, repl) \
|
||||||
|
rbh_PatchBytes(len, (ADDR)addr, (BYTE*)repl);
|
||||||
|
|
||||||
|
#define BlInit if(!rbh_InitInternal()) return false;
|
||||||
|
|
||||||
|
#define BlPrintf rbh_BlPrintf
|
||||||
|
|
||||||
|
bool rbh_InitInternal();
|
||||||
|
ADDR rbh_ScanFunctionCode(char*, char*);
|
||||||
|
ADDR rbh_ScanFunctionHex(char*);
|
||||||
|
void rbh_HookFunction(ADDR, ADDR, BYTE*);
|
||||||
|
void rbh_UnhookFunction(ADDR, BYTE*);
|
||||||
|
int rbh_PatchAllMatches(unsigned int, char*, char*, char*, bool);
|
||||||
|
void rbh_PatchByte(ADDR, BYTE);
|
||||||
|
void rbh_PatchBytes(unsigned int, ADDR, BYTE*);
|
||||||
|
void rbh_PatchInt(ADDR, int);
|
||||||
|
|
||||||
|
BlFunctionDefExtern(void, , rbh_BlPrintf, const char*, ...);
|
||||||
Reference in New Issue
Block a user