diff --git a/NOTES.md b/NOTES.md index 59243ca..cba987a 100644 --- a/NOTES.md +++ b/NOTES.md @@ -9,14 +9,14 @@ * `:`: Get Game Engine Global Variable * `: `: Set Game Engine Global Variable * `?`: Show all Global Variable -* `?`: Show all Global Variable matching -* `/`: Run Command defined in QuickConsole.py(c) 'import quickconsole;quickconsole.%s()' -* `/ ,`: Run function in QuickConsole.py(c) with argument(s) 'import quickconsole;quickconsole.%s(%s)' +* `?`: Show all Global Variable matching `` +* `/`: Run Command defined in `QuickConsole.py`: `import quickconsole;quickconsole.%s()` +* `/ ,`: Run function in `QuickConsole.py` with argument(s) `import quickconsole;quickconsole.%s(%s)` ## External Console (Scenegraph Debugging?) (Handler@0x5f9520): * `listar luces` * `listar` -* `arbol` (Patch Scrap.exe@offset 0x314bc9 replace 0x20 with 0x00 (or just type `arbol ` with the space at the end)) +* `arbol` (Patch Scrap.exe@offset 0x314bc9 replace 0x20 with 0x00 (or just type `arbol ` with a space at the end)) * `mem` * `ver uniones` * Easter Eggs: @@ -29,7 +29,7 @@ - InitPyMod @ 0x5A8FB0 - PyExec @ 0x5A8390 -# Other Functions: +## Other Functions: - FindEntity @ 0x404a50 - HashTable hashfunc @ 0x404bb0 @@ -47,46 +47,74 @@ - SM3 Scene Loader @ 0x650f80 (?) - M3D Model Loader @ 0x6665a0 (??) - World_Init @ 0x479b20 (???) +- World_DeInit @ 0x402510 # Data Structures -## Game World/State Pointer @ 0x7fe944 +## Game World/State Pointer @ `0x7fe944` -Points to GameState struct +Points to World struct + +| Offset | Type | Description | +|--------|------------|----------------------------------| +| 0x0000 | `void**` | Virtual Method Table | +| 0x0004 | `uint32_t` | Size of Entity Hashtable | +| 0x0008 | `void**` | Pointer to Entity Hashtable | +| 0x02B8 | `uint32_t` | Number of entity lists | +| 0x02BC | `void**` | Pointer to entity list Hashtable | +| 0x0330 | `float[3]` | Time (why 3 times?) | +| 0x1C6C | `float` | Alarm level | +| 0x1C68 | `float` | Alarm Grow Level | +| 0x2158 | `???` | Used in `World_Init` | +| 0x2170 | `???` | Used in `World_Init` | +| 0x2180 | `???` | Used in `World_Init` | +| 0x2188 | `???` | Used in `World_Init` | +| 0x218C | `???` | Used in `World_Init` | +| 0x2190 | `???` | Used in `World_Init` | +| 0x2198 | `???` | Used in `World_Init` | +| 0x219C | `???` | Used in `World_Init` | +| 0x21A0 | `???` | Used in `World_Init` | +| 0x21B4 | `???` | Used in `World_Init` | +| 0x21C8 | `???` | Used in `World_Init` | +| 0x2204 | `???` | Used in `World_Init` | +| 0x2230 | `???` | Used in `World_Init` | +| 0x2238 | `???` | Used in `World_Init` | +| 0x2254 | `???` | Used in `World_Init` | -| Offset | Type | Description | -| ------ | -------- | --------------------------- | -| 0x0 | void** | Virtual Method Table | -| 0x4 | uint32_t | Size of Entity Hashtable | -| 0x8 | void** | Pointer to Entity Hashtable | -| 0x330 | float[3] | Time (why 3 times?) | -| 0x1c6c | float | Alarm level | -| 0x1C68 | float | Alarm Grow Level | ## Entity Hash Table -Hashfunction 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: -| Offset | Type | Description | -| ------ | ----------- | ------------------------------ | -| 0x0 | void* | Pointer to data | -| 0x4 | const char* | key as string | -| 0x8 | void* | Pointer to next entry in chain | +| Offset | Type | Description | +|--------|---------------|--------------------------------| +| 0x0 | `void*` | Pointer to data | +| 0x4 | `const char*` | key as `char*` | +| 0x8 | `void*` | Pointer to next entry in chain | Data format: -| Offset | Type | Description | -| ------ | ----------- | -------------------- | -| 0x0 | void** | Virtual Method Table | -| 0x4 | const char* | name as string | -| 0x14 | void* | pointer to self | -| 0x28 | float[3] | Position | +| Offset | Type | Description | +|--------|---------------|--------------------------| +| 0x0 | `void**` | Virtual Method Table (?) | +| 0x4 | `const char*` | name as string | +| 0x14 | `void*` | pointer to self (why?) | +| 0x28 | `float[3]` | Position in Game World | + +## Game Window Object (?) Pointer @ `0x7fa380` + +| Offset | Type | Description | +|--------|---------------|----------------------| +| 0x0000 | `void**` | Virtual Method Table | +| 0x0004 | `const char*` | Some Model Name (?) | +| 0x0008 | `void*` | Pointer to something | +| 0x000C | `void*` | Ditto | # File Formats -## *.packed File Format: +## .packed File Format: ``` Header: "BFPK\0\0\0\0" @@ -98,19 +126,24 @@ Header: Int32ul: offset in file ``` + + ## Loading Custom Content (not really working) 1. Create a folder `mods` 2. Drop a `*.packed` file into it +3. Change `Scrap.cfg` as follows + 1. Add `ModPathName = mods` + 2. Add `ModFileName = ` ## Interesting file: -* m3d.ini: Rendering Engine Configuration -* scripts/: Game Engine Scripts +* `m3d.ini`: Rendering Engine Configuration +* `scripts/`: Game Engine Scripts # How to enable External Console: 1. exctract `Data.packed` -2. in m3d.ini uncomment (remove `;`) "ConsolaWnd" (GUI Console) or "ConsolaTxt" (Text Console) and set the value to "SI" -3. repack "Data.packed" +2. in m3d.ini uncomment (remove `;`) `ConsolaWnd` (GUI Console) and/or `ConsolaTxt` (Text Console) and set the value to `SI` +3. repack `Data.packed` or right click on the title bar (in windowed mode) and click "Switch Console" @@ -118,3 +151,69 @@ or Use a custom Content Pack (**untested!**) # Misc. Interesting things - sys.path contains "./lib" so you can load your own Python Modules + +# Code Snippets + +## [Kaitai Struct](http://kaitai.io/) Parser for .packed files +```yaml +meta: + id: packed + application: Scrapland + file-extension: packed + endian: le + xref: http://wiki.xentax.com/index.php/Scrapland_PACKED + license: MIT + encoding: UTF-8 +seq: + - id: magic + contents: BFPK + doc: File Magic + - id: magic2 + contents: [0,0,0,0] + doc: Second File Magic + - id: num_files + type: u4 + doc: Number of files + - id: files + type: file_entry + repeat: expr + repeat-expr: num_files + doc: Directory entry for each file +types: + file_entry: + seq: + - id: path_len + type: u4 + doc: Length of file path + - id: path + type: str + size: path_len + doc: File path + - id: size + type: u4 + doc: File size + - id: offset + type: u4 + doc: Absoulte File offset + instances: + data: + pos: offset + size: size +``` + +## Hashfunction used in Entity Hash-Table + +```c +unsigned long ElfHash(const unsigned char *s) +{ + unsigned long h = 0, high; + while ( *s ) + { + h = ( h << 4 ) + *s++; + if ( high = h & 0xF0000000 ) + h ^= high >> 24; + h &= ~high; + } + return h; +} +``` \ No newline at end of file diff --git a/README.md b/README.md index cb99f78..1b6c5ad 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ WIP Memory hacking library # Tools used: - [Python 3](https://python.org/) + [Construct](https://construct.readthedocs.io/en/latest/) -- [IDA](https://www.hex-rays.com/products/ida/index.shtml) and [x32dbg](https://x64dbg.com/#start) +- [IDA](https://www.hex-rays.com/products/ida/index.shtml) and [x32dbg](https://x64dbg.com/) - [Reclass.NET](https://github.com/ReClassNET/ReClass.NET) - [HxD](https://mh-nexus.de/en/hxd/) +- [Kaitai Struct](http://kaitai.io/) \ No newline at end of file diff --git a/ScrapHacks/Injector/Injector/Injector.cpp b/ScrapHacks/Injector/Injector/Injector.cpp index 040a863..3602535 100644 --- a/ScrapHacks/Injector/Injector/Injector.cpp +++ b/ScrapHacks/Injector/Injector/Injector.cpp @@ -226,6 +226,10 @@ int main(int argc, char *argv[]) HANDLE hProc = INVALID_HANDLE_VALUE; HANDLE hThread = INVALID_HANDLE_VALUE; DWORD PID = 0; + char s_PID[MAX_PATH]; + snprintf(s_PID, MAX_PATH, "%d", GetCurrentProcessId()); + SetEnvironmentVariable("Inj_PID", s_PID); + cout << "[*] Injector PID: " << GetCurrentProcessId() << endl; if ((argc>1)&&fexists(argv[1])) { cout << "[*] Spawning process for \"" << argv[1] << "\"" << endl; vector handles = spawn(argv[1]); @@ -254,6 +258,7 @@ int main(int argc, char *argv[]) if (hThread != INVALID_HANDLE_VALUE) { while (ResumeThread(hThread)); } + SetEnvironmentVariable("Inj_PID", NULL); cout << "[*] Done!" << endl; return 0; } diff --git a/ScrapHacks/Injector/Injector/Injector.vcxproj b/ScrapHacks/Injector/Injector/Injector.vcxproj index 15a021c..5d72e78 100644 --- a/ScrapHacks/Injector/Injector/Injector.vcxproj +++ b/ScrapHacks/Injector/Injector/Injector.vcxproj @@ -23,7 +23,7 @@ {7C91C225-D95C-4B7A-9251-0CE358BAF556} Win32Proj Injector - 10.0.17134.0 + 10.0.17763.0 diff --git a/ScrapHacks/ScrapHack/D3D8_Hook.h b/ScrapHacks/ScrapHack/D3D8_Hook.h index 7b658f0..561df44 100644 --- a/ScrapHacks/ScrapHack/D3D8_Hook.h +++ b/ScrapHacks/ScrapHack/D3D8_Hook.h @@ -9,60 +9,44 @@ bool overlay = false; LPD3DXFONT m_pFont; HFONT hFont; HBRUSH hBrush; -D3DCOLOR color = D3DCOLOR_XRGB(255, 0, 0); +D3DCOLOR color = D3DCOLOR_ARGB(255,255, 0, 0); RECT Rect = { 0,0,0,0 }; D3DRECT panel; -void Render(LPDIRECT3DDEVICE8 dev) { +LPDIRECT3DDEVICE8 Render(LPDIRECT3DDEVICE8 dev) { if (!overlay) { - return; + return dev; } - char text[MAX_PATH]; - snprintf(text, MAX_PATH, "Frame: [%lld]\nTest", ++frame); + char text[1024]; + snprintf(text, 1024, "ScrapHack v0.1\nFrame: [%lld]", ++frame); if (m_pFont == nullptr) { D3DXCreateFont(dev, hFont, &m_pFont); CloseHandle(hFont); } m_pFont->Begin(); m_pFont->DrawTextA(text, -1, &Rect, DT_CALCRECT, 0); - - dev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(25, 0, 0, 0),0.5,0); - m_pFont->DrawTextA(text, -1, &Rect, DT_LEFT, color); m_pFont->End(); + return dev; } HRESULT WINAPI H_EndScene(LPDIRECT3DDEVICE8 dev) { typedef HRESULT(WINAPI *t_func)(LPDIRECT3DDEVICE8); shared_ptr hook = Hook::get(H_EndScene); - _asm push esi; - _asm pushad; - Render(dev); - hook->disable(); - HRESULT ret = hook->func()(dev); - hook->enable(); - _asm popad; - _asm pop esi; - return ret; + return hook->func(Render(dev)); } HRESULT WINAPI H_CreateDevice(void* pDirect3D, unsigned int uiAdapter, D3DDEVTYPE pDeviceType, HWND hFocusWindow, unsigned long ulBehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, LPDIRECT3DDEVICE8* ppReturnedDeviceInterface) { - _asm push esi; - _asm pushad; typedef HRESULT(WINAPI *t_func)(void*, unsigned int, D3DDEVTYPE, HWND, unsigned long, D3DPRESENT_PARAMETERS*, LPDIRECT3DDEVICE8*); shared_ptr hook = Hook::get(H_CreateDevice); - hook->disable(); - HRESULT ret = hook->func()(pDirect3D, uiAdapter, pDeviceType, hFocusWindow, ulBehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); - cout << "CreateDevice ->" << ret << endl; + HRESULT ret = hook->func(pDirect3D, uiAdapter, pDeviceType, hFocusWindow, ulBehaviorFlags, pPresentationParameters, ppReturnedDeviceInterface); + cout << "CreateDevice -> " << ret << endl; void* EndScene = reinterpret_cast(GetVTable(ppReturnedDeviceInterface[0])[35]); cout << "EndScene @ " << EndScene << endl; // EndScene Hook::addr(EndScene, H_EndScene); Hook::drop(H_CreateDevice); - _asm popad; - _asm pop esi; - return ret; } @@ -70,24 +54,29 @@ LPDIRECT3D8 WINAPI H_Direct3DCreate8(unsigned int SDKVersion) { typedef LPDIRECT3D8(_stdcall *t_func)(unsigned int); shared_ptr hook = Hook::get(H_Direct3DCreate8); - _asm push esi; - _asm pushad; - - hook->disable(); - LPDIRECT3D8 ret = hook->func()(SDKVersion); + LPDIRECT3D8 ret = hook->func(SDKVersion); cout << "D3D8-Create: " << SDKVersion << " -> " << ret << endl; void *CreateDevice = reinterpret_cast(GetVTable(ret)[15]); + void *Release = reinterpret_cast(GetVTable(ret)[2]); cout << "CreateDevice @ " << CreateDevice << endl; // CreateDevice Hook::addr(CreateDevice, H_CreateDevice); Hook::drop(H_Direct3DCreate8); - _asm popad; - _asm pop esi; - + return ret; } +void unhook_d3d8() { + if (hFont != INVALID_HANDLE_VALUE) { + CloseHandle(hFont); + } + if (m_pFont != nullptr) { + m_pFont->Release(); + } + Hook::drop(H_EndScene); +} + void hook_d3d8() { hFont = CreateFont(20, 0, 0, 0, FW_BOLD, 0, 0, 0, ANSI_CHARSET, 0, 0, 0, 0, "Verdana"); hBrush = CreateSolidBrush(D3DCOLOR_ARGB(25, 0, 0, 0)); Hook::module("d3d8.dll","Direct3DCreate8", H_Direct3DCreate8); -} +} \ No newline at end of file diff --git a/ScrapHacks/ScrapHack/Hook.h b/ScrapHacks/ScrapHack/Hook.h index 64e719e..1ea88f5 100644 --- a/ScrapHacks/ScrapHack/Hook.h +++ b/ScrapHacks/ScrapHack/Hook.h @@ -99,9 +99,16 @@ public: } } - template - T func() { - return reinterpret_cast(this->orig); + void* get_orig() { + return this->orig; + } + + template + decltype(auto) func(Args... args) { + disable(); + auto ret=reinterpret_cast(this->orig)(args...); + enable(); + return ret; } }; diff --git a/ScrapHacks/ScrapHack/ScrapHack.cpp b/ScrapHacks/ScrapHack/ScrapHack.cpp index 409011c..24faa26 100644 --- a/ScrapHacks/ScrapHack/ScrapHack.cpp +++ b/ScrapHacks/ScrapHack/ScrapHack.cpp @@ -31,15 +31,18 @@ bool initialized = false; bool running = true; HMODULE mod = 0; -void dump_ht(HashTable* ht) { +void DllUnload(HMODULE); +void hook_exit(); + +size_t dump_ht(HashTable* ht) { size_t cnt = 0; for (size_t i = 0; i < ht->size; ++i) { - HashTableEntry* ent = ht->chains[i]; - if (ent != NULL) { + HashTableEntry* ent = ht->chains[i]; + if (ent) { cout << i << ": "; - while (ent != NULL) { + while (ent) { ++cnt; - cout << ent->name; + cout << "[ " << ent->name << ": " << ent->data << "]"; if (ent->next) { cout << " -> "; }; @@ -48,13 +51,35 @@ void dump_ht(HashTable* ht) { cout << endl; } } - cout << cnt << " Entities" << endl; - return; + cout << cnt << " Entries" << endl; + return cnt; +} + +size_t dump_ht(HashTable* ht) { + size_t cnt = 0; + for (size_t i = 0; i < ht->size; ++i) { + HashTableEntry* ent = ht->chains[i]; + if (ent) { + cout << i << ": "; + while (ent) { + ++cnt; + cout << "[ " << ent->name << ": " << ent->data<<"]"; + if (ent->next) { + cout << " -> "; + }; + ent = ent->next; + } + cout << endl; + } + } + cout << cnt << " Entries" << endl; + return cnt; } void MainLoop(HMODULE mod) { Sleep(100); + hook_exit(); cout << "[*] Starting main Loop" << endl; cout << endl; cout << "[F3 ] Unload ScrapHacks" << endl; @@ -107,8 +132,10 @@ void MainLoop(HMODULE mod) } if (key_down_norepeat(VK_F9)) { - HashTable *ht = ptr(P_WORLD,O_HASHTABLE); - dump_ht(ht); + cout << "Entities:" << endl; + dump_ht(ptr>(P_WORLD, O_ENTS)); + cout << "Entity Lists:" << endl; + dump_ht(ptr>(P_WORLD, O_ENTLISTS)); } if (key_down_norepeat(VK_F10)) { @@ -140,9 +167,7 @@ int hooked_console(const char* cmd) { handle_command(++cmd); return 0; } - hook->disable(); - int ret= hook->func()(cmd); - hook->enable(); + int ret= hook->func(cmd); return ret; } @@ -150,14 +175,62 @@ void hook_console() { Hook::addr(reinterpret_cast(P_CON_HANDLER) , hooked_console); } +void H_Exit(){ + typedef void(_cdecl *t_func)(void); + shared_ptr hook = Hook::get(H_Exit); + DllUnload(mod); + hook->disable(); + HWND hMainWindow = ptr(0x7FA830, 0x7c)[0]; + SendMessage(hMainWindow, WM_CLOSE, 0, 0); + return; +} + +void hook_exit() { + //void* p_exit = ptr(0x7fa830,0x0, 0x20); + Hook::addr(reinterpret_cast(0x4010c0), H_Exit); +} + +DWORD PPID() { + DWORD PID = GetCurrentProcessId(); + HANDLE hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 procentry; + if (hSnapShot == INVALID_HANDLE_VALUE) { + cout << GetLastErrorAsString() << endl; + return -1; + } + if (Process32First(hSnapShot, &procentry)) { + do { + if (procentry.th32ProcessID == PID) { + CloseHandle(hSnapShot); + return procentry.th32ParentProcessID; + } + procentry.dwSize = sizeof(PROCESSENTRY32); + } while (Process32Next(hSnapShot, &procentry)); + } + CloseHandle(hSnapShot); + return -1; +} + void DllPreInit(HMODULE _mod) { char mfn[1024]; + char inj[MAX_PATH]; + DWORD INJ_PID=0; InitConsole(); GetModuleFileName(0, mfn, 1024); Py = get_modules(P_PY_MODS); - cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << endl; + cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << " (PID: " << std::hex << GetCurrentProcessId() <{72CB1B9E-50C7-4010-BEAD-82FACF87A87A} Win32Proj ScrapHack - 10.0.17134.0 + 10.0.17763.0 diff --git a/ScrapHacks/ScrapHack/Scrapland.h b/ScrapHacks/ScrapHack/Scrapland.h index 08d6b44..36d25b4 100644 --- a/ScrapHacks/ScrapHack/Scrapland.h +++ b/ScrapHacks/ScrapHack/Scrapland.h @@ -4,7 +4,8 @@ #define O_MONEY 0x2090 #define O_ALARM 0x1C6C #define O_ALARM_GROW 0x1C68 -#define O_HASHTABLE 0x4 +#define O_ENTS 0x4 +#define O_ENTLISTS 0x2b8 //POINTERS #define P_WORLD 0x7FE944 diff --git a/ScrapHacks/ScrapHack/Structures.h b/ScrapHacks/ScrapHack/Structures.h index df3affb..a79bb67 100644 --- a/ScrapHacks/ScrapHack/Structures.h +++ b/ScrapHacks/ScrapHack/Structures.h @@ -1,4 +1,5 @@ #pragma once +template struct HashTableEntry; struct Vector3 { float x; @@ -34,17 +35,27 @@ struct Module }; struct Entity { - void* VMT; - + void* vmt; + const char* name; }; +struct EntityList { + const char* name; + void* unk_1; + void* unk_2; + const char* mod; + const char* func; +}; + +template struct HashTable { uint32_t size; - HashTableEntry** chains; + HashTableEntry** chains; }; +template struct HashTableEntry { - Entity* data; + T* data; const char* name; HashTableEntry* next; }; \ No newline at end of file