Android App Penetration Testing & Frida Hooks

Frida Scripting for Android Shared Memory Forensics and Data Extraction

Google AdSense Native Placement - Horizontal Top-Post banner

Introduction to Android Shared Memory and Forensics

Android applications often leverage shared memory mechanisms for inter-process communication (IPC), efficient data sharing, or high-performance graphics. These regions can contain sensitive information, temporary data, or even user credentials. For penetration testers and forensic analysts, understanding and extracting data from these shared memory regions is a critical skill. While traditional debugging tools might offer limited visibility, Frida – a dynamic instrumentation toolkit – provides unparalleled capabilities to inspect and manipulate memory at runtime, making it an ideal choice for Android shared memory forensics.

Shared memory on Android manifests in several forms, including Ashmem (Android Shared Memory), memory-mapped files, and ION memory. Regardless of the underlying mechanism, these regions are mapped into a process’s virtual address space, making them accessible via memory manipulation techniques. Our goal is to use Frida to identify these regions, understand their purpose, and extract their contents.

Prerequisites and Setup

Before diving into Frida scripts, ensure you have the following:

  • A rooted Android device or emulator.
  • Frida server running on the Android device (e.g., frida-server -l 0.0.0.0:27042).
  • Frida tools installed on your host machine (pip install frida-tools).
  • Basic understanding of JavaScript for writing Frida scripts.
  • A target Android application for analysis.

To connect to your device and spawn a process, you’ll typically use commands like:

adb forward tcp:27042 tcp:27042frida -U -f com.example.targetapp -l your_script.js --no-pause

The -U flag connects to a USB device, -f spawns the app, and -l loads your Frida script. --no-pause ensures the app starts immediately after Frida injects.

Identifying Shared Memory Regions with Frida

The first step in shared memory forensics is to identify the relevant memory regions. In Linux-based systems like Android, the /proc/<pid>/maps file provides a detailed map of a process’s virtual memory layout. Frida allows us to programmatically access and filter this information through its JavaScript API.

We can use Process.enumerateRangesSync() to list all memory ranges within a target process. Filtering these ranges for specific properties (e.g., shared, writable, specific names) helps narrow down our search.

Frida Script: Enumerate Memory Maps

Here’s a basic Frida script to enumerate memory ranges and log their details. We’ll pay close attention to ranges often associated with shared memory, such as those with [anon:...] labels or common shared memory patterns.

Java.perform(function() {    console.log("[*] Enumerating memory ranges...");    Process.enumerateRangesSync({        protection: 'rw-', // Read/Write regions        coalesce: false    }).forEach(function(range) {        if (range.file) {            // Memory-mapped files often contain useful data            if (range.file.path.indexOf("ashmem") !== -1 || range.file.path.indexOf("memfd") !== -1) {                console.log("[ASHMEM/MEMFD] Address: " + range.base + ", Size: " + range.size + ", Path: " + range.file.path + ", Protection: " + range.protection);            } else if (range.file.path.indexOf(".so") === -1 && range.file.path.indexOf(".jar") === -1 && range.file.path.indexOf(".apk") === -1) {                // Log other potentially interesting mapped files                // console.log("[MMAP FILE] Address: " + range.base + ", Size: " + range.size + ", Path: " + range.file.path + ", Protection: " + range.protection);            }        } else {            // Anonymous memory regions            if (range.base.isNull()) return; // Skip null base addresses            const module = Process.getModuleByAddress(range.base);            if (!module) {                // Look for common shared memory patterns in anonymous regions                // e.g., Dalvik LinearAlloc, ION memory, or other IPC mechanisms                const description = Memory.scanSync(range.base, range.size, "414e4f4e"); // Example: Look for "ANON" string                if (description.length > 0 || range.size > 0x100000) { // Or large anonymous regions                    console.log("[ANON MEMORY] Address: " + range.base + ", Size: " + range.size + ", Protection: " + range.protection);                }            }        }    });    console.log("[*] Memory enumeration complete.");});

This script filters for read/write memory regions. It specifically looks for Ashmem and Memfd paths, and also logs large anonymous memory regions which are often used for shared buffers or dynamic allocations.

Dumping Data from Shared Memory Regions

Once you’ve identified a promising shared memory region (e.g., from the output of the previous script), the next step is to dump its contents. Frida’s Memory.readByteArray() and Memory.readUtf8String() are invaluable for this task.

Frida Script: Dump Specific Memory Region

Let’s assume the previous script identified a region at address 0xdeadbeef with a size of 0x1000 bytes that looks interesting. We can create a new script to dump its contents.

Java.perform(function() {    const targetAddress = ptr("0x7c73b00000"); // Replace with the actual address found    const targetSize = 0x1000; // Replace with the actual size    try {        const buffer = Memory.readByteArray(targetAddress, targetSize);        console.log("[*] Dumping memory from " + targetAddress + " with size " + targetSize + "...");        // Send the buffer back to the host        send({            type: 'dump',            address: targetAddress.toString(),            size: targetSize        }, buffer);        console.log("[*] Memory dump sent.");    } catch (e) {        console.error("Error dumping memory: " + e.message);    }    // Optionally, read as string if you suspect text data    // try {    //     const contentString = Memory.readUtf8String(targetAddress, targetSize);    //     console.log("[+] Content as UTF-8 string: " + contentString);    // } catch (e) {    //     console.warn("Could not read as UTF-8 string: " + e.message);    // }});

To receive the dumped data on your host machine, you need a Python script that listens for messages from Frida:

import fridaimport sysdef on_message(message, data):    if message['type'] == 'send':        payload = message['payload']        if payload['type'] == 'dump':            address = payload['address']            size = payload['size']            print(f"[+] Received dump from {address} with size {size}")            # Save to a file            with open(f"dump_{address}_{size}.bin", "wb") as f:                f.write(data)            print(f"[+] Saved to dump_{address}_{size}.bin")        else:            print(f"[FRIDA] {message}")    else:        print(f"[FRIDA] {message}")device = frida.get_usb_device(timeout=10)pid = device.spawn(["com.example.targetapp"]) # Replace with your app's package nameprint(f"[+] Spawning {pid}")session = device.attach(pid)script = session.create_script(open("dump_script.js").read())script.on('message', on_message)script.load()device.resume(pid)sys.stdin.read()

Save the first script as dump_script.js and the second as dump_receiver.py. Run the Python script: python3 dump_receiver.py. It will spawn the app, inject the Frida script, and save the dumped memory to a file.

Advanced Shared Memory Forensics

Analyzing Data Structures

Raw memory dumps are often unreadable without context. If you suspect specific data structures are stored in shared memory, you’ll need to reverse engineer the application to understand their layout. This might involve:

  • Static Analysis: Disassembling the native libraries (e.g., .so files) to find where shared memory is allocated and how data is written to/read from it. Tools like Ghidra or IDA Pro are essential here.
  • Dynamic Analysis with Hooks: Hooking functions like shm_open, mmap, ashmem_create_region, or custom memory allocation routines can reveal the size, protection, and initial contents of shared memory regions as they are created. This helps you understand what is being put into shared memory and when.
Java.perform(function() {    const libc = Module.findExportByName(null, 'mmap');    if (libc) {        Interceptor.attach(libc, {            onEnter: function(args) {                this.fd = args[4].toInt32();                this.prot = args[2].toInt32();                this.flags = args[3].toInt32();                // Filter for shared mappings                if (this.flags & MAP_SHARED) {                    console.log("[+] mmap called with MAP_SHARED:");                    console.log("  Address (hint): " + args[0]);                    console.log("  Length: " + args[1]);                    console.log("  Protection: " + args[2]);                    console.log("  Flags: " + args[3]);                    console.log("  File Descriptor: " + this.fd);                }            },            onLeave: function(retval) {                if (this.flags & MAP_SHARED) {                    console.log("  Mapped address: " + retval);                }            }        });    }    // Similar hooks can be set for ashmem_create_region, shm_open, etc.});

Dealing with Encrypted Data

If the data within shared memory is encrypted, you’ll need to identify the encryption/decryption routines. Frida can be used to hook these functions, dump the plaintext data before encryption, or extract the encryption keys as they are used.

Conclusion

Frida is an incredibly powerful tool for Android shared memory forensics and data extraction. By combining memory enumeration, targeted dumping, and dynamic hooking, security researchers can gain deep insights into an application’s runtime behavior and uncover hidden data. The techniques outlined here provide a solid foundation for exploring and exploiting shared memory vulnerabilities, enhancing your Android penetration testing and forensic capabilities.

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