Welcome back for the third episode of this hate story between me and The Sabouter. Yes it isn’t a love story as it is not ended yet but in two weeks there were some updates.
If you missed the previous episodes that explain the project and the first step, check here.
Reverse Engineering the game: Lua
This time I approached with 2 + 1 sides to attack the game, in a way to let me able to load custom lua code.
The First way it was the usual, patch the luap package file with custom lua bytecode.
WarrantyVoid did an amazing tool that let you to change the bytecode itself and repackage everything. Basically he studied how lua bytecode for 5.1 works and rewrote everything with a UI to patch lua bytecode itself.
This is a screen of a single value I changed as a way to swap an if condition, to do some tests. The code should be able to get the player as invincible, but the code doesn’t work in that way at the end (reading the lua code is looking for a variable form the binary).
I used also the same tool to compare the original luap and the modded version with my previous tool, just to get some hints.
I am now investigating to some changes to a specific file and code to see in the game itself. As I cannot change all the code but just swap some values or conditions.
The second way I am investigating on how to cross compile lua bytecode. Lua doesn’t support cross compile, someone hacked lua to target powerpc, but there is a lua fork with some changes that let to specify the endianness. Also, there is a patch to compile a 32bit from a 64bit, so I hacked a bit…
The only option untested is to use QEMU to compile Lua 5.1 on a powerpc machine and see if it is different. In this way I can determine if the game includes some specific changes to lua itself or the patches I am doing are not enough.
Discoveries from game binary
If the game doesn’t found the luap file automatically will load the lua file (not in bytecode) from a specific folder! In the computer version this solution works!
At same time on Xbox 360 no, the game crashes. So I started analyzing the game code too.
You can notice some facts:
- The pseudo code is basically the same
- Ghidra on Xbox360 is not able to analyze rightly all the ppc64 code as xbox use also the VMX subset that is not supported yet on Ghidra
I guess that also the Playstation 3 is the same, as also that console use powerpc.
Anyway I started do some tests also with Xenia, the xbox 360 emulator. I tried to compile it on linux but the support is not complete compared to the windows version.
With wine on Linux (I am using the Proton wine version of Steam that is more complete for libraries), a bit of support from the emulator community with this command I was getting the files loaded by the binary.
WINEPREFIX=/home/mte90/.steam/steam/steamapps/compatdata/312530/pfx ../../.steam/steam/steamapps/common/Proton\ 5.0/dist/bin/wine ./xenia.exe –debug /home/mte90/Desktop/The\ Saboteur/default.xex –log_level=3
You can see that I am specifying the xex binary, it is an exe for xbox 360 that is compressed but with
log_level=3 I was able to get a more verbose log.
This is an extract of Xenia, in the case the LuaScripts.luap file is missing and when is present:
d> F8000028 RtlInitAnsiString(4018F508, 409F4AA0(d:\LuaScripts.luap)) d> F8000028 NtCreateFile(4018F500(4018F560), 80100080, 4018F518(FFFFFFFD,d:\LuaScripts.luap,00000040), 4018F510, 00000000, 00000000, 00000001, 00000001, 00000868) F> F8000028 HostPathDevice::ResolvePath() i> F8000028 RtlNtStatusToDosError C000000F => 2 d> F8000028 RtlInitAnsiString(4018F508, 409F4AA0(d:\LuaScripts.luap)) d> F8000028 NtCreateFile(4018F500(4018F560), 80100080, 4018F518(FFFFFFFD,d:\LuaScripts.luap,00000040), 4018F510, 00000000, 00000000, 00000001, 00000001, 00000868) F> F8000028 HostPathDevice::ResolvePath() i> F8000028 Added handle:F80000FC for class xe::kernel::XObject
Clearly the game crashes because don’t find it, but the code is the same as the computer version and should move on and load from a folder, like in the computer version. Maybe on xbox there are other difference in the binary…
Anyway I had another idea, maybe I can create an empty file with the same name so the game load the files from a folder, as the pseudocode is based on an if condition and some other functions called later.
d> F8000028 RtlInitAnsiString(4018F508, 409F4AA0(d:\LuaScripts.luap)) d> F8000028 NtCreateFile(4018F500(4018F560), 80100080, 4018F518(FFFFFFFD,d:\LuaScripts.luap,00000040), 4018F510, 00000000, 00000000, 00000001, 00000001, 00000868) F> F8000028 HostPathDevice::ResolvePath() i> F8000028 RtlNtStatusToDosError C000000F => 2 d> F8000028 NtSuspendThread(F8000058, 4018F470(00000000))
And yes, the game load luap but crash again.
Just as reference those are the files in the game folder:
On my testings with Xenia, it is like the game doesn’t reach the scripts folder because crash early.
- I don’t understand why is not possible to do natively cross compile in Lua
- Maybe the Lua version of the game was hacked internally (not just with adding more modules)
- Patch the xbox 360 binary to remove those file checking and just load Lua files from a folder
- Remote debugging the xbox360 game on console to check the loading from that folder
- Xenia debugger is cool but there is no documentation, and it is very difficult to understand how works
- Xenia log is very helpful
- About Xbox 360 there aren’t many articles or tutorial about modding like I am doing it. Just maybe how to find an offset for trainers or just texture hacking based on the computer version of the game (like for GTA 5). Compared to other console about the Xbox world the modding scene is very poor.
So for the future the last steps are:
- Create a custom lua compiler as some that I found, create a different bytecode from analyzing the original game Lua. The issue is that I have no idea how to start also because I don’t work with C++, basically this is too much for me
- Patch LuapBrowser a specific line code to see if the game react to those changes (I am looking to a specific variable or something like a value swap can be easily detected)
- Xbox 360 remote debugging to see what happens really inside the game with that folder with Ghidra or do it with the Xenia debugger. More easy, it will require some time to bootstrap but is something that can give some information, like how the game check that folder and load all the files
- Patch the original retail xex binary with Ghidra and remove that if, maybe also change the scripts folder path as to me just load the Modules subfolder and not the others
- Create a fake luap file with just a single file, so the file will be loaded and move on loading for the Scripts folder itself
I want to say thanks to WarrantyVoider for his Ghidra’s tools and LuapBrowser, that is an impressive work of less of 2 weeks. Probably that tool can be converted to manipulate any Lua bytecode for powerpc (supports only that version now) or to compile from scratch a lua to bytecode for powerpc. I don’t know C# and don’t work with Windows since 10 years, but I guess that can be ported to other languages, but it will take time that I don’t have.
Anyway I don’t know if I have still interests or forces on that as it is becoming more difficult for my knowledge to do those things.
You can find me on Telegram, Discord, Reddit, Twitter and so on just check with Mte90 nickname or on the blue sidebar on the right.