> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/HackTricks-wiki/hacktricks/llms.txt
> Use this file to discover all available pages before exploring further.

# Android Frida Tutorial

> Hands-on guide to instrumenting Android applications with Frida — installation, server vs. Gadget modes, hooking patterns, DEX dumping, and fuzzing.

Frida is a dynamic instrumentation toolkit that lets you inject JavaScript into running processes, hook functions, change return values, extract secrets at runtime, and build custom tooling. This page covers Android-specific usage.

## Installation

<Steps>
  <Step title="Install Frida Tools on Host">
    ```bash theme={null}
    pip install frida-tools frida
    ```
  </Step>

  <Step title="Push Frida Server to Device">
    Download the matching server binary from the [Frida releases page](https://github.com/frida/frida/releases), then:

    ```bash theme={null}
    adb root
    adb push frida-server-<ver>-android-<arch> /data/local/tmp/frida-server
    adb shell chmod 755 /data/local/tmp/frida-server
    adb shell /data/local/tmp/frida-server &
    ```
  </Step>

  <Step title="Verify the Connection">
    ```bash theme={null}
    frida-ps -U          # List processes
    frida-ps -Uai        # List all installed apps
    ```
  </Step>
</Steps>

## Frida Server vs. Gadget

<Tabs>
  <Tab title="Frida Server (Rooted)">
    Push and run a native daemon — lets you attach to any process.

    ```bash theme={null}
    frida-ps -Uai
    frida -U -n com.example.app
    frida -U -f com.example.app -l hook.js --no-pause
    ```

    Best for rooted devices or emulators.
  </Tab>

  <Tab title="Frida Gadget (No Root)">
    Bundle Frida as a shared library inside the APK so it loads within the target process.

    ```bash theme={null}
    # Decompile APK
    apktool d app.apk -o app_m
    # ... add libfrida-gadget.so to lib/<abi>/ and create gadget config ...
    apktool b app_m -o app_gadget.apk
    uber-apk-signer -a app_gadget.apk -o out_signed
    adb install -r out_signed/app_gadget-aligned-debugSigned.apk
    ```

    Gadget config (`assets/frida-gadget.config`):

    ```json theme={null}
    {
      "interaction": { "type": "script", "path": "/sdcard/ssl-bypass.js" },
      "runtime": { "logFile": "/sdcard/frida-gadget.log" }
    }
    ```
  </Tab>

  <Tab title="JDWP Injection (Debuggable)">
    If the APK has `android:debuggable="true"`, inject without root or repackaging using [frida-jdwp-loader](https://github.com/frankheat/frida-jdwp-loader):

    ```bash theme={null}
    git clone https://github.com/frankheat/frida-jdwp-loader.git
    cd frida-jdwp-loader
    python frida-jdwp-loader.py frida -n com.example.myapplication
    # Keep breakpoint suspended for early hooks:
    python frida-jdwp-loader.py frida -n com.example.myapplication -s
    # Run a local script via Gadget script mode:
    python frida-jdwp-loader.py frida -n com.example.myapplication -i script -l script.js
    ```
  </Tab>
</Tabs>

## frida-ui (Browser-Based Controller)

```bash theme={null}
# Install
uv tool install frida-ui --with frida==16.7.19

# Run
frida-ui
frida-ui --host 127.0.0.1 --port 8000 --reload
```

The web UI at `http://127.0.0.1:8000` discovers USB/local devices, supports Attach/Spawn/Spawn & Run modes, has a script editor with CodeShare import, and can connect to remote servers (`frida-server -l 0.0.0.0:27042 -D`).

## Quick Examples — Hooking Java Methods

### Hook 1: Boolean Bypass

Override a PIN check to always return `true`:

```javascript theme={null}
Java.perform(function () {
  var PinUtil = Java.use("infosecadventures.fridademo.utils.PinUtil")
  PinUtil.checkPin.implementation = function (pin) {
    console.log("[+] PIN check bypassed!")
    return true
  }
})
```

### Hook 2: Brute-Force a Static Function

```javascript theme={null}
Java.perform(function () {
  var PinUtil = Java.use("infosecadventures.fridademo.utils.PinUtil")
  for (var i = 1000; i < 9999; i++) {
    if (PinUtil.checkPin(i + "") == true) {
      console.log("[+] Found correct PIN: " + i)
    }
  }
})
```

### Hook 3: Log Arguments and Return Values

```javascript theme={null}
Java.perform(function () {
  var EncryptionUtil = Java.use("infosecadventures.fridademo.utils.EncryptionUtil")
  EncryptionUtil.encrypt.implementation = function (key, value) {
    console.log("Key: " + key)
    console.log("Value: " + value)
    var result = this.encrypt(key, value)
    console.log("Encrypted: " + result)
    return result
  }
})
```

### Hook with Overloads

When multiple overloads exist, specify argument types explicitly:

```javascript theme={null}
var Cls = Java.use("com.example.Class")
Cls.doThing.overload('java.lang.String', 'int').implementation = function(s, i) {
  return this.doThing(s, i)
}
```

### Get an Existing Object Instance

```javascript theme={null}
Java.choose("com.example.a11x256.frida_test.my_activity", {
  onMatch: function (instance) {
    console.log("Found instance: " + instance)
    console.log("secret(): " + instance.secret())
  },
  onComplete: function () {}
})
```

## Clearing FLAG\_SECURE at Runtime

Apps that set `FLAG_SECURE` block screenshots and recordings. Hook the Window methods to strip it:

```javascript theme={null}
Java.perform(function () {
  var LayoutParams = Java.use("android.view.WindowManager$LayoutParams")
  var FLAG_SECURE = LayoutParams.FLAG_SECURE.value
  var Window = Java.use("android.view.Window")
  var Activity = Java.use("android.app.Activity")

  function strip(value) {
    var masked = value & (~FLAG_SECURE)
    if (masked !== value) console.log("[-] Stripped FLAG_SECURE")
    return masked
  }

  Window.setFlags.overload('int', 'int').implementation = function (flags, mask) {
    return this.setFlags.call(this, strip(flags), strip(mask))
  }
  Window.addFlags.implementation = function (flags) {
    return this.addFlags.call(this, strip(flags))
  }
  Activity.onResume.implementation = function () {
    this.onResume()
    var self = this
    Java.scheduleOnMainThread(function () {
      try { self.getWindow().clearFlags(FLAG_SECURE) } catch (e) {}
    })
  }
})
```

Run with: `frida -U -f <package> -l disable-flag-secure.js --no-pause`

## Dynamic DEX Dumping with clsdumper

`clsdumper` survives hardened apps by combining anti-Frida bypass with multiple DEX discovery strategies.

```bash theme={null}
pip install clsdumper

# Attach to running app
clsdumper com.example.app

# Spawn first (hooks before early loaders)
clsdumper com.example.app --spawn

# Select specific strategies
clsdumper com.example.app --strategies fart_dump,oat_extract

# Extract smali after dumping
clsdumper com.example.app --extract-classes
```

Output layout:

```
dump_com.example.app/
  dex/classes_001.dex ...
  classes/           # only with --extract-classes
  metadata.json      # strategy per hit + hashes
```

## Stealthy Injection with Zygisk Gadget

Some apps detect `ptrace` or `frida-server` strings. Zygisk injects the Gadget inside Zygote so no process is ptraced:

```bash theme={null}
# Configure target package and startup delay
adb shell "su -c 'echo infosecadventures.fridademo,5000 > /data/local/tmp/re.zyg.fri/target_packages'"

# Launch app, then attach to Gadget process
frida -U -n Gadget -l hook.js
```

## Frida 17+ and Android 14–16 Notes

<Warning>
  From Frida 17.1.x+, Java hooking on Android 14–16 is stable again (ART quick entrypoint offsets were fixed). If `Java.choose` returns nothing on Android 14+, upgrade `frida-server`, `frida-gadget`, and the Python packages to >= 17.1.5.
</Warning>

## References

* [Frida Releases](https://github.com/frida/frida/releases)
* [Frida Gadget Documentation](https://frida.re/docs/gadget/)
* [Objection (SensePost)](https://github.com/sensepost/objection)
* [frida-jdwp-loader](https://github.com/frankheat/frida-jdwp-loader)
* [frida-ui](https://github.com/adityatelange/frida-ui)
* [clsdumper](https://github.com/TheQmaks/clsdumper)
* [DERE-ad2001/Frida-Labs](https://github.com/DERE-ad2001/Frida-Labs)
* [Android Frida Hooking: Disabling FLAG\_SECURE](https://www.securify.nl/en/blog/android-frida-hooking-disabling-flagsecure/)
