Previously in this series of articles, we’ve acquired the various artifacts I intend to work on and extracted files out of it. To start reverse-engineering them, I will be using Ghidra, an open-source software reverse engineering framework, but we need a bit of a setup beforehand.

There will be far less hand-holding for Ghidra here than in my series of articles on reverse-engineering. Please refer to it for a crash course on how to use this tool.

Setting up a Ghidra server the wrong way

I don’t want to lose all of my progress should my computer blows up with all of my reverse-engineering work on it. Fortunately, one can set up a Ghidra server, which is meant for collaborative work.

I do not expect that the set of people capable of decompiling PlayStation video games intersected with the set of people wanting to decompile Tenchu: Stealth Assassins contains anyone besides me (and the former statement remains to be proved), but the server will be handy as a remote backup where I can check-in my reverse-engineering work regularly.

Setting up a Ghidra server is not required in order to use Ghidra as shown in part 6 of my series of articles on reverse-engineering. One can simply use non-shared projects, which will be accessed and stored locally on the computer.

Unfortunately, I’m too cheap to pay for a VPS and the only server I have on hand is a loaf-sized Synology DS218 that is 5 years old, with no support for virtual machines or Linux containers.

Is it a good idea to set up a Ghidra server on this measly hardware? Probably not, but that’s not going to stop me.

Needless to say, this is not officially supported and definitely not how you should set up a Ghidra server.

First, I need a Java run-time environment to run the Ghidra server. I’ve installed the Java 17 OpenJDK package from SynoCommunity, a third-party package repository for Synology DSM. To store the repositories, I’ve created a shared folder named ghidra. After that, it’s time to shoehorn the Ghidra server into running on the NAS:

$ cd /volume1/
$ sudo wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.0_build/ghidra_11.0_PUBLIC_20231222.zip
$ sudo unzip ghidra_11.0_PUBLIC_20231222.zip 
$ sudo mv ghidra_11.0_PUBLIC @ghidra_11.0_PUBLIC
$ sudo sed -i 's|ghidra\.repositories\.dir=.*|ghidra.repositories.dir=/volume1/ghidra|' '/volume1/@ghidra_11.0_PUBLIC/server/server.conf'
$ sudo touch /volume1/ghidra/users
$ sudo synoacltool -add /volume1/ghidra 'group:daemon:allow:rwxpdDaARWc--:fd--'
$ sudo chown daemon:daemon -R /volume1/ghidra /volume1/@ghidra_11.0_PUBLIC
$ chmod +x '/volume1/@ghidra_11.0_PUBLIC/server/ghidraSvr' '/volume1/@ghidra_11.0_PUBLIC/server/svrAdmin' '/volume1/@ghidra_11.0_PUBLIC/support/launch.sh'

To allow Ghidra clients to use a user-supplied user ID rather than their system user name (what a mouthful), add an entry for wrapper.app.parameter containing the option -u.

This option must appear before the option specifying the repositories directory (${ghidra.repositories.dir}), otherwise the server will fail to start.

Now, I don’t want to risk breaking my setup each time Synology releases an update for DSM, so I generally try to set up my custom modifications without patching the operating system on-disk. Therefore, the remaining steps are run as a scheduled task on boot and runs entirely from RAM (this script requires root privileges):

#!/bin/sh

cat >/run/systemd/system/ghidrad.service <<EOF
[Unit]
Description=Ghidra Server
After=network.target

[Service]
ExecStart=/volume1/@ghidra_11.0_PUBLIC/server/ghidraSvr console
User=daemon
Group=daemon

[Install]
WantedBy=multi-user.target
EOF

mount /tmp -o remount,exec
systemctl daemon-reload
systemctl enable ghidrad
systemctl start ghidrad

After rebooting the NAS, the Ghidra server should be up and running:

boricj@DS218:~$ sudo systemctl status ghidrad
● ghidrad.service - Ghidra Server
   Loaded: loaded (/run/systemd/system/ghidrad.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2024-01-13 17:55:20 CET; 17h ago
 Main PID: 10777 (bash)
   CGroup: /system.slice/ghidrad.service
           ├─10777 bash /volume1/@ghidra_11.0_PUBLIC/server/ghidraSvr console
           ├─11932 /volume1/@appstore/java-17-openjdk/jvm/openjdk-17.0.6/bin/java -Djna_tmpdir=/tmp -Djava.io.tmpdir=/tmp -jar /volume1/@ghidra_11.0_PUBLIC/server/../Ghidra/Features/GhidraServer/data/yajsw-stable-13.09/wrapper.jar -c /volume1/@ghidra_11.0_PUBLIC/server...
           └─14172 /volume1/@appstore/java-17-openjdk/jvm/openjdk-17.0.6/bin/java -classpath /volume1/@ghidra_11.0_PUBLIC/Ghidra/Features/GhidraServer/data/yajsw-stable-13.09/wrapperApp.jar:/volume1/@ghidra_11.0_PUBLIC/Ghidra/Features/GhidraServer/data/yajsw-stable-13....

Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Instantiating User Manager (w/password management) (UserManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  User file contains 0 entries (UserManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Known Users: (UserManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Known Repositories: (RepositoryManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|WARN  Ignoring repository directory with bad name: /volume1/ghidra/@eaDir (RepositoryManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO     <none> (RepositoryManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Command watcher started (RepositoryManager)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Starting Block Stream Server... (BlockStreamServer)
Jan 13 17:56:06 DS218 ghidraSvr[10777]: 14172/0|INFO  Registering Ghidra Server... (GhidraServer)
Jan 13 17:56:07 DS218 ghidraSvr[10777]: 14172/0|INFO  Registered Ghidra Server. (GhidraServer)

I’ve previously configured the firewall to drop all non-local traffic besides TCP ports 80 and 443 (yes, as of the time of writing this article my blog is hosted on this NAS).

Please administrate internet-visible servers responsibly.

All that is left to do is creating a user to connect to the Ghidra server:

$ cd '/volume1/@ghidra_11.0_PUBLIC/server'
$ sudo ./svrAdmin -add boricj --p
openjdk version "17.0.6" 2023-01-17 LTS
OpenJDK Runtime Environment (build 17.0.6+10-LTS)
OpenJDK 64-Bit Server VM (build 17.0.6+10-LTS, mixed mode)
server.conf: /volume1/@ghidra_11.0_PUBLIC/server/server.conf
Using config file: /volume1/@ghidra_11.0_PUBLIC/server/server.conf
Using server directory: /volume1/ghidra
Enter password for user 'boricj'
New password: 
Retype new password: 
Initializing Random Number Generator...
Random Number Generator initialization complete: NativePRNGNonBlocking
Command queued.

The Ghidra server is set up and ready to go, it’s time to actually use it.

Check out the documentation for the Ghidra server for further information on how to administrate a Ghidra server.

Connecting to a Ghidra shared project

On the client side, I’m using a somewhat battle-scarred Acer Chromebook Spin 13 as my primary daily computer, mostly because any device I try to administrate myself tends to blow up every so often (and I need at least one of them to work reliably in spite of my best sysadmin efforts).

There is another reason for the Ghidra server, besides using it as a backup strategy. If I work from another computer (like my desktop for example), it’s easier to synchronize everything from a central server rather than shuttling around a Ghidra project.

Therefore, I’ll spin up a new Linux container on this Chromebook and keep all of these shenanigans safely quarantined within it:

$ sudo apt-get update && sudo apt-get upgrade -y
$ wget https://github.com/NationalSecurityAgency/ghidra/releases/download/Ghidra_11.0_build/ghidra_11.0_PUBLIC_20231222.zip
$ sudo unzip ghidra_11.0_PUBLIC_20231222.zip -d /opt/
$ sudo chmod +x /opt/ghidra_11.0_PUBLIC/{ghidraRun,support/launch.sh}
$ mkdir -p .local/bin
ln -s /opt/ghidra_11.0_PUBLIC/ghidraRun .local/bin/ghidraRun
$ sudo apt-get install openjdk-17-jdk -y
$ ghidraRun

I’m also installing the PlayStation executable loader as well as my own delinker extension, prior to setting up a new shared project:

  • Click on File > New Project (or hit Ctrl-N) ;
  • Select Shared Project for the project type ;
  • Enter the server name and server port ;
  • Log in the Ghidra server using the credentials ;
  • Create a new repository ;
  • Set up the repository user rights (defaults are fine) ;
  • Enter a project directory and project name for the local computer.

Ghidra will present an active project window, similar but slightly different than the non-shared variant. Notably, there is a connection status icon on the bottom-right corner. We can confirm the status of the project with Project > View Project Info...:

Importing files in bulk

There are a lot of files in these games and it would be tedious to import every executable one by one in Ghidra. Thankfully, we have the option to import files in bulk. When cliking on File > Batch Import and selecting every game directory to process, Ghidra will automatically detect anything it recognizes:

After confirming the selection of files to import (and waiting a bit for all files to be processed), we have all 35 executable files imported for every version of the game inside the Ghidra project:

Conclusion

We have a Ghidra server, a centralized repository and a Ghidra shared project set up. Next time, we can actually start what this was all about: reverse-engineering Tenchu: Stealth Assassins.