Frida Dynamic Instrumentation Guide

Frida Dynamic Instrumentation Guide

Master runtime analysis and function hooking with Frida for mobile and desktop security research.

9 min de lectura
← Back to blog

Frida Dynamic Instrumentation Guide

Frida is a dynamic instrumentation toolkit that revolutionizes the way security researchers analyze applications. By injecting JavaScript into running processes, Frida enables real-time code manipulation, function hooking, and memory inspection without recompilation or static patching.

What is Frida?

Frida is an open-source framework that allows you to:

  • Hook functions in native and managed code
  • Modify behavior of running applications at runtime
  • Trace execution and log function calls
  • Bypass security mechanisms like SSL pinning and root detection
  • Extract sensitive data from memory during execution

Why Frida?

Traditional static analysis has limitations:

  • Obfuscated code is hard to understand
  • Anti-tampering mechanisms detect modifications
  • Dynamic behavior is difficult to predict

Frida solves these problems by operating at runtime, allowing you to: ✅ See actual execution flow ✅ Bypass anti-debugging checks ✅ Test hypothesis immediately ✅ Automate complex analysis tasks

Installation and Setup

Installing Frida

Python package (includes frida-tools):

pip install frida-tools

Verify installation:

frida --version
frida-ps --version

Setting Up for Mobile Analysis

Android Setup:

# Download frida-server for your device architecture
wget https://github.com/frida/frida/releases/download/16.0.0/frida-server-16.0.0-android-arm64.xz
unxz frida-server-16.0.0-android-arm64.xz

# Push to device
adb push frida-server-16.0.0-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"

# Run frida-server (requires root)
adb shell "/data/local/tmp/frida-server &"

iOS Setup (jailbroken device):

# Install via Cydia/Sileo
# Add repository: https://build.frida.re
# Install "Frida" package

# Verify connection
frida-ps -U

Basic Frida Concepts

Process Attachment

Frida can attach to processes in three ways:

1. Attach to running process:

frida -U -n "com.example.app"  # By name
frida -U -p 1234                # By PID

2. Spawn new process:

frida -U -f com.example.app --no-pause

3. Script injection:

frida -U -l script.js com.example.app

JavaScript API Basics

Frida scripts use JavaScript with special APIs:

// Entry point
Java.perform(function() {
    console.log("[*] Script loaded");

    // Your instrumentation code here
});

Hooking Techniques

Hooking Java Methods (Android)

Basic method hooking:

Java.perform(function() {
    var MainActivity = Java.use("com.example.MainActivity");

    // Hook method with no arguments
    MainActivity.checkLicense.implementation = function() {
        console.log("[+] checkLicense called");
        return true; // Always return true
    };
});

Method with arguments and return value:

Java.perform(function() {
    var Crypto = Java.use("com.example.security.Crypto");

    Crypto.encrypt.overload('java.lang.String', 'java.lang.String')
        .implementation = function(plaintext, key) {

        console.log("[+] encrypt() called");
        console.log("    Plaintext: " + plaintext);
        console.log("    Key: " + key);

        // Call original method
        var result = this.encrypt(plaintext, key);

        console.log("    Result: " + result);
        return result;
    };
});

Handling method overloads:

Java.perform(function() {
    var Utils = Java.use("com.example.Utils");

    // Hook specific overload
    Utils.process.overload('int').implementation = function(value) {
        console.log("[+] process(int): " + value);
        return this.process(value);
    };

    Utils.process.overload('java.lang.String').implementation = function(text) {
        console.log("[+] process(String): " + text);
        return this.process(text);
    };
});

Hooking Native Functions

Hooking C/C++ functions in shared libraries:

// Find module
var libcrypto = Process.getModuleByName("libcrypto.so");

// Find function by name
var EVP_EncryptInit = libcrypto.getExportByName("EVP_EncryptInit");

// Hook the function
Interceptor.attach(EVP_EncryptInit, {
    onEnter: function(args) {
        console.log("[+] EVP_EncryptInit called");
        console.log("    ctx: " + args[0]);
        console.log("    cipher: " + args[1]);
    },
    onLeave: function(retval) {
        console.log("    Return value: " + retval);
    }
});

Hooking by address:

// Hook function at specific address
var targetAddress = ptr("0x12345678");

Interceptor.attach(targetAddress, {
    onEnter: function(args) {
        console.log("[+] Function at 0x12345678 called");
        console.log("    arg0: " + args[0]);
        console.log("    arg1: " + args[1]);
    }
});

Hooking Objective-C Methods (iOS)

Basic method hooking:

if (ObjC.available) {
    var className = "ViewController";
    var hook = ObjC.classes[className];

    // Hook instance method
    var checkPremium = hook["- checkPremiumStatus"];

    Interceptor.attach(checkPremium.implementation, {
        onEnter: function(args) {
            console.log("[+] checkPremiumStatus called");
        },
        onLeave: function(retval) {
            retval.replace(ptr("0x1")); // Return true
        }
    });
}

Method swizzling:

var hook = ObjC.classes.ViewController;
hook['- isJailbroken'].implementation = ObjC.implement(hook['- isJailbroken'], function(handle, selector) {
    console.log("[+] Jailbreak check bypassed");
    return 0; // Return NO
});

Practical Attack Scenarios

1. Bypassing SSL Pinning

Universal Android SSL pinning bypass:

Java.perform(function() {
    // Hook OkHttp3
    try {
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
        CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function() {
            console.log('[+] OkHttp3 pinning bypassed');
        };
    } catch(e) {}

    // Hook TrustManager
    var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    var SSLContext = Java.use('javax.net.ssl.SSLContext');

    var TrustManager = Java.registerClass({
        name: 'com.frida.TrustManager',
        implements: [X509TrustManager],
        methods: {
            checkClientTrusted: function(chain, authType) {},
            checkServerTrusted: function(chain, authType) {},
            getAcceptedIssuers: function() { return []; }
        }
    });

    var TrustManagers = [TrustManager.$new()];
    var SSLContext_init = SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom');

    SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
        console.log('[+] SSLContext.init bypassed');
        SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
    };
});

2. Defeating Root Detection

Android root detection bypass:

Java.perform(function() {
    // Hook common root check methods
    var RootBeer = Java.use("com.scottyab.rootbeer.RootBeer");

    RootBeer.isRooted.implementation = function() {
        console.log("[+] Root check bypassed");
        return false;
    };

    RootBeer.detectRootManagementApps.implementation = function() {
        console.log("[+] Root management apps check bypassed");
        return false;
    };

    RootBeer.detectTestKeys.implementation = function() {
        console.log("[+] Test keys check bypassed");
        return false;
    };

    // Hook su binary check
    var Runtime = Java.use('java.lang.Runtime');
    Runtime.exec.overload('[Ljava.lang.String;').implementation = function(cmd) {
        var command = cmd[0];
        if (command.indexOf("su") !== -1 || command.indexOf("which") !== -1) {
            console.log("[+] Blocked command: " + command);
            throw new Error("Command not found");
        }
        return this.exec(cmd);
    };
});

3. Extracting Encryption Keys

Capturing AES keys:

Java.perform(function() {
    var SecretKeySpec = Java.use('javax.crypto.spec.SecretKeySpec');

    SecretKeySpec.$init.overload('[B', 'java.lang.String').implementation = function(key, algorithm) {
        console.log("[+] SecretKeySpec created");
        console.log("    Algorithm: " + algorithm);
        console.log("    Key: " + bytesToHex(key));

        return this.$init(key, algorithm);
    };

    function bytesToHex(bytes) {
        var hex = [];
        for (var i = 0; i < bytes.length; i++) {
            hex.push(('0' + (bytes[i] & 0xFF).toString(16)).slice(-2));
        }
        return hex.join('');
    }
});

4. Monitoring API Calls

Logging HTTP requests:

Java.perform(function() {
    var URL = Java.use("java.net.URL");

    URL.openConnection.overload().implementation = function() {
        var connection = this.openConnection();
        console.log("[+] HTTP Connection");
        console.log("    URL: " + this.toString());
        return connection;
    };

    // Hook OkHttp
    var Request = Java.use("okhttp3.Request");
    var RequestBuilder = Java.use("okhttp3.Request$Builder");

    RequestBuilder.build.implementation = function() {
        var request = this.build();
        console.log("[+] OkHttp Request");
        console.log("    URL: " + request.url().toString());
        console.log("    Method: " + request.method());

        var headers = request.headers();
        for (var i = 0; i < headers.size(); i++) {
            console.log("    " + headers.name(i) + ": " + headers.value(i));
        }

        return request;
    };
});

Advanced Frida Techniques

Memory Scanning and Modification

Search for strings in memory:

var pattern = "password123";
Process.enumerateRanges('rw-').forEach(function(range) {
    Memory.scan(range.base, range.size, pattern, {
        onMatch: function(address, size) {
            console.log("[+] Found '" + pattern + "' at: " + address);
        },
        onComplete: function() {
            console.log("[*] Memory scan complete");
        }
    });
});

Modify memory values:

var targetAddress = ptr("0x12345000");
Memory.writeUtf8String(targetAddress, "new_password");
console.log("[+] Memory modified at: " + targetAddress);

RPC Communication

Create a bidirectional channel between Frida script and Python:

Frida script (script.js):

rpc.exports = {
    decrypt: function(encrypted) {
        var result = performDecryption(encrypted);
        return result;
    },

    getKeys: function() {
        return {
            aes: capturedAESKey,
            rsa: capturedRSAKey
        };
    }
};

Python client:

import frida
import sys

def on_message(message, data):
    print(message)

device = frida.get_usb_device()
pid = device.spawn(["com.example.app"])
session = device.attach(pid)

with open("script.js") as f:
    script = session.create_script(f.read())

script.on('message', on_message)
script.load()

# Call RPC functions
decrypted = script.exports.decrypt("encrypted_data")
print("Decrypted:", decrypted)

keys = script.exports.get_keys()
print("Keys:", keys)

device.resume(pid)
sys.stdin.read()

Stalking and Tracing

Function call tracing:

var targetFunction = Module.findExportByName("libc.so", "open");

Stalker.follow(Process.getCurrentThreadId(), {
    events: {
        call: true
    },
    onReceive: function(events) {
        console.log("[+] Call trace:");
        console.log(Stalker.parse(events));
    }
});

// Follow specific function calls
Interceptor.attach(targetFunction, {
    onEnter: function() {
        Stalker.follow();
    },
    onLeave: function() {
        Stalker.unfollow();
    }
});

Frida Tools Ecosystem

Objection

A runtime mobile security toolkit built on Frida:

# Install
pip install objection

# Launch app with objection
objection -g com.example.app explore

# Common commands
android hooking list activities
android hooking list services
android sslpinning disable
android root disable
memory list modules
memory search "password" --string

Frida-Trace

Automatically trace function calls:

# Trace all ObjC methods
frida-trace -U -f com.example.app -m "*[* *]"

# Trace specific Java methods
frida-trace -U -f com.example.app -j '*!check*/i'

# Trace native functions
frida-trace -U -f com.example.app -i open -i read -i write

Frida-PS

List processes and applications:

# List running processes on device
frida-ps -U

# List installed applications
frida-ps -Uai

# Filter by name
frida-ps -U | grep example

Best Practices

1. Error Handling

Always wrap your hooks in try-catch:

Java.perform(function() {
    try {
        var TargetClass = Java.use("com.example.Target");
        TargetClass.sensitiveMethod.implementation = function() {
            console.log("[+] Hooked successfully");
            return this.sensitiveMethod();
        };
    } catch(e) {
        console.log("[-] Error: " + e);
    }
});

2. Performance Considerations

Avoid excessive logging in hot paths:

var callCount = 0;
var logInterval = 100;

TargetClass.frequentMethod.implementation = function() {
    callCount++;
    if (callCount % logInterval === 0) {
        console.log("[+] Called " + callCount + " times");
    }
    return this.frequentMethod();
};

3. Clean Hooking

Preserve original functionality:

var original = TargetClass.method;
TargetClass.method.implementation = function() {
    // Pre-processing
    console.log("[+] Before call");

    // Call original
    var result = original.apply(this, arguments);

    // Post-processing
    console.log("[+] After call, result: " + result);

    return result;
};

Real-World Use Cases

Security Testing

  • Identify vulnerabilities in mobile applications
  • Test authentication mechanisms
  • Verify encryption implementation
  • Audit API security

Malware Analysis

  • Understand malicious behavior at runtime
  • Extract C2 server addresses
  • Decrypt communications
  • Bypass anti-analysis techniques

Research and Development

  • Reverse engineer proprietary protocols
  • Understand closed-source libraries
  • Prototype security features
  • Create proof-of-concept exploits

Resources and Learning

Official Documentation

Community Resources

  • Blog Posts: Follow security researchers sharing Frida scripts
  • YouTube Channels: Many security channels feature Frida tutorials
  • Discord/Telegram: Join Frida community channels

Practice Platforms

  • OWASP MSTG Crackmes: Practice mobile security with Frida
  • HackTheBox Mobile Challenges: Apply Frida to CTF challenges
  • Your Own Apps: Best way to learn is by practicing on your own applications

Conclusion

Frida has revolutionized dynamic analysis by making runtime instrumentation accessible and powerful. Whether you’re performing security assessments, analyzing malware, or conducting research, Frida provides the tools needed to understand and manipulate application behavior in real-time.

Key takeaways:

  • ✅ Frida enables runtime code manipulation without recompilation
  • ✅ JavaScript API makes hooking accessible to a wide audience
  • ✅ Works across Android, iOS, Windows, macOS, and Linux
  • ✅ Extensive ecosystem of tools and community scripts
  • ✅ Essential skill for modern mobile security professionals

Start with simple hooks, experiment with different techniques, and gradually build your Frida toolkit. The ability to see and modify application behavior at runtime opens up endless possibilities for security research and reverse engineering.

Next Steps: Explore our companion guides on Android Reverse Engineering and Binary Analysis Fundamentals to complement your Frida skills.


Happy hooking and stay curious!

Contents

0/0 sections read

No headings found
Press ESC to closeNavigation

Latest Posts

See all posts

Let's work together