Berkner Tech

Reverse Engineering Firmware With Ghidra

Firmware reverse engineering pipeline with Ghidra, from binary load to decompiled pseudocode

Once the firmware is extracted, the binaries inside still have to be understood. Ghidra, the open reverse-engineering suite the NSA released, turns raw machine code into readable pseudocode for free. It is how you find the hardcoded password, the hidden command, or the broken crypto that a vendor assumed no one would ever look at. Here is the workflow I use.

Why Decompilation Changes the Game

Vendors often treat compiled firmware as if it were secret. It is not. A decompiler reconstructs C-like logic from machine code, so a binary you cannot read in a hex editor becomes a function you can follow line by line. Stripping symbols slows this down; it does not stop it. Assume any logic in the firmware is readable by a motivated analyst, because it is.

Step 1: Triage With strings First

Before loading anything, pull the printable strings. Five seconds here often points straight at the code worth decompiling:

strings -n 8 fw.bin | grep -iE 'http|key|pass|admin'
Example output
https://api.vendor-cloud.example/v1/telemetry
admin
Login failed for user %s
AES key load failed
/etc/config/secret.conf

Hardcoded URLs, an admin user, and a reference to an AES key are all leads. Each one has code behind it that you can now jump to directly.

Step 2: Load It Into Ghidra

For a Linux-based device you can import an executable straight from the extracted filesystem. For bare-metal firmware, use the headless analyzer and tell it the exact processor:

Anatomy of a Ghidra analyzeHeadless import command for firmware reverse engineering
analyzeHeadless ./proj MyProj -import fw.bin \
  -processor ARM:LE:32:Cortex
Example output
INFO  Opening project: /home/lab/proj/MyProj
INFO  IMPORTING: fw.bin
INFO  REPORT: Analysis succeeded for file: fw.bin
INFO  ANALYZING all memory and code: fw.bin (ARM:LE:32:Cortex)
INFO  Packed database into project file

Getting the processor and the load address right is the whole game for bare-metal images. Set the wrong base address and every string reference and function pointer lands in empty space.

Where to Start Reading

A firmware image can hold thousands of functions. You do not read them in order; you pivot in from one of three anchors:

Three places to start reverse engineering firmware in Ghidra: strings, cross-references, and crypto constants

Step 3: Read the Decompiler

Follow a string like "Login failed" to the function that prints it, and the decompiler shows you the logic in near-C. This is where the findings live:

Example output
undefined4 check_login(char *user,char *pass)
{
  int iVar1;
  iVar1 = strcmp(pass,"SuperSecret123");
  if (iVar1 == 0) {
    return 1;
  }
  return 0;
}

A password compared against a string literal baked into the firmware is a backdoor that ships in every unit. This is one of the highest-impact findings a firmware review produces, and the decompiler hands it to you in plain sight.

Defending Your Firmware

You cannot make a binary unreadable, so do not rely on obscurity. Remove debug backdoors and test credentials before release, keep real secrets in a secure element rather than in code, and have someone reverse your own firmware before an attacker does. The goal is that when the binary is read, and it will be, there is nothing embarrassing inside.

Where This Fits

Firmware reverse engineering is where a product security assessment finds the issues automated tools miss: logic bugs, backdoors, and broken cryptography. Reading the binary the way an attacker would is the kind of work we do at Berkner Tech.


References and Further Reading