From 7e044f01144fa24d61d60b575321f4953d1c20a2 Mon Sep 17 00:00:00 2001 From: Daniel Seiller Date: Fri, 3 Jan 2020 03:22:09 +0100 Subject: [PATCH] Lots of changes (expand to read more) - Update NOTES with new findings - Add Cutter link to README - Add ASMJIT, ASMTK and Zydis to CMake - Make DX8 setting cofigurable via ScrapHacks REPL - Add scaffolding for build hook trampolines using asmjit - Add on the fly assembling of code to REPL - Clean up command structure - Add memory RWX to REPL - Add stack dumping to REPL - Add Gamevar dumping to REPL - Add hook check to overlay commands (don't work if DX8 not hooked) - Allow nested command definitions for cleaner REPL - AllocConsole() as early as possible - shuffle some code around for cleanup - Add GameVar, PakEntry and HashIndex structures --- .vscode/settings.json | 6 +- NOTES.md | 210 +++++--- README.md | 2 +- ScrapHacks/.vscode/c_cpp_properties.json | 25 + ScrapHacks/CMakeLists.txt | 129 +++-- ScrapHacks/README.md | 16 +- ScrapHacks/build.bat | 10 + ScrapHacks/src/D3D8_Hook.hpp | 70 ++- ScrapHacks/src/D3D8_VMT.hpp | 500 +++++++++--------- ScrapHacks/src/Hook.hpp | 25 +- ScrapHacks/src/REPL.hpp | 577 ++++++++++++++++----- ScrapHacks/src/ScrapHack.cpp | 29 +- ScrapHacks/src/Scrapland.hpp | 131 ++++- ScrapHacks/src/Structures.hpp | 42 +- ScrapHacks/src/Util.hpp | 163 ++---- ScrapHacks/src/dllmain.cpp | 2 - ScrapHacks/{src => utils}/make_D3D8_VMT.py | 2 - lib/dbg.py | 14 + parse_save.py | 5 +- r2_analyze.py | 409 +++++++++++---- 20 files changed, 1607 insertions(+), 760 deletions(-) create mode 100644 ScrapHacks/.vscode/c_cpp_properties.json create mode 100644 ScrapHacks/build.bat rename ScrapHacks/{src => utils}/make_D3D8_VMT.py (92%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 118362a..fcf0c98 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,9 @@ "markdown", "latex", "plaintext" - ] + ], + "files.associations": { + "xstring": "cpp", + "iterator": "cpp" + } } \ No newline at end of file diff --git a/NOTES.md b/NOTES.md index 78e1e51..bf470fd 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,4 +1,5 @@ # Infos + - Engine: ScrapEngine - Ingame Scripting Language: Python 1.5.2 @@ -9,7 +10,8 @@ # Functions identified: -## Ingame-Console (Ctrl+\^ or right click on titlebar and select "switch console") (Handler@0x402190): +## Ingame-Console (Ctrl+\^ or right click on titlebar and select "switch console") (Handler@`0x402190`): + * ``: Try to evaluate Command as Python expression * `:`: Get Game Engine Global Variable * `: `: Set Game Engine Global Variable @@ -18,7 +20,8 @@ * `/`: Run Command defined in `QuickConsole.py`: `import quickconsole;quickconsole.%s()` * `/ ,`: Run function in `QuickConsole.py` with argument(s) `import quickconsole;quickconsole.%s(%s)` -## External Console (Scenegraph Debugging?) (Handler@0x5f9520): +## External Console (Scenegraph Debugging?) (Handler @ `0x5f9520`): + * `listar luces` * `listar` * `arbol` (Patch Scrap.exe@offset 0x314bc9 replace 0x20 with 0x00 (or just type `arbol ` with a space at the end)) @@ -30,36 +33,114 @@ * `capullo` ## Python Stuff -- Modules List @ 0x79C698 (Module Name as `char*` followed by Pointer to Init Function) -- InitPyMod @ 0x5A8FB0 -- PyExec @ 0x5A8390 + +- `0x79C698`: Modules List (Module Name as `char*` followed by Pointer to Init Function) +- `0x5A8FB0`: InitPyMod +- `0x5A8390`: PyExec + +## Other interesting Memory Addresses + +- `0x852914`: D3D8-Device pointer +- `0x7FCC00`: number of opened `.packed` files +- `0x84cb64`: pointer to console command handler +- `0x7fac84`: pointer to C++ callback list structure +- `0x80b2cc`: pointer to ActionClassList (???) +- `0x807a20`: pointer to SScorer (ingame GUI/Menu/Text system) structure (???) +- `0x80a398`: pointer to SoundSystem (???) +- `0x8b18f0`: pointer to Models Data (can be dumped using scenegraph debugging console) +- `0x8b18f4`: pointer to Scenes Data (can be dumped using scenegraph debugging console) +- `0x8b18f8`: pointer to active Models Data (can be dumped using scenegraph debugging console) + +## Hash-function used in Hash-Tables + +```c +unsigned long hash(const unsigned char *s) +{ + unsigned long h = 0, high; + while ( *s ) + { + h = ( h << 4 ) + *s++; + if ( high = h & 0xF0000000 ) + h ^= high >> 24; + h &= ~high; + } + return h; +} +``` + ## Other Functions: -- FindEntity @ 0x404a50 -- HashTable hashfunc @ 0x404bb0 -- Register C Callback @ 0x404460 -- Load Game @ 0x417470 -- File opening functions @ 0x5e3800 and 0x419950 -- Scrap_Debug_Init @ 0x403370 -- Scrap_Init @ 0x401770 -- Scrap_InitPy @ 0x4026d0 -- Scrap_OpenPak @ 0x41ab50 -- PyExec @ 0x5a8390 -- Setup_Game_Var @ 0x414570 -- Throw_Assertion @ 0x5fbc50 -- m3d.ini loader @ 0x5f7000 -- SM3 Scene Loader @ 0x650f80 (?) -- M3D Model Loader @ 0x6665a0 (??) -- World_Constructor @ 0x479b20 (???) -- World_Init @ 0x479b40 -- World_DeInit @ 0x402510 -- Make_World @ 0x479870 -- RenderFrame(?) @ 0x602a70 +Check `r2_analyze.py` for full list -# Data Structures +## File Index struct @ `0x7fcbec` -D3D8-Device @ `0x852914` +```cpp + +struct FileEntry { + uint32_t offset; + uint32_t size; + uint32_t unk; // seems to always be 0xBADFOO1 + unsigned char* path; + FileEntry* next; // next entry in hashtable chain +} + +struct FileIDX { + uint32_t size; + FileEntry** entries; +}; +``` + +## Packed Index struct (array @ `0x7fc1b0`) +```cpp +struct PackedIDX { + void** VMT; + unsigned char* filename; + uint32_t locked; // not sure + void* data; + uint32_t seek; +} +``` + +## C(++)-Callbacks @ `0x7fac84` + +Structure: + +```cpp +struct CPP_Callback { + const char* name; + void* func; + CPP_Callback* left; + CPP_Callback* right; +} +``` + +## Game engine Variables Pointer @ `0x7FBE4C` + +Structure: + +```cpp +struct GameVar { + GameVar* next; + const char* name; + const char* desc; + uint8_t subtype; + uint8_t type; + uint16_t unk; + void* value; + void* def_value; +} +``` + +Types + +| Value | Type | +| ------ | ----------------------- | +| `0x10` | const char* | +| `0x20` | int32_t | +| `0x30` | User Control Definition | +| `0x40` | float | +| `0x60` | Callback function | ## Game World/State Pointer @ `0x7fe944` @@ -70,6 +151,10 @@ Points to World struct | 0x0000 | `void**` | Virtual Method Table | | 0x0004 | `uint32_t` | Size of Entity Hashtable | | 0x0008 | `void**` | Pointer to Entity Hashtable | +| 0x0288 | `pyEntity*` | UsrEntity[0] | +| 0x028C | `pyEntity*` | UsrEntity[1] | +| 0x0290 | `pyEntity*` | UsrEntity[2] | +| 0x0294 | `pyEntity*` | UsrEntity[3] | | 0x02B8 | `uint32_t` | Number of entity lists | | 0x02BC | `void**` | Pointer to entity list Hashtable | | 0x0330 | `float[3]` | Time (why 3 times?) | @@ -91,18 +176,19 @@ Points to World struct | 0x2238 | `???` | Used in `World_Init` | | 0x2254 | `float` | Used in `World_Init` | - ## Entity Hash Table Hash-function used: [PJW](https://en.wikipedia.org/wiki/PJW_hash_function) (Same parameters as the example implementation) Entry format: -| Offset | Type | Description | -| ------ | ------------- | ------------------------------ | -| 0x0 | `void*` | Pointer to data | -| 0x4 | `const char*` | key as `char*` | -| 0x8 | `void*` | Pointer to next entry in chain | +```cpp +struct HT_Entry { + void* data; + const char* key; + HT_Entry* next; +} +``` Data format: @@ -113,10 +199,19 @@ Data format: | 0x14 | `void*` | pointer to self (why?) | | 0x28 | `float[3]` | Position in Game World | +## EntityList Hash Table + +Attributes: +- `Near` +- `First` +- `Num` +- `OnDeath` +- `OnDamage` # File Formats ## .packed File Format: + ``` Header: "BFPK\0\0\0\0" @@ -128,35 +223,44 @@ Header: Int32ul: offset in file ``` +# Virtual Method Tables: +check `r2_analyze.py` for full list ## Loading Custom Content (not really working) + 1. Create a folder `mods` 2. Drop a `*.packed` file into it 3. Change `Scrap.cfg` as follows 1. Add `ModPathName = mods` 2. Add `ModFileName = ` -## Interesting file: +## Interesting file inside `Data.packed` + * `m3d.ini`: Rendering Engine Configuration * `scripts/`: Game Engine Scripts # How to enable External Console: -1. exctract `Data.packed` + +1. Right click on the title bar (in windowed mode) and click "Switch Console" +2. or Use a custom Content Pack (**untested!**) + +# How to enable Scenegraph debugging console + +1. extract `Data.packed` 2. in m3d.ini uncomment (remove `;`) `ConsolaWnd` (GUI Console) and/or `ConsolaTxt` (Text Console) and set the value to `SI` 3. repack `Data.packed` -or right click on the title bar (in windowed mode) and click "Switch Console" - -or Use a custom Content Pack (**untested!**) - # Misc. Interesting things -- sys.path contains "./lib" so you can load your own Python Modules + +- `sys.path` contains "./lib" so you can import your own Python Modules +- Games crashes when starting a multiplayer server and feeding it random UDP data # Code Snippets ## [Kaitai Struct](http://kaitai.io/) Parser for .packed files + ```yaml meta: id: packed @@ -170,9 +274,9 @@ seq: - id: magic contents: BFPK doc: File Magic - - id: magic2 + - id: version contents: [0,0,0,0] - doc: Second File Magic + doc: File Version - id: num_files type: u4 doc: Number of files @@ -180,7 +284,7 @@ seq: type: file_entry repeat: expr repeat-expr: num_files - doc: Directory entry for each file + doc: Entry for each file types: file_entry: seq: @@ -203,19 +307,9 @@ types: size: size ``` -## Hashfunction used in Entity Hash-Table +# TODO: -```c -unsigned long ElfHash(const unsigned char *s) -{ - unsigned long h = 0, high; - while ( *s ) - { - h = ( h << 4 ) + *s++; - if ( high = h & 0xF0000000 ) - h ^= high >> 24; - h &= ~high; - } - return h; -} -``` \ No newline at end of file +- Figure out how C++ Callbacks work +- Figure out SM3 (Models), CM3 (Animations) file formats +- Figure out rest of World structure +- Figure out rest of Entity structure \ No newline at end of file diff --git a/README.md b/README.md index 68a933d..b5c42b2 100644 --- a/README.md +++ b/README.md @@ -29,4 +29,4 @@ WIP Memory hacking library - [Reclass.NET](https://github.com/ReClassNET/ReClass.NET) - [HxD](https://mh-nexus.de/en/hxd/) - [Kaitai Struct](http://kaitai.io/) -- [Radare2](https://www.radare.org/) \ No newline at end of file +- [Radare2](https://www.radare.org/) + [Cutter](https://cutter.re/) \ No newline at end of file diff --git a/ScrapHacks/.vscode/c_cpp_properties.json b/ScrapHacks/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..26cf26b --- /dev/null +++ b/ScrapHacks/.vscode/c_cpp_properties.json @@ -0,0 +1,25 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${vcpkgRoot}/x64-windows/include", + "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/include" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.18362.0", + "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.22.27905/bin/Hostx64/x64/cl.exe", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "msvc-x86", + "configurationProvider": "vector-of-bool.cmake-tools", + "compileCommands": "${workspaceFolder}/build/compile_commands.json" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/ScrapHacks/CMakeLists.txt b/ScrapHacks/CMakeLists.txt index b7b78b6..9d96ca6 100644 --- a/ScrapHacks/CMakeLists.txt +++ b/ScrapHacks/CMakeLists.txt @@ -6,88 +6,123 @@ project(ScrapHacks DESCRIPTION "Scrapland memory hacking library" LANGUAGES CXX) -set(CMAKE_BUILD_TYPE "Release") +message(STATUS "Fetching Scrapland installation folder") +get_filename_component(SCRAPLAND_DIR "[HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\MercurySteam Entertainment\\Scrapland;DIRECTORY]" ABSOLUTE CACHE) + +if(NOT IS_ABSOLUTE "${SCRAPLAND_DIR}" OR NOT EXISTS "${SCRAPLAND_DIR}") + message(FATAL_ERROR "Scrapland installation folder not found!") +endif() + +message(STATUS "Checking Scrap.exe hash") +file(SHA1 "${SCRAPLAND_DIR}/Bin/Scrap.exe" SCRAP_EXE_HASH) + +if(NOT ${SCRAP_EXE_HASH} STREQUAL "d2dde960e8eca69d60c2e39a439088b75f0c89fa") + message(FATAL_ERROR "Scrap.exe hash miss match!") +endif() + +set(FETCHCONTENT_QUIET 0) +set(CMAKE_BUILD_TYPE "RelMinSize") set(CMAKE_INSTALL_PREFIX "${CMAKE_BINARY_DIR}") +set(ASMJIT_EMBED true) +set(ASMTK_EMBED true) +set(ZYDIS_BUILD_TOOLS false) +set(ZYDIS_BUILD_EXAMPLES false) + + if(WIN32) if(MSVC) # ensure we use minimal "windows.h" lib without the crazy min max macros - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"WIN32_LEAN_AND_MEAN\"") + # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"WIN32_LEAN_AND_MEAN\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"NOMINMAX\"") # disable SAFESEH - to avoid "LNK2026: module unsafe" set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"SAFESEH:NO\"") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /SAFESEH:NO /ignore:4217") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO /ignore:4217") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO /ignore:4217") endif(MSVC) endif(WIN32) -include(ExternalProject) +include(FetchContent) -ExternalProject_Add( +FetchContent_Declare( 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) +FetchContent_MakeAvailable(DirectX) -ExternalProject_Get_Property(DirectX SOURCE_DIR) -include_directories(AFTER ${SOURCE_DIR}/8.0/include/) -link_directories(AFTER ${SOURCE_DIR}/8.0/lib/) +FetchContent_Declare( + ASM_JIT + PREFIX ${CMAKE_CURRENT_BINARY_DIR} + GIT_REPOSITORY git@github.com:asmjit/asmjit.git + GIT_SHALLOW true + GIT_PROGRESS true +) + +FetchContent_MakeAvailable(ASM_JIT) + +FetchContent_Declare( + ASM_TK + PREFIX ${CMAKE_CURRENT_BINARY_DIR} + GIT_REPOSITORY git@github.com:asmjit/asmtk.git + GIT_SHALLOW true + GIT_PROGRESS true +) + +FetchContent_MakeAvailable(ASM_TK) + +set(ASMJIT_DIR ${asm_jit_SOURCE_DIR}) + +include(${asm_tk_SOURCE_DIR}/CMakeLists.txt) + + +FetchContent_Declare( + Zydis + PREFIX ${CMAKE_CURRENT_BINARY_DIR} + GIT_REPOSITORY git@github.com:zyantific/zydis.git + GIT_SHALLOW true + GIT_PROGRESS true +) + +# FetchContent_MakeAvailable(Zydis) + +include_directories(AFTER ${directx_SOURCE_DIR}/8.0/include/ ${ASMTK_INCLUDE_DIRS} ${ASMJIT_INCLUDE_DIRS}) +link_directories(AFTER ${directx_SOURCE_DIR}/8.0/lib/) find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter) -add_custom_target( - MAKE_D3D8_VMT ${Python3_EXECUTABLE} ${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 +add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/utils/make_D3D8_VMT.py ${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp ${directx_SOURCE_DIR}/8.0/include/d3d8.h + COMMENT "Generating D3D8_VMT.hpp from d3d8.h" + VERBATIM ) - -# 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_custom_target(D3D8_VMT ALL + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp") 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 - ) + ${ASMTK_SRC} + ${ASMJIT_SRC} +) + 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 D3D8_VMT) # add_dependencies(ScrapHack Python152) # add_dependencies(ScrapHack Python152_Bin) target_link_libraries(ScrapHack d3d8 d3dx8 dxerr8 + gdiplus # PYTHON15 + # Zydis legacy_stdio_definitions) + +install(TARGETS ScrapHack RUNTIME DESTINATION ${SCRAPLAND_DIR}/lib) target_compile_features(ScrapHack PUBLIC cxx_std_11) diff --git a/ScrapHacks/README.md b/ScrapHacks/README.md index a1ea3ab..bc2ec9a 100644 --- a/ScrapHacks/README.md +++ b/ScrapHacks/README.md @@ -1,3 +1,12 @@ +## Features + +- read and write memory +- change DirectX state +- Draw DirectX overlay (still need to make a useful overlay) +- Dump various data structures to the console +- Assemble and execute code on the fly +- Can be controlled via keyboard shortcuts (TODO: allow defining own shortcuts for commands) + ## Prerequisites - Visual Studio 2017/2019 (others might work) @@ -13,16 +22,13 @@ cmake -G"NMake Makefiles" -B build cmake --build build --target install ``` -this will generate `ScrapHack.pyd` in `./build` +This will find the Games's installation folder, verify that the version you have is compatible with ScrapHacks and drop the compiled `.pyd` file into the correct folder to be imported -## Usage +## Getting started -- create a `lib` folder next to `Scrapland.exe` -- copy `ScrapHack.pyd` into said folder - open the ingame console (Ctrl+^) - type `import ScrapHack` - type `$help` -- Done! ## Notes diff --git a/ScrapHacks/build.bat b/ScrapHacks/build.bat new file mode 100644 index 0000000..d367289 --- /dev/null +++ b/ScrapHacks/build.bat @@ -0,0 +1,10 @@ +@echo off +setlocal +if "%VSINSTALLDIR%"=="" ( + for /f "usebackq tokens=*" %%i in (`vswhere -latest -find **\vcvarsall.bat`) do ( + call "%%i" x86 + ) +) +if not exist build cmake -G"NMake Makefiles" -B build +cmake --build build --target install +endlocal \ No newline at end of file diff --git a/ScrapHacks/src/D3D8_Hook.hpp b/ScrapHacks/src/D3D8_Hook.hpp index f15cf29..6334785 100644 --- a/ScrapHacks/src/D3D8_Hook.hpp +++ b/ScrapHacks/src/D3D8_Hook.hpp @@ -1,33 +1,55 @@ #pragma once +#include +using namespace std; + #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 hooked = false; bool overlay = false; LPD3DXFONT m_pFont; HFONT hFont; HBRUSH hBrush; -D3DCOLOR color = D3DCOLOR_ARGB(255, 255, 0, 0); +D3DCOLOR color = D3DCOLOR_XRGB(255, 0, 0); RECT Rect = {0, 0, 0, 0}; D3DRECT panel; +D3DFILLMODE fillmode = D3DFILLMODE::D3DFILL_SOLID; +boolean use_z; + size_t size_ht(HashTable *ht); size_t size_ht(HashTable *ht); +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) + +#define DX_Check(call) (_DX_Check(call,TOSTRING(call),__LINE__,__FILE__)) + +HRESULT _DX_Check(HRESULT res,char* call,size_t line, char* file) { + if (res!=D3D_OK) { + return DXTraceA(file,line,res,call,true); + } + return res; +} + LPDIRECT3DDEVICE8 Render(LPDIRECT3DDEVICE8 dev) { if (!overlay) { return dev; } + IDirect3DSurface8* surf; char text[4096]; int32_t money = 0; size_t num_ents = 0; @@ -37,30 +59,42 @@ Render(LPDIRECT3DDEVICE8 dev) { 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); + 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); hFont = nullptr; } m_pFont->Begin(); m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0); + D3DRECT rec = {Rect.left,Rect.top,Rect.right,Rect.bottom}; + // dev->Clear(1,NULL,D3DCLEAR_TARGET,D3DCOLOR_ARGB(10,255,255,255),0,0); m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color); m_pFont->End(); return dev; } + +LPDIRECT3DDEVICE8 +BeforeRender(LPDIRECT3DDEVICE8 dev) { + 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_BeginScene(LPDIRECT3DDEVICE8 dev) { + typedef decltype(&H_BeginScene) t_func; + shared_ptr hook = Hook::get(H_BeginScene); + HRESULT ret=hook->func(dev); + BeforeRender(dev); + return ret; +} + + HRESULT WINAPI H_SetLight(LPDIRECT3DDEVICE8 dev, DWORD index, D3DLIGHT8 *light) { typedef decltype(&H_SetLight) t_func; @@ -87,13 +121,13 @@ HRESULT WINAPI H_DrawIndexedPrimitive(LPDIRECT3DDEVICE8 dev, 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); + // dev->GetRenderState(D3DRS_AMBIENT, &AMBIENT); + // dev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255)); + dev->SetRenderState(D3DRS_FILLMODE, fillmode); + dev->SetRenderState(D3DRS_ZENABLE, use_z); auto ret = hook->func(dev, Type, minIndex, NumVertices, startIndex, primCount); - dev->SetRenderState(D3DRS_AMBIENT, AMBIENT); + // dev->SetRenderState(D3DRS_AMBIENT, AMBIENT); dev->SetRenderState(D3DRS_ZENABLE, 1); return ret; } @@ -107,6 +141,7 @@ void unhook_d3d8() { m_pFont = nullptr; } Hook::drop(H_EndScene); + Hook::drop(H_BeginScene); Hook::drop(H_DrawIndexedPrimitive); Hook::drop(H_SetLight); hooked=false; @@ -116,9 +151,6 @@ 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); @@ -127,7 +159,11 @@ void hook_d3d8() { } Sleep(100); }; + 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)); Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_EndScene], H_EndScene); + Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_BeginScene], H_BeginScene); Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_DrawIndexedPrimitive], H_DrawIndexedPrimitive); Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_SetLight], H_SetLight); diff --git a/ScrapHacks/src/D3D8_VMT.hpp b/ScrapHacks/src/D3D8_VMT.hpp index a3b636c..104034e 100644 --- a/ScrapHacks/src/D3D8_VMT.hpp +++ b/ScrapHacks/src/D3D8_VMT.hpp @@ -1,274 +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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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; + 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 index 9690f5f..d898648 100644 --- a/ScrapHacks/src/Hook.hpp +++ b/ScrapHacks/src/Hook.hpp @@ -4,9 +4,29 @@ #include #include #include +#include +#include using namespace std; +/* +vector make_trampoline(uintptr_t orig,uintptr_t hook) { + using namespace asmjit; + JitRuntime rt; + CodeHolder code; + CodeInfo ci=rt.codeInfo(); + code.init(ci); + x86::Assembler a(&code); + a.jmp(hook); + a.ret(); + code.flatten(); + code.resolveUnresolvedLinks(); + code.relocateToBase(orig); + size_t code_size=code.sectionById(0)->buffer().size(); + code.copyFlattenedData((void*)orig, code_size, CodeHolder::kCopyWithPadding); +} +*/ + class Hook { private: MEMORY_BASIC_INFORMATION mbi; @@ -19,6 +39,7 @@ class Hook { public: Hook(void *func, void *detour) { + // TODO: build jmp_bytes using asmjit uintptr_t dest = reinterpret_cast(detour); uintptr_t src = reinterpret_cast(func); this->orig = func; @@ -83,7 +104,7 @@ class Hook { } void disable() { - if (enabled) { + if (this->enabled) { // cout << "Disabling: [" << this->orig << " <- " << this->detour << // "]" // << endl; @@ -91,7 +112,7 @@ class Hook { PAGE_EXECUTE_READWRITE, NULL); memcpy(this->orig, this->orig_bytes, 1 + 4 + 1); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL); - enabled = false; + this->enabled = false; } } void enable() { diff --git a/ScrapHacks/src/REPL.hpp b/ScrapHacks/src/REPL.hpp index 9e33dad..1724e8a 100644 --- a/ScrapHacks/src/REPL.hpp +++ b/ScrapHacks/src/REPL.hpp @@ -1,9 +1,15 @@ #pragma once #include +#include + +#define ASMJIT_EMBED +#define ASMTK_EMBED #include #include +#include +#include #include "Scrapland.hpp" #include "Util.hpp" @@ -13,15 +19,196 @@ void DllUnload(); void unhook_d3d8(); void hook_d3d8(); -typedef void(_cdecl *t_cmd_func)(vector); +struct Command; -struct t_cmd { +typedef void(_cdecl *t_cmd_func)(Command*,vector); + +size_t assemble(vector assembly,uint64_t base) { + using namespace asmjit; + using namespace asmtk; + + char err_msg[1024]; + Error err; + + CodeInfo ci(ArchInfo::kIdX86); + ci.setBaseAddress(base); + CodeHolder code; + code.init(ci); + x86::Assembler a(&code); + AsmParser p(&a); + + for (string line:assembly) { + if (err = p.parse((line+"\n").c_str())) { + snprintf(err_msg,1024,"PARSE ERROR: [%s] %08x (%s)\n",line.c_str(), err, DebugUtils::errorAsString(err)); + scrap_log(ERR_COLOR,err_msg); + return 0; + } + } + if (err=code.flatten()) { + snprintf(err_msg,1024,"FLATTEN ERROR: %08x (%s)\n", err, DebugUtils::errorAsString(err)); + scrap_log(ERR_COLOR,err_msg); + return 0; + } + if (err=code.resolveUnresolvedLinks()) { + snprintf(err_msg,1024,"RESOLVE ERROR: %08x (%s)\n", err, DebugUtils::errorAsString(err)); + scrap_log(ERR_COLOR,err_msg); + return 0; + } + CodeBuffer& buffer = code.sectionById(0)->buffer(); + + if (base==0) { + return buffer.size(); + } + + MEMORY_BASIC_INFORMATION mbi; + + if (VirtualQuery((void*)base, &mbi, sizeof(mbi)) == 0) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + return 0; + }; + if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect)) + { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + return 0; + }; + memcpy((void*)base,buffer.data(),buffer.size()); + scrap_log(INFO_COLOR,"Code:"+hexdump_s((void*)base,buffer.size())); + VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL); + return buffer.size(); +} + +size_t asm_size(vector assembly) { + return assemble(assembly,0); +} + + + +struct Command { t_cmd_func func; - const char* usage; - const char* doc; + string usage; + string doc; + map subcommands; + + + Command(t_cmd_func func=nullptr,string usage="",string doc="",map subcommands={}) { + this->func=func; + this->usage=usage; + this->doc=doc; + this->subcommands=subcommands; + } + + + Command(string usage="",string doc="",map subcommands={}): + Command(nullptr,usage,doc,subcommands) {}; + + void add_subcommand(string name,Command *cmd) { + this->subcommands[name]=cmd; + } + + void set_subcommands(const map &subcommands) { + for (auto subcmd:subcommands) { + this->add_subcommand(subcmd.first,subcmd.second); + } + } + + bool has_subcommand(string cmd) { + return this->subcommands.count(cmd)>0; + } + + void exec(vector args) { + if (args.size()>1) { + string cmd=args[0]; + if (this->has_subcommand(cmd)) { + // matching subcommand found, strip first part and forward args + args.erase(args.begin()); + return this->subcommands[cmd]->exec(args); + }; + } + // args vector empty or no subcommand found, check if we have a func ptr and call it + if (this->func!=nullptr) { + this->func(this,args); + return; + } + scrap_log(ERR_COLOR, "Unknown command!\n"); + return; + } + }; -void cmd_help(vector); +struct REPL { + + map commands; + REPL(map commands) { + this->commands=commands; + } + + bool has_command(string cmd) { + return this->commands.count(cmd)>0; + } + + bool exec(vector args) { + vector> cmd_stack; + map cmds=this->commands; + if (args.size()==0) { + return false; + } + while (cmds.count(args[0])) { + cmd_stack.push_back(make_tuple(args[0],cmds[args[0]])); + cmds=cmds[args[0]]->subcommands; + args.erase(args.begin()); + if (args.empty()) break; + } + while (cmd_stack.size()) { + auto elem=cmd_stack.back(); + string cmd=get<0>(elem); + Command* cmd_ptr=get<1>(elem); + cmd_stack.pop_back(); + if (cmd_ptr->func!=nullptr) { + cmd_ptr->func(cmd_ptr,args); + return true; + } + args.insert(args.begin(),cmd); + } + return false; + } + + string help(vector args) { + map cmds=this->commands; + string ret; + if (args.empty()) { + return this->show_commands(cmds); + } + pair cmd=make_pair("",nullptr); + for (string part: args) { + if (cmds.count(part)==0) { + return "No help for (sub)command '"+part+"'"; + } + cmd=make_pair(part,cmds[part]); + cmds=cmd.second->subcommands; + } + ret=cmd.first+": "+cmd.second->usage+"\n"; + if (cmds.size()) { + ret+="Subcommands:\n"+this->show_commands(cmds,1)+"\n"; + }; + return ret; + } + + string show_commands(map cmds,size_t depth=0) { + string s; + for (auto cmd:cmds) { + for (size_t n=0;ndoc+" ("+cmd.second->usage+")\n"+this->show_commands(cmd.second->subcommands,depth+1); + } + return s; + } + +}; DWORD get_protection(void *addr) { @@ -30,66 +217,127 @@ get_protection(void *addr) { return mbi.Protect; } -void cmd_write(vector args) { +void cmd_exec(Command* cmd, vector args) { + void *addr; MEMORY_BASIC_INFORMATION mbi; - if (args.size() != 2) { - scrap_log(ERR_COLOR, "Usage: $w \n"); - return; + if (args.size()<1) { + scrap_log(ERR_COLOR, cmd->usage); + scrap_log(ERR_COLOR, "\n"); } - 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"); + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, e.what()); + scrap_log(ERR_COLOR, "\n"); return; } - uint8_t *buffer = new uint8_t[data.size()]; + + if (VirtualQuery(addr, &mbi, sizeof(mbi)) == 0) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + return; + }; + if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect)) + { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + }; + CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)addr,0,NULL,NULL); +} + +void cmd_write(Command* cmd,vector args) { + MEMORY_BASIC_INFORMATION mbi; + if (args.size()==0) { + scrap_log(ERR_COLOR, cmd->usage); + scrap_log(ERR_COLOR, "\n"); + return; + } + uint8_t *buffer = nullptr; + vector data; + try { + if (args.size()>1) { + buffer = (uint8_t *)stoull(args[0], 0, 16); + data = fromhex(args[1]); + } else { + data = fromhex(args[0]); + buffer = new uint8_t[data.size()]; + if (buffer==nullptr) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, "new[] failed"); + scrap_log(ERR_COLOR, "\n"); + return; + } + char ptr[255]; + snprintf(ptr,255,"Buffer @ %p\n",buffer); + scrap_log(INFO_COLOR,ptr); + } + } catch (exception e) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, e.what()); + scrap_log(ERR_COLOR, "\n"); + return; + } + if (VirtualQuery(buffer, &mbi, sizeof(mbi)) == 0) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + return; + }; + if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, &mbi.Protect)) + { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + }; + 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) { +void cmd_read(Command* cmd,vector args) { MEMORY_BASIC_INFORMATION mbi; - if (args.size() != 2) { - scrap_log(ERR_COLOR, "Usage: $r \n"); + if (args.size()<1) { + scrap_log(ERR_COLOR, cmd->usage); + scrap_log(ERR_COLOR, "\n"); return; } uintptr_t addr = UINTPTR_MAX; - size_t size = 0; + size_t size = 0xff; unsigned char *buffer; try { addr = stoull(args[0], 0, 16); - size = stoull(args[1]); + if (args.size()>1) { + size = stoull(args[1]); + } buffer = new unsigned char[size]; } catch (exception e) { - scrap_log(ERR_COLOR, "ERROR!\n"); + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, e.what()); + scrap_log(ERR_COLOR, "\n"); return; } void *mptr = reinterpret_cast(addr); if (VirtualQuery(mptr, &mbi, sizeof(mbi)) == 0) { - scrap_log(ERR_COLOR, "ERROR!\n"); + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\n"); + return; + }; + if (!VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE, + &mbi.Protect)) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, GetLastErrorAsString()); + scrap_log(ERR_COLOR, "\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); @@ -99,26 +347,78 @@ void cmd_read(vector args) { return; } -void cmd_dx8(vector args) { +void cmd_hook_dx8(Command* cmd,vector args) { + hook_d3d8(); + scrap_log(INFO_COLOR,"DX8 hooked!\n"); + return; +} + +void cmd_unhook_dx8(Command* cmd,vector args) { + unhook_d3d8(); + scrap_log(INFO_COLOR,"DX8 unhooked!\n"); + return; +} + + +void cmd_dx8(Command* cmd,vector args) { if (args.size()!=1) { - scrap_log(ERR_COLOR, "Usage: $dx8 (hook|unhook)\n"); + scrap_log(ERR_COLOR, cmd->usage); + scrap_log(ERR_COLOR, "\n"); return; } - if (args[0]=="hook") { - hook_d3d8(); - scrap_log(INFO_COLOR,"DX8 hooked!\n"); + if (args[0]=="zenable:true") { + use_z=true; + scrap_log(INFO_COLOR,"DX8 mode switched!\n"); return; - } - if (args[0]=="unhook") { - unhook_d3d8(); - scrap_log(INFO_COLOR,"DX8 unhooked!\n"); + }; + if (args[0]=="zenable:false") { + use_z=false; + scrap_log(INFO_COLOR,"DX8 mode switched!\n"); + return; + }; + + if (args[0]=="fill:wire") { + fillmode=D3DFILLMODE::D3DFILL_WIREFRAME; + scrap_log(INFO_COLOR,"DX8 mode switched!\n"); + return; + }; + + if (args[0]=="fill:solid") { + fillmode=D3DFILLMODE::D3DFILL_SOLID; + scrap_log(INFO_COLOR,"DX8 mode switched!\n"); + return; + }; + + if (args[0]=="fill:point") { + fillmode=D3DFILLMODE::D3DFILL_POINT; + scrap_log(INFO_COLOR,"DX8 mode switched!\n"); return; }; scrap_log(ERR_COLOR,"Invalid argument!\n"); return; } -void cmd_dump_py(vector args) { +void cmd_dump_stack(Command* cmd, vector args) { + void** stack=(void**)_AddressOfReturnAddress(); + cout<<"ESP: "< args) { stringstream out; for (auto mod : Py) { for (auto meth : mod.second.methods) { @@ -129,7 +429,18 @@ void cmd_dump_py(vector args) { scrap_log(INFO_COLOR,out.str().c_str()); } -void cmd_dump_ents(vector args) { +void cmd_dump_vars(Command* cmd, vector args) { + stringstream out; + GameVar* var=ptr(P_VARS,0); + out << "GameVars:" << endl; + while (var!=nullptr) { + out<name<< "[" <type <<","<< (uint16_t)var->subtype << std::dec << "]: " << var->desc<<" ("<value<<", "<default<<")"<next; + } + scrap_log(INFO_COLOR,out.str().c_str()); +} + +void cmd_dump_ents(Command* cmd,vector args) { stringstream out; out << "Entities:" << endl; dump_ht(ptr>(P_WORLD, O_ENTS), &out); @@ -139,7 +450,11 @@ void cmd_dump_ents(vector args) { return; } -void cmd_toggle_overlay(vector args) { +void cmd_toggle_overlay(Command* cmd,vector args) { + if (!hooked) { + scrap_log(INFO_COLOR,"DX8 not hooked, run '$dx8 hook' first!\n"); + return; + } overlay=!overlay; if (overlay) { scrap_log(INFO_COLOR,"Overlay enabled!\n"); @@ -148,7 +463,20 @@ void cmd_toggle_overlay(vector args) { } } -void cmd_print_alarm(vector args) { +void cmd_enable_overlay(Command* cmd,vector args) { + if (!overlay) { + cmd_toggle_overlay(cmd,args); + } +} + +void cmd_disable_overlay(Command* cmd,vector args) { + if (overlay) { + cmd_toggle_overlay(cmd,args); + } +} + + +void cmd_print_alarm(Command* cmd,vector args) { stringstream out; float *alarm = ptr(P_WORLD, O_ALARM); float *alarm_grow = ptr(P_WORLD, O_ALARM_GROW); @@ -157,103 +485,88 @@ void cmd_print_alarm(vector args) { return; } -void cmd_unload(vector args) { +void cmd_unload(Command* cmd,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"); +void cmd_asm(Command* cmd, vector args) { + string code; + uintptr_t buffer_addr; + bool has_addr=false; + if (args.size()<1) { + scrap_log(ERR_COLOR, cmd->usage); + return; + } + try { + buffer_addr=stoull(args[0], 0, 16); + has_addr=true; + } catch (exception e) { + // NOP + has_addr=false; + } + if (has_addr) { + // remove address from args + args.erase(args.begin()); + } + for (string arg:args) { + code+=arg+" "; + }; + size_t data_size=asm_size(split(code,';')); + if (!has_addr) { + buffer_addr = (uintptr_t)malloc(data_size); + if (buffer_addr==0) { + scrap_log(ERR_COLOR, "ERROR: "); + scrap_log(ERR_COLOR, "malloc() failed"); + scrap_log(ERR_COLOR, "\n"); + return; } - return; + char ptr[255]; + snprintf(ptr,255,"Buffer @ %p\n",(void*)buffer_addr); + scrap_log(INFO_COLOR,ptr); } - 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; + assemble(split(code,';'),buffer_addr); } +void cmd_help(Command* cmd,vector args); + +static REPL* repl=new REPL( +{ + {"mem",new Command("Usage: $mem (read|write)","Manipulate memory",{ + {"read",new Command(cmd_read,"Usage: $mem read [size]","Read memory")}, + {"write",new Command(cmd_write,"Usage: $mem write [addr] ","Write memory, if no address is specifiew we VirtualAlloc() a region")}, + {"exec",new Command(cmd_exec,"Usage: $exec ","Start a new thread at the specified address")}, + {"asm",new Command(cmd_asm,"Usage: $asm [addr] ;;...","Assemble instructions at address, if no address is given allocate memory and assemble code into that")}, + {"stack",new Command(cmd_dump_stack,"Usage: $mem stack","Dump stack contents")}, + })}, + {"unload",new Command(cmd_unload,"Usage: $unload","Unload ScrapHacks")}, + {"dx8",new Command(cmd_dx8,"Usage: $dx8 ","Manipulate DirectX 8 functions and state",{ + {"overlay",new Command("Usage: $dx8 overlay ","Control DX8 overlay",{ + {"toggle",new Command(cmd_toggle_overlay,"Usage: $dx8 overlay toggle","Toggle overlay")}, + {"enable",new Command(cmd_enable_overlay,"Usage: $dx8 overlay enable","Enable overlay")}, + {"disable",new Command(cmd_disable_overlay,"Usage: $dx8 overlay disable","Disable overlay")}, + })}, + {"hook",new Command(cmd_hook_dx8,"Usage: $dx8 hook","Enable DirectX 8 hook")}, + {"unhook",new Command(cmd_unhook_dx8,"Usage: $dx8 hook","Disable DirectX 8 hook")} + })}, + {"dump",new Command("Usage: $dump ","Dump various data to the console",{ + {"py",new Command(cmd_dump_py,"Usage: $dump py","Dump python module information")}, + {"ents",new Command(cmd_dump_ents,"Usage: $dump ents","Dump entity information")}, + {"alarm",new Command(cmd_print_alarm,"Usage: $dump alarm","Print alarm status")}, + {"vars",new Command(cmd_dump_vars,"Usage: $dump vars","Print engine variables")}, + })}, + {"help",new Command(cmd_help,"Usage: $help [command]","Print help for ScrapHacks command")} +}); + +void cmd_help(Command* cmd,vector args) { + scrap_log(INFO_COLOR,repl->help(args)+"\n"); +}; 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"); - } + repl->exec(split(string(_cmd), ' ')); return; } \ No newline at end of file diff --git a/ScrapHacks/src/ScrapHack.cpp b/ScrapHacks/src/ScrapHack.cpp index 4586d2c..5cb9e23 100644 --- a/ScrapHacks/src/ScrapHack.cpp +++ b/ScrapHacks/src/ScrapHack.cpp @@ -9,12 +9,6 @@ #include -// Socket stuff - -#include -#include -#include - using namespace std; #include "D3D8_Hook.hpp" @@ -34,22 +28,6 @@ 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); @@ -127,7 +105,6 @@ void hook_exit() { 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 @@ -150,16 +127,22 @@ void DllInit(HMODULE mod) { } void *H_port_FixupExtension(char *name, char *filename) { + cout<<"FixupExtension: "<(0x5a9ca0), H_port_FixupExtension); Hook::addr(reinterpret_cast(0x5cdb00), H_PyEval_CallObjectWithKeywords); diff --git a/ScrapHacks/src/Scrapland.hpp b/ScrapHacks/src/Scrapland.hpp index a6ce786..76d731b 100644 --- a/ScrapHacks/src/Scrapland.hpp +++ b/ScrapHacks/src/Scrapland.hpp @@ -1,4 +1,8 @@ #pragma once +#include "Structures.hpp" + +#include +using namespace std; // OFFSETS #define O_MONEY 0x2090 @@ -9,6 +13,7 @@ // POINTERS #define P_WORLD 0x7FE944 +#define P_VARS 0x7FBE4C #define P_PY_MODS 0x79C698 // FUNCTION ADDRESSES @@ -22,7 +27,8 @@ #define P_PyArg_ParseTuple 0x5bb9d0 -#define MSG_COLOR scrap_RGB(255,128,0) +#define MSG_COLOR scrap_RGB(128,0,255) +#define WARN_COLOR scrap_RGB(255,128,0) #define ERR_COLOR scrap_RGB(255,0,0) #define INFO_COLOR scrap_RGB(0,0,255) @@ -50,4 +56,127 @@ int scrap_log(unsigned int color,const char* 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); +} + + +int scrap_log(unsigned int color,string msg) { + return ((t_scrap_log)P_SCRAP_LOG)(color,msg.c_str()); +} + +int scrap_log(uint8_t r,uint8_t g,uint8_t b,string msg) { + return ((t_scrap_log)P_SCRAP_LOG)(scrap_RGB(r,g,b),msg.c_str()); +} + + +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/Structures.hpp b/ScrapHacks/src/Structures.hpp index 85166dd..93ff0c6 100644 --- a/ScrapHacks/src/Structures.hpp +++ b/ScrapHacks/src/Structures.hpp @@ -1,5 +1,7 @@ #pragma once +using namespace std; template struct HashTableEntry; + struct Vector3 { float x; float y; @@ -7,9 +9,9 @@ struct Vector3 { }; struct Matrix3x3 { - Vector3 a; - Vector3 b; - Vector3 c; + struct Vector3 a; + struct Vector3 b; + struct Vector3 c; }; struct PyMethodDef { @@ -42,9 +44,41 @@ struct EntityList { const char *func; }; +struct GameVar { + struct GameVar* next; + const char* name; + const char* desc; + uint8_t type; + uint8_t subtype; + uint16_t unk; + void* value; + void* default; +}; + +struct PakEntry { + unsigned char* filename; + uint32_t locked; + void* data; + uint32_t seek; +}; + +struct HashIndexEntry { + uint32_t offset; + uint32_t size; + uint32_t status; + const char* name; + struct HashIndexEntry* next; +}; + +struct HashIndex { + uint32_t size; + struct HashIndexEntry** data; +}; + + template struct HashTable { uint32_t size; - HashTableEntry **chains; + struct HashTableEntry **chains; }; template struct HashTableEntry { diff --git a/ScrapHacks/src/Util.hpp b/ScrapHacks/src/Util.hpp index dd0a0f0..ccc7a1c 100644 --- a/ScrapHacks/src/Util.hpp +++ b/ScrapHacks/src/Util.hpp @@ -9,9 +9,6 @@ #include #include -#include "Structures.hpp" -#include "Py_Utils.hpp" - using namespace std; #define DLL_EXPORT extern "C" __declspec(dllexport) @@ -116,18 +113,56 @@ bool key_down_norepeat(int keycode, int delay = 100) { return false; } -string hexdump_s(void *addr, size_t count=0xff) { +bool addr_exists(void* addr) { + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr,&mbi,sizeof(mbi))) { + return false; + }; + return true; +} + +bool can_read(void* addr) { + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr,&mbi,sizeof(mbi))) { + return false; + }; + return (mbi.Protect==PAGE_EXECUTE_READ)||(mbi.Protect==PAGE_EXECUTE_READWRITE)||(mbi.Protect==PAGE_READONLY)||(mbi.Protect==PAGE_READWRITE); + +} + +bool can_write(void* addr) { + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr,&mbi,sizeof(mbi))) { + return false; + }; + return (mbi.Protect==PAGE_EXECUTE_READWRITE)||(mbi.Protect==PAGE_EXECUTE_WRITECOPY)||(mbi.Protect==PAGE_READWRITE)||(mbi.Protect==PAGE_WRITECOPY); +} + +bool can_execute(void* addr) { + MEMORY_BASIC_INFORMATION mbi; + if (!VirtualQuery(addr,&mbi,sizeof(mbi))) { + return false; + }; + return (mbi.Protect==PAGE_EXECUTE_READWRITE)||(mbi.Protect==PAGE_EXECUTE_WRITECOPY)||(mbi.Protect==PAGE_EXECUTE_READ)||(mbi.Protect==PAGE_EXECUTE); +} + + +string hexdump_s(void *addr, size_t count=0x100,bool compact=false) { 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) << ": "; + if (!compact) { + out << endl; + out << setfill('0') << setw(8) << std::hex << std::uppercase << (offset+i) << ": "; + } } out << setfill('0') << setw(2) << std::hex << val << " "; } - out << endl; + if (!compact) { + out << endl; + } return out.str(); } @@ -258,118 +293,4 @@ vector split(string str, char sep) { 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 index 611da67..ae3eed1 100644 --- a/ScrapHacks/src/dllmain.cpp +++ b/ScrapHacks/src/dllmain.cpp @@ -1,7 +1,5 @@ - #include - #define DLL_EXPORT extern "C" __declspec(dllexport) using namespace std; diff --git a/ScrapHacks/src/make_D3D8_VMT.py b/ScrapHacks/utils/make_D3D8_VMT.py similarity index 92% rename from ScrapHacks/src/make_D3D8_VMT.py rename to ScrapHacks/utils/make_D3D8_VMT.py index 0fe87c4..349704b 100644 --- a/ScrapHacks/src/make_D3D8_VMT.py +++ b/ScrapHacks/utils/make_D3D8_VMT.py @@ -2,7 +2,6 @@ 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 @@ -22,7 +21,6 @@ with open(infile,"r") as infh: 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) diff --git a/lib/dbg.py b/lib/dbg.py index 5918051..dc84ad1 100644 --- a/lib/dbg.py +++ b/lib/dbg.py @@ -244,6 +244,20 @@ def nuke(): E = Scrap.GetEntity(E.NextInSlot) +def test_func(): + E = Scrap.GetFirst() + me = Scrap.UsrEntity(0) + while E: + if E.Name == me.Name: + E = Scrap.GetEntity(E.NextInSlot) + try: + E.Money=1024*1024*1024 + # SAI.SetStateVehicle(8,me.Name,E.Name) + except: + pass + E = Scrap.GetEntity(E.NextInSlot) + + def become(name): import CharConversor enable_all_conv() diff --git a/parse_save.py b/parse_save.py index ed84be2..3810f23 100644 --- a/parse_save.py +++ b/parse_save.py @@ -1,3 +1,4 @@ +import sys from construct import * from pprint import pprint @@ -11,10 +12,10 @@ ScrapSave = "ScarpSaveGame" / Struct( "data" / PrefixedArray(Int32ul, ScrapSaveVar), Terminated, ) -with open("Save0.sav", "rb") as sav_file: +with open(sys.argv[1], "rb") as sav_file: save = ScrapSave.parse_stream(sav_file) print("ID:", save.id) print("Title:", save.title) for var in save.data: - print(" - {}: {}".format(var.name, var.data)) + print(" {}: {}".format(var.name, var.data)) diff --git a/r2_analyze.py b/r2_analyze.py index f894499..92715c5 100644 --- a/r2_analyze.py +++ b/r2_analyze.py @@ -1,14 +1,20 @@ import r2pipe import os import json +from datetime import datetime +import subprocess as SP from tqdm import tqdm from pprint import pprint import os import sys r2cmds = [] -scrap_exe = sys.argv[1] +x64_dbg_script=[] +scrap_exe = os.path.abspath(sys.argv[1]) folder = os.path.abspath(os.path.dirname(scrap_exe)) +script_path=os.path.join(folder, "scrap_dissect.r2") +x64_dbg_script_path=os.path.join(folder, "scrap_dissect.x32dbg.txt") +json_path=os.path.join(folder, "scrap_dissect.json") assert os.path.isfile(scrap_exe), "File not found!" r2 = r2pipe.open(scrap_exe) @@ -20,6 +26,14 @@ target_hashes = { assert file_hashes == target_hashes, "Hash mismatch" +def x64_dbg_label(addr,name,prefix=None): + global x64_dbg_script + if isinstance(addr,int): + addr=hex(addr) + if prefix: + x64_dbg_script.append(f'lbl {addr},"{prefix}.{name}"') + else: + x64_dbg_script.append(f'lbl {addr},"{name}"') def r2_cmd(cmd): global r2, r2cmds @@ -38,54 +52,139 @@ def r2_cmdJ(cmd): r2cmds.append(cmd) return r2.cmdJ(cmd) +t_start=datetime.today() -print("[*] Running 'aaaaa'") +def analysis(full=False): + print("[*] Running analysis") + steps=[] + if full: + steps=[ + "e anal.dataref = true", + # "e anal.esil = true", + "e anal.jmp.after = true", + "e anal.jmp.indir = true", + "e anal.loads = true", + "e anal.pushret = true", + "e anal.refstr = true", + "e anal.strings = true", + "e anal.vinfun = true", + "e asm.anal = true", + ] + steps+=["aaaaa"] + for ac in steps: + print(f"[*] Running '{ac}'") + r2_cmd(f"{ac} 2>NUL") + +# 0x7fac20 +# 0x7fac19 +# 0x7faa4c +# 0x7fac1c # activate viewer +# 0x84d400 # lib preloaded -r2_cmd("aaaaa") +# 0x413ee0 -#0x7fac20 -#0x7fac19 -#0x7faa4c -#0x7fac1c # activate viewer -#0x84d400 +# 0x7d2094 refcnt -#0x413ee0 +comments= { + 0x6113f9:"Check if Window exists" +} -#0x7d2094 refcnt -flags = {0x7FE944: ("P_World", 4), 0x79C698: ("Py_Mods", 4),0x852914: ("P_D3D8_Dev",4)} +flags = { + 0x7FE944: "P_World", + 0x7FBE4C: "P_Vars", + 0x79C698: "Py_Mods", + 0x852914: "P_D3D8_Dev", + 0x7FCC00: "N_Paks_opened", + 0x7fcbec: "Hash_Index_Size", + 0x7fcbf0: "P_Hash_Index", + 0x7fcc08: "Lst_File", + 0x7fcc04: "Pak_Locked", + 0x7fc1b0: "Pak_Index", + 0x84cb64: "P_ConHandler", + 0x801e10: "num_arrows", + 0x7fac84: "P_Callbacks", + 0x80b2cc: "P_ActClassList", + 0x807a20: "P_Scorer", + 0x80a398: "P_SoundSys", + 0x84cb58: "H_RichEd", + 0x84cb4c: "P_HWND_Console", + 0x80cb40: "Console_Win_Buffer", + 0x84d400: "Lib_preloaded", + 0x7fac1c: "Activate_Viewer", + 0x8b18f0: "P_Models", + 0x8b18f4: "P_Scenes", + 0x8b18f8: "P_ActiveModels", + 0x803bc0: "net_is_server", + 0x8045e4: "net_is_master", + 0x8038a8: "net_is_client", + 0x7fadd8: "is_python", + 0x7fc084: "pak_lock", + 0x7fbe7c: "current_language", +} -types = ["struct PyMethodDef {char *ml_name; void *ml_meth; int ml_flags; char *ml_doc;};"] +VMTs = { + 0x78d4d8: "Py_entity", + 0x78cc6c: "World", + 0x78b680: "FilePak_1", + 0x78b6a4: "FilePak_2", + 0x78b638: "AbstractFile", + 0x78b4d8: "App", + 0x78b480: "Window", + 0x78b5c0: "File", + 0x78b65c: "FileMem", + 0x78b6d0: "IDevice_1", + 0x78b6f4: "IDevice_2", + 0x78b6fc: "IDevice_Kb", + 0x78b720: "IDevice_Mouse", + 0x78b74c: "IDevice_Joy", + 0x7933ac: "3d_Gfx", + 0x7933a0: "NodeFX", +} + +types = [ + "struct PyMethodDef { char *ml_name; void *ml_meth; int ml_flags; char *ml_doc;};", + "struct GameVar { struct GameVar* next; const char* name; const char* desc; uint64_t d_type; void* value; void* def_value; };", + "struct HT_Entry { void* data; const char* key; struct HT_Entry* next;};", + "struct PakEntry { unsigned char* filename; bool locked; void* data; uint32_t seek;};", + "struct HashIndexEntry { uint32_t offset; uint32_t size; uint32_t status; const char* name; struct HashIndexEntry* next; };", + "struct HashIndex { uint32_t size; struct HashIndexEntry** data; };", + "struct HashTableEntry { void* data; const char *key; struct HashTableEntry* next; };", + "struct HashTable { uint32_t size; struct HashTableEntry** data; };", +] func_sigs = { - 0x5a8390: "int py_exec(const char* script);", - 0x5bb9d0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);", - 0x4134c0: "int write_log(unsigned int color, const char* msg);", + 0x5A8390: "int py_exec(const char* script);", + 0x5BB9D0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);", + 0x413ee0: "int dbg_log(const char* fmt,...);", + 0x4134C0: "int write_log(unsigned int color, const char* msg);", 0x47C1E0: "int ht_hash_ent_list(const char* str);", 0x404BB0: "int ht_hash_ent(const char* str);", - 0x4016f0: "int reg_get_val(const char* value);", + 0x4016F0: "int reg_get_val(const char* value);", 0x414280: "int prepare_html_log(const char* filename);", - 0x6b1c70: "bool strcmp(const char* s1,const char* s2);", + 0x6597d0: "bool read_ini_entry(void* dest,const char* key, const char* section);", 0x5A8FB0: "void* Py_InitModule(const char* name,void* methods);", - 0x5E3800: "int fopen_1(const char* filename);", + 0x5E3800: "int fopen_from_pak(const char* filename);", 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);" + 0x404460: "int register_c_callback(const char* name,void* func);", + 0x414070: "void throw_assertion_2(const char* check,const char* file,const char* date, unsigned int line);", + 0x5FBC50: "void throw_assertion_1(const char* check,const char* file, unsigned int line);", + 0x5BC140: "static char* convertsimple1(void *arg, char **p_format, void *p_va);", + 0x5E3800: "int32_t fopen_from_pak(const char* filename,const char* mode);", + 0x5a90f0: "void* Py_BuildValue(const char* format, ...);" } functions = { - 0x6b1c70: "strcmp", - 0x5bb9d0: "PyArg_ParseTuple", - 0x5dd510: "init_engine_3d", + 0x6B1C70: "strcmp", + 0x5BB9D0: "PyArg_ParseTuple", + 0x5DD510: "init_engine_3d", 0x401180: "create_window", 0x401240: "create_main_window", - 0x4016f0: "reg_get_val", - 0x4134c0: "write_log", + 0x4016F0: "reg_get_val", + 0x4134C0: "write_log", 0x414280: "prepare_html_log", 0x418220: "get_version_info", - 0x4137e0: "write_html_log", + 0x4137E0: "write_html_log", 0x402190: "handle_console_input", 0x5F9520: "handle_render_console_input", 0x404A50: "find_entity", @@ -93,9 +192,9 @@ functions = { 0x404BB0: "ht_hash_ent", 0x404460: "register_c_callback", 0x417470: "load_game", - 0x5E3800: "fopen_1", - 0x419950: "fopen_2", - 0x403370: "debug_init", + 0x5E3800: "fopen_from_pak", + 0x5e3500: "fopen", + 0x403370: "init_debug", 0x401770: "init", 0x4026D0: "init_py", 0x405B40: "init_py_sub", @@ -105,25 +204,105 @@ functions = { 0x414570: "setup_game_vars", 0x5FBC50: "throw_assertion_1", 0x414070: "throw_assertion_2", - 0x5F7000: "load_m3d_ini", + 0x5F7000: "read_ini", 0x650F80: "load_sm3", 0x6665A0: "load_m3d_1", 0x666900: "load_m3d_2", 0x479B20: "world_constructor", - 0x479B40: "world_init", - 0x402510: "world_deinit", + 0x479B40: "init_world", + 0x402510: "deinit_world", 0x479870: "make_world", - 0x602a70: "render_frame" + 0x602A70: "render_frame", + 0x6B738C: "handle_exception", + 0x5B9E70: "py_getattr", + 0x413ee0: "dbg_log", + 0x5f75e0: "init_d3d", + 0x63a2f0: "gdi_draw_line", + 0x5e3250: "read_stream", + 0x5e3bb0: "read_stream_wrapper", + 0x50b9b0: "init_scorer", + 0x582e10: "init_action_class_list", + 0x528910: "init_sound_sys", + 0x5268d0: "try_init_sound_sys", + 0x404280: "cPyFunction_set_func", + 0x414680: "load_config", + 0x414810: "save_config", + 0x4f42a0: "close_server_socket", + 0x4f4d10: "close_server", + 0x4f48e0: "close_client", + 0x4f4fb0: "is_server", + 0x4f4a10: "is_client", + 0x4fac50: "is_master", + 0x526910: "close_sound_sys", + 0x526520: "shutdown_sound_sys", + 0x5dd700: "close_3d_engine", + 0x5a7320: "close_window", + 0x5dff20: "set_exception_handler", + 0x5a7f20: "get_console_wnd", + 0x5a73a0: "show_console", + 0x666c60: "read_m3d", + 0x417df0: "snprintf", + 0x5fc930: "printf", + 0x6597d0: "read_ini_entry", + 0x5fc0a0: "engine_debug_log", + 0x5a7440: "create_console_window", + 0x6114e0: "setup_window", + 0x404420: "clear_functions", + 0x405ca0: "close_py_subsys", + 0x50bcb0: "close_scorer", + 0x479b20: "close_world", + 0x582e70: "close_action_class", + 0x50b6a0: "get_scorer", + 0x50ea20: "scorer_parse_type", + 0x636580: "list_models", + 0x5a90f0: "Py_BuildValue", + 0x41c5a0: "has_lst_file", + 0x5a8e90: "py_error", + 0x5a9890: "get_module_dict", + 0x5c7bb0: "get_current_thread", + 0x5aa140: "preload_lib", + 0x413c10: "sprintf", + 0x405850: "check_is_python", + 0x47bf90: "setup_ent_list", + 0x474f80: "ent_list_get_set", } +# 0x853954 ??? some obj ptr + +# [0x7fbe98] + +# [0x853954]+0x2a3cc debug flag, checked in 0x006113a0 called from 0x005dd5ea +cfg=""" +e asm.cmt.right = true +e cmd.stack = true +e scr.utf8 = true +e asm.describe = false +e graph.cmtright = true +e cfg.sandbox = false +e cfg.newtab = true +e cfg.fortunes.type = tips,fun,creepy,nsfw +e dbg.status = true +e pdb.autoload = true +e emu.str = true +e asm.flags.offset = true +""".strip().splitlines() +for line in cfg: + r2_cmd(line) + +analysis(False) + +for addr,comment in comments.items(): + r2_cmd(f"CC {comment} @ {hex(addr)}") + for t in types: r2_cmd(f'"td {t}"') -for addr, args in flags.items(): - name, size = args - r2_cmd(f"f loc.{name} {size} {hex(addr)}") - +for addr, name in flags.items(): + x64_dbg_label(addr,name,"loc") + r2_cmd(f"f loc.{name} 4 {hex(addr)}") + for addr, name in functions.items(): + x64_dbg_label(addr,name,"fcn") r2_cmd(f"afr fcn.{name} {hex(addr)}") if addr in func_sigs: r2_cmd(f'"afs {func_sigs[addr]}" @{hex(addr)}') @@ -135,9 +314,13 @@ def vtables(): vtables = r2_cmdJ("avj") for c in tqdm(vtables, ascii=True): methods = [] - for m in tqdm(c.methods, ascii=True, leave=False): + name=VMTs.get(c.offset,f"{c.offset:08x}") + x64_dbg_label(c.offset,name,"vmt") + r2_cmd(f"f vmt.{name} 4 {hex(c.offset)}") + for idx,m in enumerate(tqdm(c.methods, ascii=True, leave=False)): methods.append(hex(m.offset)) - r2.cmd(f"afr @{hex(m.offset)} 2>{os.devnull}") + x64_dbg_label(m.offset,f"{name}.{idx}","fcn.vmt") + r2_cmd(f"afr fcn.vmt.{name}.{idx} {hex(m.offset)} 2>NUL") ret[hex(c.offset)] = methods return ret @@ -147,60 +330,71 @@ def c_callbacks(): funcs = {} res = r2_cmd("/r fcn.register_c_callback ~CALL[1]").splitlines() for addr in tqdm(res, ascii=True): - func, name = r2_cmdJ(f"s {addr};so -3;pdj 2") + r2_cmd(f"s {addr}") + r2_cmd(f"so -3") + func, name = r2_cmdJ(f"pdj 2") func = func.refs[0].addr name = r2_cmd(f"psz @{hex(name.refs[0].addr)}").strip() - r2_cmd(f"afr fcn.CB_{name} {hex(func)} 2>NUL") + r2_cmd(f"afr fcn.callbacks.{name} {hex(func)} 2>NUL") + x64_dbg_label(func,f"{name}","fcn.callbacks") funcs[name] = hex(func) return funcs def assertions(): assertions = {} - for (n_args,a_addr) in [(4,"fcn.throw_assertion_1"), (3,"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() - dis=r2_cmdJ(f"s {addr};so -{n_args};pij {n_args}") # seek and print disassembly - if n_args==4: + r2_cmd(f"s {addr}") + r2_cmd(f"so -{n_args}") + dis=r2_cmdJ(f"pij {n_args}") + if n_args == 4: file, msg, date, line = dis - elif n_args==3: - date=None + 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() 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({'line':line,'date':date,'addr':addr,'msg': msg}) + date = r2_cmd(f"psz @{date.refs[0].addr}").strip() + line = line.val + file = file.replace("\\\\", "\\") + assertions.setdefault(file, []) + assertions[file].append( + {"line": line, "date": date, "addr": addr, "msg": msg} + ) except: pass for path in assertions: - assertions[path].sort(key=lambda v:v['line']) + assertions[path].sort(key=lambda v: v["line"]) return assertions + def bb_refs(addr): - ret={} + ret = {} res = r2_cmd(f"/r {addr} ~fcn[0,1]").splitlines() print() for ent in res: - func,hit=ent.split() - ret[hit]={'asm':[],'func':func} + func, hit = ent.split() + ret[hit] = {"asm": [], "func": func} for ins in r2_cmdJ(f"pdbj @{hit}"): - ret[hit]['asm'].append(ins.disasm) + 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") @@ -212,7 +406,9 @@ def py_mods(): print() py_mods = {} for call_loc in tqdm(res, ascii=True): - args = r2_cmdJ(f"s {call_loc};so -3;pdj 3") + r2_cmd(f"s {call_loc}") + r2_cmd(f"so -3") + args = r2_cmdJ("pdj 3") refs = [] if not all([arg.type == "push" for arg in args]): continue @@ -222,7 +418,8 @@ def py_mods(): doc = r2_cmd(f"psz @{doc}").strip() name = r2_cmd(f"psz @{name}").strip() r2_cmd(f"s {methods}") - r2_cmd(f"f loc.py.MethodDef_{name} 4 {methods}") + r2_cmd(f"f py.{name} 4 {methods}") + x64_dbg_label(methods,f"{name}","py") py_mods[name] = {"methods_addr": methods, "doc": doc, "methods": {}} while True: m_name, m_func, _, m_doc = [v.value for v in r2_cmdJ(f"pfj xxxx")] @@ -230,16 +427,21 @@ def py_mods(): break m_name, m_func, m_doc = map(hex, (m_name, m_func, m_doc)) m_name = r2_cmd(f"psz @{m_name}").strip() - r2_cmd(f"f Py_{name}_{m_name}_doc 4 {m_doc}") - m_doc = r2_cmd(f"psz @{m_doc}").strip() + r2_cmd(f"f py.{name}.{m_name}.__doc__ 4 {m_doc}") + if int(m_doc,16)!=0: + x64_dbg_label(m_doc,f"{name}.{m_name}.__doc__","py") + m_doc = r2_cmd(f"psz @{m_doc}").strip() + else: + m_doc=None py_mods[name]["methods"][m_name] = {"addr": m_func, "doc": m_doc} - r2_cmd(f"afr fcn.py.{name}.{m_name} {m_func} 2>NUL") + r2_cmd(f"afr py.{name}.{m_name} {m_func} 2>NUL") + x64_dbg_label(m_func,f"{name}.{m_name}","fcn.py") r2_cmd("s +16") return py_mods def game_vars(): - ret={} + ret = {} print("[*] Parsing Game variables") res = r2_cmd("/r fcn.setup_game_vars ~CALL[1]").splitlines() print() @@ -249,27 +451,28 @@ def game_vars(): args = r2_cmd("pdj -5") # seek and print disassembly if not args: continue - args=json.loads(args) + args = json.loads(args) args_a = [] - push_cnt=0 + push_cnt = 0 for arg in args[::-1]: - if arg['type'] not in ["push","mov"]: + if arg["type"] not in ["push", "mov"]: continue - if arg['type']=="push": - push_cnt+=1 + if arg["type"] == "push": + push_cnt += 1 args_a.append(arg) - if push_cnt==3: + if push_cnt == 3: break - if len(args_a)!=4: + if len(args_a) != 4: continue - if not all(['val' in v for v in args_a]): + if not all(["val" in v for v in args_a]): continue - addr,name,_,desc=[v['val'] for v in args_a] - name=r2_cmd(f"psz @{hex(name)}").strip() - desc=r2_cmd(f"psz @{hex(desc)}").strip() - addr=hex(addr) - r2_cmd(f"f var_{name} 4 {addr}") - ret[addr]={'name':name,'desc':desc} + addr, name, _, desc = [v["val"] for v in args_a] + name = r2_cmd(f"psz @{hex(name)}").strip() + desc = r2_cmd(f"psz @{hex(desc)}").strip() + addr = hex(addr) + r2_cmd(f"f loc.gvar.{name} 4 {addr}") + x64_dbg_label(addr,f"{name}","loc.gvar") + ret[addr] = {"name": name, "desc": desc} return ret @@ -283,23 +486,45 @@ ret = dict( render=render(), ) -r2_cmd("aaaaa") # Propagate type infos +analysis(True) -with open(os.path.join(folder, "Scrap_dissect.json"), "w") as of: +with open(json_path, "w") as of: json.dump(ret, of, indent=4) -print("[+] Wrote Scrap_dissect.json") -with open(os.path.join(folder, "Scrap_dissect.r2"), "w") as of: +print("[+] Wrote scrap_dissect.json") + +with open(x64_dbg_script_path,"w") as of: + of.write("\n".join(x64_dbg_script)) + +print("[+] Wrote scrap_dissect.x32dbg.txt") + +with open(script_path, "w") as of: wcmds = [] for cmd in r2cmds: - for start in ["f ", "afr ", "aaaaa","afs"]: + record=True + for start in ["p","/","s"]: if cmd.strip('"').startswith(start): - wcmds.append(cmd) - break + record=False + if record: + wcmds.append(cmd) of.write("\n".join(wcmds)) -print("[+] Wrote Scrap_dissect.r2") -print(f"[*] Done, now cd to '{folder}'...") -print( - "[*] ...and run 'r2 -i Scrap_dissect.r2 Scrap.exe' to load the parsed infos into radare2" -) +print("[+] Wrote scrap_dissect.r2") + +r2.quit() + +def start_program(cmdl,**kwargs): + if os.name=='nt': + return SP.Popen(['cmd','/c','start']+cmdl,**kwargs) + else: + return SP.Popen(cmdl,**kwargs) + +print("[+] Analysis took:",datetime.today()-t_start) + + +print("[+] Executing Cutter") +try: + start_program(['cutter','-A','0','-i',script_path,scrap_exe],cwd=folder,shell=False) +except FileNotFoundError: + print("[-] cutter not installed, falling back to r2") + start_program(['r2','-i',script_path,scrap_exe],cwd=folder,shell=False) \ No newline at end of file