Android Fundamentals

About Android

What is Android?

Android is a mobile operating system primarily designed for touchscreen devices such as smartphones and tablets. Built on a modified version of the Linux kernel, it offers flexibility, scalability, and a wide ecosystem of applications. The system was initially developed by the Open Handset Alliance, a consortium of technology companies, and has been commercially sponsored and led by Google since its early years.

Most Android devices come bundled with Google Mobile Services (GMS), a proprietary suite of applications and APIs that includes popular apps such as Google Play Store, Google Maps, and Google Chrome. These services extend the functionality of the base operating system and serve as the backbone of Google’s ecosystem on mobile devices.

Google also works closely with hardware vendors like Samsung, HTC, Motorola, and Huawei, enabling them to customize Android with their own user interfaces (UI) and software enhancements. This is why devices from different manufacturers often look and behave differently despite running the same core OS. Beyond smartphones and tablets, Android has expanded to smart TVs (Android TV), wearables (Wear OS), automotive systems (Android Auto), and even IoT devices.

Application Distribution

One of Android’s strongest features is its app distribution model. While the Google Play Store remains the primary and most trusted source, Android’s open nature allows apps to be distributed through multiple platforms, including:

  • Amazon Appstore
  • Samsung Galaxy Store
  • Huawei AppGallery
  • Aptoide (open-source)
  • F-Droid (open-source)
  • APKPure and APKMirror (alternative APK repositories)

This flexibility makes Android highly accessible but also introduces potential security risks, since apps can be installed from third-party sources that may not be as strictly monitored as Google Play.

A Brief History of Android

Early Beginnings (2003–2005)

Android started as a small startup founded in October 2003 by Andy Rubin, Rich Miner, Nick Sears, and Chris White in Palo Alto, California. The company aimed to create a smart operating system for digital cameras but quickly pivoted toward mobile phones.

By 2005, Google acquired Android Inc. for around $50 million, bringing Andy Rubin and his team under its wing. This acquisition marked the start of what would become the world’s most widely used mobile operating system.

First Commercial Device (2007–2008)

In 2007, the team at Google developed a prototype device that relied on a physical QWERTY keyboard and no touchscreen. This evolved into the HTC Dream, also known as the T-Mobile G1, which was released in September 2008 as the first commercial Android smartphone.

The early versions of Android adopted a fun tradition of naming releases after desserts and sweets, such as Cupcake, Donut, Eclair, Froyo, Gingerbread, Honeycomb, and beyond. This naming convention lasted until Android 10.

Expansion and the Nexus Line (2010–2014)

In 2010, Google introduced the Nexus series, a line of smartphones built in partnership with different manufacturers but running a “pure” version of Android. Nexus devices were especially popular among developers and enthusiasts because they received updates directly from Google.

In 2013, Google experimented with Google Play edition devices, including special variants of the Samsung Galaxy S4, HTC One, and Moto G, all of which ran stock Android.

The following year, in June 2014, Google launched Android One, a program designed to help manufacturers in emerging markets create affordable smartphones with consistent, high-quality user experiences.

The Pixel Era and Beyond (2016–present)

In October 2016, Google shifted its strategy by introducing the Pixel and Pixel XL smartphones. Unlike the Nexus line, these devices were designed, marketed, and sold directly by Google, marking a new era where Google controlled the entire hardware and software experience.

On September 3, 2019, Google released Android 10 (initially referred to as Android Q) for Pixel devices. This release ended the long-standing dessert-naming tradition, moving toward a simpler numeric naming scheme (Android 10, 11, 12, etc.).

What is the primary hardware architecture used in the majority of Android devices?

The primary hardware architecture used in most Android devices is ARM.

Android Operating System

As discussed in the previous section, Android is built on top of the Linux kernel, which means that once someone gains access to a shell on the device, they can execute familiar Linux commands. This is a critical concept for both developers and penetration testers, as the shell offers direct interaction with the underlying operating system.

The Linux shell acts as a text-based input/output (I/O) interface between the user and the system kernel. In practice, this means you can type commands into the terminal, and the system will process them and return the results in text form. This approach gives far more control than the graphical interface, making it the preferred environment for troubleshooting, automation, and security testing.

For example, once you have shell access, you can navigate through the Android file system much like you would on a traditional Linux machine. A common directory of interest is /sdcard/, which often stores user data such as downloads, media, and app-related files. Using the ls command allows you to list the contents of this directory, as shown in the example below:

cd /sdcard/
ls

This simple demonstration shows how the shell provides direct visibility into the file system and highlights why shell access can be powerful (and dangerous in the wrong hands). In the upcoming sections, we’ll explore more shell commands, their practical applications, and how they can be leveraged during Android analysis and penetration testing.

What type of files do Android Runtime and Dalvik VM execute?

Android Runtime and Dalvik VM run compiled application code stored in DEX format. These files contain bytecode optimized for execution on Android devices.

Android Security Features

Kotlin and Java are the predominant languages for building Android apps. Developers use the Android SDK to compile source code together with resources and assets into an Android Package (APK) — a .apk archive that bundles everything required to install and run the app: compiled bytecode (.dex), the app manifest, resources, and any native libraries.

App isolation and core security pillars

Every Android app runs inside its own protected sandbox, a design made possible by Android’s Linux-based foundation. Key security mechanisms that support this model include:

  • Per-app Linux user model: Android treats each application like a separate Linux user. The system assigns a distinct Linux user ID (UID) to every app; the OS uses that UID for access control, though the app itself doesn’t directly see the numeric UID.
  • Filesystem permissions: Files created for an app are owned by that app’s UID, so only that app can access its private data directories by default.
  • Process and runtime isolation: Each app runs in its own process and gets its own instance of the Android Runtime (ART, previously Dalvik), providing memory and execution isolation.
  • Lifecycle control: The OS creates and destroys app processes as needed, helping contain resource usage and limiting exposure.
  • Least privilege: Apps only receive privileges they declare. Extra capabilities must be requested in the manifest and, depending on the API level, approved by the user or the system.

The kernel-enforced sandbox

Because the sandbox is enforced at the kernel level (via Linux UIDs and process separation), it uniformly protects code running above the kernel — including native binaries, system services, libraries, and user applications. Cross-app access or privilege escalation generally requires exploiting the kernel itself (i.e., a kernel-level vulnerability) to break out of the sandbox.

For example, App1 and App2 run as separate processes under ART (Dalvik VM) and store their private data in separate directories such as:

  • /data/data/com.example.myapp1
  • /data/data/com.example.myapp2

Each of these directories is owned by a different UID, so only the corresponding app process can read or write its files.

To observe the per-app ownership in practice, you can run a command from your host machine (via ADB), for example:

adb shell ls -l /data/data

The listing will show different owners for each application directory, reflecting the distinct UIDs assigned by the system.

Which Signature Scheme versions are vulnerable to CVE-2017-13156? (Format: 3 words)

The versions affected by CVE-2017-13156 are those that only verify the JAR entries rather than the full APK structure. This is exactly the case for Signature Scheme v1.

Native Apps

Native applications are programs written in a specific programming language and designed to run directly on a particular platform or operating system. On Android, native apps are most commonly developed in Java or Kotlin, using Android Studio as the official integrated development environment (IDE). Developers also rely heavily on components from the Android Software Development Kit (SDK), which provides the tools and libraries needed to build, test, and debug apps.

Java vs. Kotlin in Android Development

Although Java was the original language for Android development, Google has officially promoted Kotlin as the preferred language in recent years due to its concise syntax, null-safety features, and modern programming paradigms. Even so, Java continues to be widely used, especially in existing projects, and many developers still favor it because of its maturity and extensive community support.

In this example, we’ll walk through the process of creating a very simple Java-based Android application. Since the project has already been set up, our focus will be on understanding how the app’s layout ties together with its Java logic and resources.

Exploring the Project Structure

Inside Android Studio, the Project Explorer shows the typical Android app structure:

  • manifests/ → contains the AndroidManifest.xml, which defines essential app information (package name, permissions, activities).
  • java/ → holds the Java (or Kotlin) source code, including the main entry point, usually MainActivity.java.
  • res/ → contains application resources, such as images, layouts, and values.
    • layout/ → includes XML files defining UI screens.
    • values/ → includes colors.xml, strings.xml, and themes.xml for reusable style and text definitions.

For our example, we’ll focus on the res/layout/activity_main.xml file, which defines the UI of the main activity.

Defining Layouts in Android

Layouts control the structure and visual presentation of the user interface. In Android, layouts are written in XML rather than code, making them easier to design, preview, and modify. A layout can contain text elements, buttons, images, and more — all of which are later linked to logic in the Java or Kotlin files.

Here’s a basic XML snippet for activity_main.xml that creates a simple layout with a TextView and a Button. When the button is tapped, a message will appear on the screen:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:id="@+id/textViewMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, Android!"
        android:textSize="18sp"
        android:layout_marginBottom="20dp" />

    <Button
        android:id="@+id/buttonShowMessage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tap Me" />

</LinearLayout>

This layout defines a simple vertical arrangement with one text label and one button.

What is the name of the file that contains the application’s hardcoded strings?

The file that contains an application’s hardcoded strings is strings.xml.

Native Code

Native code refers to software that is compiled to run directly on a specific processor architecture, such as ARM, ARM64, or x86. Because it bypasses the virtual machine layer used by Java and Kotlin, native code can sometimes offer better performance on supported hardware. For example, tasks that require heavy computation — like real-time image processing, audio processing, or graphics rendering — often benefit from this direct execution.

The Role of the Android NDK

Android Studio makes it possible to incorporate native code through the Native Development Kit (NDK). The NDK provides headers, libraries, and build tools that allow developers to write parts of an app in C or C++ alongside the standard Java/Kotlin codebase.

Some common reasons developers choose to use the NDK include:

  • Performance Optimization → Offloading compute-intensive logic to native code to achieve faster execution.
  • Hardware Access → Leveraging low-level capabilities of the CPU, GPU, or DSP that aren’t exposed in the higher-level SDK.
  • Security Hardening → Obscuring sensitive logic (like encryption algorithms or license checks) in native binaries to make reverse engineering more difficult.

Shared Libraries and JNI

When native code is compiled, it is packaged into shared libraries with the .so (shared object) extension. These libraries are bundled inside the application and loaded at runtime.

To bridge the gap between managed code (Java/Kotlin) and unmanaged code (C/C++), Android relies on the Java Native Interface (JNI). JNI defines a standard way for these different languages to interact:

  • Java/Kotlin can call functions implemented in native code.
  • Native code can return values or interact with objects from the Java side.
  • Data exchange (like strings, arrays, or complex objects) is made possible through well-defined JNI methods.

This combination allows developers to write most of their app in Java/Kotlin for maintainability, while still dropping down into C or C++ for specialized tasks that benefit from native execution.

What is the name of the method that loads the library statically? (Format: Name.name()).

The method used to statically load a library is: System.loadLibrary()

What is the name of the method that loads the library dynamically? (Format: Name.name()).

The method that loads a library dynamically in Android is System.load(). It allows the application to load a shared library at runtime, providing flexibility compared to static loading. Using System.load() ensures that the library is available for native method calls only when needed.

What is the name of the function that returns the string inside the cpp file? (Format: FunctionName()).

The function used to return a string from a C++ file via JNI is NewStringUTF(). This function creates a new Java String object from a UTF-8 encoded C-style string, allowing native code to pass text back to the Java layer safely.

Javascript & WebViews

WebViews are powerful components that let developers embed and render web content directly inside an Android application. Instead of opening an external browser, a WebView allows the app to display HTML, CSS, and JavaScript as part of its own user interface. This makes them especially useful for hybrid applications, in-app documentation, or apps that blend native functionality with existing web content.

However, with this flexibility comes significant security risk. Poorly configured or misused WebViews have historically been the source of severe vulnerabilities in Android apps. If not handled carefully, features like JavaScript execution or local file access can expose an application to attacks such as:

  • Cross-Site Scripting (XSS): Injecting malicious JavaScript into the WebView.
  • Local File Inclusion (LFI): Exploiting file access to read sensitive data on the device.
  • Privilege Escalation: Using JavaScript bridges to access unsafe native functionality.

Because of this, the official Android documentation strongly recommends using the system’s default browser (Intent.ACTION_VIEW) whenever possible, rather than embedding a WebView, unless you have a strong reason to render the content in-app.

Modern WebView Security Defaults

To reduce common risks, recent Android versions ship WebViews with more restrictive default settings. For instance:

  • JavaScript is disabled by default (developers must explicitly enable it).
  • File access and cross-origin requests are more tightly controlled.
  • Safe browsing features help block known malicious content.

While these restrictions help, developers often enable advanced features for functionality — such as allowing JavaScript execution — and this is where things must be handled with extreme caution. A single misstep can open the door to serious exploits.

Creating a Simple WebView Application

Let’s walk through building a basic app to understand how WebViews work. First, add a WebView element to your activity_main.xml layout file:

<WebView
    android:id="@+id/webview"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

This gives us a full-screen container where we can display web content.

Connecting the WebView in Java

Next, we connect the WebView to our MainActivity.java class and instruct it to load a local HTML file:

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        WebView webview = (WebView) findViewById(R.id.webview);
        webview.getSettings().setJavaScriptEnabled(true);
        webview.loadUrl("file:///android_asset/html/index.html");
    }
}

Here’s what happens:

  • findViewById(R.id.webview) creates a reference to the WebView defined in XML.
  • setJavaScriptEnabled(true) allows the WebView to execute JavaScript (disabled by default).
  • loadUrl("file:///android_asset/html/index.html") loads a local HTML file stored in the app’s assets/html directory.

Security Warning in Android Studio

Notice the line:

webview.getSettings().setJavaScriptEnabled(true);

Android Studio immediately flags this with a lint warning. This is because enabling JavaScript in a WebView can expose the app to major security vulnerabilities if the content isn’t fully trusted and controlled. The IDE reminds developers to double-check their use case and apply safeguards before shipping the app.

What technology do hybrid applications use to load web content?

Hybrid applications use WebView technology to load web content. This component allows the app to display web pages and run HTML, CSS, and JavaScript directly within the native application environment, blending web and native functionality.

What WebView setting can introduce an XSS vulnerability in an Android application? (Format: nameOfMethod(value))

Enabling JavaScript in a WebView can open the door to XSS if untrusted content is loaded.
setJavaScriptEnabled(true) Avoid enabling it unless necessary and always validate/sanitize content before loading.

Application Frameworks

Creating Android apps from the ground up is complex because it involves multiple languages, tools, and platform specifics. To speed development, improve code quality, and simplify maintenance, developers commonly rely on application frameworks — collections of libraries and utilities that provide ready-made components and conventions. These frameworks typically supply UI widgets, authentication and security helpers, error-handling patterns, logging facilities, and other building blocks that make development faster and more consistent.

Because frameworks abstract and centralize many features, they also broaden an app’s attack surface: different frameworks, IDEs, and language ecosystems introduce distinct dependencies and configuration nuances. That means a penetration test for a framework-based app requires different approaches and checks than a test of a purely native app. In the sections that follow we’ll look at examples of popular Android frameworks and how their design choices affect security testing. These walkthroughs are intended to deepen your conceptual understanding — you don’t need to reproduce every step in a lab environment.

What is the extension of the file generated from disassembling a Flutter application? (Format: .ext)

The file generated from disassembling a Flutter application is typically a shared library with the extension .so. These .so files contain compiled native code used by the Flutter app for performance-critical or platform-specific functionality.

What is the extension of the file generated from disassembling a Xamarin application? (Format: .ext)

The file generated from disassembling a Xamarin application typically has the extension .dll. These files contain the compiled .NET assemblies, which include the application’s managed code and resources.

Applications created using frameworks like React Native, Apache Cordova, and Ionic might also be susceptible to what type of attacks, compared to native applications? (Format: 1 word)

Applications created with frameworks like React Native, Apache Cordova, and Ionic might also be susceptible to web attacks, as they rely on embedded web content and can inherit typical web vulnerabilities.

Activities

When building an Android app, it’s not just about writing Java or Kotlin code — the real structure of an application is defined by its components. These building blocks represent both the user-facing elements (like screens) and the background functionality (like services or data providers). Every Android app declares its components inside the AndroidManifest.xml file, where the system can see what parts make up the app and how they interact with one another.

Since each Android application runs in its own process, Google had to design a way for apps and their components to talk to each other securely. This is where Interprocess Communication (IPC) comes in. IPC ensures that different components, whether inside the same app or across different apps, can exchange messages, share data, or trigger functionality when needed. Without IPC, Android apps would be isolated islands, unable to integrate with system services or third-party apps.

Core Application Components

Android defines four primary application components, each serving a distinct purpose. Together, they allow developers to structure an app into modular, reusable pieces.

1. Activities

Activities are the most recognizable component because they represent a single screen with a user interface. Every time you open an app and see a screen — whether it’s a login page, a settings panel, or a main dashboard — you’re interacting with an Activity.

  • Display modes: Activities can be launched in different formats, such as full-screen, floating windows, embedded views, or even in multi-window setups on newer Android versions.
  • Entry points: They can be started by other Activities, by other apps, or directly in response to system events (e.g., tapping a notification).
  • Responsibilities:
    • Rendering and managing the UI.
    • Handling user interaction (taps, swipes, form entries).
    • Managing the application lifecycle — deciding what happens when the screen is paused, resumed, or destroyed.

Activities are critical for the user experience, but they also connect deeply to IPC. For instance, an Activity in one app can trigger an Activity in another app using Intents, a key Android IPC mechanism.

Which Activity and corresponding method is a common entry point for penetration testers when conducting application penetration testing? (Format: ActivityName:methodName())

A common entry point for penetration testers in Android apps is the MainActivity:onCreate(). This method initializes the activity and is often the first code executed when the app launches.

What is the attribute and action of the `<intent-filter>` element in the `AndroidManifest.xml` file that defines an Activity as the application’s entry point? (Format: attribute:name=”value.of.the.action”)

The attribute and action in the <intent‑filter> element that marks an Activity as the app’s entry point is android:name=”android.intent.action.MAIN”. This signals the system that this Activity should be started when the application is launched.

What is the method used to launch an Activity programmatically, expecting results to be returned, and what is the method that handles the returned results? (Format: methodName():methodName())

The method used to launch an Activity programmatically expecting results is startActivityForResult(), and the method that handles the returned results is onActivityResult(). Together, they allow one Activity to start another and receive data back once the second Activity finishes.

Services

In Android, not every task needs to happen on the screen. Some jobs — such as playing music, downloading large files, or syncing with a server — run best in the background. To handle these long-running operations without tying them to the user interface, Android provides the Service component.

A Service is an application component that has no direct user interface but performs important work behind the scenes. Once started, a Service can continue running even if the user switches to another app, making it a powerful tool for handling ongoing tasks.

Types of Services in Android

Android defines three main types of Services, each suited for different use cases:

1. Foreground Services

Foreground services are used when the user needs to be aware of what the app is doing. These services:

  • Run with higher priority and are less likely to be killed by the system.
  • Must display a notification while active, ensuring transparency.
  • Are ideal for tasks where the user expects continuity, such as:
    • Music players streaming audio.
    • GPS navigation apps providing turn-by-turn directions.
    • Fitness apps tracking steps or workouts in real time.

A foreground service is typically started with the startService() method. Because they run visibly, Android requires developers to include a persistent notification so users know the service is active.

2. Background Services

Background services are designed for tasks that don’t need user interaction. Examples include syncing data periodically or cleaning up cached files.

However, starting with Android 8.0 (API level 26), Google restricted background services. Apps can no longer keep them running indefinitely when they are not in the foreground. This change was made to:

  • Improve battery life.
  • Optimize performance.
  • Prevent resource abuse by apps running silently in the background.

Instead, developers are encouraged to use alternatives like WorkManager or JobScheduler for deferred background tasks.

3. Bound Services

Bound services are unique because they act like a server that other components (clients) can connect to. When an app component calls bindService(), it establishes a two-way communication channel with the service.

Key features of bound services:

  • Multiple clients can bind to the same service.
  • Clients can interact with the service using Interprocess Communication (IPC).
  • They are often used when an app needs continuous interaction with a service, such as:
    • An app binding to a service to fetch data from a remote server.
    • A media player UI binding to a music service to control playback.

Bound services remain active only while components are bound to them, making them resource-efficient.

Why Services Matter

Services are crucial for ensuring apps feel responsive and seamless. By offloading heavy or ongoing work from the UI thread to a background service, Android prevents the app from freezing or crashing while maintaining user expectations.

From a security and pentesting perspective, services also deserve attention because they:

  • May expose sensitive APIs through IPC.
  • Can run with elevated privileges in the background.
  • Might leak data if not implemented with proper permissions and restrictions.

What method should be called to start a Foreground service? (Format: methodName())

The method used to start a Foreground service is startService(). This method initiates the service, allowing it to run in the background, and for foreground services, it must be accompanied by a call to startForeground() to display a persistent notification.

What method should be called for a Bound service to allow other application components to bind to it? (Format: methodName())

The method that allows other application components to bind to a Bound service is bindService(). This enables communication between the client and the service while the service runs only as long as it is bound.

Broadcast Receivers

Among the four core Android application components, Broadcast Receivers play a special role because they can be seen as both Application Components and Interprocess Communication (IPC) mechanisms. They enable apps to listen for and respond to events, whether those events come from the Android system itself, other applications, or even the app’s own broadcasts.

Broadcast Receivers as IPC Mechanisms

At their core, Broadcast Receivers rely on Intents to deliver messages across the Android ecosystem. An Intent is like a lightweight messaging object that carries information about an event or an action.

  • System broadcasts: Generated by Android itself (e.g., low battery warning, device charging, Wi-Fi connected).
  • App broadcasts: Sent by applications to share information with other apps (e.g., “new data downloaded,” “sync completed”).
  • Self-broadcasts: Used internally within the same app to coordinate components.

By subscribing to a broadcast, an app can seamlessly communicate with other processes without needing a direct connection.

Broadcast Receivers as Application Components

When viewed as application components, Broadcast Receivers are designed to react to events. They don’t provide a user interface, but they can trigger actions in the background — such as launching a service, updating data, or displaying a notification.

To use a Broadcast Receiver, developers typically:

  1. Extend the BroadcastReceiver class.
  2. Override the onReceive() method, which defines what happens when the specified event occurs.
  3. Register the receiver, either in the AndroidManifest.xml or programmatically at runtime with an Intent filter.

Example: Detecting When the Device is Charging

Here’s a simple example of a Broadcast Receiver that listens for the system event indicating that the device is charging:

public class ChargingReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
            // Device started charging
            Toast.makeText(context, "Device is charging", Toast.LENGTH_SHORT).show();
        } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
            // Device stopped charging
            Toast.makeText(context, "Device is not charging", Toast.LENGTH_SHORT).show();
        }
    }
}

To make this work, the Broadcast Receiver must be declared in the AndroidManifest.xml with the appropriate Intent filters:

<receiver android:name=".ChargingReceiver">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
        <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
    </intent-filter>
</receiver>

Now, whenever the user plugs in or unplugs the device from a power source, the Broadcast Receiver reacts instantly.

Why Broadcast Receivers Matter

  • System Integration: They let apps integrate tightly with system events (battery state, connectivity, boot events, etc.).
  • App Communication: They enable apps to talk to each other without direct interaction.
  • Automation: Perfect for background tasks like syncing, triggering services, or showing alerts.

From a pentesting perspective, Broadcast Receivers are particularly interesting because:

  • Some apps may register insecure receivers, allowing malicious apps to spoof broadcasts.
  • Sensitive information can sometimes be exposed if broadcasts are not properly protected with permissions.

What element must be used in the AndroidManifest.xml to declare a Broadcast Receiver? (Format: <element>)

The element used in the AndroidManifest.xml to declare a Broadcast Receiver is <receiver>. This tells the Android system that the application can listen for and respond to broadcasted intents.

Content Providers

In Android, Content Providers are both an Application Component and an Interprocess Communication (IPC) mechanism. Their main purpose is to manage access to structured data and provide a standardized way for apps to share that data securely.

Think of a Content Provider as a middleman: it sits between the application’s data storage and any component (or even another app) that wants to access that data. This design ensures that apps don’t need to know the implementation details of how or where the data is stored — they only interact through a consistent API.

Content Providers as IPC Mechanisms

From the IPC perspective, Content Providers allow communication between applications. They expose data through a well-defined interface, which is accessed using the ContentResolver class. With this mechanism, apps can:

  • Create new records.
  • Read existing data.
  • Update records.
  • Delete records.

This is the familiar CRUD model that underpins most data interactions.

Because the interface is standardized, one app can request data from another (if permissions allow) without needing to know how that data is stored. The actual storage could be:

  • An SQLite database.
  • Files in internal or external storage.
  • Even data from a remote server.

Content Providers as Application Components

When considered as application components, Content Providers are responsible for:

  • Managing the app’s data.
  • Exposing that data to other apps in a controlled way.
  • Enabling internal sharing between different parts of the same app.

This makes them essential for apps that need to share structured data, like:

  • The Contacts app, which provides access to phonebook data.
  • The Calendar app, which shares event details.
  • The User Dictionary, which stores custom words.

How Access Works: The ContentResolver and CursorLoader

Accessing a Content Provider typically involves multiple moving parts:

  1. A UI component (like an Activity or Fragment) initiates a request.
  2. The request is passed to a CursorLoader, which performs the query asynchronously.
  3. The CursorLoader uses the ContentResolver, which acts as a client-side API.
  4. The ContentResolver communicates with the Content Provider.
  5. The Content Provider fetches the requested data from the underlying data storage and returns it back up the chain.

This process keeps the UI responsive, since queries run in the background rather than blocking the main thread.

Which method is used to perform a read operation on a ContentProvider in Android? (Format: method())

The method used to perform a read operation on a ContentProvider in Android is query(). This method allows an application to request data from the provider by specifying the URI, columns, selection criteria, and sort order.

Intents

In Android, Intents act as lightweight messaging objects that allow applications — and even the operating system itself — to request actions from different components. Whether it’s opening a new screen, starting a background service, or broadcasting a system-wide event, Intents are the glue that holds much of the Android ecosystem together.

Although Intents were not originally designed as a primary Interprocess Communication (IPC) mechanism, they can still be used when an application needs to interact with components running in different processes. For example, one app might send an Intent to start a service in another app, as long as permissions allow it.

Which method is used to pass data between components in the form of key-value pairs? (Format: method())

The method used to pass data between components in the form of key-value pairs is putExtra(). This method is commonly used with Intents to send additional information when starting an Activity or Service.

Binders

The Binder serves as the fundamental Interprocess Communication (IPC) framework in Android, providing a reliable, efficient, and secure way for processes to exchange information. Instead of having each process operate in isolation, Binder creates a bridge that enables them to communicate directly. It is built upon the concept of a Remote Procedure Call (RPC) model, which makes cross-process communication feel seamless: a client process can request methods on a remote object running in a different process as though it were interacting with a local object in its own memory space. This abstraction greatly simplifies development by hiding the complexity of the underlying IPC implementation.

In the context of this discussion, the term remote service refers to a service that belongs to the same application package but runs in a completely separate process. This separation allows Android to isolate workloads while still giving developers a standardized way to share resources and functionality across processes. In practice, Binder is most commonly used through a Service that implements an interface defined in an AIDL (Android Interface Definition Language) file. The AIDL file acts as a contract, specifying the methods, parameters, and return values available for remote communication. This ensures that both the client and the service understand exactly how to interact with one another.

The Service itself is responsible for providing the actual business logic or functionality requested, while the Binder mechanism manages the technical details of marshaling data, delivering requests, and returning responses across process boundaries. In other words, the Service delivers the features, and the Binder guarantees that communication between the client and the service is smooth and secure.

The following code snippets illustrate how a Service can expose functionality using a Binder, and how a client process can connect to that service to execute operations remotely. By studying these examples, it becomes easier to understand how Android applications make use of Binder under the hood to enable flexible and powerful IPC.

What is the attribute and the action of the `<service>` element in the `AndroidManifest.xml` file that specifies that the service runs in a different process? (Format: attribute:name=”value.of.the.action”)

The attribute and action that specify a service runs in a different process are android:process=”:remote”. This tells the Android system to execute the service in a separate process from the main application, isolating its execution environment.

Android Emulators

To perform effective Android application penetration testing we need three things: a host machine, an Android device (emulated or physical), and the set of tools we’ll use during the tests. Below we’ll review common emulator choices and walk through the emulator we’ll use in the upcoming sections.

Why emulators matter for pentesting

Android emulators let you run Android OS and apps on a desktop or laptop, making them indispensable for both developers and security testers. Compared to maintaining many physical devices, emulators are economical and convenient: they allow you to test across multiple Android API levels, device profiles, and configurations without buying hardware. Modern emulators also replicate many real-device features — incoming calls and SMS, camera input, sensors, Bluetooth, and even access to the Google Play Store — enabling realistic test scenarios that mirror how apps behave in the wild.

For penetration testers, emulators are especially handy because they let you quickly reproduce conditions, snapshot and roll back device state, and instrument the system for dynamic analysis without the risk or expense of using a fleet of physical phones.

Architecture differences matter

Most production Android devices run on ARM or ARM64 CPUs optimized for mobile power and performance. Conversely, emulators typically run x86 or x86_64 system images so they can leverage the host machine’s processor for faster execution. This difference in CPU architecture can change how certain vulnerabilities or exploits behave — for example, native binaries (.so) built for ARM won’t run natively on an x86 emulator unless translated or recompiled. Keep this limitation in mind when validating exploits or analyzing native code: some issues may only surface (or behave differently) on actual ARM hardware.

Our choice: Android Virtual Device (AVD)

For the labs and examples that follow we’ll use the Android Virtual Device (AVD) system provided by Android Studio. AVDs are flexible, well-integrated, and widely used: they let you create multiple virtual devices with different screen sizes, Android versions, and hardware profiles. Since AVD is bundled with Android Studio, it also plays nicely with debugging tools (ADB), profiling, and app deployment workflows — making it a practical choice for both functional testing and security analysis.

What CPU architecture do most physical Android devices use? (Format: 1 word, not case sensitive)

Most physical Android devices use the arm CPU architecture, which is optimized for power efficiency and widely adopted in mobile devices.

Create an AVD for ‘Pixel 3a API 34 Google APIs’ using Android Studio. What is the build number of the device? (Format: build_number, Example: build_number-test)

The build number of the ‘Pixel 3a API 34 Google APIs’ AVD is sdk_gphone64_x86_64-userdebug. This indicates the system image and architecture used for the emulator instance.

What is the label of the system images that provide elevated privileges (root access) to the Android Studio’s virtual devices (AVD)? (Format: 2 words, 1 space, not case sensitive)

The label of the system images that provide elevated privileges to Android Studio AVDs is Google APIs. These images allow the emulator to access additional APIs and, in some cases, offer root-level capabilities for testing purposes.

Android Debug Bridge

Android Debug Bridge (ADB) is one of the most important tools in the Android ecosystem. It’s a versatile command-line utility that enables communication between a computer (the host) and an Android device (physical or emulated). With ADB, developers, testers, and penetration testers can:

  • Install and debug applications
  • Transfer files between the host and the device
  • Access the device through a shell for direct command execution
  • Manage multiple devices or emulators from a single machine

As part of the Android Software Development Kit (SDK), ADB plays a central role in both development and security testing workflows.

How ADB Works: The Client–Server Model

ADB operates as a client–server program consisting of three main components:

ComponentDescription
ClientRuns on the host machine and is invoked by issuing the adb command in a terminal.
Daemon (adbd)Runs as a background process on the device, executing commands sent to it.
ServerRuns on the host machine, managing communication between the client and daemon.

This architecture ensures that one ADB server can coordinate commands across multiple devices and emulators simultaneously.

Step-by-Step: The ADB Connection Process

Step 1 – Starting the Client and Server

When you issue an adb command, the client starts and checks if an ADB server is already running on the host.

  • If no server is found, the client automatically starts one.
  • The server binds to TCP port 5037 and begins listening for client commands.

Step 2 – Discovering Devices and Emulators

The ADB server scans for connected devices.

  • For emulators, it checks the odd-numbered ports from 5555 to 5585.
  • When it finds a running emulator with an active adbd, it establishes a connection.

Step 3 – Port Pairing for Each Emulator

Each emulator instance is assigned a unique port pair to prevent conflicts when multiple emulators are running:

  • Even-numbered port → console connection (for controlling the emulator, e.g., via telnet).
  • Odd-numbered port → ADB connection (for development, debugging, or penetration testing).

Example:

EmulatorServicePort
Emulator 1console5554
Emulator 1adb5555
Emulator 2console5556
Emulator 2adb5557

Step 4 – Using ADB Commands

Once connections are established, you can interact with devices using ADB. Common examples include:

adb devices            # Lists all connected devices and emulators
adb install app.apk    # Installs an APK onto the device
adb shell              # Opens a command shell on the device
adb push file.txt /sdcard/  # Transfers a file to the device
adb pull /sdcard/file.txt   # Retrieves a file from the device

With these commands, penetration testers and developers alike can easily deploy apps, manipulate files, and interact directly with the Android system for deeper analysis.

Why ADB Matters in Pentesting

For penetration testers, ADB isn’t just a developer convenience — it’s a powerful attack surface. Misconfigured devices with ADB over network enabled (adb tcpip 5555) can be accessed remotely if not secured. This makes mastering ADB critical not only for testing apps but also for identifying insecure device configurations.

Using ADB, install the `myapp.apk` and tap it to start. Then read the content of the file /sdcard/Download/flag.txt

Download and install the app

$ adb install -r myapp.apk
Performing Streamed Install
Success

Open to the folder /sdcard/Download/ and read the file

$ adb shell
a30s:/ $ cd /sdcard/Download/
a30s:/sdcard/Download $ cat flag.txt
[REDACTED]
a30s:/sdcard/Download $

After launching the application as instructed in question 1, use ADB to read the content of the file `/data/data/com.hackthebox.myapp/files/flag.txt`.

Connect on your device or emulator as administrator and read the flag.

1a30s:/ $ su
a30s:/ # cat /data/data/com.hackthebox.myapp/files/flag.txt
[REDACTED]
a30s:/ #

After launching the application as instructed in question 1, use ADB to read the content of the file `/sdcard/Download/flag.zip`.

Go to the folder /sdcard/Download/flag.zip. Execute unzip flag.zip and read the file.

cd /sdcard/Download
unzip flag.zip
cat flag.txt
[REDACTED]

Skills Assessment

Working with the emulator, running Linux commands, and exploring key directories are fundamental skills you’ll need to move forward into the upcoming modules.

The exercises that follow are designed to give you hands-on practice with the concepts introduced earlier. Based on what you’ve learned so far, start up an emulated device and complete the questions listed below.

Install myapp.apk by dragging and dropping it into the emulator. Then, open the embedded terminal in Android Studio and run `adb root && adb shell ls -l /installed/apps/`. Replace `/installed/apps/` with the correct path to find the app’s home directory. What is the full path to myapp’s home directory?

Install the app on your device

$ adb install -r myapp.apk
Performing Streamed Install

I used Jadx to get the application package

With this information, I open the /data/data folder and I found the folder of the application, wich is the package

a30s:/data/data/com.hackthebox.myspp # pwd
[REDACTED]

Find the UID of the application com.android.settings. Use the command `adb shell ls -l /full/path/` to inspect the file permissions and identify the application’s UID from the output.

Use the command bellow to get the UID

emu64xa:/ # ls -ldn /data/data/com.android.settings
drwx------ 4 1000 1000 4096 2025-09-24 18:49 /data/data/com.android.settings

ls -l /data/data/com.android.settings

The UID is 1000 or system

[REDACTED]

Following the steps provided in the Native Apps section, develop and deploy an application that will print the string returned from the `Build.MODEL` constant. Use the ‘Pixel 3a API 34 Google APIs’ (other emulators might work as well). What is the value of this string?

Use the command

$ adb shell getprop ro.product.model
daemon not running; starting now at tcp:5037
daemon started successfully
[REDACTED]

Sign the application `myapp.apk` and install it by either dragging and dropping it onto the device or using ADB. Make sure to first uninstall any previous versions of the app. After installation, tap on the app to start it. What is the message printed on the screen?

Point to you build-tools adress.

BT="$HOME/Android/Sdk/build-tools/36.1.0"

Create the variables to the app

APK_ALIGNED="myapp-aligned.apk"<br>APK_SIGNED="myapp-signed.apk"

Sign the app

"$BT/apksigner" sign \<br>--ks my-release-key.keystore \<br>--ks-key-alias my-key-alias \<br>--v1-signing-enabled true \<br>--v2-signing-enabled true \<br>--v3-signing-enabled true \<br>--out "$APK_SIGNED" "$APK_ALIGNED"

Install the app

adb install -r "$APK_SIGNED"

Open the app and get the flag