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
This commit is contained in:
Daniel S. 2020-01-03 03:22:09 +01:00
parent 48bf3773c9
commit 7e044f0114
20 changed files with 1607 additions and 760 deletions

View file

@ -6,5 +6,9 @@
"markdown", "markdown",
"latex", "latex",
"plaintext" "plaintext"
] ],
"files.associations": {
"xstring": "cpp",
"iterator": "cpp"
}
} }

210
NOTES.md
View file

@ -1,4 +1,5 @@
# Infos # Infos
- Engine: ScrapEngine - Engine: ScrapEngine
- Ingame Scripting Language: Python 1.5.2 - Ingame Scripting Language: Python 1.5.2
@ -9,7 +10,8 @@
# Functions identified: # 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`):
* `<Command>`: Try to evaluate Command as Python expression * `<Command>`: Try to evaluate Command as Python expression
* `:<Var>`: Get Game Engine Global Variable * `:<Var>`: Get Game Engine Global Variable
* `:<Var> <Val>`: Set Game Engine Global Variable * `:<Var> <Val>`: Set Game Engine Global Variable
@ -18,7 +20,8 @@
* `/<command>`: Run Command defined in `QuickConsole.py`: `import quickconsole;quickconsole.%s()` * `/<command>`: Run Command defined in `QuickConsole.py`: `import quickconsole;quickconsole.%s()`
* `/<command> <arg>,<arg>`: Run function in `QuickConsole.py` with argument(s) `import quickconsole;quickconsole.%s(%s)` * `/<command> <arg>,<arg>`: 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 luces`
* `listar` * `listar`
* `arbol` (Patch Scrap.exe@offset 0x314bc9 replace 0x20 with 0x00 (or just type `arbol ` with a space at the end)) * `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` * `capullo`
## Python Stuff ## Python Stuff
- Modules List @ 0x79C698 (Module Name as `char*` followed by Pointer to Init Function)
- InitPyMod @ 0x5A8FB0 - `0x79C698`: Modules List (Module Name as `char*` followed by Pointer to Init Function)
- PyExec @ 0x5A8390 - `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: ## Other Functions:
- FindEntity @ 0x404a50 Check `r2_analyze.py` for full list
- 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
# 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` ## Game World/State Pointer @ `0x7fe944`
@ -70,6 +151,10 @@ Points to World struct
| 0x0000 | `void**` | Virtual Method Table | | 0x0000 | `void**` | Virtual Method Table |
| 0x0004 | `uint32_t` | Size of Entity Hashtable | | 0x0004 | `uint32_t` | Size of Entity Hashtable |
| 0x0008 | `void**` | Pointer to 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 | | 0x02B8 | `uint32_t` | Number of entity lists |
| 0x02BC | `void**` | Pointer to entity list Hashtable | | 0x02BC | `void**` | Pointer to entity list Hashtable |
| 0x0330 | `float[3]` | Time (why 3 times?) | | 0x0330 | `float[3]` | Time (why 3 times?) |
@ -91,18 +176,19 @@ Points to World struct
| 0x2238 | `???` | Used in `World_Init` | | 0x2238 | `???` | Used in `World_Init` |
| 0x2254 | `float` | Used in `World_Init` | | 0x2254 | `float` | Used in `World_Init` |
## Entity Hash Table ## Entity Hash Table
Hash-function used: [PJW](https://en.wikipedia.org/wiki/PJW_hash_function) (Same parameters as the example implementation) Hash-function used: [PJW](https://en.wikipedia.org/wiki/PJW_hash_function) (Same parameters as the example implementation)
Entry format: Entry format:
| Offset | Type | Description | ```cpp
| ------ | ------------- | ------------------------------ | struct HT_Entry {
| 0x0 | `void*` | Pointer to data | void* data;
| 0x4 | `const char*` | key as `char*` | const char* key;
| 0x8 | `void*` | Pointer to next entry in chain | HT_Entry* next;
}
```
Data format: Data format:
@ -113,10 +199,19 @@ Data format:
| 0x14 | `void*` | pointer to self (why?) | | 0x14 | `void*` | pointer to self (why?) |
| 0x28 | `float[3]` | Position in Game World | | 0x28 | `float[3]` | Position in Game World |
## EntityList Hash Table
Attributes:
- `Near`
- `First`
- `Num`
- `OnDeath`
- `OnDamage`
# File Formats # File Formats
## .packed File Format: ## .packed File Format:
``` ```
Header: Header:
"BFPK\0\0\0\0" "BFPK\0\0\0\0"
@ -128,35 +223,44 @@ Header:
Int32ul: offset in file Int32ul: offset in file
``` ```
# Virtual Method Tables:
check `r2_analyze.py` for full list
## Loading Custom Content (not really working) ## Loading Custom Content (not really working)
1. Create a folder `mods` 1. Create a folder `mods`
2. Drop a `*.packed` file into it 2. Drop a `*.packed` file into it
3. Change `Scrap.cfg` as follows 3. Change `Scrap.cfg` as follows
1. Add `ModPathName = mods` 1. Add `ModPathName = mods`
2. Add `ModFileName = <filename>` 2. Add `ModFileName = <filename>`
## Interesting file: ## Interesting file inside `Data.packed`
* `m3d.ini`: Rendering Engine Configuration * `m3d.ini`: Rendering Engine Configuration
* `scripts/`: Game Engine Scripts * `scripts/`: Game Engine Scripts
# How to enable External Console: # 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` 2. in m3d.ini uncomment (remove `;`) `ConsolaWnd` (GUI Console) and/or `ConsolaTxt` (Text Console) and set the value to `SI`
3. repack `Data.packed` 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 # 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 # Code Snippets
## [Kaitai Struct](http://kaitai.io/) Parser for .packed files ## [Kaitai Struct](http://kaitai.io/) Parser for .packed files
```yaml ```yaml
meta: meta:
id: packed id: packed
@ -170,9 +274,9 @@ seq:
- id: magic - id: magic
contents: BFPK contents: BFPK
doc: File Magic doc: File Magic
- id: magic2 - id: version
contents: [0,0,0,0] contents: [0,0,0,0]
doc: Second File Magic doc: File Version
- id: num_files - id: num_files
type: u4 type: u4
doc: Number of files doc: Number of files
@ -180,7 +284,7 @@ seq:
type: file_entry type: file_entry
repeat: expr repeat: expr
repeat-expr: num_files repeat-expr: num_files
doc: Directory entry for each file doc: Entry for each file
types: types:
file_entry: file_entry:
seq: seq:
@ -203,19 +307,9 @@ types:
size: size size: size
``` ```
## Hashfunction used in Entity Hash-Table # TODO:
```c - Figure out how C++ Callbacks work
unsigned long ElfHash(const unsigned char *s) - Figure out SM3 (Models), CM3 (Animations) file formats
{ - Figure out rest of World structure
unsigned long h = 0, high; - Figure out rest of Entity structure
while ( *s )
{
h = ( h << 4 ) + *s++;
if ( high = h & 0xF0000000 )
h ^= high >> 24;
h &= ~high;
}
return h;
}
```

View file

@ -29,4 +29,4 @@ WIP Memory hacking library
- [Reclass.NET](https://github.com/ReClassNET/ReClass.NET) - [Reclass.NET](https://github.com/ReClassNET/ReClass.NET)
- [HxD](https://mh-nexus.de/en/hxd/) - [HxD](https://mh-nexus.de/en/hxd/)
- [Kaitai Struct](http://kaitai.io/) - [Kaitai Struct](http://kaitai.io/)
- [Radare2](https://www.radare.org/) - [Radare2](https://www.radare.org/) + [Cutter](https://cutter.re/)

View file

@ -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
}

View file

@ -6,88 +6,123 @@ project(ScrapHacks
DESCRIPTION "Scrapland memory hacking library" DESCRIPTION "Scrapland memory hacking library"
LANGUAGES CXX) 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(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(WIN32)
if(MSVC) if(MSVC)
# ensure we use minimal "windows.h" lib without the crazy min max macros # 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\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"NOMINMAX\"")
# disable SAFESEH - to avoid "LNK2026: module unsafe" # disable SAFESEH - to avoid "LNK2026: module unsafe"
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"SAFESEH:NO\"") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D \"SAFESEH:NO\"")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_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") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /SAFESEH:NO /ignore:4217")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /SAFESEH:NO /ignore:4217")
endif(MSVC) endif(MSVC)
endif(WIN32) endif(WIN32)
include(ExternalProject) include(FetchContent)
ExternalProject_Add( FetchContent_Declare(
DirectX DirectX
PREFIX ${CMAKE_CURRENT_BINARY_DIR} PREFIX ${CMAKE_CURRENT_BINARY_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
URL URL
https://archive.org/download/DirectX.8.0a.SDK_includes_libs_only/DirectX.8.0a.SDK.zip https://archive.org/download/DirectX.8.0a.SDK_includes_libs_only/DirectX.8.0a.SDK.zip
URL_HASH SHA1=39f168336d0df92ff14d62d5e3aef1b9e3191312) URL_HASH SHA1=39f168336d0df92ff14d62d5e3aef1b9e3191312)
FetchContent_MakeAvailable(DirectX)
ExternalProject_Get_Property(DirectX SOURCE_DIR) FetchContent_Declare(
include_directories(AFTER ${SOURCE_DIR}/8.0/include/) ASM_JIT
link_directories(AFTER ${SOURCE_DIR}/8.0/lib/) 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) find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter)
add_custom_target( add_custom_command(
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 OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp
DEPENDS DirectX 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
) )
add_custom_target(D3D8_VMT ALL
# ExternalProject_Add( DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/D3D8_VMT.hpp")
# 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(_CRT_SECURE_NO_WARNINGS)
add_compile_definitions(POINTER_64=__ptr64) add_compile_definitions(POINTER_64=__ptr64)
add_library(ScrapHack SHARED add_library(ScrapHack SHARED
${CMAKE_CURRENT_SOURCE_DIR}/src/dllmain.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/dllmain.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/ScrapHack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ScrapHack.cpp
) ${ASMTK_SRC}
${ASMJIT_SRC}
)
set_target_properties(ScrapHack PROPERTIES SUFFIX ".pyd") set_target_properties(ScrapHack PROPERTIES SUFFIX ".pyd")
add_dependencies(ScrapHack DirectX) add_dependencies(ScrapHack D3D8_VMT)
add_dependencies(ScrapHack MAKE_D3D8_VMT)
add_dependencies(MAKE_D3D8_VMT DirectX)
# add_dependencies(ScrapHack Python152) # add_dependencies(ScrapHack Python152)
# add_dependencies(ScrapHack Python152_Bin) # add_dependencies(ScrapHack Python152_Bin)
target_link_libraries(ScrapHack target_link_libraries(ScrapHack
d3d8 d3d8
d3dx8 d3dx8
dxerr8 dxerr8
gdiplus
# PYTHON15 # PYTHON15
# Zydis
legacy_stdio_definitions) legacy_stdio_definitions)
install(TARGETS ScrapHack RUNTIME DESTINATION ${SCRAPLAND_DIR}/lib)
target_compile_features(ScrapHack PUBLIC cxx_std_11) target_compile_features(ScrapHack PUBLIC cxx_std_11)

View file

@ -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 ## Prerequisites
- Visual Studio 2017/2019 (others might work) - Visual Studio 2017/2019 (others might work)
@ -13,16 +22,13 @@ cmake -G"NMake Makefiles" -B build
cmake --build build --target install 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+^) - open the ingame console (Ctrl+^)
- type `import ScrapHack` - type `import ScrapHack`
- type `$help` - type `$help`
- Done!
## Notes ## Notes

10
ScrapHacks/build.bat Normal file
View file

@ -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

View file

@ -1,33 +1,55 @@
#pragma once #pragma once
#include <algorithm>
using namespace std;
#include <Windows.h> #include <Windows.h>
#include <d3d8.h> #include <d3d8.h>
#include <d3dx8.h> #include <d3dx8.h>
#include <dxerr8.h> #include <dxerr8.h>
#include "D3D8_VMT.hpp" #include "D3D8_VMT.hpp"
#include "Hook.hpp" #include "Hook.hpp"
#include "Scrapland.hpp" #include "Scrapland.hpp"
#include "Structures.hpp" #include "Structures.hpp"
#include "Util.hpp" #include "Util.hpp"
uintmax_t frame = 0; uintmax_t frame = 0;
bool hooked=false; bool hooked = false;
bool overlay = false; bool overlay = false;
LPD3DXFONT m_pFont; LPD3DXFONT m_pFont;
HFONT hFont; HFONT hFont;
HBRUSH hBrush; HBRUSH hBrush;
D3DCOLOR color = D3DCOLOR_ARGB(255, 255, 0, 0); D3DCOLOR color = D3DCOLOR_XRGB(255, 0, 0);
RECT Rect = {0, 0, 0, 0}; RECT Rect = {0, 0, 0, 0};
D3DRECT panel; D3DRECT panel;
D3DFILLMODE fillmode = D3DFILLMODE::D3DFILL_SOLID;
boolean use_z;
size_t size_ht(HashTable<EntityList> *ht); size_t size_ht(HashTable<EntityList> *ht);
size_t size_ht(HashTable<Entity> *ht); size_t size_ht(HashTable<Entity> *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 LPDIRECT3DDEVICE8
Render(LPDIRECT3DDEVICE8 dev) { Render(LPDIRECT3DDEVICE8 dev) {
if (!overlay) { if (!overlay) {
return dev; return dev;
} }
IDirect3DSurface8* surf;
char text[4096]; char text[4096];
int32_t money = 0; int32_t money = 0;
size_t num_ents = 0; size_t num_ents = 0;
@ -37,30 +59,42 @@ Render(LPDIRECT3DDEVICE8 dev) {
num_ents = size_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS)); num_ents = size_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS));
num_ent_lst = size_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS)); num_ent_lst = size_ht(ptr<HashTable<EntityList>>(P_WORLD, O_ENTLISTS));
} }
snprintf(text, 4096, snprintf(text, 4096,"ScrapHack v0.1\nFrame: [%lld]\nMoney: [%d]\nEntities: [%ld]\nEntity Lists: [%ld]",++frame, money, num_ents, num_ent_lst);
R"(ScrapHack v0.1
Frame: [%lld]
Money: [%d]
Entities: [%ld]
Entity Lists: [%ld])",
++frame, money, num_ents, num_ent_lst);
if (m_pFont == nullptr) { if (m_pFont == nullptr) {
D3DXCreateFont(dev, hFont, &m_pFont); D3DXCreateFont(dev, hFont, &m_pFont);
hFont = nullptr; hFont = nullptr;
} }
m_pFont->Begin(); m_pFont->Begin();
m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0); 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->DrawTextA(text, -1, &Rect, DT_LEFT, color);
m_pFont->End(); m_pFont->End();
return dev; return dev;
} }
LPDIRECT3DDEVICE8
BeforeRender(LPDIRECT3DDEVICE8 dev) {
return dev;
}
HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev) { HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev) {
typedef decltype(&H_EndScene) t_func; typedef decltype(&H_EndScene) t_func;
shared_ptr<Hook> hook = Hook::get(H_EndScene); shared_ptr<Hook> hook = Hook::get(H_EndScene);
return hook->func<t_func>(Render(dev)); return hook->func<t_func>(Render(dev));
} }
HRESULT WINAPI H_BeginScene(LPDIRECT3DDEVICE8 dev) {
typedef decltype(&H_BeginScene) t_func;
shared_ptr<Hook> hook = Hook::get(H_BeginScene);
HRESULT ret=hook->func<t_func>(dev);
BeforeRender(dev);
return ret;
}
HRESULT WINAPI H_SetLight(LPDIRECT3DDEVICE8 dev, DWORD index, HRESULT WINAPI H_SetLight(LPDIRECT3DDEVICE8 dev, DWORD index,
D3DLIGHT8 *light) { D3DLIGHT8 *light) {
typedef decltype(&H_SetLight) t_func; typedef decltype(&H_SetLight) t_func;
@ -87,13 +121,13 @@ HRESULT WINAPI H_DrawIndexedPrimitive(LPDIRECT3DDEVICE8 dev,
typedef decltype(&H_DrawIndexedPrimitive) t_func; typedef decltype(&H_DrawIndexedPrimitive) t_func;
DWORD AMBIENT; DWORD AMBIENT;
shared_ptr<Hook> hook = Hook::get(H_DrawIndexedPrimitive); shared_ptr<Hook> hook = Hook::get(H_DrawIndexedPrimitive);
dev->GetRenderState(D3DRS_AMBIENT, &AMBIENT); // dev->GetRenderState(D3DRS_AMBIENT, &AMBIENT);
dev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255)); // dev->SetRenderState(D3DRS_AMBIENT, D3DCOLOR_XRGB(255, 255, 255));
dev->SetRenderState(D3DRS_FILLMODE, D3DFILLMODE::D3DFILL_SOLID); dev->SetRenderState(D3DRS_FILLMODE, fillmode);
dev->SetRenderState(D3DRS_ZENABLE, 0); dev->SetRenderState(D3DRS_ZENABLE, use_z);
auto ret = hook->func<t_func>(dev, Type, minIndex, NumVertices, startIndex, auto ret = hook->func<t_func>(dev, Type, minIndex, NumVertices, startIndex,
primCount); primCount);
dev->SetRenderState(D3DRS_AMBIENT, AMBIENT); // dev->SetRenderState(D3DRS_AMBIENT, AMBIENT);
dev->SetRenderState(D3DRS_ZENABLE, 1); dev->SetRenderState(D3DRS_ZENABLE, 1);
return ret; return ret;
} }
@ -107,6 +141,7 @@ void unhook_d3d8() {
m_pFont = nullptr; m_pFont = nullptr;
} }
Hook::drop(H_EndScene); Hook::drop(H_EndScene);
Hook::drop(H_BeginScene);
Hook::drop(H_DrawIndexedPrimitive); Hook::drop(H_DrawIndexedPrimitive);
Hook::drop(H_SetLight); Hook::drop(H_SetLight);
hooked=false; hooked=false;
@ -116,9 +151,6 @@ void hook_d3d8() {
if (hooked) { if (hooked) {
return; 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; void *dev = nullptr;
while (true) { while (true) {
dev = ptr<void>(P_D3DDEV); dev = ptr<void>(P_D3DDEV);
@ -127,7 +159,11 @@ void hook_d3d8() {
} }
Sleep(100); 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_EndScene], H_EndScene);
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_BeginScene], H_BeginScene);
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_DrawIndexedPrimitive], Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_DrawIndexedPrimitive],
H_DrawIndexedPrimitive); H_DrawIndexedPrimitive);
Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_SetLight], H_SetLight); Hook::addr(GetVTable(dev)[VMT_IDirect3DDevice8::m_SetLight], H_SetLight);

View file

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

View file

@ -4,9 +4,29 @@
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <vector>
#include <asmjit/asmjit.h>
using namespace std; using namespace std;
/*
vector<uint8_t> 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 { class Hook {
private: private:
MEMORY_BASIC_INFORMATION mbi; MEMORY_BASIC_INFORMATION mbi;
@ -19,6 +39,7 @@ class Hook {
public: public:
Hook(void *func, void *detour) { Hook(void *func, void *detour) {
// TODO: build jmp_bytes using asmjit
uintptr_t dest = reinterpret_cast<uintptr_t>(detour); uintptr_t dest = reinterpret_cast<uintptr_t>(detour);
uintptr_t src = reinterpret_cast<uintptr_t>(func); uintptr_t src = reinterpret_cast<uintptr_t>(func);
this->orig = func; this->orig = func;
@ -83,7 +104,7 @@ class Hook {
} }
void disable() { void disable() {
if (enabled) { if (this->enabled) {
// cout << "Disabling: [" << this->orig << " <- " << this->detour << // cout << "Disabling: [" << this->orig << " <- " << this->detour <<
// "]" // "]"
// << endl; // << endl;
@ -91,7 +112,7 @@ class Hook {
PAGE_EXECUTE_READWRITE, NULL); PAGE_EXECUTE_READWRITE, NULL);
memcpy(this->orig, this->orig_bytes, 1 + 4 + 1); memcpy(this->orig, this->orig_bytes, 1 + 4 + 1);
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
enabled = false; this->enabled = false;
} }
} }
void enable() { void enable() {

View file

@ -1,9 +1,15 @@
#pragma once #pragma once
#include <Windows.h> #include <Windows.h>
#include <DbgHelp.h>
#define ASMJIT_EMBED
#define ASMTK_EMBED
#include <regex> #include <regex>
#include <sstream> #include <sstream>
#include <string>
#include <asmtk/asmtk.h>
#include "Scrapland.hpp" #include "Scrapland.hpp"
#include "Util.hpp" #include "Util.hpp"
@ -13,15 +19,196 @@ void DllUnload();
void unhook_d3d8(); void unhook_d3d8();
void hook_d3d8(); void hook_d3d8();
typedef void(_cdecl *t_cmd_func)(vector<string>); struct Command;
struct t_cmd { typedef void(_cdecl *t_cmd_func)(Command*,vector<string>);
size_t assemble(vector<string> 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<string> assembly) {
return assemble(assembly,0);
}
struct Command {
t_cmd_func func; t_cmd_func func;
const char* usage; string usage;
const char* doc; string doc;
map<string,Command*> subcommands;
Command(t_cmd_func func=nullptr,string usage="",string doc="",map<string,Command*> subcommands={}) {
this->func=func;
this->usage=usage;
this->doc=doc;
this->subcommands=subcommands;
}
Command(string usage="",string doc="",map<string,Command*> subcommands={}):
Command(nullptr,usage,doc,subcommands) {};
void add_subcommand(string name,Command *cmd) {
this->subcommands[name]=cmd;
}
void set_subcommands(const map<string,Command*> &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<string> 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<string>); struct REPL {
map<string,Command*> commands;
REPL(map<string,Command*> commands) {
this->commands=commands;
}
bool has_command(string cmd) {
return this->commands.count(cmd)>0;
}
bool exec(vector<string> args) {
vector<tuple<string,Command*>> cmd_stack;
map<string,Command*> 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<string> args) {
map<string,Command*> cmds=this->commands;
string ret;
if (args.empty()) {
return this->show_commands(cmds);
}
pair<string,Command*> 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<string,Command*> cmds,size_t depth=0) {
string s;
for (auto cmd:cmds) {
for (size_t n=0;n<depth;++n) {
s+=" ";
}
s+=cmd.first+": "+cmd.second->doc+" ("+cmd.second->usage+")\n"+this->show_commands(cmd.second->subcommands,depth+1);
}
return s;
}
};
DWORD DWORD
get_protection(void *addr) { get_protection(void *addr) {
@ -30,66 +217,127 @@ get_protection(void *addr) {
return mbi.Protect; return mbi.Protect;
} }
void cmd_write(vector<string> args) { void cmd_exec(Command* cmd, vector<string> args) {
void *addr;
MEMORY_BASIC_INFORMATION mbi; MEMORY_BASIC_INFORMATION mbi;
if (args.size() != 2) { if (args.size()<1) {
scrap_log(ERR_COLOR, "Usage: $w <addr> <data(hex)>\n"); scrap_log(ERR_COLOR, cmd->usage);
return; scrap_log(ERR_COLOR, "\n");
} }
void *addr = 0;
vector<uint8_t> data;
try { try {
addr = (void *)stoull(args[0], 0, 16); addr = (void *)stoull(args[0], 0, 16);
data = fromhex(args[1]);
} catch (exception e) { } 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; 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<string> 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<uint8_t> 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; size_t idx = 0;
for (uint8_t v : data) { for (uint8_t v : data) {
buffer[idx++] = v; 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); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
if (buffer) {
free(buffer);
}
return; return;
} }
void cmd_read(vector<string> args) { void cmd_read(Command* cmd,vector<string> args) {
MEMORY_BASIC_INFORMATION mbi; MEMORY_BASIC_INFORMATION mbi;
if (args.size() != 2) { if (args.size()<1) {
scrap_log(ERR_COLOR, "Usage: $r <addr> <size>\n"); scrap_log(ERR_COLOR, cmd->usage);
scrap_log(ERR_COLOR, "\n");
return; return;
} }
uintptr_t addr = UINTPTR_MAX; uintptr_t addr = UINTPTR_MAX;
size_t size = 0; size_t size = 0xff;
unsigned char *buffer; unsigned char *buffer;
try { try {
addr = stoull(args[0], 0, 16); addr = stoull(args[0], 0, 16);
size = stoull(args[1]); if (args.size()>1) {
size = stoull(args[1]);
}
buffer = new unsigned char[size]; buffer = new unsigned char[size];
} catch (exception e) { } 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; return;
} }
void *mptr = reinterpret_cast<void *>(addr); void *mptr = reinterpret_cast<void *>(addr);
if (VirtualQuery(mptr, &mbi, sizeof(mbi)) == 0) { 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; return;
}; };
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, PAGE_EXECUTE_READWRITE,
&mbi.Protect);
string hxd = hexdump_s(mptr, size); string hxd = hexdump_s(mptr, size);
scrap_log(INFO_COLOR, hxd.c_str()); scrap_log(INFO_COLOR, hxd.c_str());
VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL); VirtualProtect(mbi.BaseAddress, mbi.RegionSize, mbi.Protect, NULL);
@ -99,26 +347,78 @@ void cmd_read(vector<string> args) {
return; return;
} }
void cmd_dx8(vector<string> args) { void cmd_hook_dx8(Command* cmd,vector<string> args) {
hook_d3d8();
scrap_log(INFO_COLOR,"DX8 hooked!\n");
return;
}
void cmd_unhook_dx8(Command* cmd,vector<string> args) {
unhook_d3d8();
scrap_log(INFO_COLOR,"DX8 unhooked!\n");
return;
}
void cmd_dx8(Command* cmd,vector<string> args) {
if (args.size()!=1) { 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; return;
} }
if (args[0]=="hook") { if (args[0]=="zenable:true") {
hook_d3d8(); use_z=true;
scrap_log(INFO_COLOR,"DX8 hooked!\n"); scrap_log(INFO_COLOR,"DX8 mode switched!\n");
return; return;
} };
if (args[0]=="unhook") { if (args[0]=="zenable:false") {
unhook_d3d8(); use_z=false;
scrap_log(INFO_COLOR,"DX8 unhooked!\n"); 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; return;
}; };
scrap_log(ERR_COLOR,"Invalid argument!\n"); scrap_log(ERR_COLOR,"Invalid argument!\n");
return; return;
} }
void cmd_dump_py(vector<string> args) { void cmd_dump_stack(Command* cmd, vector<string> args) {
void** stack=(void**)_AddressOfReturnAddress();
cout<<"ESP: "<<stack<<endl;
for (size_t n=0;n<0x100;++n) {
if (!addr_exists(stack[n])) {
continue;
}
char R=can_read(stack[n]) ? 'R' : ' ';
char W=can_write(stack[n]) ? 'W' : ' ';
char X=can_execute(stack[n]) ? 'X' : ' ';
cout<<"ESP[" << std::hex << setfill('0') << setw(2) << n <<"]: "<<stack[n]<<" "<<R<<W<<X;
if (can_read(stack[n])) {
cout<<" [ "<<hexdump_s(stack[n],0xf,true)<<"]"<<endl;
} else {
cout<<endl;
}
}
return;
}
void cmd_dump_py(Command* cmd,vector<string> args) {
stringstream out; stringstream out;
for (auto mod : Py) { for (auto mod : Py) {
for (auto meth : mod.second.methods) { for (auto meth : mod.second.methods) {
@ -129,7 +429,18 @@ void cmd_dump_py(vector<string> args) {
scrap_log(INFO_COLOR,out.str().c_str()); scrap_log(INFO_COLOR,out.str().c_str());
} }
void cmd_dump_ents(vector<string> args) { void cmd_dump_vars(Command* cmd, vector<string> args) {
stringstream out;
GameVar* var=ptr<GameVar>(P_VARS,0);
out << "GameVars:" << endl;
while (var!=nullptr) {
out<<var->name<< "[" <<std::hex <<(uint16_t)var->type <<","<< (uint16_t)var->subtype << std::dec << "]: " << var->desc<<" ("<<var->value<<", "<<var->default<<")"<<endl;
var=var->next;
}
scrap_log(INFO_COLOR,out.str().c_str());
}
void cmd_dump_ents(Command* cmd,vector<string> args) {
stringstream out; stringstream out;
out << "Entities:" << endl; out << "Entities:" << endl;
dump_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS), &out); dump_ht(ptr<HashTable<Entity>>(P_WORLD, O_ENTS), &out);
@ -139,7 +450,11 @@ void cmd_dump_ents(vector<string> args) {
return; return;
} }
void cmd_toggle_overlay(vector<string> args) { void cmd_toggle_overlay(Command* cmd,vector<string> args) {
if (!hooked) {
scrap_log(INFO_COLOR,"DX8 not hooked, run '$dx8 hook' first!\n");
return;
}
overlay=!overlay; overlay=!overlay;
if (overlay) { if (overlay) {
scrap_log(INFO_COLOR,"Overlay enabled!\n"); scrap_log(INFO_COLOR,"Overlay enabled!\n");
@ -148,7 +463,20 @@ void cmd_toggle_overlay(vector<string> args) {
} }
} }
void cmd_print_alarm(vector<string> args) { void cmd_enable_overlay(Command* cmd,vector<string> args) {
if (!overlay) {
cmd_toggle_overlay(cmd,args);
}
}
void cmd_disable_overlay(Command* cmd,vector<string> args) {
if (overlay) {
cmd_toggle_overlay(cmd,args);
}
}
void cmd_print_alarm(Command* cmd,vector<string> args) {
stringstream out; stringstream out;
float *alarm = ptr<float>(P_WORLD, O_ALARM); float *alarm = ptr<float>(P_WORLD, O_ALARM);
float *alarm_grow = ptr<float>(P_WORLD, O_ALARM_GROW); float *alarm_grow = ptr<float>(P_WORLD, O_ALARM_GROW);
@ -157,103 +485,88 @@ void cmd_print_alarm(vector<string> args) {
return; return;
} }
void cmd_unload(vector<string> args) { void cmd_unload(Command* cmd,vector<string> args) {
scrap_log(INFO_COLOR,"Unloading ScrapHacks... bye!\n"); scrap_log(INFO_COLOR,"Unloading ScrapHacks... bye!\n");
DllUnload(); DllUnload();
} }
static map<string, t_cmd> commands = { void cmd_asm(Command* cmd, vector<string> args) {
{"w", { string code;
cmd_write, uintptr_t buffer_addr;
"Usage: $w <addr> <data(hex)>", bool has_addr=false;
"Write memory" if (args.size()<1) {
}}, scrap_log(ERR_COLOR, cmd->usage);
{"r", { return;
cmd_read, }
"Usage: $r <addr> <num_bytes>", try {
"Read memory" buffer_addr=stoull(args[0], 0, 16);
}}, has_addr=true;
{"unload", { } catch (exception e) {
cmd_unload, // NOP
"Usage: $unload", has_addr=false;
"Unload ScrapHacks" }
}}, if (has_addr) {
{"dx8", { // remove address from args
cmd_dx8, args.erase(args.begin());
"Usage: $dx8 (hook|unhook)", }
"Hook/Unhook DirectX 8 functions" for (string arg:args) {
}}, code+=arg+" ";
{"dump_py",{ };
cmd_dump_py, size_t data_size=asm_size(split(code,';'));
"Usage: $dump_py", if (!has_addr) {
"Dump python modules to console" buffer_addr = (uintptr_t)malloc(data_size);
}}, if (buffer_addr==0) {
{"overlay",{ scrap_log(ERR_COLOR, "ERROR: ");
cmd_toggle_overlay, scrap_log(ERR_COLOR, "malloc() failed");
"Usage: $overlay", scrap_log(ERR_COLOR, "\n");
"Toggle DX8 Overlay" return;
}},
{"alarm",{
cmd_print_alarm,
"Usage: $alarm",
"Print alarm status"
}},
{"ents",{
cmd_dump_ents,
"Usage: $ents",
"Dump entity information"
}},
{"help", {
cmd_help,
"Usage: $help [command]",
"Print help for ScrapHacks command"}},
};
void cmd_help(vector<string> args) {
if (args.size()!=1) {
for (auto cmd: commands) {
scrap_log(INFO_COLOR,cmd.first.c_str());
scrap_log(INFO_COLOR,": ");
scrap_log(INFO_COLOR,cmd.second.doc);
scrap_log(INFO_COLOR,"\n");
} }
return; char ptr[255];
snprintf(ptr,255,"Buffer @ %p\n",(void*)buffer_addr);
scrap_log(INFO_COLOR,ptr);
} }
if (!commands.count(args[0])) { assemble(split(code,';'),buffer_addr);
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 cmd_help(Command* cmd,vector<string> args);
static REPL* repl=new REPL(
{
{"mem",new Command("Usage: $mem (read|write)","Manipulate memory",{
{"read",new Command(cmd_read,"Usage: $mem read <addr> [size]","Read memory")},
{"write",new Command(cmd_write,"Usage: $mem write [addr] <data(hex)>","Write memory, if no address is specifiew we VirtualAlloc() a region")},
{"exec",new Command(cmd_exec,"Usage: $exec <addr>","Start a new thread at the specified address")},
{"asm",new Command(cmd_asm,"Usage: $asm [addr] <inst1>;<inst2>;...","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 <subcommand>","Manipulate DirectX 8 functions and state",{
{"overlay",new Command("Usage: $dx8 overlay <subcommand>","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 <subcommand>","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<string> args) {
scrap_log(INFO_COLOR,repl->help(args)+"\n");
};
void handle_command(const char *_cmd) { void handle_command(const char *_cmd) {
scrap_log(ERR_COLOR, "$"); scrap_log(ERR_COLOR, "$");
scrap_log(ERR_COLOR, _cmd); scrap_log(ERR_COLOR, _cmd);
scrap_log(ERR_COLOR, "\n"); scrap_log(ERR_COLOR, "\n");
cout << "CMD: '" << _cmd << "'" << endl; cout << "CMD: '" << _cmd << "'" << endl;
vector<string> cmd = split(string(_cmd), ' '); repl->exec(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; return;
} }

View file

@ -9,12 +9,6 @@
#include <Windows.h> #include <Windows.h>
// Socket stuff
#include <Ws2tcpip.h>
#include <stdio.h>
#include <winsock2.h>
using namespace std; using namespace std;
#include "D3D8_Hook.hpp" #include "D3D8_Hook.hpp"
@ -34,22 +28,6 @@ void DllUnload();
int hooked_console(const char *); int hooked_console(const char *);
void hook_exit(); void hook_exit();
int hook_recvfrom(SOCKET s, char *buf, int len, int flags, sockaddr *from,
int *fromlen) {
typedef decltype(&hook_recvfrom) t_func;
shared_ptr<Hook> hook = Hook::get(hook_recvfrom);
int ret = hook->func<t_func>(s, buf, len, flags, from, fromlen);
return ret;
};
int hook_sendto(SOCKET s, const char *buf, int len, int flags,
const sockaddr *to, int tolen) {
typedef decltype(&hook_sendto) t_func;
shared_ptr<Hook> hook = Hook::get(hook_sendto);
int ret = hook->func<t_func>(s, buf, len, flags, to, tolen);
return ret;
};
void setup_hooks() { void setup_hooks() {
Hook::addr(reinterpret_cast<void *>(P_SCRAP_EXIT), hook_exit); Hook::addr(reinterpret_cast<void *>(P_SCRAP_EXIT), hook_exit);
Hook::addr(reinterpret_cast<void *>(P_CON_HANDLER), hooked_console); Hook::addr(reinterpret_cast<void *>(P_CON_HANDLER), hooked_console);
@ -127,7 +105,6 @@ void hook_exit() {
void DllInit(HMODULE mod) { void DllInit(HMODULE mod) {
hMod = mod; hMod = mod;
char mfn[1024]; char mfn[1024];
InitConsole();
GetModuleFileNameA(0, mfn, 1024); GetModuleFileNameA(0, mfn, 1024);
Py = get_modules(P_PY_MODS); Py = get_modules(P_PY_MODS);
cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << " (PID: " << std::hex 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) { void *H_port_FixupExtension(char *name, char *filename) {
cout<<"FixupExtension: "<<name<<": "<<filename<<endl;
Hook::drop(H_port_FixupExtension); Hook::drop(H_port_FixupExtension);
return NULL; return NULL;
} }
void *H_PyEval_CallObjectWithKeywords(void *func, void *arg, void *kwarg) { void *H_PyEval_CallObjectWithKeywords(void *func, void *arg, void *kwarg) {
cout<<"PyEval_CallObjectWithKeywords:"<<endl;
cout<<"\t func: "<<func<<endl;
cout<<"\t arg: "<<arg<<endl;
cout<<"\t kwarg: "<<kwarg<<endl;
Hook::drop(H_PyEval_CallObjectWithKeywords); Hook::drop(H_PyEval_CallObjectWithKeywords);
return NULL; return NULL;
} }
void DllPreInit() { void DllPreInit() {
InitConsole();
Hook::addr(reinterpret_cast<void *>(0x5a9ca0), H_port_FixupExtension); Hook::addr(reinterpret_cast<void *>(0x5a9ca0), H_port_FixupExtension);
Hook::addr(reinterpret_cast<void *>(0x5cdb00), Hook::addr(reinterpret_cast<void *>(0x5cdb00),
H_PyEval_CallObjectWithKeywords); H_PyEval_CallObjectWithKeywords);

View file

@ -1,4 +1,8 @@
#pragma once #pragma once
#include "Structures.hpp"
#include <sstream>
using namespace std;
// OFFSETS // OFFSETS
#define O_MONEY 0x2090 #define O_MONEY 0x2090
@ -9,6 +13,7 @@
// POINTERS // POINTERS
#define P_WORLD 0x7FE944 #define P_WORLD 0x7FE944
#define P_VARS 0x7FBE4C
#define P_PY_MODS 0x79C698 #define P_PY_MODS 0x79C698
// FUNCTION ADDRESSES // FUNCTION ADDRESSES
@ -22,7 +27,8 @@
#define P_PyArg_ParseTuple 0x5bb9d0 #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 ERR_COLOR scrap_RGB(255,0,0)
#define INFO_COLOR scrap_RGB(0,0,255) #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) { 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); 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<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t size_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
} }

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
using namespace std;
template <typename T> struct HashTableEntry; template <typename T> struct HashTableEntry;
struct Vector3 { struct Vector3 {
float x; float x;
float y; float y;
@ -7,9 +9,9 @@ struct Vector3 {
}; };
struct Matrix3x3 { struct Matrix3x3 {
Vector3 a; struct Vector3 a;
Vector3 b; struct Vector3 b;
Vector3 c; struct Vector3 c;
}; };
struct PyMethodDef { struct PyMethodDef {
@ -42,9 +44,41 @@ struct EntityList {
const char *func; 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 <typename T> struct HashTable { template <typename T> struct HashTable {
uint32_t size; uint32_t size;
HashTableEntry<T> **chains; struct HashTableEntry<T> **chains;
}; };
template <typename T> struct HashTableEntry { template <typename T> struct HashTableEntry {

View file

@ -9,9 +9,6 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "Structures.hpp"
#include "Py_Utils.hpp"
using namespace std; using namespace std;
#define DLL_EXPORT extern "C" __declspec(dllexport) #define DLL_EXPORT extern "C" __declspec(dllexport)
@ -116,18 +113,56 @@ bool key_down_norepeat(int keycode, int delay = 100) {
return false; 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; ostringstream out;
uintptr_t offset=reinterpret_cast<uintptr_t>(addr); uintptr_t offset=reinterpret_cast<uintptr_t>(addr);
for (size_t i = 0; i < count; ++i) { for (size_t i = 0; i < count; ++i) {
unsigned int val = (unsigned int)(((unsigned char *)(offset+i))[0]); unsigned int val = (unsigned int)(((unsigned char *)(offset+i))[0]);
if ((i % 16) == 0) { if ((i % 16) == 0) {
out << endl; if (!compact) {
out << setfill('0') << setw(8) << std::hex << std::uppercase << (offset+i) << ": "; out << endl;
out << setfill('0') << setw(8) << std::hex << std::uppercase << (offset+i) << ": ";
}
} }
out << setfill('0') << setw(2) << std::hex << val << " "; out << setfill('0') << setw(2) << std::hex << val << " ";
} }
out << endl; if (!compact) {
out << endl;
}
return out.str(); return out.str();
} }
@ -258,118 +293,4 @@ vector<string> split(string str, char sep) {
if (part != "") if (part != "")
ret.push_back(part); ret.push_back(part);
return ret; return ret;
}
size_t size_ht(HashTable<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t size_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
while (ent) {
++cnt;
ent = ent->next;
}
}
}
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
cout << i << ": ";
while (ent) {
++cnt;
cout << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
cout << " -> ";
};
ent = ent->next;
}
cout << endl;
}
}
cout << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<EntityList> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<EntityList> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
}
size_t dump_ht(HashTable<Entity> *ht,stringstream *out) {
size_t cnt = 0;
for (size_t i = 0; i < ht->size; ++i) {
HashTableEntry<Entity> *ent = ht->chains[i];
if (ent) {
*out << i << ": ";
while (ent) {
++cnt;
*out << "[ " << ent->name << ": " << ent->data << "]";
if (ent->next) {
*out << " -> ";
};
ent = ent->next;
}
*out << endl;
}
}
*out << cnt << " Entries" << endl;
return cnt;
} }

View file

@ -1,7 +1,5 @@
#include <Windows.h> #include <Windows.h>
#define DLL_EXPORT extern "C" __declspec(dllexport) #define DLL_EXPORT extern "C" __declspec(dllexport)
using namespace std; using namespace std;

View file

@ -2,7 +2,6 @@ import os
import sys import sys
import re import re
outfile,infile=sys.argv[1:] outfile,infile=sys.argv[1:]
re_interface=re.compile(r"^DECLARE_INTERFACE_{0,1}\((.*?)\)$") re_interface=re.compile(r"^DECLARE_INTERFACE_{0,1}\((.*?)\)$")
re_method=re.compile(r"^\w*STDMETHOD_{0,1}\((.*?)\)\((.*?)\).*;") re_method=re.compile(r"^\w*STDMETHOD_{0,1}\((.*?)\)\((.*?)\).*;")
name=None name=None
@ -22,7 +21,6 @@ with open(infile,"r") as infh:
meth_name=meth_name.split(",")[-1].strip() meth_name=meth_name.split(",")[-1].strip()
VMTs[name][meth_name]=idx VMTs[name][meth_name]=idx
idx+=1 idx+=1
print(f"Generating: {outfile} from {infile} ...")
with open(outfile,"w") as ofh: with open(outfile,"w") as ofh:
for name in sorted(VMTs.keys()): for name in sorted(VMTs.keys()):
print(f"namespace {name} {{",file=ofh) print(f"namespace {name} {{",file=ofh)

View file

@ -244,6 +244,20 @@ def nuke():
E = Scrap.GetEntity(E.NextInSlot) 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): def become(name):
import CharConversor import CharConversor
enable_all_conv() enable_all_conv()

View file

@ -1,3 +1,4 @@
import sys
from construct import * from construct import *
from pprint import pprint from pprint import pprint
@ -11,10 +12,10 @@ ScrapSave = "ScarpSaveGame" / Struct(
"data" / PrefixedArray(Int32ul, ScrapSaveVar), "data" / PrefixedArray(Int32ul, ScrapSaveVar),
Terminated, Terminated,
) )
with open("Save0.sav", "rb") as sav_file: with open(sys.argv[1], "rb") as sav_file:
save = ScrapSave.parse_stream(sav_file) save = ScrapSave.parse_stream(sav_file)
print("ID:", save.id) print("ID:", save.id)
print("Title:", save.title) print("Title:", save.title)
for var in save.data: for var in save.data:
print(" - {}: {}".format(var.name, var.data)) print(" {}: {}".format(var.name, var.data))

View file

@ -1,14 +1,20 @@
import r2pipe import r2pipe
import os import os
import json import json
from datetime import datetime
import subprocess as SP
from tqdm import tqdm from tqdm import tqdm
from pprint import pprint from pprint import pprint
import os import os
import sys import sys
r2cmds = [] 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)) 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!" assert os.path.isfile(scrap_exe), "File not found!"
r2 = r2pipe.open(scrap_exe) r2 = r2pipe.open(scrap_exe)
@ -20,6 +26,14 @@ target_hashes = {
assert file_hashes == target_hashes, "Hash mismatch" 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): def r2_cmd(cmd):
global r2, r2cmds global r2, r2cmds
@ -38,54 +52,139 @@ def r2_cmdJ(cmd):
r2cmds.append(cmd) r2cmds.append(cmd)
return r2.cmdJ(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 # 0x7d2094 refcnt
#0x7fac19
#0x7faa4c
#0x7fac1c # activate viewer
#0x84d400
#0x413ee0 comments= {
0x6113f9:"Check if Window exists"
}
#0x7d2094 refcnt flags = {
flags = {0x7FE944: ("P_World", 4), 0x79C698: ("Py_Mods", 4),0x852914: ("P_D3D8_Dev",4)} 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 = { func_sigs = {
0x5a8390: "int py_exec(const char* script);", 0x5A8390: "int py_exec(const char* script);",
0x5bb9d0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);", 0x5BB9D0: "int PyArg_ParseTuple(void* PyObj, char* format, ...);",
0x4134c0: "int write_log(unsigned int color, const char* msg);", 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);", 0x47C1E0: "int ht_hash_ent_list(const char* str);",
0x404BB0: "int ht_hash_ent(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);", 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);", 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);", 0x419950: "int fopen_2(const char* filename);",
0x41AB50: "int open_pak(const char* filename, int unk_1,void* unk_ptr);", 0x41AB50: "int open_pak(const char* filename, int unk_1,void* unk_ptr);",
0x404460: "int register_c_callback(const char* name,void* func);" 0x404460: "int register_c_callback(const char* name,void* func);",
0x414070: "void throw_assertion_2(const char* check,const char* file, unsigned int line);" 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,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);" 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 = { functions = {
0x6b1c70: "strcmp", 0x6B1C70: "strcmp",
0x5bb9d0: "PyArg_ParseTuple", 0x5BB9D0: "PyArg_ParseTuple",
0x5dd510: "init_engine_3d", 0x5DD510: "init_engine_3d",
0x401180: "create_window", 0x401180: "create_window",
0x401240: "create_main_window", 0x401240: "create_main_window",
0x4016f0: "reg_get_val", 0x4016F0: "reg_get_val",
0x4134c0: "write_log", 0x4134C0: "write_log",
0x414280: "prepare_html_log", 0x414280: "prepare_html_log",
0x418220: "get_version_info", 0x418220: "get_version_info",
0x4137e0: "write_html_log", 0x4137E0: "write_html_log",
0x402190: "handle_console_input", 0x402190: "handle_console_input",
0x5F9520: "handle_render_console_input", 0x5F9520: "handle_render_console_input",
0x404A50: "find_entity", 0x404A50: "find_entity",
@ -93,9 +192,9 @@ functions = {
0x404BB0: "ht_hash_ent", 0x404BB0: "ht_hash_ent",
0x404460: "register_c_callback", 0x404460: "register_c_callback",
0x417470: "load_game", 0x417470: "load_game",
0x5E3800: "fopen_1", 0x5E3800: "fopen_from_pak",
0x419950: "fopen_2", 0x5e3500: "fopen",
0x403370: "debug_init", 0x403370: "init_debug",
0x401770: "init", 0x401770: "init",
0x4026D0: "init_py", 0x4026D0: "init_py",
0x405B40: "init_py_sub", 0x405B40: "init_py_sub",
@ -105,25 +204,105 @@ functions = {
0x414570: "setup_game_vars", 0x414570: "setup_game_vars",
0x5FBC50: "throw_assertion_1", 0x5FBC50: "throw_assertion_1",
0x414070: "throw_assertion_2", 0x414070: "throw_assertion_2",
0x5F7000: "load_m3d_ini", 0x5F7000: "read_ini",
0x650F80: "load_sm3", 0x650F80: "load_sm3",
0x6665A0: "load_m3d_1", 0x6665A0: "load_m3d_1",
0x666900: "load_m3d_2", 0x666900: "load_m3d_2",
0x479B20: "world_constructor", 0x479B20: "world_constructor",
0x479B40: "world_init", 0x479B40: "init_world",
0x402510: "world_deinit", 0x402510: "deinit_world",
0x479870: "make_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: for t in types:
r2_cmd(f'"td {t}"') r2_cmd(f'"td {t}"')
for addr, args in flags.items(): for addr, name in flags.items():
name, size = args x64_dbg_label(addr,name,"loc")
r2_cmd(f"f loc.{name} {size} {hex(addr)}") r2_cmd(f"f loc.{name} 4 {hex(addr)}")
for addr, name in functions.items(): for addr, name in functions.items():
x64_dbg_label(addr,name,"fcn")
r2_cmd(f"afr fcn.{name} {hex(addr)}") r2_cmd(f"afr fcn.{name} {hex(addr)}")
if addr in func_sigs: if addr in func_sigs:
r2_cmd(f'"afs {func_sigs[addr]}" @{hex(addr)}') r2_cmd(f'"afs {func_sigs[addr]}" @{hex(addr)}')
@ -135,9 +314,13 @@ def vtables():
vtables = r2_cmdJ("avj") vtables = r2_cmdJ("avj")
for c in tqdm(vtables, ascii=True): for c in tqdm(vtables, ascii=True):
methods = [] 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)) 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 ret[hex(c.offset)] = methods
return ret return ret
@ -147,60 +330,71 @@ def c_callbacks():
funcs = {} funcs = {}
res = r2_cmd("/r fcn.register_c_callback ~CALL[1]").splitlines() res = r2_cmd("/r fcn.register_c_callback ~CALL[1]").splitlines()
for addr in tqdm(res, ascii=True): 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 func = func.refs[0].addr
name = r2_cmd(f"psz @{hex(name.refs[0].addr)}").strip() 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) funcs[name] = hex(func)
return funcs return funcs
def assertions(): def assertions():
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}") print(f"[*] Parsing C assertions for {a_addr}")
res = r2_cmd(f"/r {a_addr} ~CALL[1]").splitlines() res = r2_cmd(f"/r {a_addr} ~CALL[1]").splitlines()
print() print()
for line in tqdm(res, ascii=True): for line in tqdm(res, ascii=True):
addr = line.strip() addr = line.strip()
dis=r2_cmdJ(f"s {addr};so -{n_args};pij {n_args}") # seek and print disassembly r2_cmd(f"s {addr}")
if n_args==4: r2_cmd(f"so -{n_args}")
dis=r2_cmdJ(f"pij {n_args}")
if n_args == 4:
file, msg, date, line = dis file, msg, date, line = dis
elif n_args==3: elif n_args == 3:
date=None date = None
file, msg, line = dis file, msg, line = dis
try: try:
file = r2_cmd(f"psz @{file.refs[0].addr}").strip() file = r2_cmd(f"psz @{file.refs[0].addr}").strip()
msg = r2_cmd(f"psz @{msg.refs[0].addr}").strip() msg = r2_cmd(f"psz @{msg.refs[0].addr}").strip()
if date: if date:
r2_cmd(f"psz @{date.refs[0].addr}").strip() date = r2_cmd(f"psz @{date.refs[0].addr}").strip()
line=line.val line = line.val
file=file.replace("\\\\", "\\") file = file.replace("\\\\", "\\")
os.path.isabs(file): assertions.setdefault(file, [])
file = os.path.abspath(file) assertions[file].append(
assertions.setdefault(path, []) {"line": line, "date": date, "addr": addr, "msg": msg}
assertions[path].append({'line':line,'date':date,'addr':addr,'msg': msg}) )
except: except:
pass pass
for path in assertions: for path in assertions:
assertions[path].sort(key=lambda v:v['line']) assertions[path].sort(key=lambda v: v["line"])
return assertions return assertions
def bb_refs(addr): def bb_refs(addr):
ret={} ret = {}
res = r2_cmd(f"/r {addr} ~fcn[0,1]").splitlines() res = r2_cmd(f"/r {addr} ~fcn[0,1]").splitlines()
print() print()
for ent in res: for ent in res:
func,hit=ent.split() func, hit = ent.split()
ret[hit]={'asm':[],'func':func} ret[hit] = {"asm": [], "func": func}
for ins in r2_cmdJ(f"pdbj @{hit}"): for ins in r2_cmdJ(f"pdbj @{hit}"):
ret[hit]['asm'].append(ins.disasm) ret[hit]["asm"].append(ins.disasm)
return ret return ret
def world(): def world():
print("[*] Parsing World offsets") print("[*] Parsing World offsets")
return bb_refs("loc.P_World") return bb_refs("loc.P_World")
def render(): def render():
print("[*] Parsing D3D_Device offsets") print("[*] Parsing D3D_Device offsets")
return bb_refs("loc.P_D3D8_Dev") return bb_refs("loc.P_D3D8_Dev")
@ -212,7 +406,9 @@ def py_mods():
print() print()
py_mods = {} py_mods = {}
for call_loc in tqdm(res, ascii=True): 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 = [] refs = []
if not all([arg.type == "push" for arg in args]): if not all([arg.type == "push" for arg in args]):
continue continue
@ -222,7 +418,8 @@ def py_mods():
doc = r2_cmd(f"psz @{doc}").strip() doc = r2_cmd(f"psz @{doc}").strip()
name = r2_cmd(f"psz @{name}").strip() name = r2_cmd(f"psz @{name}").strip()
r2_cmd(f"s {methods}") 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": {}} py_mods[name] = {"methods_addr": methods, "doc": doc, "methods": {}}
while True: while True:
m_name, m_func, _, m_doc = [v.value for v in r2_cmdJ(f"pfj xxxx")] m_name, m_func, _, m_doc = [v.value for v in r2_cmdJ(f"pfj xxxx")]
@ -230,16 +427,21 @@ def py_mods():
break break
m_name, m_func, m_doc = map(hex, (m_name, m_func, m_doc)) m_name, m_func, m_doc = map(hex, (m_name, m_func, m_doc))
m_name = r2_cmd(f"psz @{m_name}").strip() m_name = r2_cmd(f"psz @{m_name}").strip()
r2_cmd(f"f Py_{name}_{m_name}_doc 4 {m_doc}") r2_cmd(f"f py.{name}.{m_name}.__doc__ 4 {m_doc}")
m_doc = r2_cmd(f"psz @{m_doc}").strip() 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} 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") r2_cmd("s +16")
return py_mods return py_mods
def game_vars(): def game_vars():
ret={} ret = {}
print("[*] Parsing Game variables") print("[*] Parsing Game variables")
res = r2_cmd("/r fcn.setup_game_vars ~CALL[1]").splitlines() res = r2_cmd("/r fcn.setup_game_vars ~CALL[1]").splitlines()
print() print()
@ -249,27 +451,28 @@ def game_vars():
args = r2_cmd("pdj -5") # seek and print disassembly args = r2_cmd("pdj -5") # seek and print disassembly
if not args: if not args:
continue continue
args=json.loads(args) args = json.loads(args)
args_a = [] args_a = []
push_cnt=0 push_cnt = 0
for arg in args[::-1]: for arg in args[::-1]:
if arg['type'] not in ["push","mov"]: if arg["type"] not in ["push", "mov"]:
continue continue
if arg['type']=="push": if arg["type"] == "push":
push_cnt+=1 push_cnt += 1
args_a.append(arg) args_a.append(arg)
if push_cnt==3: if push_cnt == 3:
break break
if len(args_a)!=4: if len(args_a) != 4:
continue continue
if not all(['val' in v for v in args_a]): if not all(["val" in v for v in args_a]):
continue continue
addr,name,_,desc=[v['val'] for v in args_a] addr, name, _, desc = [v["val"] for v in args_a]
name=r2_cmd(f"psz @{hex(name)}").strip() name = r2_cmd(f"psz @{hex(name)}").strip()
desc=r2_cmd(f"psz @{hex(desc)}").strip() desc = r2_cmd(f"psz @{hex(desc)}").strip()
addr=hex(addr) addr = hex(addr)
r2_cmd(f"f var_{name} 4 {addr}") r2_cmd(f"f loc.gvar.{name} 4 {addr}")
ret[addr]={'name':name,'desc':desc} x64_dbg_label(addr,f"{name}","loc.gvar")
ret[addr] = {"name": name, "desc": desc}
return ret return ret
@ -283,23 +486,45 @@ ret = dict(
render=render(), 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) 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 = [] wcmds = []
for cmd in r2cmds: for cmd in r2cmds:
for start in ["f ", "afr ", "aaaaa","afs"]: record=True
for start in ["p","/","s"]:
if cmd.strip('"').startswith(start): if cmd.strip('"').startswith(start):
wcmds.append(cmd) record=False
break if record:
wcmds.append(cmd)
of.write("\n".join(wcmds)) of.write("\n".join(wcmds))
print("[+] Wrote Scrap_dissect.r2") print("[+] Wrote scrap_dissect.r2")
print(f"[*] Done, now cd to '{folder}'...")
print( r2.quit()
"[*] ...and run 'r2 -i Scrap_dissect.r2 Scrap.exe' to load the parsed infos into radare2"
) 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)