Home > Software > This Dust Remembers What It Once Was
This Dust Remembers What It Once Was
Table of contents
Introduction
This Dust Remembers What It Once Was ("TDR") is a reverse-engineering toolkit I wrote for use with the NSA'a amazing tool Ghidra. Ghidra is a completely free, open-source binary reverse-engineering toolkit that includes not only a disassembler, but a decompiler that must have been written using black magic. I can't thank its authors and the NSA enough for releasing it last year.
I wanted to use Ghidra to help reverse engineer Soul Reaver, my favourite game of all time, but at least when I started, there were a couple of obstacles in my way: Ghidra doesn't support the proprietary PSX-EXE format used for PlayStation binaries, and it also doesn't support the PsyQ .SYM debug symbol format.
I originally started writing TDR specifically for that one project, but I've tried to generalize it enough to work with any PlayStation title that has PsyQ debug symbols available. The PSX-EXE-to-ELF converter means that any PlayStation binary should be importable into Ghidra, even if it wasn't written using PsyQ at all.
I have some additional componnents in mind for later that will extend it to other gaming platforms, but I'm not sure when I'll have time to get around to that.
Be warned, the current version of TDR should be considered an alpha release, in the traditional sense: it's feature-complete, but it's probably full of bugs. I don't know how frequently I'll be able to work on it, so I wanted to get it out there in case it was useful to someone even in its current state.
TDR is a highly-specialized reverse-engineering tool. The documentation below is pretty barebones at the moment, and assumes extensive pre-existing knowledge. I'd like to expand it in the future.
TDR itself is open-source, licensed under the GPLv3. Warning: you may regret looking at some of the code. This is a project that grew organically over about six months. It involved lots of on-the-fly design changes because I was learning about some of the low-level details as I went.
Components
The current version of TDR is made up of four tools:
CreateSkeleton.exe does the bulk of the work in the current version of TDR. From the input data, it generates the following:
Basic Walkthrough
TDR was developed and tested with debug builds of Soul Reaver, so this walkthrough will assume you have one of those. I used the one with a build date of 1999-06-01, but any prototype version should work as long as it includes the .SYM file. Important: some of the European retail versions of Soul Reaver include .SYM files, but I don't think they actually match up with the game binary, as they're much older. Those probably won't work.
You'll probably want to copy all of the TDR binaries and DLLs to C:\Windows for convenience, or at least add their location to your PATH environment variable.
PlayStationELFConverter.exe --exe2elf KAIN2.EXE KAIN2.ELF > PlayStationELFConverter_Log.txt 2>&1
SymDumpTE.exe --json KAIN2.SYM KAIN2.json > SymDumpTE_Log.txt 2>&1
CreateSkeleton.exe --name KAIN2 --ignore-labels --externs-to-labels --output Output KAIN2.json > CreateSkeleton_Log.txt 2>&1
PopulateSkeleton.exe --name KAIN2 --input-json KAIN2.json --input-source KAIN2.C --output Output > PopulateSkeleton_Log.txt 2>&1
The one required change for the Soul Reaver files (mentioned above):
As discussed above, there are lots of changes that would be good to make, but one is absolutely required for Soul Reaver, because if you don't, Ghidra won't be able to parse the C header file.
Open KAIN2.json and search for "name": "_walbossAttributes". You should find a section that looks like this:
"UsedByFunctions": [],
"struct_member_signature": "struct .253fake attackDeltas[0]; // size=0, offset=24",
"class_type": "struct_member",
"c_type": "struct .253fake[0]",
"type_name": "struct .253fake[0]",
"size": 0,
"offset": 24,
"parent_name": "struct _walbossAttributes",
"parent_hashcode": 0,
"name": "attackDeltas",
"source_file": null,
"hashcode": 0
}
],
"name": "_walbossAttributes",
"source_file": null,
"hashcode": -1378913783
There should be two instances of the text struct .253fake, or similar depending on which build of Soul Reaver you're looking at (struct .255fake, etc.) Change both instances of the struct name so that they read struct _wba253fake. There's nothing special about this replacement name, it just needs to be unique because there's another struct or union with the same name elsewhere in the debug symbols.
When you're done, the section should look something like this:
"UsedByFunctions": [],
"struct_member_signature": "struct _wba253fake attackDeltas[0]; // size=0, offset=24",
"class_type": "struct_member",
"c_type": "struct _wba253fake[0]",
"type_name": "struct _wba253fake[0]",
"size": 0,
"offset": 24,
"parent_name": "struct _walbossAttributes",
"parent_hashcode": 0,
"name": "attackDeltas",
"source_file": null,
"hashcode": 0
}
],
"name": "_walbossAttributes",
"source_file": null,
"hashcode": -1378913783
Scroll up just past the beginning of the _walbossAttributes struct definition, and you should find the definition of the .253fake struct that it references. It should look something like this:
{
"UsedByFunctions": [],
"members": [
{
"UsedByFunctions": [],
"struct_member_signature": "short plusDelta; // size=0, offset=0",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 0,
"parent_name": "struct .253fake",
"parent_hashcode": 0,
"name": "plusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short minusDelta; // size=0, offset=2",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 2,
"parent_name": "struct .253fake",
"parent_hashcode": 0,
"name": "minusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short validAtHitPoint; // size=0, offset=4",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 4,
"parent_name": "struct .253fake",
"parent_hashcode": 0,
"name": "validAtHitPoint",
"source_file": null,
"hashcode": 0
}
],
"name": ".253fake",
"source_file": null,
"hashcode": -1080400537
},
Replace all the occurrences of .253fake (or whatever it's called in the build you're looking at) with your replacement name, so that it looks like this:
{
"UsedByFunctions": [],
"members": [
{
"UsedByFunctions": [],
"struct_member_signature": "short plusDelta; // size=0, offset=0",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 0,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "plusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short minusDelta; // size=0, offset=2",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 2,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "minusDelta",
"source_file": null,
"hashcode": 0
},
{
"UsedByFunctions": [],
"struct_member_signature": "short validAtHitPoint; // size=0, offset=4",
"class_type": "struct_member",
"c_type": "short",
"type_name": "short",
"size": 2,
"offset": 4,
"parent_name": "struct _wba253fake",
"parent_hashcode": 0,
"name": "validAtHitPoint",
"source_file": null,
"hashcode": 0
}
],
"name": "_wba253fake",
"source_file": null,
"hashcode": -1080400537
},
Results
This section will be greatly expanded in the future.
TDR works pretty well with all of the debug builds of Soul Reaver I've tested it against.
It also does a solid job against the 1997-10-30 beta build of Biohazard 2. I didn't even need to manually edit the JSON file to do a basic decompilation of that one.
It does not do so well with the 1996-08-05 prototype version of Wipeout XL, because the .SYM file for that game only includes labels, not other types of symbols.
Downloads
Download | ||||
File | Size | Version | Release Date | Author |
This Dust Remembers What It Once Was | 558 KiB | 0.2 | 2019-08-06 | Ben Lincoln |
This is the .NET executable version of the TDR suite. If you want to use the tool, this is probably the file you want to download. |
Download | ||||
File | Size | Version | Release Date | Author |
This Dust Remembers What It Once Was (Source Code) | 1 MiB | 0.2 | 2019-08-06 | Ben Lincoln |
This is the .NET source code for the TDR suite. |
Download | ||||
File | Size | Version | Release Date | Author |
This Dust Remembers What It Once Was | 557 KiB | 0.1 | 2019-08-06 | Ben Lincoln |
This is the .NET executable version of the TDR suite. If you want to use the tool, this is probably the file you want to download. |
Download | ||||
File | Size | Version | Release Date | Author |
This Dust Remembers What It Once Was (Source Code) | 1 MiB | 0.1 | 2019-08-06 | Ben Lincoln |
This is the .NET source code for the TDR suite. |