code cleanup, expand REPL, Automatically generate D3D8-VMT header file from include file, made ScrapHacks importable as python module

This commit is contained in:
Daniel S. 2019-12-04 00:28:14 +01:00
parent 37c64ea9ce
commit 493b10e78b
26 changed files with 1894 additions and 973 deletions

127
ScrapHacks/.clang-format Normal file
View file

@ -0,0 +1,127 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: MultiLine
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeColon
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentPPDirectives: None
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseTab: Never
...

82
ScrapHacks/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,82 @@
{
"files.associations": {
"xlocale": "cpp",
"system_error": "cpp",
"fstream": "cpp",
"ostream": "cpp",
"iosfwd": "cpp",
"algorithm": "cpp",
"array": "cpp",
"atomic": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"chrono": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"complex": "cpp",
"condition_variable": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"exception": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"map": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ratio": "cpp",
"regex": "cpp",
"set": "cpp",
"shared_mutex": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"string": "cpp",
"strstream": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeindex": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"utility": "cpp",
"variant": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xhash": "cpp",
"xiosbase": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xstring": "cpp",
"xtr1common": "cpp",
"xtree": "cpp",
"xutility": "cpp"
}
}

View file

@ -19,5 +19,71 @@ if(WIN32)
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO")
endif(MSVC)
endif(WIN32)
add_subdirectory(Injector)
add_subdirectory(ScrapHack)
include(ExternalProject)
ExternalProject_Add(
DirectX
PREFIX ${CMAKE_CURRENT_BINARY_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
URL
https://archive.org/download/DirectX.8.0a.SDK_includes_libs_only/DirectX.8.0a.SDK.zip
URL_HASH SHA1=39f168336d0df92ff14d62d5e3aef1b9e3191312)
ExternalProject_Get_Property(DirectX SOURCE_DIR)
include_directories(AFTER ${SOURCE_DIR}/8.0/include/)
link_directories(AFTER ${SOURCE_DIR}/8.0/lib/)
add_custom_target(
MAKE_D3D8_VMT python ${CMAKE_CURRENT_SOURCE_DIR}/src/make_D3D8_VMT.py ${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp ${SOURCE_DIR}/8.0/include/d3d8.h
DEPENDS DirectX
)
# ExternalProject_Add(
# Python152
# PREFIX ${CMAKE_CURRENT_BINARY_DIR}
# CONFIGURE_COMMAND ""
# BUILD_COMMAND ""
# INSTALL_COMMAND ""
# URL
# https://www.python.org/ftp/python/src/py152.tgz
# URL_HASH SHA1=2d648d07b1aa1aab32a3a24851c33715141779b9
# )
# ExternalProject_Get_Property(Python152 SOURCE_DIR)
# include_directories(AFTER ${SOURCE_DIR}/Include/)
# ExternalProject_Add(
# Python152_Bin
# PREFIX ${CMAKE_CURRENT_BINARY_DIR}
# CONFIGURE_COMMAND ""
# BUILD_COMMAND ""
# INSTALL_COMMAND ""
# URL
# https://www.python.org/ftp/python/win32/py152.exe
# URL_HASH SHA1=dfaf2dcc3704fb1bbc339db4f33ff94bd61c74c6
# )
# ExternalProject_Get_Property(Python152 SOURCE_DIR)
# link_directories(AFTER ${SOURCE_DIR}/)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(POINTER_64=__ptr64)
add_library(ScrapHack SHARED
${CMAKE_CURRENT_SOURCE_DIR}/src/dllmain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ScrapHack.cpp
)
set_target_properties(ScrapHack PROPERTIES SUFFIX ".pyd")
add_dependencies(ScrapHack DirectX)
add_dependencies(ScrapHack MAKE_D3D8_VMT)
add_dependencies(MAKE_D3D8_VMT DirectX)
# add_dependencies(ScrapHack Python152)
# add_dependencies(ScrapHack Python152_Bin)
target_link_libraries(ScrapHack
d3d8
d3dx8
dxerr8
# PYTHON15
legacy_stdio_definitions)
target_compile_features(ScrapHack PUBLIC cxx_std_11)
install(TARGETS ScrapHack DESTINATION bin)

View file

@ -2,6 +2,7 @@
- Visual Studio 2017/2019 (others might work)
- CMake
- Python 3.6 or newer
## Building
@ -11,13 +12,22 @@ Open VS 32-bit command prompt (`vcvars32.bat`)
mkdir build
cd build
cmake -G"NMake Makefiles" ..
cmake --build . --target install
mkdir bin
cd bin
cmake --build .. --target install
```
this will drop the compiled files into `./build/bin`
(this has only been tested with a (cracked/deobfuscated) `Scrap.exe` v1.0 with a SHA1 checksum of `d2dde960e8eca69d60c2e39a439088b75f0c89fa`, other version might crash if the memory offsets don't match)
## Usage
## TODO
- create a `lib` folder next to `Scrapland.exe`
- copy `ScrapHack.pyd` into said folder
- open the ingame console (Ctrl+^)
- type `import ScrapHack`
- Done!
## Notes
(this has only been tested with a (cracked/de-obfuscated) `Scrap.exe` v1.0 with a SHA1 checksum of `d2dde960e8eca69d60c2e39a439088b75f0c89fa`, other version might crash if the memory offsets don't match)
- Injector-less version (patch Scrap.exe to load DLL)

View file

@ -1,24 +0,0 @@
include(ExternalProject)
ExternalProject_Add(
DirectX
PREFIX ${CMAKE_CURRENT_BINARY_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
URL
https://archive.org/download/DirectX.8.0a.SDK_includes_libs_only/DirectX.8.0a.SDK.zip
URL_HASH SHA1=39f168336d0df92ff14d62d5e3aef1b9e3191312)
ExternalProject_Get_Property(DirectX SOURCE_DIR)
include_directories(AFTER ${SOURCE_DIR}/8.0/include/)
link_directories(AFTER ${SOURCE_DIR}/8.0/lib/)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(POINTER_64=__ptr64)
add_library(ScrapHack SHARED ScrapHack.cpp dllmain.cpp)
add_dependencies(ScrapHack DirectX)
target_link_libraries(ScrapHack
d3d8
d3dx8
dxerr8
legacy_stdio_definitions)
target_compile_features(ScrapHack PUBLIC cxx_std_11)
install(TARGETS ScrapHack DESTINATION bin)

View file

@ -1,82 +0,0 @@
#pragma once
#include <Windows.h>
#include <d3d8.h>
#include <d3dx8.h>
#include <dxerr8.h>
uintmax_t frame = 0;
DWORD *GetVTable(void *addr)
{
return (DWORD *)(*(DWORD *)addr);
}
bool overlay = false;
LPD3DXFONT m_pFont;
HFONT hFont;
HBRUSH hBrush;
D3DCOLOR color = D3DCOLOR_ARGB(255, 255, 0, 0);
RECT Rect = {0, 0, 0, 0};
D3DRECT panel;
size_t size_ht(HashTable<EntityList> *ht);
size_t size_ht(HashTable<Entity> *ht);
LPDIRECT3DDEVICE8 Render(LPDIRECT3DDEVICE8 dev)
{
if (!overlay)
{
return dev;
}
char text[4096];
int32_t money = 0;
size_t num_ents = 0;
size_t num_ent_lst = 0;
if (ptr<void>(P_WORLD, 0) != nullptr)
{
money = ptr<int32_t>(P_WORLD, O_MONEY)[0];
num_ents = size_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS));
num_ent_lst = size_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS));
}
snprintf(text, 4096, "ScrapHack v0.1\nFrame: [%lld]\nMoney: [%d]\nEntities: [%ld]\nEntity Lists: [%ld]", ++frame, money, num_ents, num_ent_lst);
if (m_pFont == nullptr)
{
D3DXCreateFont(dev, hFont, &m_pFont);
CloseHandle(hFont);
}
m_pFont->Begin();
m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0);
m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color);
m_pFont->End();
return dev;
}
HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev)
{
typedef HRESULT(WINAPI * t_func)(LPDIRECT3DDEVICE8);
shared_ptr<Hook> hook = Hook::get(H_EndScene);
return hook->func<t_func>(Render(dev));
}
void unhook_d3d8()
{
if (hFont != INVALID_HANDLE_VALUE)
{
CloseHandle(hFont);
}
if (m_pFont != nullptr)
{
m_pFont->Release();
}
Hook::drop(H_EndScene);
}
void hook_d3d8()
{
typedef void(_cdecl * t_func)();
hFont = CreateFont(20, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, "Verdana");
hBrush = CreateSolidBrush(D3DCOLOR_ARGB(25, 0, 0, 0));
Hook::addr(ptr<void>(0x853954,0x2a3d8,0,4*35,0),H_EndScene);
shared_ptr<Hook> hook = Hook::get(hook_d3d8);
hook->func_void<t_func>();
hook->disable();
Hook::drop(hook_d3d8);
return;
}

View file

@ -1,141 +0,0 @@
#pragma once
#include <functional>
class Hook
{
private:
MEMORY_BASIC_INFORMATION mbi;
void *orig;
void *detour;
bool enabled;
uint8_t orig_bytes[6];
uint8_t jmp_bytes[6];
static map<uintptr_t, shared_ptr<Hook>> hooks;
public:
Hook(void *func, void *detour)
{
uintptr_t dest = reinterpret_cast<uintptr_t>(detour);
uintptr_t src = reinterpret_cast<uintptr_t>(func);
this->orig = func;
this->detour = detour;
this->jmp_bytes[0] = 0x68; // push
this->jmp_bytes[1] = (dest >> 0) & 0xff;
this->jmp_bytes[2] = (dest >> 8) & 0xff;
this->jmp_bytes[3] = (dest >> 16) & 0xff;
this->jmp_bytes[4] = (dest >> 24) & 0xff;
this->jmp_bytes[5] = 0xC3; // ret
VirtualQuery(func, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect);
memcpy(this->orig_bytes, this->orig, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
this->enabled = false;
}
~Hook()
{
cout << "Unhooking: [" << this->orig << " <- " << this->detour << "]" << endl;
this->disable();
}
static void addr(void *addr, void *detour)
{
cout << "Hooking: [" << addr << " -> " << detour << "]" << endl;
uintptr_t key = reinterpret_cast<uintptr_t>(detour);
hooks[key] = make_shared<Hook>(addr, detour);
hooks[key]->enable();
}
static void module(const char *mod, const char *func, void *detour)
{
cout << "Hooking: [" << mod << "]." << func << " -> " << detour << endl;
void *addr = GetProcAddress(GetModuleHandle(mod), func);
if (addr != NULL)
{
Hook::addr(addr, detour);
}
else
{
cerr << "[" << mod << "]." << func << " not found!" << endl;
};
}
static shared_ptr<Hook> get(void *func)
{
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return Hook::get(addr);
}
static shared_ptr<Hook> get(uintptr_t addr)
{
return hooks.at(addr);
}
static size_t drop(void *func)
{
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return Hook::drop(addr);
}
static size_t drop(uintptr_t addr)
{
return hooks.erase(addr);
}
static void clear()
{
cout << "Clearing Hooks" << endl;
for (pair<uintptr_t, shared_ptr<Hook>> h : hooks)
{
h.second->disable();
}
return hooks.clear();
}
void disable()
{
if (enabled)
{
//cout << "Disabling: [" << this->orig << " <- " << this->detour << "]" << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->orig_bytes, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = false;
}
}
void enable()
{
if (!enabled)
{
//cout << "Enabling: [" << this->orig << " -> " << this->detour << "]" << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->jmp_bytes, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = true;
}
}
void *get_orig()
{
return this->orig;
}
template <typename F, typename... Args>
void func_void(Args... args)
{
disable();
reinterpret_cast<F>(this->orig)(args...);
enable();
return;
}
template <typename F, typename... Args>
decltype(auto) func(Args... args)
{
disable();
auto ret = reinterpret_cast<F>(this->orig)(args...);
enable();
return ret;
}
};
map<uintptr_t, shared_ptr<Hook>> Hook::hooks;

View file

@ -1,62 +0,0 @@
#pragma once
#include "Structures.h"
map<string, Module> Py;
PyMethodDef *find_method_table(uintptr_t base, uintptr_t needle)
{
for (ptrdiff_t offset = 0; offset < 64; ++offset)
{
uintptr_t instr = reinterpret_cast<uintptr_t *>(base + offset)[0];
if (instr == needle)
{
uintptr_t mod_addr = reinterpret_cast<uintptr_t *>(base + offset - (1 + 4))[0];
return reinterpret_cast<PyMethodDef *>(mod_addr);
}
}
return reinterpret_cast<PyMethodDef *>(0);
}
map<string, Module> get_modules(uintptr_t base)
{
map<string, Module> Py;
PyMod *modules = reinterpret_cast<PyMod *>(base);
for (size_t i = 0; modules[i].init_func != NULL; i++)
{
Module mod;
mod.mod = &modules[i];
PyMethodDef *method_table = find_method_table((size_t)modules[i].init_func, reinterpret_cast<uintptr_t>(modules[i].name));
for (size_t j = 0; method_table != NULL && method_table[j].ml_name != NULL; j++)
{
mod.methods[method_table[j].ml_name] = &method_table[j];
}
Py[mod.mod->name] = mod;
}
return Py;
}
void *get_py(const char *mod, const char *meth)
{
try
{
return Py.at(mod).methods.at(meth)->ml_meth;
}
catch (out_of_range)
{
return NULL;
}
}
void inject(const char *mod, const char *meth, void *detour)
{
try
{
void *orig = get_py(mod, meth);
Py.at(mod).methods.at(meth)->ml_meth = detour;
cout << mod << "." << meth << ": " << orig << " -> " << detour << endl;
}
catch (out_of_range)
{
cout << mod << "." << meth << " not found!" << endl;
}
}

View file

@ -1,54 +0,0 @@
#include <sstream>
#include <regex>
#include <Windows.h>
#include "Util.h"
DWORD get_protection(void *addr)
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(addr, &mbi, sizeof(mbi));
return mbi.Protect;
}
void handle_command(const char *_cmd)
{
cout<<"CMD: '"<<_cmd<<"'"<<endl;
vector<string> cmd = split(string(_cmd), ' ');
cout<<"PARTS: ";
for (string c:cmd) {
cout<<"'"<<c<<"' ";
}
cout<<endl;
if (cmd.size() == 0)
{
cout<<"EMPTY!"<<endl;
return;
}
scrap_log(0x00ff00,_cmd);
scrap_log(0x00ff00,"\n");
if (cmd[0] == "r")
{
if (cmd.size()!=2) {
scrap_log(0xff0000, "Usage: $r <addr> [size]\n");
return;
}
scrap_log(0xff0000, "READ!\n");
cout<<"READ!"<<endl;
}
else if (cmd[0] == "w")
{
if (cmd.size()!=2) {
scrap_log(0xff0000, "Usage: $w <addr> <hex_data>\n");
return;
}
scrap_log(0xff0000, "WRITE!\n");
cout<<"WRITE!"<<endl;
}
else
{
scrap_log(0xff0000, "Unknown command!\n");
}
scrap_log(0x00ff00, "HAXX\n");
return;
}

View file

@ -1,266 +0,0 @@
#include <string>
#include <vector>
#include <map>
#include <iomanip>
#include <iostream>
#include <typeinfo>
#include <functional>
#include <Windows.h>
#include <TlHelp32.h>
using namespace std;
#include "Scrapland.h"
#include "Util.h"
#include "Structures.h"
#include "Py_Utils.h"
#include "Hook.h"
#include "D3D8_Hook.h"
#include "REPL.h"
HMODULE hD3D8Dll = 0;
bool initialized = false;
bool running = true;
bool redirect_console = false;
HMODULE mod = 0;
void DllUnload(HMODULE);
int hooked_console(const char *);
void H_Exit();
size_t size_ht(HashTable<EntityList> *ht)
{
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i)
{
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent)
{
while (ent)
{
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t size_ht(HashTable<Entity> *ht)
{
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i)
{
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent)
{
while (ent)
{
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht)
{
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i)
{
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent)
{
cout << i << ": ";
while (ent)
{
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next)
{
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht)
{
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i)
{
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent)
{
cout << i << ": ";
while (ent)
{
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next)
{
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
void MainLoop(HMODULE mod)
{
Sleep(100);
Hook::addr(reinterpret_cast<void *>(P_SCRAP_EXIT), H_Exit);
Hook::addr(reinterpret_cast<void *>(P_D3DCHECK),hook_d3d8);
Hook::addr(reinterpret_cast<void *>(P_CON_HANDLER), hooked_console);
overlay=true;
cout << "[*] Starting main Loop" << endl;
cout << endl;
cout << "[F2 ] Redirect game console to ScapHacks console" << endl;
cout << "[F3 ] Unload ScrapHacks" << endl;
cout << "[F5 ] Show Overlay" << endl;
cout << "[F6 ] Show Alarm status" << endl;
cout << "[F7 ] Set Money to 0x7fffffff" << endl;
cout << "[F8 ] Dump python modules" << endl;
cout << "[F9 ] Dump Entity hashtable" << endl;
cout << "[F10] Enable python tracing" << endl;
cout << "[ F ] \"Handbrake\" (*Will* crash the game after some time!)" << endl;
while (running)
{
Sleep(100);
while (key_down('F'))
{
scrap_exec("dbg.brake()");
}
if (key_down_norepeat(VK_F2))
{
redirect_console = !redirect_console;
}
if (key_down_norepeat(VK_F3))
{
break;
}
if (key_down_norepeat(VK_F5))
{
overlay = !overlay;
}
if (key_down_norepeat(VK_F6))
{
float *alarm = ptr<float>(P_WORLD, O_ALARM);
float *alarm_grow = ptr<float>(P_WORLD, O_ALARM_GROW);
cout << "Alarm: " << alarm[0] << " + " << alarm_grow[0] << endl;
}
if (key_down_norepeat(VK_F7))
{
int32_t *money = ptr<int32_t>(P_WORLD, O_MONEY);
*money = 0x7fffffff;
}
if (key_down_norepeat(VK_F8))
{
for (auto mod : Py)
{
for (auto meth : mod.second.methods)
{
cout << mod.first << "." << meth.first << " @ " << meth.second->ml_meth << endl;
}
}
}
if (key_down_norepeat(VK_F9))
{
cout << "Entities:" << endl;
dump_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS));
cout << "Entity Lists:" << endl;
dump_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS));
}
if (key_down_norepeat(VK_F10))
{
scrap_exec("dbg.settrace()");
}
}
FreeLibraryAndExitThread(mod, 0);
}
void InitConsole()
{
char me[1024];
GetModuleFileName(mod, me, 1024);
SetupConsole(me);
}
int hooked_console(const char *cmd)
{
typedef int(_cdecl * t_func)(const char *);
if (cmd[0] == '$')
{
handle_command(++cmd);
return 0;
}
shared_ptr<Hook> hook = Hook::get(hooked_console);
int ret = hook->func<t_func>(cmd);
return ret;
}
void H_Exit()
{
typedef void(_cdecl * t_func)(void);
shared_ptr<Hook> hook = Hook::get(H_Exit);
DllUnload(mod);
HWND hMainWindow = ptr<HWND>(0x7FA830, 0x7c)[0];
SendMessage(hMainWindow, WM_CLOSE, 0, 0);
return;
}
void DllPreInit(HMODULE _mod)
{
char mfn[1024];
InitConsole();
GetModuleFileNameA(0, mfn, 1024);
Py = get_modules(P_PY_MODS);
cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << " (PID: " << std::hex << GetCurrentProcessId() << std::dec << ")" << endl;
}
void DllInit(HMODULE _mod)
{
initialized = true;
mod = _mod;
cout << "[*] World: " << ptr<void>(P_WORLD, 0) << endl;
cout << "[*] Importing python dbg module" << endl;
scrap_exec("import dbg");
scrap_log(0xff0000, "ScrapHacks loaded!\n");
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MainLoop, mod, 0, 0);
cout << "[*] Starting message pump" << endl;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return;
}
void DllUnload(HMODULE _mod)
{
SetConsoleCtrlHandler(NULL, false);
unhook_d3d8();
Hook::clear();
scrap_log(0xff0000, "ScrapHacks unloaded!\n");
cout << "[+] ScrapHacks unloaded, you can now close the console!" << endl;
FreeConsole();
DestroyWindow(GetConsoleWindow());
return;
}

View file

@ -1,26 +0,0 @@
#pragma once
//OFFSETS
#define O_MONEY 0x2090
#define O_ALARM 0x1C6C
#define O_ALARM_GROW 0x1C68
#define O_ENTS 0x4
#define O_ENTLISTS 0x2b8
//POINTERS
#define P_WORLD 0x7FE944
#define P_PY_MODS 0x79C698
//FUNCTIONS
#define P_CON_HANDLER 0x402190
#define P_SCRAP_LOG 0x4134C0
#define P_SCRAP_EXEC 0x5a8390
#define P_SCRAP_EXIT 0x4010c0
#define P_D3DCHECK 0x602a70
//FUNCTION TYPES
#define T_SCRAP_LOG int(_cdecl *)(unsigned int, const char *)
#define T_SCRAP_EXEC int(_cdecl *)(const char *)
auto scrap_log = (T_SCRAP_LOG)P_SCRAP_LOG;
auto scrap_exec = (T_SCRAP_EXEC)P_SCRAP_EXEC;

View file

@ -1,66 +0,0 @@
#pragma once
template <typename T>
struct HashTableEntry;
struct Vector3
{
float x;
float y;
float z;
};
struct Matrix3x3
{
Vector3 a;
Vector3 b;
Vector3 c;
};
struct PyMethodDef
{
char *ml_name;
void *ml_meth;
int ml_flags;
char *ml_doc;
};
struct PyMod
{
char *name;
void *init_func;
};
struct Module
{
PyMod *mod;
map<string, PyMethodDef *> methods;
};
struct Entity
{
void *vmt;
const char *name;
};
struct EntityList
{
const char *name;
void *unk_1;
void *unk_2;
const char *mod;
const char *func;
};
template <typename T>
struct HashTable
{
uint32_t size;
HashTableEntry<T> **chains;
};
template <typename T>
struct HashTableEntry
{
T *data;
const char *name;
HashTableEntry *next;
};

View file

@ -1,206 +0,0 @@
#pragma once
#include <string>
#define DLL_EXPORT extern "C" __declspec(dllexport)
string GetLastErrorAsString()
{
DWORD errorMessageID = GetLastError();
if (errorMessageID == 0)
return "No error";
LPSTR messageBuffer = NULL;
size_t m_size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
string message(messageBuffer, m_size);
LocalFree(messageBuffer);
if (!message.empty() && message[message.length() - 1] == '\n')
{
message.erase(message.length() - 1);
}
return message;
}
void SetupStreams()
{
FILE *fIn;
FILE *fOut;
freopen_s(&fIn, "conin$", "r", stdin);
freopen_s(&fOut, "conout$", "w", stdout);
freopen_s(&fOut, "conout$", "w", stderr);
ios::sync_with_stdio();
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
std::wcin.clear();
std::cin.clear();
}
void SetupConsole()
{
if (!AttachConsole(-1)) {
if (!AllocConsole())
{
FreeConsole();
AllocConsole();
}
AttachConsole(GetCurrentProcessId());
}
SetupStreams();
}
void SetupConsole(const char *title)
{
SetupConsole();
SetConsoleTitleA(title);
}
void FreeConsole(bool wait)
{
if (wait)
{
cout << "[?] Press Enter to Exit";
cin.ignore();
}
FreeConsole();
}
bool in_foreground = false;
BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam)
{
DWORD lpdwProcessId;
GetWindowThreadProcessId(hwnd, &lpdwProcessId);
if (lpdwProcessId == lParam)
{
in_foreground = (hwnd == GetForegroundWindow()) || (hwnd == GetActiveWindow());
return FALSE;
}
return TRUE;
}
bool key_down(int keycode, int delay = 100)
{
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground)
{
if (GetAsyncKeyState(keycode))
{
Sleep(delay);
return true;
}
}
return false;
}
bool key_down_norepeat(int keycode, int delay = 100)
{
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground)
{
if (GetAsyncKeyState(keycode))
{
while (GetAsyncKeyState(keycode))
{
Sleep(delay);
}
return true;
}
}
return false;
}
void hexdump(void *addr, size_t count)
{
for (size_t i = 0; i < count; ++i)
{
unsigned int val = (unsigned int)((unsigned char *)addr)[i];
cout << setfill('0') << setw(2) << std::hex << val << " ";
if (((i + 1) % 16) == 0)
{
cout << endl;
}
}
cout << endl;
}
template <typename T>
T *__ptr(uintptr_t addr)
{
return reinterpret_cast<T *>(addr);
}
template <typename T>
T *__ptr(uintptr_t addr, ptrdiff_t offset)
{
//cout << "[" << (void*)addr << "] + " << (void*)offset << " = ";
addr = reinterpret_cast<uintptr_t *>(addr)[0] + offset;
//cout << (void*)addr << endl;;
auto ret = __ptr<T>(addr);
return ret;
}
template <typename T, typename... Offsets>
T *__ptr(uintptr_t addr, ptrdiff_t offset, Offsets... offsets)
{
//cout << "[" << (void*)addr << "] + " << (void*)offset << " = ";
addr = reinterpret_cast<uintptr_t *>(addr)[0] + offset;
//cout << (void*)addr << endl;;
auto ret = __ptr<T>(addr, offsets...);
return ret;
}
template <typename T, typename... Offsets>
T *ptr(uintptr_t addr, Offsets... offsets)
{
auto ret = __ptr<T>(addr, offsets...);
return ret;
}
DWORD PPID()
{
DWORD PID = GetCurrentProcessId();
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 procentry;
if (hSnapShot == INVALID_HANDLE_VALUE)
{
cout << GetLastErrorAsString() << endl;
return -1;
}
if (Process32First(hSnapShot, &procentry))
{
do
{
if (procentry.th32ProcessID == PID)
{
CloseHandle(hSnapShot);
return procentry.th32ParentProcessID;
}
procentry.dwSize = sizeof(PROCESSENTRY32);
} while (Process32Next(hSnapShot, &procentry));
}
CloseHandle(hSnapShot);
return -1;
}
vector<string> split(string str, char sep)
{
vector<string> ret;
string part;
for (auto n : str)
{
if (n == sep)
{
ret.push_back(part);
part.clear();
}
else
{
part = part + n;
}
}
if (part != "")
ret.push_back(part);
return ret;
}

View file

@ -1,30 +0,0 @@
#include <Windows.h>
#define DLL_EXPORT extern "C" __declspec(dllexport)
void DllInit(HMODULE);
void DllPreInit(HMODULE);
void DllUnload(HMODULE);
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
HANDLE hThread = INVALID_HANDLE_VALUE;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
DllPreInit(hModule);
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)DllInit, hModule, 0, 0);
if (hThread) {
CloseHandle(hThread);
}
break;
case DLL_PROCESS_DETACH:
DllUnload(hModule);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
}
return TRUE;
}

View file

@ -0,0 +1,136 @@
#pragma once
#include <Windows.h>
#include <d3d8.h>
#include <d3dx8.h>
#include <dxerr8.h>
#include "D3D8_VMT.hpp"
#include "Hook.hpp"
#include "Scrapland.hpp"
#include "Structures.hpp"
#include "Util.hpp"
uintmax_t frame = 0;
bool hooked=false;
bool overlay = false;
LPD3DXFONT m_pFont;
HFONT hFont;
HBRUSH hBrush;
D3DCOLOR color = D3DCOLOR_ARGB(255, 255, 0, 0);
RECT Rect = {0, 0, 0, 0};
D3DRECT panel;
size_t size_ht(HashTable<EntityList> *ht);
size_t size_ht(HashTable<Entity> *ht);
LPDIRECT3DDEVICE8
Render(LPDIRECT3DDEVICE8 dev) {
if (!overlay) {
return dev;
}
char text[4096];
int32_t money = 0;
size_t num_ents = 0;
size_t num_ent_lst = 0;
if (ptr<void>(P_WORLD, 0) != nullptr) {
money = ptr<int32_t>(P_WORLD, O_MONEY)[0];
num_ents = size_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS));
num_ent_lst = size_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS));
}
snprintf(text, 4096,
R"(ScrapHack v0.1
Frame: [%lld]
Money: [%d]
Entities: [%ld]
Entity Lists: [%ld])",
++frame, money, num_ents, num_ent_lst);
if (m_pFont == nullptr) {
D3DXCreateFont(dev, hFont, &m_pFont);
hFont = nullptr;
}
m_pFont->Begin();
m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0);
m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color);
m_pFont->End();
return dev;
}
HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev) {
typedef decltype(&H_EndScene) t_func;
shared_ptr<Hook> hook = Hook::get(H_EndScene);
return hook->func<t_func>(Render(dev));
}
HRESULT WINAPI H_SetLight(LPDIRECT3DDEVICE8 dev, DWORD index,
D3DLIGHT8 *light) {
typedef decltype(&H_SetLight) t_func;
shared_ptr<Hook> hook = Hook::get(H_SetLight);
light->Diffuse.r = ((float)rand() / (float)RAND_MAX);
light->Diffuse.g = ((float)rand() / (float)RAND_MAX);
light->Diffuse.b = ((float)rand() / (float)RAND_MAX);
light->Diffuse.a = 1.0;
light->Specular.r = ((float)rand() / (float)RAND_MAX);
light->Specular.g = ((float)rand() / (float)RAND_MAX);
light->Specular.b = ((float)rand() / (float)RAND_MAX);
light->Specular.r = 1.0;
light->Ambient.r = ((float)rand() / (float)RAND_MAX);
light->Ambient.g = ((float)rand() / (float)RAND_MAX);
light->Ambient.b = ((float)rand() / (float)RAND_MAX);
light->Ambient.a = 1.0;
return hook->func<t_func>(dev, index, light);
}
HRESULT WINAPI H_DrawIndexedPrimitive(LPDIRECT3DDEVICE8 dev,
D3DPRIMITIVETYPE Type, UINT minIndex,
UINT NumVertices, UINT startIndex,
UINT primCount) {
typedef decltype(&H_DrawIndexedPrimitive) t_func;
DWORD AMBIENT;
shared_ptr<Hook> hook = Hook::get(H_DrawIndexedPrimitive);
dev->GetRenderState(D3DRS_AMBIENT, &AMBIENT);
dev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255));
dev->SetRenderState(D3DRS_FILLMODE, D3DFILLMODE::D3DFILL_SOLID);
dev->SetRenderState(D3DRS_ZENABLE, 0);
auto ret = hook->func<t_func>(dev, Type, minIndex, NumVertices, startIndex,
primCount);
dev->SetRenderState(D3DRS_AMBIENT, AMBIENT);
dev->SetRenderState(D3DRS_ZENABLE, 1);
return ret;
}
void unhook_d3d8() {
if (!hooked) {
return;
}
if (m_pFont != nullptr) {
m_pFont->Release();
m_pFont = nullptr;
}
Hook::drop(H_EndScene);
Hook::drop(H_DrawIndexedPrimitive);
Hook::drop(H_SetLight);
hooked=false;
}
void hook_d3d8() {
if (hooked) {
return;
}
hFont = CreateFontA(15, 0, 0, 0, FW_EXTRABOLD, 0, 0, 0, ANSI_CHARSET, 0, 0,
0, 0, "Lucida Console");
hBrush = CreateSolidBrush(D3DCOLOR_ARGB(25, 0, 0, 0));
void *dev = nullptr;
while (true) {
dev = ptr<void>(P_D3DDEV);
if (dev) {
break;
}
Sleep(100);
};
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_EndScene], H_EndScene);
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_DrawIndexedPrimitive],
H_DrawIndexedPrimitive);
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_SetLight], H_SetLight);
hooked=true;
return;
}

274
ScrapHacks/src/D3D8_VMT.hpp Normal file
View file

@ -0,0 +1,274 @@
namespace VMT_IDirect3D8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_RegisterSoftwareDevice = 3;
const size_t m_GetAdapterCount = 4;
const size_t m_GetAdapterIdentifier = 5;
const size_t m_GetAdapterModeCount = 6;
const size_t m_EnumAdapterModes = 7;
const size_t m_GetAdapterDisplayMode = 8;
const size_t m_CheckDeviceType = 9;
const size_t m_CheckDeviceFormat = 10;
const size_t m_CheckDeviceMultiSampleType = 11;
const size_t m_CheckDepthStencilMatch = 12;
const size_t m_GetDeviceCaps = 13;
const size_t m_GetAdapterMonitor = 14;
const size_t m_CreateDevice = 15;
}
namespace VMT_IDirect3DBaseTexture8_IDirect3DResource8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_SetLOD = 11;
const size_t m_GetLOD = 12;
const size_t m_GetLevelCount = 13;
}
namespace VMT_IDirect3DCubeTexture8_IDirect3DBaseTexture8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_SetLOD = 11;
const size_t m_GetLOD = 12;
const size_t m_GetLevelCount = 13;
const size_t m_GetLevelDesc = 14;
const size_t m_GetCubeMapSurface = 15;
const size_t m_LockRect = 16;
const size_t m_UnlockRect = 17;
const size_t m_AddDirtyRect = 18;
}
namespace VMT_IDirect3DDevice8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_TestCooperativeLevel = 3;
const size_t m_GetAvailableTextureMem = 4;
const size_t m_ResourceManagerDiscardBytes = 5;
const size_t m_GetDirect3D = 6;
const size_t m_GetDeviceCaps = 7;
const size_t m_GetDisplayMode = 8;
const size_t m_GetCreationParameters = 9;
const size_t m_SetCursorProperties = 10;
const size_t m_SetCursorPosition = 11;
const size_t m_ShowCursor = 12;
const size_t m_CreateAdditionalSwapChain = 13;
const size_t m_Reset = 14;
const size_t m_Present = 15;
const size_t m_GetBackBuffer = 16;
const size_t m_GetRasterStatus = 17;
const size_t m_SetGammaRamp = 18;
const size_t m_GetGammaRamp = 19;
const size_t m_CreateTexture = 20;
const size_t m_CreateVolumeTexture = 21;
const size_t m_CreateCubeTexture = 22;
const size_t m_CreateVertexBuffer = 23;
const size_t m_CreateIndexBuffer = 24;
const size_t m_CreateRenderTarget = 25;
const size_t m_CreateDepthStencilSurface = 26;
const size_t m_CreateImageSurface = 27;
const size_t m_CopyRects = 28;
const size_t m_UpdateTexture = 29;
const size_t m_GetFrontBuffer = 30;
const size_t m_SetRenderTarget = 31;
const size_t m_GetRenderTarget = 32;
const size_t m_GetDepthStencilSurface = 33;
const size_t m_BeginScene = 34;
const size_t m_EndScene = 35;
const size_t m_Clear = 36;
const size_t m_SetTransform = 37;
const size_t m_GetTransform = 38;
const size_t m_MultiplyTransform = 39;
const size_t m_SetViewport = 40;
const size_t m_GetViewport = 41;
const size_t m_SetMaterial = 42;
const size_t m_GetMaterial = 43;
const size_t m_SetLight = 44;
const size_t m_GetLight = 45;
const size_t m_LightEnable = 46;
const size_t m_GetLightEnable = 47;
const size_t m_SetClipPlane = 48;
const size_t m_GetClipPlane = 49;
const size_t m_SetRenderState = 50;
const size_t m_GetRenderState = 51;
const size_t m_BeginStateBlock = 52;
const size_t m_EndStateBlock = 53;
const size_t m_ApplyStateBlock = 54;
const size_t m_CaptureStateBlock = 55;
const size_t m_DeleteStateBlock = 56;
const size_t m_CreateStateBlock = 57;
const size_t m_SetClipStatus = 58;
const size_t m_GetClipStatus = 59;
const size_t m_GetTexture = 60;
const size_t m_SetTexture = 61;
const size_t m_GetTextureStageState = 62;
const size_t m_SetTextureStageState = 63;
const size_t m_ValidateDevice = 64;
const size_t m_GetInfo = 65;
const size_t m_SetPaletteEntries = 66;
const size_t m_GetPaletteEntries = 67;
const size_t m_SetCurrentTexturePalette = 68;
const size_t m_GetCurrentTexturePalette = 69;
const size_t m_DrawPrimitive = 70;
const size_t m_DrawIndexedPrimitive = 71;
const size_t m_DrawPrimitiveUP = 72;
const size_t m_DrawIndexedPrimitiveUP = 73;
const size_t m_ProcessVertices = 74;
const size_t m_CreateVertexShader = 75;
const size_t m_SetVertexShader = 76;
const size_t m_GetVertexShader = 77;
const size_t m_DeleteVertexShader = 78;
const size_t m_SetVertexShaderConstant = 79;
const size_t m_GetVertexShaderConstant = 80;
const size_t m_GetVertexShaderDeclaration = 81;
const size_t m_GetVertexShaderFunction = 82;
const size_t m_SetStreamSource = 83;
const size_t m_GetStreamSource = 84;
const size_t m_SetIndices = 85;
const size_t m_GetIndices = 86;
const size_t m_CreatePixelShader = 87;
const size_t m_SetPixelShader = 88;
const size_t m_GetPixelShader = 89;
const size_t m_DeletePixelShader = 90;
const size_t m_SetPixelShaderConstant = 91;
const size_t m_GetPixelShaderConstant = 92;
const size_t m_GetPixelShaderFunction = 93;
const size_t m_DrawRectPatch = 94;
const size_t m_DrawTriPatch = 95;
const size_t m_DeletePatch = 96;
}
namespace VMT_IDirect3DIndexBuffer8_IDirect3DResource8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_Lock = 11;
const size_t m_Unlock = 12;
const size_t m_GetDesc = 13;
}
namespace VMT_IDirect3DResource8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
}
namespace VMT_IDirect3DSurface8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_GetContainer = 7;
const size_t m_GetDesc = 8;
const size_t m_LockRect = 9;
const size_t m_UnlockRect = 10;
}
namespace VMT_IDirect3DSwapChain8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_Present = 3;
const size_t m_GetBackBuffer = 4;
}
namespace VMT_IDirect3DTexture8_IDirect3DBaseTexture8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_SetLOD = 11;
const size_t m_GetLOD = 12;
const size_t m_GetLevelCount = 13;
const size_t m_GetLevelDesc = 14;
const size_t m_GetSurfaceLevel = 15;
const size_t m_LockRect = 16;
const size_t m_UnlockRect = 17;
const size_t m_AddDirtyRect = 18;
}
namespace VMT_IDirect3DVertexBuffer8_IDirect3DResource8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_Lock = 11;
const size_t m_Unlock = 12;
const size_t m_GetDesc = 13;
}
namespace VMT_IDirect3DVolume8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_GetContainer = 7;
const size_t m_GetDesc = 8;
const size_t m_LockBox = 9;
const size_t m_UnlockBox = 10;
}
namespace VMT_IDirect3DVolumeTexture8_IDirect3DBaseTexture8 {
const size_t m_QueryInterface = 0;
const size_t m_AddRef = 1;
const size_t m_Release = 2;
const size_t m_GetDevice = 3;
const size_t m_SetPrivateData = 4;
const size_t m_GetPrivateData = 5;
const size_t m_FreePrivateData = 6;
const size_t m_SetPriority = 7;
const size_t m_GetPriority = 8;
const size_t m_PreLoad = 9;
const size_t m_GetType = 10;
const size_t m_SetLOD = 11;
const size_t m_GetLOD = 12;
const size_t m_GetLevelCount = 13;
const size_t m_GetLevelDesc = 14;
const size_t m_GetVolumeLevel = 15;
const size_t m_LockBox = 16;
const size_t m_UnlockBox = 17;
const size_t m_AddDirtyBox = 18;
}

124
ScrapHacks/src/Hook.hpp Normal file
View file

@ -0,0 +1,124 @@
#pragma once
#include <Windows.h>
#include <functional>
#include <iostream>
#include <map>
using namespace std;
class Hook {
private:
MEMORY_BASIC_INFORMATION mbi;
void *orig;
void *detour;
bool enabled;
uint8_t orig_bytes[6];
uint8_t jmp_bytes[6];
static map<uintptr_t, shared_ptr<Hook>> hooks;
public:
Hook(void *func, void *detour) {
uintptr_t dest = reinterpret_cast<uintptr_t>(detour);
uintptr_t src = reinterpret_cast<uintptr_t>(func);
this->orig = func;
this->detour = detour;
this->jmp_bytes[0] = 0x68; // push
this->jmp_bytes[1] = (dest >> 0) & 0xff;
this->jmp_bytes[2] = (dest >> 8) & 0xff;
this->jmp_bytes[3] = (dest >> 16) & 0xff;
this->jmp_bytes[4] = (dest >> 24) & 0xff;
this->jmp_bytes[5] = 0xC3; // ret
VirtualQuery(func, &mbi, sizeof(mbi));
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE,
&mbi.Protect);
memcpy(this->orig_bytes, this->orig, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
this->enabled = false;
}
~Hook() {
cout << "Unhooking: [" << this->orig << " <- " << this->detour << "]"
<< endl;
this->disable();
}
static void addr(void *addr, void *detour) {
cout << "Hooking: [" << addr << " -> " << detour << "]" << endl;
uintptr_t key = reinterpret_cast<uintptr_t>(detour);
hooks[key] = make_shared<Hook>(addr, detour);
hooks[key]->enable();
}
static void module(const char *mod, const char *func, void *detour) {
cout << "Hooking: [" << mod << "]." << func << " -> " << detour << endl;
void *addr = GetProcAddress(GetModuleHandle(mod), func);
if (addr != NULL) {
Hook::addr(addr, detour);
} else {
cerr << "[" << mod << "]." << func << " not found!" << endl;
};
}
static shared_ptr<Hook> get(void *func) {
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return Hook::get(addr);
}
static shared_ptr<Hook> get(uintptr_t addr) { return hooks.at(addr); }
static size_t drop(void *func) {
uintptr_t addr = reinterpret_cast<uintptr_t>(func);
return Hook::drop(addr);
}
static size_t drop(uintptr_t addr) { return hooks.erase(addr); }
static void clear() {
cout << "Clearing Hooks" << endl;
for (pair<uintptr_t, shared_ptr<Hook>> h : hooks) {
h.second->disable();
}
return hooks.clear();
}
void disable() {
if (enabled) {
// cout << "Disabling: [" << this->orig << " <- " << this->detour <<
// "]"
// << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize,
PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->orig_bytes, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = false;
}
}
void enable() {
if (!this->enabled) {
// cout << "Enabling: [" << this->orig << " -> " << this->detour <<
// "]" << endl;
VirtualProtect(mbi.BaseAddress, mbi.RegionSize,
PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->jmp_bytes, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
this->enabled = true;
}
}
template <typename F, typename... Args> void func_void(Args... args) {
this->disable();
reinterpret_cast<F>(this->orig)(args...);
this->enable();
return;
}
template <typename F, typename... Args> auto func(Args... args) {
this->disable();
auto ret = reinterpret_cast<F>(this->orig)(args...);
this->enable();
return ret;
}
};
map<uintptr_t, shared_ptr<Hook>> Hook::hooks;

View file

@ -0,0 +1,58 @@
#pragma once
#include <iostream>
#include <map>
#include <string>
#include "Structures.hpp"
using namespace std;
map<string, Module> Py;
PyMethodDef *find_method_table(uintptr_t base, uintptr_t needle) {
for (ptrdiff_t offset = 0; offset < 64; ++offset) {
uintptr_t instr = reinterpret_cast<uintptr_t *>(base + offset)[0];
if (instr == needle) {
uintptr_t mod_addr =
reinterpret_cast<uintptr_t *>(base + offset - (1 + 4))[0];
return reinterpret_cast<PyMethodDef *>(mod_addr);
}
}
return reinterpret_cast<PyMethodDef *>(0);
}
map<string, Module> get_modules(uintptr_t base) {
map<string, Module> Py;
PyMod *modules = reinterpret_cast<PyMod *>(base);
for (size_t i = 0; modules[i].init_func != NULL; i++) {
Module mod;
mod.mod = &modules[i];
PyMethodDef *method_table =
find_method_table((size_t)modules[i].init_func,
reinterpret_cast<uintptr_t>(modules[i].name));
for (size_t j = 0;
method_table != NULL && method_table[j].ml_name != NULL; j++) {
mod.methods[method_table[j].ml_name] = &method_table[j];
}
Py[mod.mod->name] = mod;
}
return Py;
}
void *get_py(const char *mod, const char *meth) {
try {
return Py.at(mod).methods.at(meth)->ml_meth;
} catch (out_of_range) {
return NULL;
}
}
void inject(const char *mod, const char *meth, void *detour) {
try {
void *orig = get_py(mod, meth);
Py.at(mod).methods.at(meth)->ml_meth = detour;
cout << mod << "." << meth << ": " << orig << " -> " << detour << endl;
} catch (out_of_range) {
cout << mod << "." << meth << " not found!" << endl;
}
}

259
ScrapHacks/src/REPL.hpp Normal file
View file

@ -0,0 +1,259 @@
#pragma once
#include <Windows.h>
#include <regex>
#include <sstream>
#include "Scrapland.hpp"
#include "Util.hpp"
void DllUnload();
void unhook_d3d8();
void hook_d3d8();
typedef void(_cdecl *t_cmd_func)(vector<string>);
struct t_cmd {
t_cmd_func func;
const char* usage;
const char* doc;
};
void cmd_help(vector<string>);
DWORD
get_protection(void *addr) {
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(addr, &mbi, sizeof(mbi));
return mbi.Protect;
}
void cmd_write(vector<string> args) {
MEMORY_BASIC_INFORMATION mbi;
if (args.size() != 2) {
scrap_log(ERR_COLOR, "Usage: $w <addr> <data(hex)>\n");
return;
}
void *addr = 0;
vector<uint8_t> data;
try {
addr = (void *)stoull(args[0], 0, 16);
data = fromhex(args[1]);
} catch (exception e) {
scrap_log(ERR_COLOR, "ERROR!\n");
return;
}
uint8_t *buffer = new uint8_t[data.size()];
size_t idx = 0;
for (uint8_t v : data) {
buffer[idx++] = v;
}
cout << "W:" << (void *)addr << endl;
cout << buffer << endl;
if (VirtualQuery(addr, &mbi, sizeof(mbi)) == 0) {
scrap_log(ERR_COLOR, "ERROR!\n");
return;
};
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE,
&mbi.Protect);
memcpy(addr, buffer, data.size());
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
if (buffer) {
free(buffer);
}
return;
}
void cmd_read(vector<string> args) {
MEMORY_BASIC_INFORMATION mbi;
if (args.size() != 2) {
scrap_log(ERR_COLOR, "Usage: $r <addr> <size>\n");
return;
}
uintptr_t addr = UINTPTR_MAX;
size_t size = 0;
unsigned char *buffer;
try {
addr = stoull(args[0], 0, 16);
size = stoull(args[1]);
buffer = new unsigned char[size];
} catch (exception e) {
scrap_log(ERR_COLOR, "ERROR!\n");
return;
}
void *mptr = reinterpret_cast<void *>(addr);
if (VirtualQuery(mptr, &mbi, sizeof(mbi)) == 0) {
scrap_log(ERR_COLOR, "ERROR!\n");
return;
};
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE,
&mbi.Protect);
string hxd = hexdump_s(mptr, size);
scrap_log(INFO_COLOR, hxd.c_str());
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
if (buffer) {
free(buffer);
}
return;
}
void cmd_dx8(vector<string> args) {
if (args.size()!=1) {
scrap_log(ERR_COLOR, "Usage: $dx8 (hook|unhook)\n");
return;
}
if (args[0]=="hook") {
hook_d3d8();
scrap_log(INFO_COLOR,"DX8 hooked!\n");
return;
}
if (args[0]=="unhook") {
unhook_d3d8();
scrap_log(INFO_COLOR,"DX8 unhooked!\n");
return;
};
scrap_log(ERR_COLOR,"Invalid argument!\n");
return;
}
void cmd_dump_py(vector<string> args) {
stringstream out;
for (auto mod : Py) {
for (auto meth : mod.second.methods) {
out << mod.first << "." << meth.first << " @ "
<< meth.second->ml_meth << endl;
}
}
scrap_log(INFO_COLOR,out.str().c_str());
}
void cmd_dump_ents(vector<string> args) {
stringstream out;
out << "Entities:" << endl;
dump_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS), &out);
out << "Entity Lists:" << endl;
dump_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS), &out);
scrap_log(INFO_COLOR,out.str().c_str());
return;
}
void cmd_toggle_overlay(vector<string> args) {
overlay=!overlay;
if (overlay) {
scrap_log(INFO_COLOR,"Overlay enabled!\n");
} else {
scrap_log(INFO_COLOR,"Overlay disabled!\n");
}
}
void cmd_print_alarm(vector<string> args) {
stringstream out;
float *alarm = ptr<float>(P_WORLD, O_ALARM);
float *alarm_grow = ptr<float>(P_WORLD, O_ALARM_GROW);
out << "Alarm: " << alarm[0] << " + " << alarm_grow[0] << endl;
scrap_log(INFO_COLOR,out.str().c_str());
return;
}
void cmd_unload(vector<string> args) {
scrap_log(INFO_COLOR,"Unloading ScrapHacks... bye!\n");
DllUnload();
}
static map<string, t_cmd> commands = {
{"w", {
cmd_write,
"Usage: $w <addr> <data(hex)>",
"Write memory"
}},
{"r", {
cmd_read,
"Usage: $r <addr> <num_bytes>",
"Read memory"
}},
{"unload", {
cmd_unload,
"Usage: $unload",
"Unload ScrapHacks"
}},
{"dx8", {
cmd_dx8,
"Usage: $dx8 (hook|unhook)",
"Hook/Unhook DirectX 8 functions"
}},
{"dump_py",{
cmd_dump_py,
"Usage: $dump_py",
"Dump python modules to console"
}},
{"overlay",{
cmd_toggle_overlay,
"Usage: $overlay",
"Toggle DX8 Overlay"
}},
{"alarm",{
cmd_print_alarm,
"Usage: $alarm",
"Print alarm status"
}},
{"ents",{
cmd_dump_ents,
"Usage: $ents",
"Dump entity information"
}},
{"help", {
cmd_help,
"Usage: $help [command]",
"Print help for ScrapHacks command"}},
};
void cmd_help(vector<string> args) {
if (args.size()!=1) {
for (auto cmd: commands) {
scrap_log(INFO_COLOR,cmd.first.c_str());
scrap_log(INFO_COLOR,": ");
scrap_log(INFO_COLOR,cmd.second.doc);
scrap_log(INFO_COLOR,"\n");
}
return;
}
if (!commands.count(args[0])) {
scrap_log(ERR_COLOR, "Unknown command '");
scrap_log(ERR_COLOR, args[0].c_str());
scrap_log(ERR_COLOR, "'!\n");
return;
}
t_cmd cmd=commands[args[0]];
scrap_log(INFO_COLOR,args[0].c_str());
scrap_log(INFO_COLOR,": ");
scrap_log(INFO_COLOR,cmd.usage);
scrap_log(INFO_COLOR,"\n\t");
scrap_log(INFO_COLOR,cmd.doc);
scrap_log(INFO_COLOR,"\n");
return;
}
void handle_command(const char *_cmd) {
scrap_log(ERR_COLOR, "$");
scrap_log(ERR_COLOR, _cmd);
scrap_log(ERR_COLOR, "\n");
cout << "CMD: '" << _cmd << "'" << endl;
vector<string> cmd = split(string(_cmd), ' ');
if (cmd.size() == 0) {
return;
}
if (commands.count(cmd[0])) {
string command = cmd[0];
cmd.erase(cmd.begin());
commands[command].func(cmd);
} else {
scrap_log(ERR_COLOR, "Unknown command '");
scrap_log(ERR_COLOR, cmd[0].c_str());
scrap_log(ERR_COLOR, "'!\n");
}
return;
}

View file

@ -0,0 +1,177 @@
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <string>
#include <typeinfo>
#include <vector>
#include <Windows.h>
// Socket stuff
#include <Ws2tcpip.h>
#include <stdio.h>
#include <winsock2.h>
using namespace std;
#include "D3D8_Hook.hpp"
#include "Hook.hpp"
#include "Py_Utils.hpp"
#include "REPL.hpp"
#include "Scrapland.hpp"
#include "Structures.hpp"
#include "Util.hpp"
bool initialized = false;
bool running = true;
HMODULE hMod = nullptr;
void DllUnload();
int hooked_console(const char *);
void hook_exit();
int hook_recvfrom(SOCKET s, char *buf, int len, int flags, sockaddr *from,
int *fromlen) {
typedef decltype(&hook_recvfrom) t_func;
shared_ptr<Hook> hook = Hook::get(hook_recvfrom);
int ret = hook->func<t_func>(s, buf, len, flags, from, fromlen);
return ret;
};
int hook_sendto(SOCKET s, const char *buf, int len, int flags,
const sockaddr *to, int tolen) {
typedef decltype(&hook_sendto) t_func;
shared_ptr<Hook> hook = Hook::get(hook_sendto);
int ret = hook->func<t_func>(s, buf, len, flags, to, tolen);
return ret;
};
void setup_hooks() {
Hook::addr(reinterpret_cast<void *>(P_SCRAP_EXIT), hook_exit);
Hook::addr(reinterpret_cast<void *>(P_CON_HANDLER), hooked_console);
}
void MainLoop() {
setup_hooks();
overlay = true;
cout << "[*] Starting main Loop" << endl;
cout << endl;
cout << "[F2 ] Redirect game console to ScapHacks console" << endl;
cout << "[F3 ] Unload ScrapHacks" << endl;
cout << "[F5 ] Show Overlay" << endl;
cout << "[F6 ] Show Alarm status" << endl;
cout << "[F7 ] Set Money to 0x7fffffff" << endl;
cout << "[F8 ] Dump python modules" << endl;
cout << "[F9 ] Dump Entity hashtable" << endl;
cout << "[F10] Enable python tracing" << endl;
cout << "[ F ] \"Handbrake\" (*Will* crash the game after some time!)"
<< endl;
while (running) {
Sleep(100);
while (key_down('F')) {
scrap_exec("dbg.brake()");
}
if (key_down_norepeat(VK_F3)) {
break;
}
if (key_down_norepeat(VK_F7)) {
int32_t *money = ptr<int32_t>(P_WORLD, O_MONEY);
money[0] = 0x7fffffff;
}
if (key_down_norepeat(VK_F9)) {
cout << "Entities:" << endl;
dump_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS));
cout << "Entity Lists:" << endl;
dump_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS));
}
if (key_down_norepeat(VK_F10)) {
scrap_exec("dbg.settrace()");
}
}
FreeLibraryAndExitThread(hMod, 0);
}
void InitConsole() {
char me[1024];
GetModuleFileName(hMod, me, 1024);
SetupConsole(me);
}
int hooked_console(const char *cmd) {
typedef decltype(&hooked_console) t_func;
if (cmd[0] == '$') {
handle_command(++cmd);
return 0;
}
shared_ptr<Hook> hook = Hook::get(hooked_console);
int ret = hook->func<t_func>(cmd);
return ret;
}
void hook_exit() {
typedef decltype(&hook_exit) t_func;
shared_ptr<Hook> hook = Hook::get(hook_exit);
DllUnload();
HWND hMainWindow = ptr<HWND>(0x7FA830, 0x7c)[0];
SendMessage(hMainWindow, WM_CLOSE, 0, 0);
return;
}
void DllInit(HMODULE mod) {
hMod = mod;
char mfn[1024];
InitConsole();
GetModuleFileNameA(0, mfn, 1024);
Py = get_modules(P_PY_MODS);
cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << " (PID: " << std::hex
<< GetCurrentProcessId() << std::dec << ")" << endl;
initialized = true;
cout << "[*] World: " << ptr<void>(P_WORLD, 0) << endl;
cout << "[*] Importing python dbg module" << endl;
scrap_exec("import dbg");
scrap_log(INFO_COLOR, "=== ScrapHacks loaded! ===\n");
scrap_log(INFO_COLOR, "=== Use '$help' for help! ===\n");
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MainLoop, NULL, 0, 0);
cout << "[*] Starting message pump" << endl;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return;
}
void *H_port_FixupExtension(char *name, char *filename) {
Hook::drop(H_port_FixupExtension);
return NULL;
}
void *H_PyEval_CallObjectWithKeywords(void *func, void *arg, void *kwarg) {
Hook::drop(H_PyEval_CallObjectWithKeywords);
return NULL;
}
void DllPreInit() {
Hook::addr(reinterpret_cast<void *>(0x5a9ca0), H_port_FixupExtension);
Hook::addr(reinterpret_cast<void *>(0x5cdb00),
H_PyEval_CallObjectWithKeywords);
}
void DllUnload() {
SetConsoleCtrlHandler(NULL, false);
unhook_d3d8();
Hook::clear();
scrap_log(0xff0000, "ScrapHacks unloaded!\n");
cout << "[+] ScrapHacks unloaded, you can now close the console!" << endl;
FreeConsole();
DestroyWindow(GetConsoleWindow());
return;
}

View file

@ -0,0 +1,53 @@
#pragma once
// OFFSETS
#define O_MONEY 0x2090
#define O_ALARM 0x1C6C
#define O_ALARM_GROW 0x1C68
#define O_ENTS 0x4
#define O_ENTLISTS 0x2b8
// POINTERS
#define P_WORLD 0x7FE944
#define P_PY_MODS 0x79C698
// FUNCTION ADDRESSES
#define P_CON_HANDLER 0x402190
#define P_SCRAP_LOG 0x4134C0
#define P_SCRAP_EXEC 0x5a8390
#define P_SCRAP_EXIT 0x4010c0
#define P_D3DCHECK 0x602a70
#define P_D3DDEV 0x852914
#define P_Py_InitModule 0x5A8FB0
#define P_PyArg_ParseTuple 0x5bb9d0
#define MSG_COLOR scrap_RGB(255,128,0)
#define ERR_COLOR scrap_RGB(255,0,0)
#define INFO_COLOR scrap_RGB(0,0,255)
uint32_t scrap_RGB(uint8_t r,uint8_t g,uint8_t b) {
return r|g<<8|b<<16;
}
// FUNCTION TYPES
typedef int(_cdecl *t_scrap_log)(unsigned int color, const char *message);
typedef int(_cdecl *t_scrap_exec)(const char *code);
typedef int(_cdecl *t_PyArg_ParseTuple)(void *PyObj, char *format, ...);
typedef int(_cdecl *t_Py_InitModule)(const char *name, void *methods,
const char *doc, void *passthrough,
int module_api_version);
// GLOBAL FUNCTIONS
auto scrap_exec = (t_scrap_exec)P_SCRAP_EXEC;
auto pyarg_parsetuple = (t_PyArg_ParseTuple)P_PyArg_ParseTuple;
auto py_initmodule = (t_Py_InitModule)P_Py_InitModule;
int scrap_log(unsigned int color,const char* msg) {
return ((t_scrap_log)P_SCRAP_LOG)(color,msg);
}
int scrap_log(uint8_t r,uint8_t g,uint8_t b,const char* msg) {
return ((t_scrap_log)P_SCRAP_LOG)(scrap_RGB(r,g,b),msg);
}

View file

@ -0,0 +1,54 @@
#pragma once
template <typename T> struct HashTableEntry;
struct Vector3 {
float x;
float y;
float z;
};
struct Matrix3x3 {
Vector3 a;
Vector3 b;
Vector3 c;
};
struct PyMethodDef {
char *ml_name;
void *ml_meth;
int ml_flags;
char *ml_doc;
};
struct PyMod {
char *name;
void *init_func;
};
struct Module {
PyMod *mod;
map<string, PyMethodDef *> methods;
};
struct Entity {
void *vmt;
const char *name;
};
struct EntityList {
const char *name;
void *unk_1;
void *unk_2;
const char *mod;
const char *func;
};
template <typename T> struct HashTable {
uint32_t size;
HashTableEntry<T> **chains;
};
template <typename T> struct HashTableEntry {
T *data;
const char *name;
HashTableEntry *next;
};

375
ScrapHacks/src/Util.hpp Normal file
View file

@ -0,0 +1,375 @@
#pragma once
#include <TlHelp32.h>
#include <Windows.h>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "Structures.hpp"
#include "Py_Utils.hpp"
using namespace std;
#define DLL_EXPORT extern "C" __declspec(dllexport)
template <typename T> void **GetVTable(T *obj) {
void *addr = reinterpret_cast<void **>(obj)[0];
return (void **)(*(void **)addr);
}
string GetLastErrorAsString() {
DWORD errorMessageID = GetLastError();
if (errorMessageID == 0)
return "No error";
LPSTR messageBuffer = NULL;
size_t m_size = FormatMessageA(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&messageBuffer, 0, NULL);
string message(messageBuffer, m_size);
LocalFree(messageBuffer);
if (!message.empty() && message[message.length() - 1] == '\n') {
message.erase(message.length() - 1);
}
return message;
}
void SetupStreams() {
FILE *fIn;
FILE *fOut;
freopen_s(&fIn, "conin$", "r", stdin);
freopen_s(&fOut, "conout$", "w", stdout);
freopen_s(&fOut, "conout$", "w", stderr);
ios::sync_with_stdio();
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
std::wcin.clear();
std::cin.clear();
}
void SetupConsole() {
if (!AttachConsole(-1)) {
if (!AllocConsole()) {
FreeConsole();
AllocConsole();
}
AttachConsole(GetCurrentProcessId());
}
SetupStreams();
}
void SetupConsole(const char *title) {
SetupConsole();
SetConsoleTitleA(title);
}
void FreeConsole(bool wait) {
if (wait) {
cout << "[?] Press Enter to Exit";
cin.ignore();
}
FreeConsole();
}
bool in_foreground = false;
BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam) {
DWORD lpdwProcessId;
GetWindowThreadProcessId(hwnd, &lpdwProcessId);
if (lpdwProcessId == lParam) {
in_foreground =
(hwnd == GetForegroundWindow()) || (hwnd == GetActiveWindow());
return FALSE;
}
return TRUE;
}
bool key_down(int keycode, int delay = 100) {
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground) {
if (GetAsyncKeyState(keycode)) {
Sleep(delay);
return true;
}
}
return false;
}
bool key_down_norepeat(int keycode, int delay = 100) {
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground) {
if (GetAsyncKeyState(keycode)) {
while (GetAsyncKeyState(keycode)) {
Sleep(delay);
}
return true;
}
}
return false;
}
string hexdump_s(void *addr, size_t count=0xff) {
ostringstream out;
uintptr_t offset=reinterpret_cast<uintptr_t>(addr);
for (size_t i = 0; i < count; ++i) {
unsigned int val = (unsigned int)(((unsigned char *)(offset+i))[0]);
if ((i % 16) == 0) {
out << endl;
out << setfill('0') << setw(8) << std::hex << std::uppercase << (offset+i) << ": ";
}
out << setfill('0') << setw(2) << std::hex << val << " ";
}
out << endl;
return out.str();
}
void hexdump(void *addr, size_t count=0xff) {
uintptr_t offset=reinterpret_cast<uintptr_t>(addr);
for (size_t i = 0; i < count; ++i) {
unsigned int val = (unsigned int)(((unsigned char *)(offset+i))[0]);
if ((i % 16) == 0) {
cout << endl;
cout << setfill('0') << setw(8) << std::hex << std::uppercase << (offset+i) << ": ";
}
cout << setfill('0') << setw(2) << std::hex << val << " ";
}
cout << endl;
return;
}
template <typename T> T *__ptr(uintptr_t addr) {
return reinterpret_cast<T *>(addr);
}
template <typename T> T *__ptr(uintptr_t addr, ptrdiff_t offset) {
// cout << "[" << (void*)addr << "] + " << (void*)offset << " = ";
addr = reinterpret_cast<uintptr_t *>(addr)[0] + offset;
// cout << (void*)addr << endl;;
auto ret = __ptr<T>(addr);
return ret;
}
template <typename T, typename... Offsets>
T *__ptr(uintptr_t addr, ptrdiff_t offset, Offsets... offsets) {
// cout << "[" << (void*)addr << "] + " << (void*)offset << " = ";
addr = reinterpret_cast<uintptr_t *>(addr)[0] + offset;
// cout << (void*)addr << endl;;
auto ret = __ptr<T>(addr, offsets...);
return ret;
}
template <typename T, typename... Offsets>
T *ptr(uintptr_t addr, Offsets... offsets) {
auto ret = __ptr<T>(addr, offsets...);
return ret;
}
template <typename T>
void __to_str(ostream& o, T t)
{
o << t;
}
template<typename T, typename... Args>
void __to_str(ostream& o, T t, Args... args) // recursive variadic function
{
__to_str(o, t);
__to_str(o, args...);
}
template<typename... Args>
const char* to_str(Args... args)
{
ostringstream oss;
__to_str(oss, args...);
return oss.str().c_str();
}
DWORD
PPID() {
DWORD PID = GetCurrentProcessId();
HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 procentry;
if (hSnapShot == INVALID_HANDLE_VALUE) {
cout << GetLastErrorAsString() << endl;
return -1;
}
if (Process32First(hSnapShot, &procentry)) {
do {
if (procentry.th32ProcessID == PID) {
CloseHandle(hSnapShot);
return procentry.th32ParentProcessID;
}
procentry.dwSize = sizeof(PROCESSENTRY32);
} while (Process32Next(hSnapShot, &procentry));
}
CloseHandle(hSnapShot);
return -1;
}
vector<uint8_t> fromhex(string input) {
vector<uint8_t> ret = {};
if (input.size() % 2) {
return ret;
}
transform(input.begin(), input.end(), input.begin(), ::toupper);
string hc = "0123456789ABCDEF";
int v = 0;
size_t n = 0;
size_t idx;
for (unsigned char c : input) {
idx = hc.find(c);
if (idx != size_t(-1)) {
if ((n++) % 2 == 0) {
v = hc.find(c) << 4;
} else {
v |= hc.find(c);
ret.push_back(v);
}
} else {
cout << "Invalid Character in hex string" << endl;
ret.clear();
return ret;
}
}
return ret;
}
vector<string> split(string str, char sep) {
vector<string> ret;
string part;
for (auto n : str) {
if (n == sep) {
ret.push_back(part);
part.clear();
} else {
part = part + n;
}
}
if (part != "")
ret.push_back(part);
return ret;
}
size_t size_ht(HashTable<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t size_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
}

View file

@ -0,0 +1,31 @@
#include <Windows.h>
#define DLL_EXPORT extern "C" __declspec(dllexport)
using namespace std;
void DllInit(HMODULE);
void DllUnload();
void DllPreInit();
HANDLE hThread = INVALID_HANDLE_VALUE;
bool loaded = false;
HMODULE mod = nullptr;
DLL_EXPORT void initScrapHack() {
DllPreInit();
if (!loaded) {
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)DllInit, mod,
0, 0);
CloseHandle(hThread);
loaded = true;
}
}
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved) {
mod = hModule;
return true;
}

View file

@ -0,0 +1,31 @@
import os
import sys
import re
outfile,infile=sys.argv[1:]
re_interface=re.compile(r"^DECLARE_INTERFACE_{0,1}\((.*?)\)$")
re_method=re.compile(r"^\w*STDMETHOD_{0,1}\((.*?)\)\((.*?)\).*;")
name=None
idx=0
VMTs={}
with open(infile,"r") as infh:
for line in infh:
line=line.strip()
interf=re_interface.match(line)
meth=re_method.match(line)
if interf:
idx=0
name="VMT_"+"_".join([name for name in interf.groups()[0].split(", ") if name!="IUnknown"])
VMTs[name]={}
if meth:
meth_name,meth_args=meth.groups()
meth_name=meth_name.split(",")[-1].strip()
VMTs[name][meth_name]=idx
idx+=1
print(f"Generating: {outfile} from {infile} ...")
with open(outfile,"w") as ofh:
for name in sorted(VMTs.keys()):
print(f"namespace {name} {{",file=ofh)
for method,idx in sorted(VMTs[name].items(),key=lambda v:v[1]):
print(f"\tconst size_t m_{method} = {idx};",file=ofh)
print("}",file=ofh)

View file

@ -52,7 +52,7 @@ r2_cmd("aaaaa")
#0x413ee0
#0x7d2094 refcnt
flags = {0x7FE944: ("World_Ptr", 4), 0x79C698: ("Py_Mods", 4)}
flags = {0x7FE944: ("P_World", 4), 0x79C698: ("Py_Mods", 4),0x852914: ("P_D3D8_Dev",4)}
types = ["struct PyMethodDef {char *ml_name; void *ml_meth; int ml_flags; char *ml_doc;};"]
@ -70,6 +70,9 @@ func_sigs = {
0x419950: "int fopen_2(const char* filename);",
0x41AB50: "int open_pak(const char* filename, int unk_1,void* unk_ptr);",
0x404460: "int register_c_callback(const char* name,void* func);"
0x414070: "void throw_assertion_2(const char* check,const char* file, unsigned int line);"
0x5FBC50: "void throw_assertion_1(const char* check,const char* file,const char* date, unsigned int line);",
0x5bc140: "static char* convertsimple1(void *arg, char **p_format, void *p_va);"
}
functions = {
@ -154,30 +157,38 @@ def c_callbacks():
def assertions():
assertions = {}
for a_addr in ["fcn.throw_assertion_1", "fcn.throw_assertion_2"]:
for (n_args,a_addr) in [(4,"fcn.throw_assertion_1"), (3,"fcn.throw_assertion_2")]:
print(f"[*] Parsing C assertions for {a_addr}")
res = r2_cmd(f"/r {a_addr} ~CALL[1]").splitlines()
print()
for line in tqdm(res, ascii=True):
addr = line.strip()
file, msg = r2_cmdJ(f"s {addr};so -2;pij 2") # seek and print disassembly
dis=r2_cmdJ(f"s {addr};so -{n_args};pij {n_args}") # seek and print disassembly
if n_args==4:
file, msg, date, line = dis
elif n_args==3:
date=None
file, msg, line = dis
try:
file = r2_cmd(f"psz @{file.refs[0].addr}").strip()
msg = r2_cmd(f"psz @{msg.refs[0].addr}").strip()
path = os.path.abspath(file.replace("\\\\", "\\"))
if date:
r2_cmd(f"psz @{date.refs[0].addr}").strip()
line=line.val
file=file.replace("\\\\", "\\")
os.path.isabs(file):
file = os.path.abspath(file)
assertions.setdefault(path, [])
assertions[path].append([addr, msg])
assertions[path].append({'line':line,'date':date,'addr':addr,'msg': msg})
except:
pass
for path in assertions:
assertions[path].sort(key=lambda v:int(v[0],16))
assertions[path].sort(key=lambda v:v['line'])
return assertions
def world():
def bb_refs(addr):
ret={}
print("[*] Parsing World offsets")
res = r2_cmd("/r loc.World_Ptr ~fcn[0,1]").splitlines()
res = r2_cmd(f"/r {addr} ~fcn[0,1]").splitlines()
print()
for ent in res:
func,hit=ent.split()
@ -186,6 +197,15 @@ def world():
ret[hit]['asm'].append(ins.disasm)
return ret
def world():
print("[*] Parsing World offsets")
return bb_refs("loc.P_World")
def render():
print("[*] Parsing D3D_Device offsets")
return bb_refs("loc.P_D3D8_Dev")
def py_mods():
print("[*] Parsing Python modules")
res = r2_cmd("/r fcn.Py_InitModule ~CALL[1]").splitlines()
@ -260,6 +280,7 @@ ret = dict(
assertions=assertions(),
vtables=vtables(),
world=world(),
render=render(),
)
r2_cmd("aaaaa") # Propagate type infos