What reverse engineering is
Reverse engineering is the process of taking something already built and working backwards to understand how it works. In software, that usually means taking a compiled program, a binary with no source code attached, and figuring out what it does, what data it holds, and how it behaves.
A compiled binary is not meant to be read by people. The source code a developer wrote has been turned into machine instructions. Reverse engineering is how we recover the meaning that got lost in that translation.
Why it matters in security work
For anyone doing offensive security or ethical hacking, reverse engineering answers questions you may not be able answer any other way:
- It exposes hardcoded secrets. Developers leave API keys, passwords, and tokens inside binaries far more often than they should. Reversing can find them.
- It reveals hidden logic. License checks, authentication routines, and backdoors all live in the code. Reading that code shows you how to bypass or abuse it.
- It drives malware analysis. When you pull a suspicious file off a host, reversing can tell you what it does, its origin, and what it was after.
- It supports vulnerability research. Understanding how a program handles input is the first step to finding the flaw that breaks it.
- In short, reverse engineering turns a black box into something you can reason about. That is a real advantage in an engagement.
The tools, and why Ghidra
There are several tools worth knowing. IDA Pro is the long standing commercial standard with a free tier. Binary Ninja is a popular paid option with a clean interface. radare2 and its successor rizin are powerful command line frameworks. For quick work you also have objdump for disassembly, gdb for dynamic analysis, and strings for pulling readable text straight out of a file.
For this tutorial we will use Ghidra. It is free and open source, it ships with a genuinely good decompiler that turns machine code back into readable pseudo C, and it runs on every major operating system. That combination makes it the easiest place to start without paying for a licence.
What you need
I am running everything here on Kali Linux, where Ghidra is available through the package manager. If it is not already installed, run:
$ sudo apt install ghidra
Ghidra is written in Java, so a Java runtime comes along as a dependency. It is not tied to Kali. The same steps work on Windows and macOS, you just download Ghidra from the official site and make sure a recent JDK is installed. The interface and the workflow are identical across all three.
We will work through two small binaries. Grab them from here, mark them executable, and confirm what they are:
$ chmod +x configsync tokenvault
$ file configsync tokenvault
Both report as 64 bit ELF executables for Linux, and both are not stripped, which keeps the walkthrough clear.
Scenario one: secrets hiding in plain sight
The first binary, configsync, is the kind of internal tool a company builds to move backups to the cloud. Run it and watch what it does:
$ ./configsync
A normal run only prints status messages. Nothing sensitive shows up on screen.
On the surface it is boring. It loads a config, talks to an S3 endpoint, uploads some files, and exits. Nothing sensitive appears. But that output is only what the developer chose to print. It says nothing about what is stored inside the file itself.
The simplest first move against any unknown binary is to look at the readable text it contains. That is exactly what the strings command does. Run it directly on the binary with no other arguments:
$ strings configsync
Buried in the usual noise are live AWS credentials, an S3 endpoint, and a region.
strings walks the whole file and prints every run of readable characters it finds. Most of it is noise, library names, compiler tags, and the status messages you already saw. But sitting in the middle of that noise are an AWS access key, a secret key, the S3 bucket URL, and the region. The developer hardcoded live cloud credentials straight into the program.
On a larger binary the output would be far longer, and reading it line by line is not practical. Once you know what you are hunting for, you pipe the output into grep to filter it. This is the natural next step after you have seen the raw output at least once:
$ strings configsync | grep -iE "AKIA|amazonaws|secret|eu-west"
That narrows thousands of lines down to the handful that matter.
Why this matters on an engagement
Picture a real assessment. The client hands your team an internal utility their operations staff run every night. It looks harmless. Ten seconds with strings hands you a working AWS access key. From there you can reach their backup bucket, read database dumps and exported vaults, and pivot deeper into their cloud account. One careless line in the source code, and a routine tool becomes a full breach. This is not a rare finding. It is one of the first things worth checking on any binary you are given or stumble upon.
Scenario two: when strings comes up empty
The second binary, tokenvault, loads a service token so an agent can authenticate to an API. Run it:
$ ./tokenvault
The tool says it loaded and deobfuscated a token, then authenticated. The token itself is never printed.
It tells you a token was loaded and deobfuscated in memory, and that authentication succeeded. But the token never touches the screen. So we do what worked last time and run strings against it, looking for anything token shaped:
$ strings tokenvault | grep -iE "sk_live|token=|secret"
Nothing comes back. This is the point of the exercise. The developer did not store the token as plain text. It sits inside the binary in an obfuscated form and is only rebuilt in memory when the program runs. strings can only see readable text on disk, so it cannot see a secret that does not exist in readable form until runtime. When your quick tool comes up empty, you move to a stronger one. This is where Ghidra earns its place.
Opening the binary in Ghidra
-
Launch Ghidra. On Kali, run ghidra from a terminal. Create a new project with File then New Project, choose Non-Shared Project, and give it a name. Then use the File menu to import your binary.
The Ghidra project window. Everything starts from the File menu.
-
Import the file with “File” then Import File, and point it at tokenvault (this would be whatever you named your desired project folder). Ghidra detects the format on its own.
Ghidra correctly reads the file as a 64 bit ELF built with gcc. Accept the defaults and click OK.
-
Double click the imported file to open it in the CodeBrowser. Ghidra offers to analyze it. Click Yes and keep the default options. After a few seconds the analysis populates the Symbol Tree on the left, including a Functions list.
The binary loaded and analyzed. Functions the program defines now appear in the Symbol Tree.
-
In the Symbol Tree, expand Functions and click main. This jumps you to the program entry logic.
Selecting main in the Symbol Tree.
-
Read the Decompiler panel on the right. Ghidra has turned the machine code back into readable pseudo C. This is not the original source code. When a program is compiled, the developer's variable names, function names, comments, and much of the type information are thrown away, because the machine does not need them to run, and Ghidra cannot bring back what was deleted. What it gives you is a faithful reconstruction of what the code does, not the text the developer wrote. That is why you see names like local_58, param_1, and undefined8, and why loops and branches may come back in a different shape than the original. That doesn't matter so much here, because the behavior survives compilation even when the names and formatting do not.
The decompiled main function. It calls unprotect() on an array named ENC_TOKEN, then authenticates. That call is where the secret gets rebuilt.
This one view tells the whole story. main calls a function called unprotect and hands it an array named ENC_TOKEN. That is the obfuscated secret. To recover it, we need two things: the encoded bytes, and whatever key unprotect uses to decode them.
Recovering the secret
-
Double click ENC_TOKEN in the Decompiler to jump to it in the Listing. Ghidra shows the raw encoded bytes, all thirty two of them.
The ENC_TOKEN array in the Listing. These are the encoded bytes, stored in the binary but meaningless on their own.
-
Do the same for XOR_KEY (directly under ENC_TOKEN). The decode routine is a simple repeating XOR, and the key is only four bytes: 0x13, 0x37, 0x42, 0xa5.
The four byte XOR key, sitting right next to the encoded data.
-
XOR the encoded bytes against the repeating key and the plaintext falls out. You can do this in Ghidra's own Python console, or with a one line script in your terminal:
$ python3 -c "
enc=[0x60,0x5c,0x1d,0xc9,0x7a,0x41,0x27,0xfa,0x24,0x51,0x71,0xe7,0x6b,0x0e,0x13,0xd5,
0x49,0x05,0x2f,0xeb,0x27,0x5c,0x0e,0xd3,0x41,0x0f,0x36,0xfc,0x70,0x60,0x73,0xc0]
k=[0x13,0x37,0x42,0xa5]
print(''.join(chr(b^k[i%4]) for i,b in enumerate(enc)))"
The recovered token. What strings could not see, Ghidra handed us in full.
Out comes a live service token, sk_live_7f3Bx9QpZ2mN4kLvR8tYcW1e. The homegrown obfuscation bought the developer nothing, because the program has to decode the token to use it, and everything it needs to do that decoding is sitting right there in the binary.
Why this matters on an engagement
Weak custom encoding like this is common, especially in mobile apps, desktop agents, and firmware. A developer XORs a secret, assumes it is safe because it no longer shows up in strings, and ships it. But obfuscation is not encryption. If the program can reverse it at runtime, so can you. Recovering that token might hand you access to a payment API, an internal service, or a customer account. The lesson for the report writes itself: hiding a secret is not the same as protecting it, and the only real fix is to keep secrets out of the binary entirely.
Wrapping up
Two binaries, two lessons. The first showed that a single command can expose credentials a developer thought were buried. The second showed what to do when that command finds nothing, and why a decompiler like Ghidra is worth the time to learn.
The bigger takeaway is method. Start simple with strings and the quick wins. When the easy tool runs dry, do not assume the file is clean, assume the secret is hidden better, and reach for a tool that reads the code itself. That habit, moving from the shallow check to the deep one, is what separates a quick look from real analysis.
From here, try changing the obfuscation in tokenvault, rebuild it, and reverse your own version. The fastest way to get comfortable in Ghidra is to keep opening things and asking what they really do.
Never miss a tutorial
Related Tutorials
How to Hide Data in Plain Sight: Steganography Us…
Learn to hide and retrieve data inside images and other files using steghide. T…
Read Tutorial
How to Recover Deleted Files Using Foremost
Learn how to recover deleted files using Foremost, a powerful forensic tool for…
Read Tutorial
Discussion (0 comments)
Join the Discussion
No comments yet
Be the first to share your thoughts on this tutorial!