Skip to main content
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

1

Install Frida Tools on Host

pip install frida-tools frida
2

Push Frida Server to Device

Download the matching server binary from the Frida releases page, then:
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 &
3

Verify the Connection

frida-ps -U          # List processes
frida-ps -Uai        # List all installed apps

Frida Server vs. Gadget

Push and run a native daemon — lets you attach to any process.
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.

frida-ui (Browser-Based Controller)

# 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:
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

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

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:
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

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:
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.
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:
# 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

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.

References

Build docs developers (and LLMs) love