From 493b10e78b227ad5669b0bf91c0f03449c67eeaa Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Wed, 4 Dec 2019 00:28:14 +0100 Subject: [PATCH] code cleanup, expand REPL, Automatically generate D3D8-VMT header file from include file, made ScrapHacks importable as python module --- ScrapHacks/.clang-format | 127 ++++++++++ ScrapHacks/.vscode/settings.json | 82 ++++++ ScrapHacks/CMakeLists.txt | 70 +++++- ScrapHacks/README.md | 18 +- ScrapHacks/ScrapHack/CMakeLists.txt | 24 -- ScrapHacks/ScrapHack/D3D8_Hook.h | 82 ------ ScrapHacks/ScrapHack/Hook.h | 141 ----------- ScrapHacks/ScrapHack/Py_Utils.h | 62 ----- ScrapHacks/ScrapHack/REPL.h | 54 ---- ScrapHacks/ScrapHack/ScrapHack.cpp | 266 -------------------- ScrapHacks/ScrapHack/Scrapland.h | 26 -- ScrapHacks/ScrapHack/Structures.h | 66 ----- ScrapHacks/ScrapHack/Util.h | 206 --------------- ScrapHacks/ScrapHack/dllmain.cpp | 30 --- ScrapHacks/src/D3D8_Hook.hpp | 136 ++++++++++ ScrapHacks/src/D3D8_VMT.hpp | 274 ++++++++++++++++++++ ScrapHacks/src/Hook.hpp | 124 +++++++++ ScrapHacks/src/Py_Utils.hpp | 58 +++++ ScrapHacks/src/REPL.hpp | 259 +++++++++++++++++++ ScrapHacks/src/ScrapHack.cpp | 177 +++++++++++++ ScrapHacks/src/Scrapland.hpp | 53 ++++ ScrapHacks/src/Structures.hpp | 54 ++++ ScrapHacks/src/Util.hpp | 375 ++++++++++++++++++++++++++++ ScrapHacks/src/dllmain.cpp | 31 +++ ScrapHacks/src/make_D3D8_VMT.py | 31 +++ r2_analyze.py | 41 ++- 26 files changed, 1894 insertions(+), 973 deletions(-) create mode 100644 ScrapHacks/.clang-format create mode 100644 ScrapHacks/.vscode/settings.json delete mode 100644 ScrapHacks/ScrapHack/CMakeLists.txt delete mode 100644 ScrapHacks/ScrapHack/D3D8_Hook.h delete mode 100644 ScrapHacks/ScrapHack/Hook.h delete mode 100644 ScrapHacks/ScrapHack/Py_Utils.h delete mode 100644 ScrapHacks/ScrapHack/REPL.h delete mode 100644 ScrapHacks/ScrapHack/ScrapHack.cpp delete mode 100644 ScrapHacks/ScrapHack/Scrapland.h delete mode 100644 ScrapHacks/ScrapHack/Structures.h delete mode 100644 ScrapHacks/ScrapHack/Util.h delete mode 100644 ScrapHacks/ScrapHack/dllmain.cpp create mode 100644 ScrapHacks/src/D3D8_Hook.hpp create mode 100644 ScrapHacks/src/D3D8_VMT.hpp create mode 100644 ScrapHacks/src/Hook.hpp create mode 100644 ScrapHacks/src/Py_Utils.hpp create mode 100644 ScrapHacks/src/REPL.hpp create mode 100644 ScrapHacks/src/ScrapHack.cpp create mode 100644 ScrapHacks/src/Scrapland.hpp create mode 100644 ScrapHacks/src/Structures.hpp create mode 100644 ScrapHacks/src/Util.hpp create mode 100644 ScrapHacks/src/dllmain.cpp create mode 100644 ScrapHacks/src/make_D3D8_VMT.py diff --git a/ScrapHacks/.clang-format b/ScrapHacks/.clang-format new file mode 100644 index 0000000..157147f --- /dev/null +++ b/ScrapHacks/.clang-format @@ -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 +... + diff --git a/ScrapHacks/.vscode/settings.json b/ScrapHacks/.vscode/settings.json new file mode 100644 index 0000000..6d52be3 --- /dev/null +++ b/ScrapHacks/.vscode/settings.json @@ -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" + } +} \ No newline at end of file diff --git a/ScrapHacks/CMakeLists.txt b/ScrapHacks/CMakeLists.txt index f9a92d3..1daa6c7 100644 --- a/ScrapHacks/CMakeLists.txt +++ b/ScrapHacks/CMakeLists.txt @@ -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) \ No newline at end of file + +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) diff --git a/ScrapHacks/README.md b/ScrapHacks/README.md index 5d57306..5eb791a 100644 --- a/ScrapHacks/README.md +++ b/ScrapHacks/README.md @@ -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) \ No newline at end of file diff --git a/ScrapHacks/ScrapHack/CMakeLists.txt b/ScrapHacks/ScrapHack/CMakeLists.txt deleted file mode 100644 index 0e1648f..0000000 --- a/ScrapHacks/ScrapHack/CMakeLists.txt +++ /dev/null @@ -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) diff --git a/ScrapHacks/ScrapHack/D3D8_Hook.h b/ScrapHacks/ScrapHack/D3D8_Hook.h deleted file mode 100644 index 8b13651..0000000 --- a/ScrapHacks/ScrapHack/D3D8_Hook.h +++ /dev/null @@ -1,82 +0,0 @@ -#pragma once -#include -#include -#include -#include -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 *ht); -size_t size_ht(HashTable *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(P_WORLD, 0) != nullptr) - { - money = ptr(P_WORLD, O_MONEY)[0]; - num_ents = size_ht(ptr>(P_WORLD, O_ENTS)); - num_ent_lst = size_ht(ptr>(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::get(H_EndScene); - return hook->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(0x853954,0x2a3d8,0,4*35,0),H_EndScene); - shared_ptr hook = Hook::get(hook_d3d8); - hook->func_void(); - hook->disable(); - Hook::drop(hook_d3d8); - return; -} diff --git a/ScrapHacks/ScrapHack/Hook.h b/ScrapHacks/ScrapHack/Hook.h deleted file mode 100644 index 01a9059..0000000 --- a/ScrapHacks/ScrapHack/Hook.h +++ /dev/null @@ -1,141 +0,0 @@ -#pragma once -#include -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> hooks; - -public: - Hook(void *func, void *detour) - { - uintptr_t dest = reinterpret_cast(detour); - uintptr_t src = reinterpret_cast(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(detour); - hooks[key] = make_shared(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 get(void *func) - { - uintptr_t addr = reinterpret_cast(func); - return Hook::get(addr); - } - - static shared_ptr get(uintptr_t addr) - { - return hooks.at(addr); - } - - static size_t drop(void *func) - { - uintptr_t addr = reinterpret_cast(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> 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 - void func_void(Args... args) - { - disable(); - reinterpret_cast(this->orig)(args...); - enable(); - return; - } - - template - decltype(auto) func(Args... args) - { - disable(); - auto ret = reinterpret_cast(this->orig)(args...); - enable(); - return ret; - } -}; - -map> Hook::hooks; diff --git a/ScrapHacks/ScrapHack/Py_Utils.h b/ScrapHacks/ScrapHack/Py_Utils.h deleted file mode 100644 index 557d47b..0000000 --- a/ScrapHacks/ScrapHack/Py_Utils.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once -#include "Structures.h" - -map Py; - -PyMethodDef *find_method_table(uintptr_t base, uintptr_t needle) -{ - for (ptrdiff_t offset = 0; offset < 64; ++offset) - { - uintptr_t instr = reinterpret_cast(base + offset)[0]; - if (instr == needle) - { - uintptr_t mod_addr = reinterpret_cast(base + offset - (1 + 4))[0]; - return reinterpret_cast(mod_addr); - } - } - return reinterpret_cast(0); -} - -map get_modules(uintptr_t base) -{ - map Py; - PyMod *modules = reinterpret_cast(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(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; - } -} \ No newline at end of file diff --git a/ScrapHacks/ScrapHack/REPL.h b/ScrapHacks/ScrapHack/REPL.h deleted file mode 100644 index dbda1a8..0000000 --- a/ScrapHacks/ScrapHack/REPL.h +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#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<<"'"< cmd = split(string(_cmd), ' '); - cout<<"PARTS: "; - for (string c:cmd) { - cout<<"'"< [size]\n"); - return; - } - scrap_log(0xff0000, "READ!\n"); - cout<<"READ!"< \n"); - return; - } - scrap_log(0xff0000, "WRITE!\n"); - cout<<"WRITE!"< -#include -#include -#include -#include -#include -#include -#include -#include - -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 *ht) -{ - size_t cnt = 0; - for (size_t i = 0; i < ht->size; ++i) - { - HashTableEntry *ent = ht->chains[i]; - if (ent) - { - while (ent) - { - ++cnt; - ent = ent->next; - } - } - } - return cnt; -} - -size_t size_ht(HashTable *ht) -{ - size_t cnt = 0; - for (size_t i = 0; i < ht->size; ++i) - { - HashTableEntry *ent = ht->chains[i]; - if (ent) - { - while (ent) - { - ++cnt; - ent = ent->next; - } - } - } - return cnt; -} - -size_t dump_ht(HashTable *ht) -{ - size_t cnt = 0; - for (size_t i = 0; i < ht->size; ++i) - { - HashTableEntry *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 *ht) -{ - size_t cnt = 0; - for (size_t i = 0; i < ht->size; ++i) - { - HashTableEntry *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(P_SCRAP_EXIT), H_Exit); - Hook::addr(reinterpret_cast(P_D3DCHECK),hook_d3d8); - Hook::addr(reinterpret_cast(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(P_WORLD, O_ALARM); - float *alarm_grow = ptr(P_WORLD, O_ALARM_GROW); - cout << "Alarm: " << alarm[0] << " + " << alarm_grow[0] << endl; - } - if (key_down_norepeat(VK_F7)) - { - int32_t *money = ptr(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>(P_WORLD, O_ENTS)); - cout << "Entity Lists:" << endl; - dump_ht(ptr>(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::get(hooked_console); - int ret = hook->func(cmd); - return ret; -} - -void H_Exit() -{ - typedef void(_cdecl * t_func)(void); - shared_ptr hook = Hook::get(H_Exit); - DllUnload(mod); - HWND hMainWindow = ptr(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(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; -} \ No newline at end of file diff --git a/ScrapHacks/ScrapHack/Scrapland.h b/ScrapHacks/ScrapHack/Scrapland.h deleted file mode 100644 index 1099a20..0000000 --- a/ScrapHacks/ScrapHack/Scrapland.h +++ /dev/null @@ -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; diff --git a/ScrapHacks/ScrapHack/Structures.h b/ScrapHacks/ScrapHack/Structures.h deleted file mode 100644 index 7a69bdb..0000000 --- a/ScrapHacks/ScrapHack/Structures.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once -template -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 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 -struct HashTable -{ - uint32_t size; - HashTableEntry **chains; -}; - -template -struct HashTableEntry -{ - T *data; - const char *name; - HashTableEntry *next; -}; \ No newline at end of file diff --git a/ScrapHacks/ScrapHack/Util.h b/ScrapHacks/ScrapHack/Util.h deleted file mode 100644 index b0da991..0000000 --- a/ScrapHacks/ScrapHack/Util.h +++ /dev/null @@ -1,206 +0,0 @@ -#pragma once -#include -#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 -T *__ptr(uintptr_t addr) -{ - return reinterpret_cast(addr); -} - -template -T *__ptr(uintptr_t addr, ptrdiff_t offset) -{ - //cout << "[" << (void*)addr << "] + " << (void*)offset << " = "; - addr = reinterpret_cast(addr)[0] + offset; - //cout << (void*)addr << endl;; - auto ret = __ptr(addr); - return ret; -} - -template -T *__ptr(uintptr_t addr, ptrdiff_t offset, Offsets... offsets) -{ - //cout << "[" << (void*)addr << "] + " << (void*)offset << " = "; - addr = reinterpret_cast(addr)[0] + offset; - //cout << (void*)addr << endl;; - auto ret = __ptr(addr, offsets...); - return ret; -} - -template -T *ptr(uintptr_t addr, Offsets... offsets) -{ - auto ret = __ptr(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 split(string str, char sep) -{ - vector 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; -} diff --git a/ScrapHacks/ScrapHack/dllmain.cpp b/ScrapHacks/ScrapHack/dllmain.cpp deleted file mode 100644 index 1665555..0000000 --- a/ScrapHacks/ScrapHack/dllmain.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include -#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; -} diff --git a/ScrapHacks/src/D3D8_Hook.hpp b/ScrapHacks/src/D3D8_Hook.hpp new file mode 100644 index 0000000..f15cf29 --- /dev/null +++ b/ScrapHacks/src/D3D8_Hook.hpp @@ -0,0 +1,136 @@ +#pragma once +#include +#include +#include +#include + +#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 *ht); +size_t size_ht(HashTable *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(P_WORLD, 0) != nullptr) { + money = ptr(P_WORLD, O_MONEY)[0]; + num_ents = size_ht(ptr>(P_WORLD, O_ENTS)); + num_ent_lst = size_ht(ptr>(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::get(H_EndScene); + return hook->func(Render(dev)); +} + +HRESULT WINAPI H_SetLight(LPDIRECT3DDEVICE8 dev, DWORD index, + D3DLIGHT8 *light) { + typedef decltype(&H_SetLight) t_func; + shared_ptr 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(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::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(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(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; +} diff --git a/ScrapHacks/src/D3D8_VMT.hpp b/ScrapHacks/src/D3D8_VMT.hpp new file mode 100644 index 0000000..a3b636c --- /dev/null +++ b/ScrapHacks/src/D3D8_VMT.hpp @@ -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; +} diff --git a/ScrapHacks/src/Hook.hpp b/ScrapHacks/src/Hook.hpp new file mode 100644 index 0000000..9690f5f --- /dev/null +++ b/ScrapHacks/src/Hook.hpp @@ -0,0 +1,124 @@ +#pragma once +#include + +#include +#include +#include + +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> hooks; + + public: + Hook(void *func, void *detour) { + uintptr_t dest = reinterpret_cast(detour); + uintptr_t src = reinterpret_cast(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(detour); + hooks[key] = make_shared(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 get(void *func) { + uintptr_t addr = reinterpret_cast(func); + return Hook::get(addr); + } + + static shared_ptr get(uintptr_t addr) { return hooks.at(addr); } + + static size_t drop(void *func) { + uintptr_t addr = reinterpret_cast(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> 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 void func_void(Args... args) { + this->disable(); + reinterpret_cast(this->orig)(args...); + this->enable(); + return; + } + + template auto func(Args... args) { + this->disable(); + auto ret = reinterpret_cast(this->orig)(args...); + this->enable(); + return ret; + } +}; + +map> Hook::hooks; diff --git a/ScrapHacks/src/Py_Utils.hpp b/ScrapHacks/src/Py_Utils.hpp new file mode 100644 index 0000000..f387224 --- /dev/null +++ b/ScrapHacks/src/Py_Utils.hpp @@ -0,0 +1,58 @@ +#pragma once +#include +#include +#include + +#include "Structures.hpp" + +using namespace std; + +map Py; + +PyMethodDef *find_method_table(uintptr_t base, uintptr_t needle) { + for (ptrdiff_t offset = 0; offset < 64; ++offset) { + uintptr_t instr = reinterpret_cast(base + offset)[0]; + if (instr == needle) { + uintptr_t mod_addr = + reinterpret_cast(base + offset - (1 + 4))[0]; + return reinterpret_cast(mod_addr); + } + } + return reinterpret_cast(0); +} + +map get_modules(uintptr_t base) { + map Py; + PyMod *modules = reinterpret_cast(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(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; + } +} \ No newline at end of file diff --git a/ScrapHacks/src/REPL.hpp b/ScrapHacks/src/REPL.hpp new file mode 100644 index 0000000..9e33dad --- /dev/null +++ b/ScrapHacks/src/REPL.hpp @@ -0,0 +1,259 @@ +#pragma once +#include + +#include +#include + +#include "Scrapland.hpp" +#include "Util.hpp" + +void DllUnload(); + + +void unhook_d3d8(); +void hook_d3d8(); + +typedef void(_cdecl *t_cmd_func)(vector); + +struct t_cmd { + t_cmd_func func; + const char* usage; + const char* doc; +}; + +void cmd_help(vector); + +DWORD +get_protection(void *addr) { + MEMORY_BASIC_INFORMATION mbi; + VirtualQuery(addr, &mbi, sizeof(mbi)); + return mbi.Protect; +} + +void cmd_write(vector args) { + MEMORY_BASIC_INFORMATION mbi; + if (args.size() != 2) { + scrap_log(ERR_COLOR, "Usage: $w \n"); + return; + } + void *addr = 0; + vector 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 args) { + MEMORY_BASIC_INFORMATION mbi; + if (args.size() != 2) { + scrap_log(ERR_COLOR, "Usage: $r \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(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 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 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 args) { + stringstream out; + out << "Entities:" << endl; + dump_ht(ptr>(P_WORLD, O_ENTS), &out); + out << "Entity Lists:" << endl; + dump_ht(ptr>(P_WORLD, O_ENTLISTS), &out); + scrap_log(INFO_COLOR,out.str().c_str()); + return; +} + +void cmd_toggle_overlay(vector 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 args) { + stringstream out; + float *alarm = ptr(P_WORLD, O_ALARM); + float *alarm_grow = ptr(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 args) { + scrap_log(INFO_COLOR,"Unloading ScrapHacks... bye!\n"); + DllUnload(); +} + +static map commands = { + {"w", { + cmd_write, + "Usage: $w ", + "Write memory" + }}, + {"r", { + cmd_read, + "Usage: $r ", + "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 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 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; +} \ No newline at end of file diff --git a/ScrapHacks/src/ScrapHack.cpp b/ScrapHacks/src/ScrapHack.cpp new file mode 100644 index 0000000..4586d2c --- /dev/null +++ b/ScrapHacks/src/ScrapHack.cpp @@ -0,0 +1,177 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include + +// Socket stuff + +#include +#include +#include + +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::get(hook_recvfrom); + int ret = hook->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::get(hook_sendto); + int ret = hook->func(s, buf, len, flags, to, tolen); + return ret; +}; + +void setup_hooks() { + Hook::addr(reinterpret_cast(P_SCRAP_EXIT), hook_exit); + Hook::addr(reinterpret_cast(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(P_WORLD, O_MONEY); + money[0] = 0x7fffffff; + } + + if (key_down_norepeat(VK_F9)) { + cout << "Entities:" << endl; + dump_ht(ptr>(P_WORLD, O_ENTS)); + cout << "Entity Lists:" << endl; + dump_ht(ptr>(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::get(hooked_console); + int ret = hook->func(cmd); + return ret; +} + +void hook_exit() { + typedef decltype(&hook_exit) t_func; + shared_ptr hook = Hook::get(hook_exit); + DllUnload(); + HWND hMainWindow = ptr(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(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(0x5a9ca0), H_port_FixupExtension); + Hook::addr(reinterpret_cast(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; +} \ No newline at end of file diff --git a/ScrapHacks/src/Scrapland.hpp b/ScrapHacks/src/Scrapland.hpp new file mode 100644 index 0000000..a6ce786 --- /dev/null +++ b/ScrapHacks/src/Scrapland.hpp @@ -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); +} \ No newline at end of file diff --git a/ScrapHacks/src/Structures.hpp b/ScrapHacks/src/Structures.hpp new file mode 100644 index 0000000..85166dd --- /dev/null +++ b/ScrapHacks/src/Structures.hpp @@ -0,0 +1,54 @@ +#pragma once +template 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 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 struct HashTable { + uint32_t size; + HashTableEntry **chains; +}; + +template struct HashTableEntry { + T *data; + const char *name; + HashTableEntry *next; +}; \ No newline at end of file diff --git a/ScrapHacks/src/Util.hpp b/ScrapHacks/src/Util.hpp new file mode 100644 index 0000000..dd0a0f0 --- /dev/null +++ b/ScrapHacks/src/Util.hpp @@ -0,0 +1,375 @@ +#pragma once +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "Structures.hpp" +#include "Py_Utils.hpp" + +using namespace std; + +#define DLL_EXPORT extern "C" __declspec(dllexport) + +template void **GetVTable(T *obj) { + void *addr = reinterpret_cast(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(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(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 T *__ptr(uintptr_t addr) { + return reinterpret_cast(addr); +} + +template T *__ptr(uintptr_t addr, ptrdiff_t offset) { + // cout << "[" << (void*)addr << "] + " << (void*)offset << " = "; + addr = reinterpret_cast(addr)[0] + offset; + // cout << (void*)addr << endl;; + auto ret = __ptr(addr); + return ret; +} + +template +T *__ptr(uintptr_t addr, ptrdiff_t offset, Offsets... offsets) { + // cout << "[" << (void*)addr << "] + " << (void*)offset << " = "; + addr = reinterpret_cast(addr)[0] + offset; + // cout << (void*)addr << endl;; + auto ret = __ptr(addr, offsets...); + return ret; +} + +template +T *ptr(uintptr_t addr, Offsets... offsets) { + auto ret = __ptr(addr, offsets...); + return ret; +} + + +template +void __to_str(ostream& o, T t) +{ + o << t; +} + +template +void __to_str(ostream& o, T t, Args... args) // recursive variadic function +{ + __to_str(o, t); + __to_str(o, args...); +} + +template +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 fromhex(string input) { + vector 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 split(string str, char sep) { + vector 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 *ht) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *ent = ht->chains[i]; + if (ent) { + while (ent) { + ++cnt; + ent = ent->next; + } + } + } + return cnt; +} + +size_t size_ht(HashTable *ht) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *ent = ht->chains[i]; + if (ent) { + while (ent) { + ++cnt; + ent = ent->next; + } + } + } + return cnt; +} + +size_t dump_ht(HashTable *ht) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *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 *ht) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *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 *ht,stringstream *out) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *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 *ht,stringstream *out) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry *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; +} \ No newline at end of file diff --git a/ScrapHacks/src/dllmain.cpp b/ScrapHacks/src/dllmain.cpp new file mode 100644 index 0000000..611da67 --- /dev/null +++ b/ScrapHacks/src/dllmain.cpp @@ -0,0 +1,31 @@ + +#include + + +#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; +} \ No newline at end of file diff --git a/ScrapHacks/src/make_D3D8_VMT.py b/ScrapHacks/src/make_D3D8_VMT.py new file mode 100644 index 0000000..bb320a0 --- /dev/null +++ b/ScrapHacks/src/make_D3D8_VMT.py @@ -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) \ No newline at end of file diff --git a/r2_analyze.py b/r2_analyze.py index 00c26d3..f894499 100644 --- a/r2_analyze.py +++ b/r2_analyze.py @@ -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