Using Ghidriff to look at heap buffer overflow example
The dragon who finds the vulnerable.
Introduction
I wanted to showcase using Ghidriff to analyse a software patch which was applied to fix a heap buffer overflow in a login server, which can lead to remote code execution. I will be looking at CVE-2025-58447 which was present in rAthena, an online game server framework. The report claims a denial of service, and Remote Code Execution (RCE). Two caveats to this blog post:
- The project is open source, meaning the source code is available to review alongside any decompilation attempts.
- I will not be looking to exploit the overflow for RCE or altering other heap data structures.
Ghidriff
Ghidriff uses Ghidra’s engine to analyse two binaries and essentially, spot the difference, showing you the pseudo-C of what changed between the binaries.
There are other tools out there, such as for Binary Ninja (my choice of RE platform) and IDA, but unfortunately these are in the form of plugins which are paid features of those tools, which I don’t have access to doing this for learning / as a hobby.
Software (discounting web applications, so we are referring to things such as VPN gateways, Microsoft Office products, Firewalls, etc etc) can be exploited through a number of means - buffer overflows, race conditions, integer under/over flows, etc. A zero-day refers to a vulnerability in some software which is not yet patched by the vendor, and is often used in the context where it allows an adversary to use that vulnerability to execute code or read memory (such as in-memory passwords in a VPN gateway). There are teams around the world, both legitimate and criminal, who race to find zero-days for software to hack into some infrastructure.
N-days however are vulnerabilities which have been patched by the vendor, but may or may not have been patched by the organisation using that software / hardware. Schrodinger’s vulnerability, if you will. It is though analysing n-days with patch diffing (aka, patched vulnerabilities) that we can reverse engineer where the bug was, and work backwards from that in seeing how to exploit it (reading memory, causing a denial of service, or worse - allowing code execution).
N-days are of interest to both defenders and attackers. For defenders they are valuable for triage and detection engineering; for attackers, they lower the amount of discovery work needed to weaponise a bug.
Technical steps
Now we can get into using Ghidriff to get this moving!
First - you need to download the pre-patch binary, and the patched binary. In this case, we can build the project from source by downloading it from GitHub and using git to select a before and after build. For what it is worth, I build the binaries in release mode, to make this as realistic as possible.
Next, we need to ensure we have Ghidra installed as well as Ghidriff - the instructions for both can be found on their GitHub pages.
Now, we can run Ghidriff via the commandline to generate output, we can simply run:
ghidriff .\old.exe .\new.exe --sxs
Adding the –sxs flag tells the tool to also output a side-by-side view of the diff, which makes it a little easier for us to rationalise. Looking at the side-by-side output for this we can see some very clear code changes (left: un-patched binary, right: patched binary):

The difference between the two binaries affects only one function, and the majority of the changes to it can be pictured in the green diff on the right hand side.
From looking at this - we can see something very interesting. There is a section of code just above the loop which involve uVar9 and uVar10. What is going on here?
Very clearly, we can see the diff is checking some input length against a size of 0x21. If the size is greater than 0x21, then the program will use the maximum size (0x21), otherwise, it will use the given size. The if statement in the middle of the green diff shows this process happening. That prevents the out-of-bounds heap write and the resulting heap corruption.
Later, it then passes uVar9 into FUN_14003e6b0 - where we know now uVar9 has had its bounds checked. In the pre-patch example on the left, you can see the function receives pcVar5 in the same position, and there is no such bounds checking performed.
Turning this corruption into a reliable exploit would require additional, environment-specific steps (e.g. heap grooming and mitigation bypasses).