November 2, 2026
I Compiled a Beans Program. Then I Ripped It Apart With Ghidra.
By K3ysTr0K3R

By Jared Brits
3 min read
People ask how malware analysts figure out what a virus does before it nukes a system. Or how exploit developers find bugs in software they don't have the source code for.
The answer is reverse engineering.
Today, we're going to take a stupidly simple C program, compile it, and tear it down to the bare metal.
Let's get into it.
1. The Human Layer
Everything starts readable. I made three strings (the "beans" messages) and told the computer to print them. To a human, this makes perfect sense. To the CPU, this is completely blind. It needs to be translated.
2. The Translation
I type a command. I tell GCC to take my text file and turn it into an executable binary. No errors, no warnings. The translation is done. Now, the computer has something it can run.
3. The Messy Reality
I opened the program up to see what's inside after compiling it. And look at this mess. The program works, but the terminal vomits out random garbage, ELF headers, and memory addresses before it spits out the text.
Why? Because when you strip away the human-readable code, you're left with raw structural data. You can run a program successfully and still have the output look like an absolute mess. Welcome to the reality of binaries.
4. Finding the Needles
Because we can't read 1s and 0s with our eyeballs, we use a tool called strings. It digs through the binary and rips out anything that looks like text.
There are our messages โ "why did you eat my beans." But look closely. You see libc.so.6 and GLIBC. This proves the program isn't self-contained. It's relying on external libraries to function.
5. Enter Ghidra
Now we're playing for keeps. This is Ghidra. It's a reverse engineering tool built by the NSA. We are taking a finished, compiled program and forcing it to reveal its blueprints.
Right now, it sees "undefined main." It found the function, but it hasn't fully broken it down yet.
6. Mapping the Skeleton
Ghidra maps out the functions. You see main, frame_dummy, and _start.
Finding _start is important. That is the entry point. That is the exact address where the operating system hands control over to the CPU. It's the front door of the software.
7. The Decompiled Code (Pseudocode)
Here is where it gets crazy. Ghidra takes the raw machine code and translates it back into C code (pseudocode).
It's not perfect. Instead of seeing my char msg[] arrays from the first image, Ghidra sees a giant block of memory it calls local_48. The compiler merged those strings into memory to save space, but because I didn't use debug symbols when I compiled it, Ghidra has to guess how that memory is organized. It doesn't know my original variable names.
But look at line 7. We see builtin_strncpy. Even without the original source, Ghidra figured out exactly what tool was being used to copy the strings. It reverse-engineered the logic. You can literally read the original messages sitting right there in the decompiled output.
8. The Raw Machinery
Finally, we hit the bottom. Assembly language. The absolute raw machinery. This is what the CPU reads.
You see instructions like MOV (move data) and LEA (load address). On the right, you see hex codes. To you, it looks like gibberish. But the computer is literally reading letters packed into memory.
Look at the very first MOV instruction: 0x6e61656220796d20.
That's not random. In ASCII hex, that spells "my bean". The very next line, 0x3f3f3f3f3f73, spells "s?????" (because 3f is the ASCII code for a question mark).
This raw assembly is exactly what Ghidra was looking at to build the pseudocode we saw in the last step. You are looking at the engine block.
My take on this
If you want to learn this, stop reading about it. Go download Ghidra, write a stupid program, compile it, and start breaking it. That's the only way it clicks.