Saturday, November 7, 2026
HomeAPK Modding & Reverse Engineering BasicsUsing Ghidra Decompiler to Analyze Native Android Binaries

Using Ghidra Decompiler to Analyze Native Android Binaries

Ghidra is a free reverse engineering tool that turns machine code into a readable form for analysis. It was built for analysing native binaries across many processor architectures and file formats. For Android work it helps analysts read .so libraries that Java tools cannot explain on their own. A Ghidra project holds a binary and the analysis results for that binary in one place. The tool disassembles machine code into assembly instructions for a chosen architecture. It then decompiles functions into a C-like view that reads faster than assembly alone. That pair of views supports both close reading and fast overview work for a library. For the native background behind this tool see reversing native shared libraries.

Ghidra analysis starts with importing a binary file into a new project for study work. The analyst chooses the processor architecture that matches the library file for the target device. ARM is the usual choice for Android phones in current use around the world. The analyst then runs auto analysis to let Ghidra find functions and strings and data. Auto analysis can take time for a large library with many functions inside it. The analyst should let it finish before judging the results for a feature in the library. A rushed judgement on partial analysis can miss functions that appear later in the process.

Setting Up A Ghidra Project

Setting up a Ghidra project starts with a clean folder for the binary and for project files. Copy the .so file from the unpacked APK into that folder before importing it into Ghidra. Name the project with the app name and the library name and the architecture for clarity. That naming helps a peer find the project later without asking the original analyst. Import the binary and confirm that Ghidra detected the correct format and architecture. A wrong architecture detection produces nonsense disassembly that wastes time during analysis. Correct the architecture manually when auto detection fails for an unusual library file.

Project organisation matters when a library has many functions and many strings to study. Create a folder for notes that link Ghidra findings to Java features in the app. Save screenshots of key functions with a date and a short description for later review. Export decompiled functions of interest into a text file for study outside Ghidra. That export helps a peer review a function without opening the full project. It also helps you compare a function across versions of the same library file. Good organisation turns a large project into a manageable set of findings for a report.

Reading Disassembly In Ghidra

Reading disassembly in Ghidra starts with the function list that auto analysis builds for the binary. The function list shows function names when symbols survive stripping in the library file. It shows generated names like FUN_00012345 when symbols were stripped during the build. Analysts should start with exported functions because other code calls those functions from outside. They should also start with functions that reference a string linked to a feature under study. That starting point narrows a large function list to a small set for close reading. Open a function and read its instructions from entry to exit in order for clarity.

Ghidra disassembly view shows addresses and instructions and comments in columns for easy reading. An address tells you where an instruction lives in the binary file for reference. An instruction tells you the operation and its operands for that step in the function. A comment may show a string reference or a data reference that the instruction uses. Analysts should follow branches to labels to understand decisions in a function flow. They should note the condition for each branch in plain words in their notes. That note turns assembly branches into a readable decision list for a function.

Using The Decompiler View

The decompiler view in Ghidra rebuilds a function into C-like code that reads faster than assembly. It shows variables with generated names and types inferred from how the code uses them. It shows loops and branches as structured code rather than as jumps to labels. That structured view helps an analyst grasp a large function in minutes rather than hours. It can also mislead when type inference guesses a wrong type for a variable in code. Analysts should compare the decompiler view with disassembly when a result looks odd. That comparison reveals whether a decompiler guess caused the odd result in the function.

Decompiler variables need renaming for long term readability during a project in Ghidra. A variable named uVar1 gives no hint about its role in the function logic. Renaming it to a guessed role like user_id helps later reading of the same function. Update the rename when new evidence confirms or corrects the guessed role for that variable. Ghidra saves renames in the project so they persist across sessions for the analyst. That persistence turns a first reading into a growing map of the function over time. A map with good names is far easier to share with a peer for review.

Strings Xrefs And Call Graphs

Strings in Ghidra link code to data that reveals function purpose during analysis work. Open the string list and search for a term linked to a feature under study in the app. Each string shows cross references to code that uses that string in a function. Follow a cross reference to see how a function uses the string in its logic. That link often points straight to the function that implements a feature for the app. Analysts should use string cross references early in a native project for speed. That technique saves hours compared with reading functions in arbitrary order during a session.

Call graphs in Ghidra show how functions call each other across a whole library file. A call graph shows a function as a node and a call as a link to another node. It reveals hub functions that many features share during app use for a library. It also reveals leaf functions that perform a single task without calling others. Analysts should study hub functions with care because a change there affects many features. They should also study leaf functions for key tasks like encryption or hashing in a library. A graph view complements linear reading by showing the whole structure at a glance.

ghidraRun
# Import libsample.so into a new Ghidra project
# Run auto analysis for ARM architecture
# Open the decompiler view for a target function

Scripts And Automation In Ghidra

Ghidra scripts automate repetitive tasks that would take too long by hand during analysis. A script can rename functions based on a pattern in their names or strings for a library. A script can list all functions that call a sensitive system function in the binary. A script can export decompiled code for all functions into text files for study. Analysts should start with built-in scripts before writing a custom script for a project. Built-in scripts cover many common tasks and are tested by the Ghidra community. Custom scripts should be saved with a clear name and a comment that states their purpose. That documentation helps a peer use the script without asking the original author.

Automation in Ghidra also helps with version comparison work for a library file over time. A script can export function lists for two versions of the same library for comparison. A diff of those lists shows new functions and removed functions between versions. That diff tells a story about what changed in an update for the app. Analysts should verify a diff finding by opening the changed function in Ghidra. That verification stops a script error from becoming a false finding in a report. It also builds trust in automation as a helper rather than as a replacement for judgement.

Learning Ghidra In Stages

Learning Ghidra in stages starts with a small library that has clear symbols for a first project. Import a small .so file and run auto analysis to see functions and strings in the tool. Open a function with a clear name and read its decompiler view first for an overview. Then open its disassembly view to see how the decompiler reached that overview for the function. That pair of views teaches the relationship between assembly and decompiled code in Ghidra. Repeat that loop for a few functions before tackling a large stripped library file.

The next stage in learning Ghidra involves a stripped library with generated function names for study. Use string cross references to find functions linked to a feature in the app for study. Rename functions as you learn their roles during reading of the library code. Build a map of renamed functions in project notes for later sessions with the same library. That map turns a stripped library into a readable project over time for the analyst. It also gives a peer a starting point when they join the project later for review.

Advanced Ghidra learning involves scripts and custom analysis for repetitive tasks in a large library. Start with built-in scripts that list functions or export decompiled code for study outside the tool. Study a built-in script to learn the Ghidra API for automation in a safe way. Then write a small custom script that renames functions based on a pattern for a project. Test that script on a small library before running it on a large library file for safety. That staged approach prevents a script error from damaging a large project analysis.

Practice with Ghidra should include version comparison for a library that changes across app updates. Export function lists for two versions of the same library file for comparison work. Diff those lists to find new and removed functions between the two versions of the app. Open each changed function in Ghidra to verify the diff result before reporting it to a developer. That verification habit keeps automation honest and builds trust in script results for a team. It also teaches how app updates change native code over time for a library.

Readers who want a related topic can explore modifying Smali code logic next in this series.

Conclusion

Ghidra gives analysts a powerful way to read native Android binaries with disassembly and decompilation. It supports ARM libraries that dominate Android phones and many other architectures as well. A project setup with correct architecture detection prevents wasted time on nonsense output. Reading disassembly and decompiler views together gives both accuracy and speed for a function. Strings and cross references and call graphs narrow a large library to the functions that matter. Scripts automate repetitive work without replacing human judgement in analysis. That balanced method turns native code into a readable story for a security review. For a broader native study context see reversing native shared libraries again in this series.

The steps above follow the approach described in the official developer documentation for Android applications and in widely used testing guidance for mobile software.

RELATED ARTICLES

Most Popular