Android Software Reverse Engineering & Decompilation

From ARM64 Assembly to C++: Reconstructing Android Native Classes & Objects

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction: Unveiling Android Native Code

Reverse engineering Android native libraries (typically shared objects, .so files) is a critical skill for security researchers, vulnerability analysts, and those aiming to understand proprietary application logic. While tools like Ghidra and IDA Pro offer powerful decompilers, the output for C++ code often remains complex, especially when dealing with object-oriented constructs on ARM64. Reconstructing classes, virtual functions, and member variables from raw ARM64 assembly can be daunting, but with a systematic approach, it’s entirely feasible. This guide delves into the methodologies for translating ARM64 assembly patterns back into recognizable C++ classes and objects.

ARM64 Fundamentals for C++ Object Analysis

Before diving into reconstruction, it’s crucial to grasp key ARM64 concepts:

  • Registers: x0-x7 are used for passing arguments to functions and receiving return values. x0 is particularly important as it often holds the this pointer for member functions.
  • Stack Frame: Functions set up stack frames for local variables and saved registers. Understanding stack offsets is key to identifying local variables.
  • Calling Conventions: The AArch64 Procedure Call Standard dictates how arguments are passed and return values are handled. For C++ member functions, the first argument (x0) is implicitly the this pointer.
  • Memory Access: Instructions like LDR (Load Register) and STR (Store Register) with base-offset addressing are used to access member variables relative to the this pointer. For example, LDR x1, [x0, #0x8] loads the value at this + 0x8 into x1.

Identifying Class Instantiation and Constructors

The creation of a C++ object typically involves memory allocation followed by a constructor call. In ARM64 assembly, this often manifests as:

  1. A call to a memory allocation function (e.g., operator new, malloc, or a custom allocator) which returns the base address of the newly allocated memory in x0.
  2. Immediately following, a branch and link (BL) instruction to the constructor function, with the newly allocated memory address (still in x0) passed as the this pointer.

Consider this simplified assembly pattern:

ADRP X0, #some_size_address@PAGE ADDI X0, X0, #some_size_address@PAGEOFF LDR X0, [X0]           ; X0 now holds the size of the object BL operator_new       ; Allocate memory, address returned in X0 BL MyClass__MyClass ; Call constructor, X0 (newly allocated addr) is 'this'

Inside the constructor, you’ll observe initializations. These often involve storing default values or other object pointers at specific offsets from x0 (the `this` pointer). A tell-tale sign of a C++ class is the initialization of the Virtual Method Table (Vtable) pointer.

Reconstructing Virtual Method Tables (Vtables)

Vtables are fundamental to C++ polymorphism. An object with virtual functions will have a pointer to its Vtable as its first member (at offset 0). The constructor is responsible for setting this pointer.

Look for patterns like:

ADRP X1, #vtable_MyClass@PAGE ADDI X1, X1, #vtable_MyClass@PAGEOFF STR X1, [X0] ; Store vtable address at this + 0x0

Here, X0 is the this pointer, and X1 is the address of the Vtable. The STR X1, [X0] instruction places the Vtable pointer at the beginning of the object. Once you’ve identified the Vtable, you can analyze its contents (a series of function pointers) to deduce the virtual methods of the class.

Virtual function calls are characterized by indirect jumps through the Vtable. For example, calling a virtual method at index N (where each entry is 8 bytes on ARM64) would look like:

LDR X8, [X0]      ; Load vtable pointer from this + 0x0 LDR X9, [X8, #0x8 * N] ; Load function pointer from vtable at offset 0x8*N BLR X9            ; Branch to the virtual function

By analyzing these calls, you can map offsets within the Vtable to specific virtual methods and their potential parameters.

Inferring Member Variables and Layout

Member variables are accessed relative to the this pointer. Inside member functions, look for LDR and STR instructions that use x0 (or a register derived from x0) as the base address, with an immediate offset.

  • LDR X1, [X0, #0x4]: Loads a 4-byte value (e.g., an integer) from this + 0x4 into X1.
  • STR X2, [X0, #0x10]: Stores a value from X2 to this + 0x10.

By observing the offsets and the size of the data being loaded/stored (e.g., LDRB for byte, LDRH for half-word, LDRSW for signed word, LDR for double word/pointer), you can begin to reconstruct the class layout:

// Example Assembly Snippet for a method:int MyClass::getValue() { return this->m_value; } LDR X0, [X0, #0x4] ; Load m_value (at offset 0x4) from 'this' into X0 RET                ; Return X0

From this, we deduce that `m_value` is an integer at offset `0x4` within `MyClass`. Pay attention to structures: often, complex objects or strings will be at specific offsets, and their methods will then be called using that offset-derived pointer.

A Step-by-Step Reconstruction Example (Conceptual)

Scenario: Decompiling a hypothetical SensorManager class

Let’s imagine we’ve found a function creating an object and then calling its methods.

Step 1: Identify Object Creation and Constructor

We find a sequence:

; ... some setup BL __cxa_allocate_exception ; Returns allocated memory in X0 BL SensorManager__SensorManager ; Calls constructor, X0 is 'this' ...

This strongly suggests SensorManager is the class name, and SensorManager__SensorManager is its constructor.

Step 2: Analyze Constructor for Vtable and Initializations

Inside SensorManager__SensorManager, we find:

ADRP X1, #_ZTV13SensorManager@PAGE ADDI X1, X1, #_ZTV13SensorManager@PAGEOFF STR X1, [X0]           ; this->vptr = &_ZTV13SensorManager ADDI X2, XZR, #0x0      STR X2, [X0, #0x8]     ; this->m_sensorCount = 0 (int at 0x8) ADRP X1, #some_default_name@PAGE ADDI X1, X1, #some_default_name@PAGEOFF STR X1, [X0, #0x10]    ; this->m_name =

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