Code cleanup

This commit is contained in:
Daniel S. 2019-02-28 17:50:52 +01:00
parent bef8fcbaaa
commit ffbcc30427
14 changed files with 328 additions and 288 deletions

View file

@ -1,6 +1,6 @@
{
"spellright.language": [
"de"
"en"
],
"spellright.documentTypes": [
"markdown",

View file

@ -2,7 +2,7 @@
- Engine: ScrapEngine
- Ingame Scripting Language: Python 1.5.2
# Ingame-Console (Ctrl+\^) (Handler@0x402190):
# Ingame-Console (Ctrl+\^ or right click on titlebar and select "switch console") (Handler@0x402190):
* `<Command>`: Try to evaluate Command as Python expression
* `:<Var>`: Get Game Engine Global Variable
* `:<Var> <Val>`: Set Game Engine Global Variable
@ -14,7 +14,7 @@
# External Console (Scenegraph Debugging?) (Handler@0x5f9520):
* `listar luces`
* `listar`
* `arbol` (Patch Scrap.exe@offset 0x314bc0 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 the space at the end))
* `mem`
* `ver uniones`
* Easter Eggs:
@ -23,27 +23,29 @@
- `capullo`
# Python Stuff
- Modules List @ 0x0079C698 (char* to Module Name followed by Pointer to Init Function)
- InitPyMod @ 0x005A8FB0
- PyExec @ 0x005A8390
- Modules List @ 0x79C698 (Module Name as `char*` followed by Pointer to Init Function)
- InitPyMod @ 0x5A8FB0
- PyExec @ 0x5A8390
## m3d.ini loader @0x05f7000
## m3d.ini loader @ 0x05f7000
## SM3 Secene Loader @ 0x650f80 (?)
## SM3 Scene Loader @ 0x650f80 (?)
## M3D File Loader @ 0x6665a0 (??)
## *.packed File Format:
Header:
"BFPK\0\0\0\0"
Int32ul: number of files
for each file:
Int32ul: path length
String: path
Int32ul: size
Int32ul: offset in file
```
Header:
"BFPK\0\0\0\0"
Int32ul: number of files
for each file:
Int32ul: path length
String: path
Int32ul: size
Int32ul: offset in file
```
## Loading Custom Content
## Loading Custom Content (not really working)
1. Create a folder `mods`
2. Drop a `*.packed` file into it
@ -54,9 +56,9 @@
# How to enable External Console:
1. exctract `Data.packed`
2. in m3d.ini uncomment "ConsolaWnd" (GUI Console) or "ConsolaTxt" (Text Console) and set the value to "SI"
2. in m3d.ini uncomment (remove `;`) "ConsolaWnd" (GUI Console) or "ConsolaTxt" (Text Console) and set the value to "SI"
3. repack "Data.packed"
or Use a custom Content Pack
or Use a custom Content Pack (**untested!**)
# Misc. Interesting things
- sys.path contains "./lib" so you can load your own Python Modules
- sys.path contains "./lib" so you can load your own Python Modules

View file

@ -4,20 +4,26 @@
* `parse_save.py`: Dumps information extracted from Save file
* `scrapper.py`: Extractor and Repacker for *.packed files, needs the `construct` and `tqdm` python modules and python 3.x
- Run `scrapper.py -h` for help
* `lib/dbg.py`: general Script for poking around inside the game's scripting system
* `lib/dbg.py`: general Script for poking around inside the game's scripting system
- Run `import dbg` inside the Game's Console,
this will load all builtin modules and enable godmode
- The dbg module also enables writing to the ingame console using `print <var>`
and defines two global functions s_write() and e_write() for writing to the Ingame Console's Stdout and Stderr Stream
- `dbg.menu()` Displays the Game's built in Debug Menu (you can't exit it though)
- `dbg.menu()` Displays the Game's built in Debug Menu (doesn't work properly)
- `dbg.enable_all_conv()` allows you to "overwrite" any character, even if they are protected/invulnerable
- `dbg.become(name)` allows you to transform into any character
- `dbg.helplib()` generates a file `helplib.txt` in the Game's folder containing all available Documentation for all available classes and functions
- `dbg.settrace()` Logs all Python function calls together with their arguments into a
- `dbg.txt` file inside the Game's folder
- `dbg.settrace()` Logs all Python function calls together with their arguments into a `dbg.txt` file inside the Game's folder
## [ScrapHacks](ScrapHacks/README.md)
WIP Memory hacking library
## [Notes](NOTES.md)
## [Notes](NOTES.md)
# 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)
- [Reclass.NET](https://github.com/ReClassNET/ReClass.NET)
- [HxD](https://mh-nexus.de/en/hxd/)

View file

@ -230,13 +230,8 @@ int main(int argc, char *argv[])
{
Sleep(100);
GetWindowThreadProcessId(FindWindowA("ScrapClass", NULL), &PID);
if (PID)
{
cout << "[+] Found PID: " << PID << endl;
cout << "[*] Sleeping 10 seconds to wait for it to fully load" << endl;
Sleep(10000);
}
}
cout << "[+] Found PID: " << PID << endl;
InjectDll(PID);
cout << "[*] Done!" << endl;
return 0;

View file

@ -5,6 +5,11 @@
0. Build Project
1. Run Injector `.\Injector.exe`
2. Run Game
3. Wait ~10 Seconds for game to load and Injector to works its magic
```
[F3 ] Unload ScrapHacks
[F7 ] Set Money to 0x7fffffff
[F8 ] Dump python modules to console
[F10] Enable python tracing
[ F ] "Handbrake" (*Will* crash the game after some time!)
```

View file

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

View file

@ -3,251 +3,35 @@
#include <sstream>
#include <vector>
#include <map>
#include <deque>
#include <iomanip>
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include "Scrapland.h"
//#include <D3d8.h>
#define DLL_EXPORT extern "C" __declspec(dllexport)
struct Module;
using namespace std;
map<string, Module> Py;
#include "Scrapland.h"
#include "Util.h"
#include "Structures.h"
#include "Py_Utils.h"
HMODULE hD3D8Dll = 0;
bool initialized = false;
bool running = true;
HMODULE mod = 0;
string GetLastErrorAsString()
{
DWORD errorMessageID = GetLastError();
if (errorMessageID == 0)
return "No error";
LPSTR messageBuffer = NULL;
size_t m_size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL);
string message(messageBuffer, m_size);
LocalFree(messageBuffer);
if (!message.empty() && message[message.length() - 1] == '\n')
{
message.erase(message.length() - 1);
}
return message;
}
void SetupStreams()
{
FILE *fIn;
FILE *fOut;
freopen_s(&fIn, "conin$", "r", stdin);
freopen_s(&fOut, "conout$", "w", stdout);
freopen_s(&fOut, "conout$", "w", stderr);
ios::sync_with_stdio();
std::wcout.clear();
std::cout.clear();
std::wcerr.clear();
std::cerr.clear();
std::wcin.clear();
std::cin.clear();
}
void SetupConsole()
{
if (!AllocConsole())
{
FreeConsole();
AllocConsole();
}
AttachConsole(GetCurrentProcessId());
SetupStreams();
}
void SetupConsole(const char *title)
{
SetupConsole();
SetConsoleTitleA(title);
}
void FreeConsole(bool wait)
{
if (wait)
{
cout << "[?] Press Enter to Exit";
cin.ignore();
}
FreeConsole();
}
bool in_foreground = false;
BOOL CALLBACK EnumWindowsProcMy(HWND hwnd, LPARAM lParam)
{
DWORD lpdwProcessId;
GetWindowThreadProcessId(hwnd, &lpdwProcessId);
if (lpdwProcessId == lParam)
{
in_foreground = (hwnd == GetForegroundWindow()) || (hwnd == GetActiveWindow());
return FALSE;
}
return TRUE;
}
bool key_down(int keycode, int delay = 100)
{
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground)
{
if (GetAsyncKeyState(keycode))
{
Sleep(delay);
return true;
}
}
return false;
}
bool key_down_norepeat(int keycode, int delay = 100)
{
in_foreground = false;
EnumWindows(EnumWindowsProcMy, GetCurrentProcessId());
if (in_foreground)
{
if (GetAsyncKeyState(keycode))
{
while (GetAsyncKeyState(keycode))
{
Sleep(delay);
}
return true;
}
}
return false;
}
struct PyMethodDef
{
char *ml_name;
void *ml_meth;
int ml_flags;
char *ml_doc;
};
struct PyMod
{
char *name;
void *init_func;
};
struct Module
{
PyMod *mod;
map<string, PyMethodDef> methods;
};
void hexdump(void *addr, size_t count)
{
for (size_t i = 0; i < count; ++i)
{
unsigned int val = (unsigned int)((unsigned char *)addr)[i];
cout << setfill('0') << setw(2) << std::hex << val << " ";
if (((i + 1) % 16) == 0)
{
cout << endl;
}
}
cout << endl;
}
PyMethodDef *find_method_table(size_t base, size_t size)
{
uint8_t *ptr = reinterpret_cast<uint8_t *>(base);
for (size_t offset = 0; offset < size; ++offset)
{
if ((uint16_t)ptr[offset] == 0x68)
{
uint32_t mod_addr = reinterpret_cast<uint32_t *>(base + offset + 1)[0];
if ((mod_addr & 0xf00000) == 0x700000)
{
if (strlen(reinterpret_cast<char *>(mod_addr)) == 3)
{
return reinterpret_cast<PyMethodDef *>(mod_addr);
}
}
}
}
return reinterpret_cast<PyMethodDef *>(0);
}
map<string, Module> get_modules(size_t base)
{
map<string, Module> Py;
PyMod *modules = reinterpret_cast<PyMod *>(base);
for (int i = 0; modules[i].init_func != NULL; i++)
{
Module mod;
mod.mod = &modules[i];
PyMethodDef *method_table = find_method_table((size_t)modules[i].init_func, 64);
for (int j = 0; method_table != NULL && method_table[j].ml_name != NULL; j++)
{
mod.methods[method_table[j].ml_name] = method_table[j];
}
Py[mod.mod->name] = mod;
}
return Py;
}
void *get_py(const char *mod, const char *meth)
{
try
{
return Py.at(mod).methods.at(meth).ml_meth;
}
catch (out_of_range)
{
return NULL;
}
}
void inject(const char *mod, const char *meth, void *detour)
{
try
{
void *orig = get_py(mod, meth);
Py.at(mod).methods.at(meth).ml_meth = detour;
cout << mod << "." << meth << ": " << orig << " -> " << detour << endl;
}
catch (out_of_range)
{
cout << mod << "." << meth << " not found!" << endl;
}
}
uint32_t ptr(uint32_t addr, vector<uint32_t> offsets)
{
cout << "[" << (void *)addr << "]";
for (uint32_t offset : offsets)
{
addr = reinterpret_cast<uint32_t *>(addr)[0];
cout << " -> [" << (void *)addr << " + " << offset << "]";
addr += offset;
};
cout << " -> " << (void *)addr;
cout << endl;
return addr;
}
void MainLoop(HMODULE mod)
{
Sleep(100);
cout << "[*] Starting main Loop" << endl;
cout << endl;
cout << "[F3 ] Unload ScrapHacks" << endl;
cout << "[F7 ] Set Money to 0x7fffffff" << endl;
cout << "[F8 ] Dump python modules" << endl;
cout << "[F10] Enable python tracing" << endl;
cout << "[F11] Unload ScrapHacks" << endl;
cout << "[ F ] \"Handbrake\" (*Will* crash the game after some time!)" << endl;
while (running)
@ -274,9 +58,8 @@ void MainLoop(HMODULE mod)
mov ecx, [7FE944h]
mov edx, [ecx + 2090h]
==========================*/
int32_t *money = reinterpret_cast<int32_t *>(ptr(WORLD, {0x2090}));
cout << "Money: " << money[0] << endl;
money[0] = 0x7fffffff;
int32_t *money = ptr<int32_t>(P_WORLD,O_MONEY);
*money = 0x7fffffff;
}
if (key_down_norepeat(VK_F8))
{
@ -284,14 +67,11 @@ void MainLoop(HMODULE mod)
{
for (auto meth : mod.second.methods)
{
if (meth.second.ml_doc != NULL)
{
cout << mod.first << "." << meth.first << " @ " << meth.second.ml_meth << " [" << &(meth.second) << "]" << endl;
}
cout << mod.first << "." << meth.first << " @ " << meth.second.ml_meth << endl;
}
}
}
if (key_down_norepeat(VK_F11))
if (key_down_norepeat(VK_F3))
{
break;
}
@ -318,12 +98,15 @@ void DllInit(HMODULE _mod)
InitConsole();
GetModuleFileName(0, mfn, 1024);
cout << "[+] ScrapHacks v0.1 Loaded in " << mfn << endl;
Py = get_modules(PY_MODS);
Sleep(3000);
Py = get_modules(P_PY_MODS);
cout << "[*] Importing python dbg module" << endl;
scrap_exec("import dbg");
cout << "[*] World: " << ptr<void>(P_WORLD,0) << endl;
hD3D8Dll = GetModuleHandle("d3d8.dll");
cout << "[*] D3D8 DLL @0x"<< hD3D8Dll << endl;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)MainLoop, mod, 0, 0);
cout << "[*] Starting message pump" << endl;
;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{

View file

@ -150,9 +150,12 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="Py_Utils.h" />
<ClInclude Include="Structures.h" />
<ClInclude Include="Scrapland.h" />
<ClInclude Include="stdafx.h" />
<ClInclude Include="targetver.h" />
<ClInclude Include="Util.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />

View file

@ -24,6 +24,15 @@
<ClInclude Include="Scrapland.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Util.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Structures.h">
<Filter>Headerdateien</Filter>
</ClInclude>
<ClInclude Include="Py_Utils.h">
<Filter>Headerdateien</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">

View file

@ -1,6 +1,7 @@
#pragma once
#define WORLD 0x7FE944
#define PY_MODS 0x79C698
#define P_WORLD 0x7FE944
#define P_PY_MODS 0x79C698
#define O_MONEY 0x2090
auto scrap_log = (int(_cdecl*)(int, const char*))0x4134C0;
auto scrap_exec = (void(_cdecl*)(const char*))0x5a8390;

View file

@ -0,0 +1,34 @@
#pragma once
struct Vector3 {
float x;
float y;
float z;
};
struct Matrix3x3 {
Vector3 a;
Vector3 b;
Vector3 c;
};
struct PyMethodDef
{
char *ml_name;
void *ml_meth;
int ml_flags;
char *ml_doc;
};
struct PyMod
{
char *name;
void *init_func;
};
struct Module
{
PyMod *mod;
map<string, PyMethodDef> methods;
};

159
ScrapHacks/ScrapHack/Util.h Normal file
View file

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

View file

@ -10,6 +10,7 @@ BOOL APIENTRY DllMain(HMODULE hModule,
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hModule);
hThread = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)DllInit, hModule, 0, 0);
break;
case DLL_PROCESS_DETACH:

View file

@ -1,19 +0,0 @@
Stack trace:
Frame Function Args
00180000000 0018006021E (00180233AB9, 001802340B9, 000FFFFC560, 000FFFFB6F0)
00180000000 00180048859 (FFFFFF00EEEEEE, 557BDB00453FCD, 56CA9F0069CDE6, 00000F22C80)
00180000000 00180048892 (00180233A96, 000FFFFC458, 000FFFFC560, 453FCD00FFFFFF)
00180000000 001800454A3 (00000000000, 00180000000, 00000000000, 001800004EC)
00180000000 0018006DF51 (FFFFFF00EEEEEE, 557BDB00453FCD, 56CA9F0069CDE6, DBB55500BEDB55)
00180000000 0018006EDDE (00000000000, 00100674A28, 00000000000, 00000000000)
00180000000 001800713A4 (00000000000, 00000000008, 00000000000, 00000000000)
006000331E0 0018013ABC1 (00100674A20, 00000000008, 00000000000, 00000000000)
006000331E0 001801214AB (00100674A20, 00000000008, 00000000000, 00000000000)
006000331E0 001004FC754 (0010057D324, 0010067B2B8, 00000000000, 0010067B2BC)
006000331E0 0010058CC73 (00600000008, 0010067C940, 00000000000, 00000000000)
006000331E0 0010057E416 (00000000000, 0005C16CB0E, 0001FCBD224, 00000010000)
006000331E0 001005E614F (00000000002, 0000000000E, 00000000002, 005FCB11020)
006000331E0 001005F46A7 (0000000002F, 00000000000, 00180214320, 00180329968)
000FFFFCCD0 00180049E84 (00000000000, 00000000000, 00000000000, 00000000000)
00000000000 00180047963 (00000000000, 00000000000, 00000000000, 00000000000)
End of stack trace (more stack frames may be present)