Android Software Reverse Engineering & Decompilation

Exploiting ARM64 NDK Binaries: Crafting Your First ROP Chain on Android

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction

Android’s Native Development Kit (NDK) allows developers to implement parts of their applications using native code languages like C and C++. While this offers performance benefits, it also introduces a class of vulnerabilities common in C/C++ applications, such as buffer overflows. On ARM64 architectures, exploiting these vulnerabilities often leads to crafting Return-Oriented Programming (ROP) chains to bypass memory protection mechanisms like No-Execute (NX) or eXecute-Never (XN).

This expert-level guide will walk you through the process of analyzing a vulnerable ARM64 NDK binary, identifying exploitable conditions, and meticulously crafting your first ROP chain on an Android device. We will delve into ARM64 assembly, gadget hunting, and the practical steps needed to achieve arbitrary code execution.

Prerequisites

  • A Linux-based host machine (Ubuntu, Kali, etc.)
  • An Android device or emulator with root access (e.g., AVD with root, Genymotion, rooted physical device)
  • Android SDK and Platform-Tools (for adb)
  • Android NDK toolchain
  • Static analysis tools: Ghidra or IDA Pro
  • Familiarity with basic ARM64 assembly and C/C++ programming
  • Basic understanding of memory exploitation concepts (buffer overflows, stack layout)

Understanding the Target

Android NDK Binaries

NDK binaries are compiled shared libraries (.so files) or executables that run directly on the Android operating system. They are often linked against libc.so, which provides standard C library functions, and other system libraries. These binaries typically execute in the context of an app’s process or as standalone daemons, and vulnerabilities within them can lead to privilege escalation or remote code execution.

ARM64 Architecture Fundamentals for Exploitation

The ARM64 (AArch64) architecture uses 31 general-purpose 64-bit registers (X0-X30), a stack pointer (SP), and a program counter (PC, often aliased with X30 for Link Register – LR). Key aspects for exploitation include:

  • Calling Convention (AAPCS64): Arguments for function calls are passed in X0-X7. Return values are in X0.
  • Link Register (LR/X30): Holds the return address after a branch with link (BL) instruction. Overwriting LR is crucial for redirecting control flow.
  • Stack Pointer (SP): Points to the top of the stack.
  • Instruction Set: Familiarity with instructions like MOV (move), LDR (load register), STR (store register), BL (branch with link), BR (branch to register), RET (return from function – effectively BR X30).

Setting Up the Environment

ADB and Device Access

Ensure adb is configured and your Android device is accessible. Root access is highly recommended for easier payload delivery and debugging.

adb devices -l
adb root
adb shell

Static Analysis Tools

Use Ghidra or IDA Pro to analyze the ARM64 binary. These tools are essential for disassembling the code, identifying potential vulnerabilities, and finding ROP gadgets. Ensure you have the correct ARM64 processor module loaded.

Identifying a Vulnerability

Our goal is to find a buffer overflow. A common pattern involves functions that copy user-controlled input into a fixed-size buffer without proper bounds checking. Consider the following vulnerable C code:

// vulnerable_app.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void vulnerable_function(char *input) {
    char buffer[64]; // Fixed-size buffer
    strcpy(buffer, input); // No bounds checking
    printf("Processed: %sn", buffer);
}

int main(int argc, char **argv) {
    if (argc < 2) {
        printf("Usage: %s <string>n", argv[0]);
        return 1;
    }
    vulnerable_function(argv[1]);
    return 0;
}

Compile this using the NDK toolchain for ARM64:

aarch64-linux-android29-clang vulnerable_app.c -o vulnerable_app -pie -fPIE -static-pie

Push it to your Android device:

adb push vulnerable_app /data/local/tmp/
adb shell chmod +x /data/local/tmp/vulnerable_app

When vulnerable_function is called, the strcpy will write past the end of buffer if input is longer than 64 bytes, eventually overwriting the saved Link Register (LR) on the stack.

Developing the Exploit

Controlling PC and SP

The primary goal of a buffer overflow exploit is to overwrite the saved LR on the stack. When vulnerable_function returns, it will attempt to return to the address stored in the overwritten LR, effectively letting us control the Program Counter (PC). We then use this control to redirect execution to our ROP chain.

Return-Oriented Programming (ROP) Fundamentals

ROP allows an attacker to execute arbitrary code in the presence of NX/XN. Instead of injecting shellcode, an attacker chains together small sequences of legitimate instructions (called

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