Android Software Reverse Engineering & Decompilation

Android ARM64 RE Lab: Reverse Engineering a Native Library Function Step-by-Step

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Android ARM64 Native Library Reverse Engineering

Reverse engineering Android native libraries, particularly those compiled for ARM64 architecture, is a crucial skill for security researchers, malware analysts, and even developers debugging complex issues. Unlike Java/Kotlin bytecode, native code compiled from C/C++ directly interacts with the underlying hardware, making its analysis more challenging but also more revealing. This guide will walk you through setting up a basic reverse engineering lab and analyzing a simple function in an ARM64 native library step-by-step.

Understanding ARM64 assembly is fundamental. ARM64 (AArch64) is a 64-bit instruction set architecture used by modern Android devices. Its register set, calling conventions, and instruction formats differ significantly from its 32-bit predecessor (ARMv7-A) and other architectures like x86.

Setting Up Your Reverse Engineering Lab

Before diving into the code, ensure you have the necessary tools:

  • Android Device/Emulator: An ARM64-based Android device or an emulator (e.g., Android Studio’s AVD) running an ARM64 system image.
  • Android Debug Bridge (ADB): For interacting with your Android device.
  • Disassembler/Decompiler: IDA Pro (commercial) or Ghidra (free, open-source) are excellent choices. We will reference general concepts applicable to both.
  • Android NDK: To compile our sample native library.
  • Text Editor/IDE: For writing our sample C code and build scripts.

Creating a Simple Native Library Target

Let’s create a minimal C function that adds two integers. This will serve as our target for reverse engineering.

First, create a directory for your project, e.g., arm64_re_lab.

simple_native.c:

#include  // Required for JNI_OnLoad, etc. But not strictly for this example.void sum_two_numbers(int a, int b, int* result) {    *result = a + b;}

Next, we need a build system. For simplicity, we’ll use a basic `Android.mk` with NDK.

Android.mk:

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := simple_nativeLOCAL_SRC_FILES := simple_native.cLOCAL_CFLAGS    := -Wall -Wextra # Good practice for warningsLOCAL_CPPFLAGS  := -std=c99   # C standard for our source fileinclude $(BUILD_SHARED_LIBRARY)

Application.mk: (Ensure ARM64 build)

APP_ABI := arm64-v8aAPP_PLATFORM := android-21 # Or higher

Navigate to your project directory (arm64_re_lab) in your terminal and compile using NDK:

/path/to/android-ndk/ndk-build

This will create libs/arm64-v8a/libsimple_native.so.

Deploying and Loading the Library

Push your compiled library to your Android device:

adb push libs/arm64-v8a/libsimple_native.so /data/local/tmp/

Now, open your disassembler (IDA Pro or Ghidra). Load the libsimple_native.so file. Ensure you select the correct processor architecture (ARM64 Little-Endian).

Identifying and Navigating to the Target Function

After loading, the disassembler will analyze the binary. Look for the sum_two_numbers function. In IDA Pro, you can use the ‘Functions’ window or press Ctrl+F to search for the function name. In Ghidra, use the ‘Symbol Tree’ or search for ‘Labels’.

Once you locate sum_two_numbers, double-click to navigate to its disassembly view.

ARM64 Assembly Fundamentals: Registers and Calling Convention

Before analyzing, a quick primer on relevant ARM64 concepts:

  • General Purpose Registers (X0-X30): 64-bit registers. W0-W30 are their 32-bit counterparts.
  • SP (Stack Pointer): Points to the current top of the stack.
  • LR (Link Register, X30): Stores the return address for function calls.
  • FP (Frame Pointer, X29): Used to manage stack frames, often alongside LR.
  • Calling Convention (AAPCS64):
    • First 8 arguments (integers/pointers) are passed in X0-X7 (or W0-W7 for 32-bit).
    • Excess arguments are pushed onto the stack.
    • Return value (if any) is placed in X0 (or W0).

Step-by-Step Analysis of sum_two_numbers

Let’s examine the disassembled code for sum_two_numbers. The exact output might vary slightly based on compiler optimizations and NDK versions, but the core logic will be similar.

Our C function: void sum_two_numbers(int a, int b, int* result)

  • a will be in W0 (32-bit part of X0).
  • b will be in W1.
  • result (pointer) will be in X2.

Expected ARM64 Disassembly (simplified example):

<code class=

Android Mobile Specs & Compare Directory

Are you researching mobile hardware properties, processor SoCs, GPU chipsets, or RAM configurations? Access our complete specs catalog to compare up to 5 devices side-by-side!

Compare Devices Specs →
Google AdSense Inline Placement - Content Footer banner