Take A Picture Of Your App Code: Android MRI Interpreter

SungHyoun Song (Financial Security Institute (FSI))

HITBSecConf2023 - Phuket · Day 2 · Track 2

Overview

In this insightful talk, SungHyoun Song from South Korea's Financial Security Institute (FSI) introduced the Android MRI Interpreter, a novel approach to analyzing Android applications and uncovering vulnerabilities. Drawing a compelling analogy between a doctor examining the human body and a security researcher analyzing an application, Song highlighted the shared challenges both professions face when trying to understand complex internal systems. Just as medical professionals evolved from invasive surgery to non-invasive imaging techniques like MRI and CT scans, Song proposes a similar paradigm shift for app analysis, moving away from traditional, often detected, methods.

Watch on YouTube

Visual summary for Take A Picture Of Your App Code: Android MRI Interpreter by SungHyoun Song
Visual summary for Take A Picture Of Your App Code: Android MRI Interpreter by SungHyoun Song

Key moments

  1. 0:00 Introduction: Doctor/Security Researcher Analogy
  2. 3:16 Introducing Android MRI Interpreter's Key Features
  3. 14:00 Modifying ART to Force Bytecode Execution
  4. 21:00 Developing Taint Analysis and Call Graphs
  5. 26:00 Bypassing RASP and Security Mitigations
  6. 36:00 Demo: Analyzing WhatsApp's Two-Step Verification
  7. 49:50 Audience Q&A: Encrypted String Tracing

Take A Picture Of Your App Code: Android MRI Interpreter

Speakers: SungHyoun Song, Researcher, Financial Security Institute (FSI)

Conference: HITB Phuket

YouTube: https://www.youtube.com/watch?v=3SPZtf1Rg04

Overview

In this insightful talk, SungHyoun Song from South Korea's Financial Security Institute (FSI) introduced the Android MRI Interpreter, a novel approach to analyzing Android applications and uncovering vulnerabilities. Drawing a compelling analogy between a doctor examining the human body and a security researcher analyzing an application, Song highlighted the shared challenges both professions face when trying to understand complex internal systems. Just as medical professionals evolved from invasive surgery to non-invasive imaging techniques like MRI and CT scans, Song proposes a similar paradigm shift for app analysis, moving away from traditional, often detected, methods.

The core of the Android MRI Interpreter is its ability to perform deep, OS-level code analysis without being hindered by Runtime Application Self-Protection (RASP) mechanisms. It achieves this by modifying the Android operating system itself, effectively creating an environment where an app's entire execution flow can be observed and controlled. This "non-invasive" technique allows researchers to obtain detailed snapshots of Dalvik instructions and register values, trace critical data flows through the application, and ultimately identify vulnerabilities, all while bypassing common security mitigations automatically.

This presentation demonstrated how the Android MRI Interpreter can be used to quickly and effectively analyze widely-used applications, specifically showcasing its power in identifying credential exposure within a popular messenger app. By providing unprecedented visibility into an app's runtime behavior, the tool offers a powerful new capability for security researchers and penetration testers, promising to significantly reduce the time and effort required for in-depth mobile application security analysis.

Background

▶ Watch: Introduction: Doctor/Security Researcher Analogy (0:00)

For security researchers, analyzing mobile applications to find vulnerabilities often presents similar challenges to a doctor performing surgery. Traditional methods frequently involve using tools that "cut into" the application's process, such as debuggers or hooking frameworks like Frida or IDA. While effective, these techniques are often detected by modern Runtime Application Self-Protection (RASP) mechanisms, which are designed to resist such invasive analysis. When RASP detects these tools, it can terminate the application, alter its behavior, or simply make analysis exceedingly difficult and time-consuming, much like a human body's defense mechanisms resisting a surgical knife, leading to shock and a long recovery period.

The speaker contrasted this with the evolution of medicine. Historically, doctors relied on invasive procedures to understand the human body. However, advancements in engineering led to the development of sophisticated medical devices like Magnetic Resonance Imaging (MRI) and Computed Tomography (CT) scans in the 1970s. These technologies allowed doctors to "take a picture" of specific organs and examine them in detail without any physical incision, significantly improving diagnostic capabilities and patient recovery.

The problem for security researchers, therefore, is the lack of an equivalent "MRI" for applications. Current methods are often caught in a cat-and-mouse game with RASP, requiring constant analysis and patching of application code to bypass protections. This iterative process is inefficient and delays vulnerability discovery. The need for a non-invasive, undetectable, and comprehensive analysis tool that operates at a fundamental level, akin to how medical imaging provides a detailed view of the body's internal structures, is precisely why the Android MRI Interpreter was conceived. It aims to provide deep insights into application behavior without triggering its self-defense mechanisms, thereby streamlining the process of identifying security flaws.

Key Findings

▶ Watch: Modifying ART to Force Bytecode Execution (14:00)

The Android MRI Interpreter introduces a paradigm shift in Android application analysis by providing a non-invasive, OS-level approach that inherently bypasses RASP. The key findings and contributions presented in this talk revolve around its three core features and the underlying methodology:

  1. OS-Level Code Analysis with Automated RASP Bypass: The most significant finding is the ability to analyze application code at the operating system level, effectively rendering RASP mechanisms irrelevant. Instead of analyzing and patching individual applications to bypass their defenses, the Android MRI Interpreter operates within a specially crafted OS environment that automatically circumvents these protections. This means researchers no longer need to spend weeks or even minutes manually bypassing RASP, as the analysis environment itself is designed to be undetectable by client-side security mitigations.
  1. Dynamic Output of Code Images (Snapshots): The tool can dynamically generate "code images" or snapshots of target functions in real-time. These images provide a highly detailed view of each executed instruction, including the function name, program counter (PC), Dalvik instruction, and the values stored in relevant registers. This capability is analogous to a medical MRI capturing cross-sectional images of specific organs, allowing for granular examination of code execution and data manipulation within a function. Researchers can use these snapshots to search for sensitive information, like credentials, within register values.
  1. Visualized Taint Analysis (Core Graph): The Android MRI Interpreter offers a powerful taint analysis feature that traces the flow of target data throughout the entire application. By monitoring register values during instruction execution, it can identify where data is first detected, moved, copied, or transferred to other functions. This data flow is then visualized as a core graph, providing an intuitive and effective way to understand the creation, propagation, and use of sensitive information. The graph highlights the initial detection point (red box) and subsequent usage points (green box), simplifying complex data flows, even in obfuscated applications, by removing unused code and merging duplicate function calls.

Beyond these features, the talk also revealed a novel approach to developing a highly resistant analysis environment. By minimizing changes to the userland OS and instead implementing critical functionalities as "backdoors" within the kernel, the Android MRI Interpreter makes it exceptionally difficult for RASP to detect its presence. This includes kernel-level modifications to spoof boot state information and create privilege escalation vectors, ensuring the analysis environment mimics a stock device while providing root capabilities for deep inspection.

Technical Deep Dive

▶ Watch: Developing Taint Analysis and Call Graphs (21:00)

The technical foundation of the Android MRI Interpreter lies in a profound understanding and modification of the Android Runtime (ART) and the underlying OS kernel.

Android Code Execution and ART

Android applications utilize several types of code:

  • DEX bytecode: The default format for Android app files.
  • AOT (Ahead-of-Time) compiled code: Native code pre-compiled during app installation for frequently used parts, improving performance.
  • JIT (Just-in-Time) compiled code: Native code compiled dynamically during runtime for "hot" methods, stored in memory as a code cache.

The execution flow within ART is complex. Functions transition through "cold," "warm," and "hot" states based on their execution count. AOT compilation happens at installation and overnight charging for warm methods, while JIT compilation occurs in real-time for hot methods. This results in a mixed execution environment where Dalvik bytecode and various forms of native code (AOT, JIT) can interoperate, calling each other. This intricate dance makes comprehensive analysis challenging, as native code, being optimized, is difficult to reverse engineer to its original form.

Forcing Bytecode Execution

The speaker's critical insight was that all execution, regardless of initial compilation state, eventually passes through two key parts within ART: one that invokes and executes compiled native code, and another that invokes and executes bytecode via the interpreter. To simplify analysis, the Android MRI Interpreter modifies ART's code invocation flow to prevent the execution of native code. When a request for native code execution is made, the modified ART ignores it and instead forces the execution of the corresponding bytecode through the interpreter. This ensures that all application code is executed solely by the Dalvik interpreter, centralizing the point of observation and control.

The MRI Interpreter's Mechanics

With all code execution funneled through the interpreter, the next step is to control and inspect the interpreter itself.

  • Threads and Managed Stack: When an Android app launches, it creates threads (main thread for UI, background threads for long-running operations). Each thread's execution context is tracked. The managed stack object within ART's runtime object stores a list of active threads.
  • Shadow Frame: Crucially, each thread object contains a managed stack, and within this, a sequence of shadow frames. Each shadow frame stores detailed information for one active function call, including:
  • The function's name.
  • The memory address of its code.
  • The program counter (PC), indicating the current instruction.
  • The values of registers that will be used during instruction execution.
  • Custom Interpreter: The Android MRI Interpreter replaces ART's default micro-interpreter and switch-interpreter. When a shadow frame is passed to this custom interpreter, it first identifies the function to be executed. Using the code pointer and PC from the shadow frame, it moves to the current instruction's address in memory.
  • Instruction and Register Inspection: The interpreter then checks the instruction's opcode (e.g., 0x01 for a move instruction) and its predefined format (e.g., k2x for move). This format dictates how registers are used. For instance, a move instruction in k2x format might move the value from virtual register B (at index 3) to virtual register A (at index 1). The MRI Interpreter can output all these details – Dalvik instruction, virtual register values, and even modify them in memory in real-time. A configuration file allows specifying target information for output or modification.

Taint Analysis and Core Graph Generation

Building on the interpreter's control, the MRI Interpreter implements taint analysis to trace data flow:

  • Instruction Monitoring: It monitors specific instructions related to data creation, movement, copying, and function calls.
  • Register Value Tracking: When such an instruction executes, the interpreter checks the source registers. If the string data at the memory address pointed to by a source register matches a predefined "target string," this information is recorded.
  • Contextual Graphing: To generate a comprehensive data flow graph, the MRI Interpreter leverages the shadow frame and managed stack again. When target data is detected, it prints the current shadow frame and all ancestral shadow frames stored in the managed stack. This provides context about not only the current function but also all preceding functions in the call stack.
  • Core Graph Visualization: By combining this information, the tool constructs a core graph. This graph visually represents the overall flow of the target data, indicating where it was first detected (red box) and where it was subsequently used, copied, or transferred (green box). The graph generation process optimizes for clarity by removing unused code and merging duplicate function calls, making it effective even against obfuscated applications.

RASP Bypass through Kernel Modification

The most innovative aspect of the Android MRI Interpreter's defensive implications is its approach to RASP bypass. Instead of modifying userland binaries or relying on detectable hooking frameworks, the solution is rooted deeply in the kernel:

  • Custom ROM with Stock Base: The speaker developed a custom Android OS by replacing only specific sub-modules within a stock ROM, rather than building from a modified AOSP source directly. This minimizes differences from a manufacturer-provided stock ROM, making it harder for RASP to detect.
  • Kernel-Level Backdoors: Most of the critical features for RASP bypass are implemented as "backdoors" directly within the kernel. This means that before sensitive data (like device state) is passed from the kernel to userland applications (where RASP checks occur), it can be modified by the kernel.
  • Spoofing Boot State: A prime example is spoofing the verified boot state. Android devices implement secure boot via a chain of trust, which checks signatures. If OEM unlock is enabled, the Android bootloader skips verification, leading to a "yellow," "red," or "orange" boot state display, which RASP and SafetyNet APIs can detect. The MRI Interpreter's kernel modifies the verified_boot_state information reported in proc/cmdline from orange to green. This is done by hardcoding stock cmdline information into the custom kernel, so the userland receives information identical to a stock, secure boot.
  • Privilege Escalation Vector: Another kernel-level backdoor involves creating a simple privilege escalation. By patching the kernel code handling the execve system call (specifically the exe_cb function), the MRI Interpreter can detect when the sh (shell) command is executed. Upon detection, it modifies the kernel to assign root credentials (UID and GID values of 0) to the calling process. This provides a lightweight, hard-to-detect root shell that RASP mechanisms would struggle to identify, as it's an intrinsic part of the kernel's behavior rather than an external exploit.

This comprehensive approach, deeply embedded within the OS kernel, makes the Android MRI Interpreter a formidable tool for app analysis, designed to be resilient against even sophisticated RASP implementations.

Demo / Proof of Concept

▶ Watch: Demo: Analyzing WhatsApp's Two-Step Verification (36:00)

The demonstration effectively showcased the Android MRI Interpreter's capabilities using a widely-used messenger application (implied to be WhatsApp, though not explicitly named by the speaker). The demo focused on two key security features: two-step verification and end-to-end encryption. The crucial aspect was that the MRI Interpreter automatically bypassed the app's restrictions against running in rooted or custom environments.

Tracing Two-Step Verification PIN

  1. Setup and Configuration:
  • The speaker first obtained a root shell on the modified Android device using the kernel-level privilege escalation (simply by executing sh in the shell).
  • A configuration file (config.txt) was opened, where the target app's UID was set (10250 for the messenger app).
  • The TraceMode was set to 2 for real-time instruction and register value tracing.
  • A specific 6-digit PIN, 135246, was set as the search_item to trace.
  1. Real-Time PIN Detection:
  • The messenger app was launched, and the two-step verification menu was accessed.
  • Upon entering the PIN 135246, the MRI Interpreter immediately began printing real-time Dalvik instructions, function names, and register values containing the PIN. This showed the raw execution flow and where the PIN was present in memory during input.
  • Crucially, even after closing and restarting the app, the PIN was detected again in memory, even when the app did not explicitly prompt for it. This highlighted how sensitive data might linger in memory or be accessed internally by the app, unnecessarily exposing it.
  • Turning off the two-step verification feature in the app prevented the PIN from being detected upon subsequent restarts, confirming the tool's accuracy.

Generating a Core Graph for PIN Data Flow

  1. Switching to Graph Mode:
  • A new PIN, 778988, was registered for two-step verification.
  • The config.txt file was modified to set TraceMode to 3 (graph mode) and the new PIN 778988 as the search_item.
  • The MRI Interpreter then outputted raw data for graph generation.
  1. Visualizing Data Flow:
  • This raw data was processed to generate a visual core graph. The graph clearly illustrated the flow of the PIN 778988 within the application.
  • A zoomed-in view of the graph revealed that the PIN was being decrypted using an Android default decryption function, specifically identified as x.zpg.a02. This indicated that the PIN, even if stored encrypted, was exposed in plaintext during decryption in memory.
  1. Deep Dive into a Target Function:
  • To further investigate the x.zpg.a02 function, its name was registered in config.txt with TraceMode set to 1 (all trace mode for a target function).
  • Restarting the app with this configuration allowed the MRI Interpreter to output all detailed instruction and register information specifically for the x.zpg.a02 function, providing granular insight into its operation and confirming the decryption process.

The speaker concluded by acknowledging that the credential exposure of the PIN in memory might not be a "critical vulnerability" in itself but served as a clear demonstration of how easily and quickly the Android MRI Interpreter could identify such sensitive data flows and behaviors within an application.

Defensive Implications

▶ Watch: Audience Q&A: Encrypted String Tracing (49:50)

The Android MRI Interpreter presents several critical defensive implications, primarily by highlighting the limitations of current RASP solutions and demonstrating a highly effective, kernel-level approach to bypassing them.

  1. RASP as a Delay, Not a Blockade: The talk reinforces the understanding that Runtime Application Self-Protection (RASP) mechanisms are primarily designed to delay attackers and annoy them, rather than completely block sophisticated attacks. The speaker, with experience bypassing numerous RASP implementations in banking apps, states that such bypasses can take anywhere from minutes to weeks, but are ultimately achievable. This implies that organizations should not rely solely on client-side RASP as their primary defense against determined adversaries.
  1. Vulnerability of Userland RASP Detections: Traditional RASP solutions often rely on detecting common indicators of compromise in userland, such as:
  • Rooted environments.
  • Presence of debuggers (e.g., IDA).
  • Hooking frameworks (e.g., Frida).
  • Custom ROMs.

The Android MRI Interpreter's approach demonstrates that by moving the "attack" capabilities into the kernel, these userland detections become largely irrelevant. An OS environment that appears stock from userland's perspective, while providing full analytical control at a deeper layer, renders many RASP checks ineffective.

  1. Importance of Kernel-Level Security: The talk underscores the critical importance of kernel integrity and secure boot mechanisms. The ability to modify the kernel to:
  • Spoof boot state information (e.g., changing verified_boot_state in proc/cmdline from "orange" to "green") bypasses RASP and SafetyNet checks that rely on device boot status. This highlights a potential weakness if kernel integrity can be compromised.
  • Create privilege escalation vectors by patching system calls like execve (specifically exe_cb for the sh command) allows for obtaining root privileges in a way that is extremely difficult for userland RASP to detect. Defenders should focus on hardening the kernel and ensuring its immutability.
  1. Need for Deeper App Analysis: The demonstration of easily tracing sensitive data like PINs in memory, even when not explicitly requested by the UI, reveals that applications might expose credentials or other sensitive information unnecessarily. Defenders, particularly developers, should:
  • Minimize sensitive data exposure: Ensure credentials and other critical data are in memory for the shortest possible duration.
  • Use secure memory handling: Implement techniques like memory wiping after use, or storing sensitive data in hardened memory regions if available.
  • Conduct thorough memory analysis: Integrate memory forensics and runtime analysis into their security testing pipelines to identify such exposures.
  1. Rethinking Android Security Models: The Android MRI Interpreter challenges existing security models by showing that a sufficiently modified OS can bypass client-side protections. This implies that for highly sensitive applications, a layered security approach, including robust server-side validation and authentication, becomes even more critical. Relying solely on client-side security is insufficient.

In essence, the Android MRI Interpreter serves as a powerful reminder that the battleground for mobile security extends deep into the OS kernel. Effective defense requires understanding these low-level attack vectors and fortifying the system from the ground up, rather than just patching userland applications.

Key Takeaways

  • Non-Invasive OS-Level Analysis: The Android MRI Interpreter enables deep analysis of Android applications at the operating system level, akin to a medical MRI, providing detailed insights without triggering application self-protection mechanisms.
  • Automated RASP Bypass via Kernel Modifications: It bypasses Runtime Application Self-Protection (RASP) by implementing critical functionalities as "backdoors" within the kernel, making the analysis environment appear as a stock ROM to userland applications.
  • Real-time Dalvik Instruction and Register Snapshots: The tool provides dynamic "code images" of target functions, outputting every executed Dalvik instruction, program counter, and register value in real-time for granular code inspection.
  • Visualized Taint Analysis for Data Flow: It traces the flow of sensitive data (like credentials) throughout an application, visualizing its creation, movement, and usage via an intuitive core graph, even in obfuscated or complex applications.
  • Kernel-Level Privilege Escalation: The MRI Interpreter demonstrates the feasibility of creating simple, hard-to-detect root access by patching kernel system calls (e.g., execve for sh command), highlighting a significant threat vector.
  • Unnecessary Credential Exposure: The demo revealed how sensitive information, such as two-step verification PINs, can be unnecessarily exposed in memory during runtime, even when not actively prompted by the user interface, emphasizing the need for robust memory handling in applications.

About the Speaker(s)

SungHyoun Song is a dedicated mobile security researcher at the Financial Security Institute (FSI) in South Korea. His work at FSI focuses on ensuring the security of the Korean financial industry, where he specializes in mobile security research and conducts penetration testing for various Korean financial companies. With a strong background in identifying and analyzing vulnerabilities in mobile applications, SungHyoun Song is an experienced speaker who has shared his research and insights at several international security conferences. His presentation on the Android MRI Interpreter at HITB Phuket exemplifies his innovative approach to solving complex challenges in mobile application security.

Reviews

Dr. Zero (Offensive Security Researcher) — STRONG ACCEPT

Real engineering work that solves an actual problem researchers face daily. Forcing all execution through the Dalvik interpreter by modifying ART's code invocation flow is a clean, elegant approach to the RASP bypass problem. The kernel-level spoofing of boot state and the execve privilege escalation backdoor are practical contributions that show the speaker actually built this thing and used it.

Heather Calloway (CISO) — SOLID

Useful tooling for mobile app security teams doing pen testing or red team work on Android. Novel approach to RASP bypass by moving to kernel-level modifications. Limited strategic implications for most CISOs unless you're running a mobile app security program or concerned about your own apps being analyzed.

→ Top-rated talks at HITBSecConf2023 - Phuket

All talks from HITBSecConf2023 - Phuket