frida::dynamic

tool frida instrumentation
SetupCLI toolsIntercept MemoryReplaceNative AndroidJavaScripts
01Setup
Install
# Install frida tools
pip install frida frida-tools

# Verify
frida --version
frida-ps --version

# For Android: push frida-server to device
# Download matching version from:
# https://github.com/frida/frida/releases

# Push and run on Android
adb push frida-server-16.x-android-arm64 /data/local/tmp/frida-server
adb shell chmod +x /data/local/tmp/frida-server
adb shell /data/local/tmp/frida-server &

# Forward port (if needed)
adb forward tcp:27042 tcp:27042
Frida API objects summary
ObjectPurpose
InterceptorHook function calls (entry/exit)
MemoryRead/write/scan/allocate memory
ModuleELF/SO info, exports, base address
ProcessModules, threads, env, platform
NativeFunctionCall native functions from JS
NativePointerPointer arithmetic + read/write
JavaHook Java methods (Android)
ObjCHook Objective-C (iOS/macOS)
StalkerCode coverage / tracing
CModuleCompile inline C in script
02CLI Tools
frida CLI
# List processes
frida-ps                          # local
frida-ps -U                        # USB (Android)
frida-ps -H host:port              # remote
frida-ps -a                        # with paths

# Attach to process
frida -n com.target.app            # by name
frida -p 1234                      # by PID
frida -U -n com.target.app        # USB device

# Spawn (start + inject before main)
frida -f ./challenge               # spawn binary
frida -U -f com.target.app        # spawn Android
frida -f ./challenge --no-pause   # don't pause at entry

# Load script
frida -f ./challenge -l hook.js
frida -p 1234 -l hook.js --no-pause
frida-trace
# Trace function calls automatically
# Creates JS handlers in __handlers__/

# Trace by module!function
frida-trace -n challenge -i "strcmp"
frida-trace -n challenge -i "*check*"  # wildcard
frida-trace -n challenge -i "libc.so!*"

# USB Android
frida-trace -U -f com.app -j '*!*verify*'  # Java method
frida-trace -U -f com.app -i '*crypt*'     # native

# frida-discover — list function names
frida-discover -n challenge

# frida-kill
frida-kill -n com.target.app
03Interceptor — Hook Functions
JSBasic hook
// hook.js — intercept any function

// By name (exported function)
const strcmp = Module.getExportByName(null, 'strcmp');
Interceptor.attach(strcmp, {
    onEnter(args) {
        // args[0], args[1] ... are NativePointers
        console.log('strcmp:',
            args[0].readUtf8String(),
            args[1].readUtf8String()
        );
    },
    onLeave(retval) {
        console.log('  → returned:', retval.toInt32());
        // Force return value:
        retval.replace(0);   // return 0 = equal
    }
});
JSHook by address
// Hook by absolute or relative address

// No PIE: absolute address
const target = ptr('0x401234');

// PIE: base + offset
const base = Module.getBaseAddress('challenge');
const target = base.add('0x1234');   // offset from base

Interceptor.attach(target, {
    onEnter(args) {
        console.log('hit!');
        console.log('rdi:', this.context.rdi);
        console.log('rsi:', this.context.rsi);
        console.log('rsp:', this.context.rsp);
    }
});

// this.context has all registers:
// rax, rbx, rcx, rdx, rdi, rsi
// r8-r15, rsp, rbp, rip, eflags
JSModify args & return
// Modify argument before function runs
Interceptor.attach(checkFunc, {
    onEnter(args) {
        // Replace first arg with "admin"
        const newArg = Memory.allocUtf8String('admin');
        args[0] = newArg;

        // Replace integer arg
        args[1] = ptr(1337);
    }
});

// Force return value
Interceptor.attach(target, {
    onLeave(retval) {
        retval.replace(1);       // return 1
        retval.replace(ptr('0x1337'));  // return pointer
    }
});

// Completely replace function
Interceptor.replace(target, new NativeCallback(
    () => 1,   // always return 1
    'int', []
));
04Memory — Read, Write, Scan
JSRead memory
const p = ptr('0x404060');

// Read various types
p.readU8()              // 1 byte unsigned
p.readS8()              // 1 byte signed
p.readU16() / p.readU32() / p.readU64()
p.readS16() / p.readS32() / p.readS64()
p.readFloat() / p.readDouble()
p.readPointer()         // read pointer (arch-width)
p.readUtf8String()      // null-terminated UTF-8
p.readUtf8String(20)    // max 20 bytes
p.readUtf16String()     // UTF-16 (Windows)
p.readByteArray(16)     // 16 raw bytes → ArrayBuffer

// Dereference
p.readPointer().readUtf8String()  // follow pointer chain
JSWrite & allocate
const p = ptr('0x404060');

// Write values
p.writeU8(0x41)
p.writeU32(0xdeadbeef)
p.writeUtf8String('new_value')
p.writeByteArray([0x90, 0x90, 0xc3])  // NOP NOP RET

// Allocate new memory
const buf = Memory.alloc(64)           // 64 bytes
const str = Memory.allocUtf8String('hello')
const arr = Memory.allocByteArray([1,2,3])

// Make memory executable
Memory.protect(p, 4096, 'rwx')

// Pointer arithmetic
p.add(8)               // p + 8
p.sub(4)               // p - 4
p.and(0xfff)
p.or(1)
p.xor(ptr('0xff'))
JSMemory scan
// Scan for byte pattern
Memory.scan(
    Module.getBaseAddress('libc.so.6'),
    0x100000,                      // size to scan
    '2f 62 69 6e 2f 73 68',       // /bin/sh
    {
        onMatch(address, size) {
            console.log('/bin/sh at', address);
        },
        onComplete() {}
    }
);

// Scan with wildcards
Memory.scan(base, 0x1000, '48 ?? ?? ?? ?? c3', {
    onMatch(addr) { console.log(addr); }
});

// Synchronous scan (returns array)
const results = Memory.scanSync(base, 0x1000,
    '48 8b 3d ?? ?? ?? ??');
results.forEach(r => console.log(r.address));
05Replace & NativeFunction
NativeFunction — call native
// Call an existing native function from JS
const puts = new NativeFunction(
    Module.getExportByName(null, 'puts'),
    'int',        // return type
    ['pointer']   // arg types
);
puts(Memory.allocUtf8String('hello from frida'));

// Type strings: void int uint32 uint64
//               pointer float double bool char
//               int8 int16 int32 int64
//               uint8 uint16 uint32 uint64

// Call function at arbitrary address
const check = new NativeFunction(
    ptr('0x401234'), 'int', ['pointer', 'int']
);
console.log(check(Memory.allocUtf8String('test'), 42));
Interceptor.replace — full replacement
// Replace function completely with JS callback
Interceptor.replace(
    Module.getExportByName(null, 'strcmp'),
    new NativeCallback(
        function(s1, s2) {
            console.log('strcmp hooked:',
                s1.readUtf8String(),
                s2.readUtf8String());
            return 0;   // always equal
        },
        'int',                     // return type
        ['pointer', 'pointer']     // arg types
    )
);

// Restore original
Interceptor.revert(target);
Interceptor.flush();               # apply all pending
06Module & Process
Module
// List loaded modules
Process.enumerateModules().forEach(m =>
    console.log(m.name, m.base, m.size)
);

// Get specific module
const mod = Process.getModuleByName('libc.so.6');
mod.base     // base address
mod.size     // size
mod.path     // full path

// Get base (shortcut)
Module.getBaseAddress('challenge')
Module.getBaseAddress('libc.so.6')

// Get export address
Module.getExportByName('libc.so.6', 'system')
Module.getExportByName(null, 'puts')  // any module

// List exports
Module.enumerateExports('challenge').forEach(e =>
    console.log(e.name, e.address)
);
Process & threads
// Process info
Process.id              // PID
Process.arch             // 'x64' | 'arm64' | 'ia32'
Process.platform         // 'linux' | 'windows' | 'darwin'
Process.pageSize         // usually 4096
Process.pointerSize      // 8 (64-bit) or 4 (32-bit)

// Memory ranges
Process.enumerateRanges('r-x').forEach(r =>
    console.log(r.base, r.size, r.file?.path)
);

// Threads
Process.enumerateThreads().forEach(t =>
    console.log(t.id, t.state, t.context.rip)
);

// Find address info
Process.findModuleByAddress(ptr('0x401234'))
07Android — Native
Android native hooks
// Hook JNI function (native method)
const lib = Module.getBaseAddress('libnative.so');

// JNI naming: Java_package_Class_method
const checkFunc = Module.getExportByName(
    'libnative.so',
    'Java_com_example_app_MainActivity_check'
);

Interceptor.attach(checkFunc, {
    onEnter(args) {
        // args[0] = JNIEnv*  args[1] = jobject
        // args[2..] = actual Java arguments
        console.log('input:', args[2].readUtf8String());
    },
    onLeave(retval) {
        retval.replace(1);   // return true
    }
});
SSL pinning bypass
// Universal Android SSL unpin
// Source: https://codeshare.frida.re/@pcipolloni/universal-android-ssl-pinning-bypass-with-frida/

frida -U -f com.target.app -l ssl-unpin.js

// Quick TrustManager bypass
Java.perform(() => {
    const TrustManager = Java.registerClass({
        name: 'com.frida.TrustManager',
        implements: [Java.use('javax.net.ssl.X509TrustManager')],
        methods: {
            checkClientTrusted: (chain, authType) => {},
            checkServerTrusted: (chain, authType) => {},
            getAcceptedIssuers: () => []
        }
    });
    // ... install via SSLContext
});
08Android — Java Hooks
JSHook Java methods
Java.perform(() => {
    // Get class
    const MainActivity = Java.use('com.example.app.MainActivity');

    // Hook instance method
    MainActivity.checkPassword.implementation = function(pass) {
        console.log('Password attempt:', pass);
        // Call original
        const result = this.checkPassword(pass);
        console.log('Result:', result);
        return true;   // override return
    };

    // Hook overloaded method (specify signature)
    MainActivity.check.overload('java.lang.String').implementation
        = function(s) {
            console.log(s);
            return true;
        };
});
JSJava utilities
Java.perform(() => {
    // Call static method
    const Log = Java.use('android.util.Log');
    Log.d('FRIDA', 'hello from JS');

    // Create new object
    const StringBuilder = Java.use('java.lang.StringBuilder');
    const sb = StringBuilder.$new();
    sb.append('test');
    console.log(sb.toString());

    // Enumerate class instances
    Java.choose('com.example.app.SecretClass', {
        onMatch(instance) {
            console.log(instance.secretKey.value);  // field
        },
        onComplete() {}
    });

    // Enumerate loaded classes
    Java.enumerateLoadedClasses({
        onMatch: name => { if(name.includes('check')) console.log(name); }
    });
});
09Script Templates & Python Host
pythonPython host script
import frida, sys

SCRIPT = """
Interceptor.attach(Module.getExportByName(null, 'strcmp'), {
    onEnter(args) {
        const s1 = args[0].readUtf8String();
        const s2 = args[1].readUtf8String();
        if (s2 && s2.includes('picoCTF')) {
            send({flag: s2});
        }
    }
});
"""

def on_message(msg, data):
    if msg['type'] == 'send':
        print('[FLAG]', msg['payload'])
    elif msg['type'] == 'error':
        print('[ERROR]', msg['stack'])

device = frida.get_local_device()
pid = device.spawn(['./challenge'])
session = device.attach(pid)
script = session.create_script(SCRIPT)
script.on('message', on_message)
script.load()
device.resume(pid)
sys.stdin.read()     # keep running
Common CTF patterns
// 1. Dump strcmp arguments to find flag
Interceptor.attach(Module.getExportByName(null, 'strcmp'), {
    onEnter(args) {
        try {
            const a = args[0].readUtf8String();
            const b = args[1].readUtf8String();
            if (a || b) console.log(`strcmp(${a}, ${b})`);
        } catch(e) {}
    }
});

// 2. Dump all strings read from file
Interceptor.attach(Module.getExportByName(null, 'fgets'), {
    onLeave(retval) {
        if (retval.isNull()) return;
        console.log('fgets:', this.args[0].readUtf8String());
    }
});

// 3. Trace all calls + args (any function)
const target = ptr('0x401234');
Interceptor.attach(target, {
    onEnter(args) {
        console.log('called!',
            'rdi='+this.context.rdi,
            'rsi='+this.context.rsi);
    }
});
QUICK REFERENCE →  frida -f ./bin -l hook.js spawn+inject  · frida-trace -n bin -i "strcmp" auto-trace  · Interceptor.attach(addr, {onEnter, onLeave})  · args[0].readUtf8String() read string arg  · retval.replace(0) override return  · Module.getBaseAddress('bin').add(offset) PIE addr  · Memory.scanSync(base, size, pattern) find bytes  · Java.perform(() => Java.use('class').method.implementation = ...) Android