Home > Software > TDR: Practice Using EdgeCase
TDR: Practice Using EdgeCase
article and software by Ben Lincoln
Table of contents
- Decompilation Walkthrough
- Discussion of Results
Decompilation Walkthrough
This is a basic walkthrough of decompiling a very simple PlayStation PSX-EXE binary (EDGECASE.EXE, which you can download at the bottom of this page) using Ghidra and This Dust Remembers What It Once Was. It's intended to introduce the general TDR
process with something that doesn't require manual workarounds and
which (for the most part) easily decompiles to something very much like
its original form in Ghidra.
The EDGECASE.EXE binary was compiled using the same PsyQ
toolchain as many real PlayStation titles. The source code and PsyQ
build instructions are included for reference/reproducibility. It was
originally written to debug some problems with SymDump/SymDumpTE.
- Copy EDGECASE.EXE, and EDGECASE.SYM to your working directory.
- Open a command prompt or PowerShell prompt and change directory to your working directory.
- Convert EDGECASE.EXE to ELF format by running the following command:
PlayStationELFConverter.exe --exe2elf EDGECASE.EXE EDGECASE.ELF > PlayStationELFConverter_Log.txt 2>&1
- Generate the JSON version of the debug symbols by running the following command:
SymDumpTE.exe --rename-for-compatibility --json EDGECASE.SYM EDGECASE.json > SymDumpTE_Log.txt 2>&1
- Generate the monolithic header and Ghidra Java scripts by running the following command:
CreateSkeleton.exe
--create-playstation-memory --assume-sn-gp-base --name EDGECASE
--externs-to-labels --output Output EDGECASE.json >
CreateSkeleton_Log.txt 2>&1
- Launch Ghidra, and create a new project. For the base directory, use the Output directory created by TDR. For the project name, use EDGECASE.
- Import the ELF file you generated earlier. Ghidra will default to 64-bit MIPS, which is wrong. Click the ... button next to the Language field. Scroll up in the list and choose MIPS/default/32/little/default processor architecture, which will show up as MIPS:LE:32:default:default in the import file window. Click OK to begin the import.
- Close the import summary dialogue.
- Double-click on EDGECASE.ELF in the project list.
- An Analyze prompt will appear. Click No, because you don't want that to happen until the debug symbols have been imported.
- From the Edit menu, choose Tool Options.
- Expand Decompiler, and select Analysis. Uncheck Eliminate unreachable code. Click OK.
- From the File menu, choose Parse C Source option. Click the green plus sign button. Open the EDGECASE.H file in the Output/source-stubs directory. Click Parse to Program.
- Click Parse to Program. Click Continue. Click Continue?.
- After a moment, you should receive a message indicating that
the header has been parsed successfully. If you don't, make sure you
resolved any naming conflicts in the JSON file, re-run the CreateSkeleton.exe above, and then re-import the EDGECASE.H file. Otherwise, Click OK, then click Dismiss.
- Copy the EDGECASETDRDefineFunctions.java, and EDGECASETDRDecompile.java scripts from the Output/ghidra_files/ directory into your own Ghidra scripts directory (probably something like C:\Users\yourname\ghidra_scripts).
- In Ghidra, from the Window menu, choose Script Manager option.
- In the Script Manager window, click on the the EDGECASETDRDefineFunctions.java entry, then click the green-and-white play button in the upper-right corner of the window.
- After a noticeable delay, you should see a EDGECASETDRDefineFunctions.java> Finished! message in the console at the bottom of the main Ghidra window.
- From the Analysis menu, choose Auto Analyze 'EDGECASE.ELF'. Check the Decompiler Parameter ID box if it's not already checked. Switch to the MIPS Constant Reference Analyzer section. Uncheck Recover global GP register writes if it's checked. Optionally, check Attempt to recover switch tables. Click Analyze.
- Wait for the analysis to complete (progress is in the lower-right corner of the main Ghidra window.
- Go back to the Script Manager window, and run the EDGECASETDRDecompile.java script.
- Wait for the decompilation to happen. This will usually take awhile. You should see a EDGECASETDRDecompile.java> Finished! message in the console at the bottom of the main Ghidra window when it's complete.
- Back in the command prompt, create another set of C source
code files which contain the decompiled functions output by Ghidra by
running the following command:
PopulateSkeleton.exe --name EDGECASE
--input-json EDGECASE.json --input-source Output\EDGECASE.C --output
Output > PopulateSkeleton_Log.txt 2>&1
- Examine the contents of Output/source-decompiled, which should contain TDR's
best attempt at reconstructing the original source code in all of the
separate files that were originally used. Anything not matched to one of
those files will be placed in Unmatched_Decompiled_Functions.C instead.
- Compare the resulting decompiled code with the original source code.
Discussion of Results
This section (coming soon!) will provide an analysis of the
results generated using the process described above, to help evaluate
the effectiveness of the TDR toolchain, as well as explain some
of the differences in the output. For the most part, Ghidra does an
unbelievably phenomenal job of reconstructing the original source code.
However, compilation to native code is a lossy process, and so while
nearly all of the results are functionally identical to the original
source code, some of them are less "readable" than others, or are
obviously machine-generated instead of the way a human might write them.
This effect can be magnified by compiler optimizations and other
factors.
Related Articles:
This Dust Remembers What It Once Was
TDR: Soul Reaver
TDR: Need For Speed 4
TDR: Biohazard 2