Frida
Frida is a dynamic instrumentation toolkit for developers, reverse-engineers, and security researchers. It allows injecting custom scripts into processes on various platforms (Windows, macOS, Linux, Android, iOS, tvOS, watchOS, QNX, etc.) to inspect, modify, and trace their behavior at runtime. The current Python binding version is 17.9.1, and it maintains a rapid release cadence with frequent updates.
Warnings
- breaking The `frida` Python package version MUST precisely match the version of `frida-server` running on the target device/host. A version mismatch (even minor versions like 17.0.x vs 17.2.x) is the most frequent cause of 'Unable to connect', 'Failed to inject', or 'Lost connection' errors.
- gotcha Frida operations often require elevated privileges. Attaching to system processes, processes owned by other users, or performing certain injections typically requires root/administrator permissions. Running without sufficient privileges will result in 'access denied' or 'permission denied' errors.
- breaking Major `frida` versions (e.g., from 16.x to 17.x) frequently introduce breaking changes to both the Python API and the underlying JavaScript (GumJS) API. This can lead to scripts failing or behaving unexpectedly after an upgrade.
- gotcha When injecting a JavaScript script, the Python host script must keep the `session` alive for the JavaScript to continue executing. If the Python script exits or `session.detach()` is called prematurely, the injected script will stop. For long-running monitoring, use `sys.stdin.read()` or block indefinitely waiting for messages.
Install
-
pip install frida -
pip install frida-tools
Imports
- frida
import frida
- Device
frida.get_local_device()
- Session
device.attach(pid)
- Script
session.create_script(source)
Quickstart
import frida
import sys
import os
import time
def on_message(message, data):
print(f"[+] Message from script: {message}, data: {data}")
try:
# Define a target process. On Windows, try 'notepad.exe'. On Unix-like, try 'bash' or 'sleep 60'.
# You can set this via an environment variable or change it directly.
# Example: FRIDA_TARGET_PROCESS=bash python your_script.py
process_name = os.environ.get('FRIDA_TARGET_PROCESS', 'notepad.exe' if sys.platform == 'win32' else 'bash')
print(f"[*] Attempting to attach to process: {process_name}")
session = None
try:
session = frida.attach(process_name)
print(f"[*] Attached to {session.pid}")
except frida.ProcessNotFoundError:
print(f"[-] Process '{process_name}' not found. Trying to spawn it.")
# Spawning might require specific paths or arguments for the process.
if sys.platform == 'win32':
spawn_cmd = [process_name] # For notepad.exe
else:
# For bash, spawn it with a command that keeps it alive for a bit
spawn_cmd = [process_name, '-c', 'sleep 60 & exec bash'] # or ['sleep', '60']
pid = frida.spawn(spawn_cmd)
session = frida.attach(pid)
print(f"[*] Spawned PID: {pid}. Attached.")
# Resume the spawned process if it was suspended (default for frida.spawn)
frida.resume(pid)
time.sleep(1) # Give it a moment to stabilize after resume
script_source = """
// Example JavaScript payload: Intercept a file opening function.
// Note: 'open' is common on Unix-like. On Windows, 'CreateFileW' is often used.
var targetFunction = null;
if (Process.platform === 'windows') {
targetFunction = Module.findExportByName('kernel32.dll', 'CreateFileW');
if (targetFunction) {
Interceptor.attach(targetFunction, {
onEnter: function(args) {
this.filename = Memory.readUtf16String(args[0]);
console.log('[JS] CreateFileW() called with filename: ' + this.filename);
},
onLeave: function(retval) {
// console.log('[JS] CreateFileW() returned: ' + retval);
}
});
console.log('[JS] Frida script for CreateFileW loaded!');
} else {
console.log('[JS] CreateFileW not found in kernel32.dll');
}
} else {
targetFunction = Module.findExportByName(null, 'open');
if (targetFunction) {
Interceptor.attach(targetFunction, {
onEnter: function(args) {
this.path = Memory.readUtf8String(args[0]);
console.log('[JS] open() called with path: ' + this.path);
},
onLeave: function(retval) {
// console.log('[JS] open() returned: ' + retval);
}
});
console.log('[JS] Frida script for open() loaded!');
} else {
console.log('[JS] open not found.');
}
}
console.log('[JS] Frida script initialization complete!');
"""
script = session.create_script(script_source)
script.on('message', on_message) # Attach a Python callback for messages from the JS script
script.load() # Inject and execute the JavaScript
print("[+] Script loaded. Intercepting calls. Press Enter to detach and exit...")
sys.stdin.read() # Keep the Python script alive to allow interaction
except frida.core.RPCException as e:
print(f"[-] Frida RPC Error: {e}")
if "Unable to connect" in str(e) or "Failed to attach" in str(e):
print(" Hint: Ensure 'frida-server' is running on the target device/host, or that you have sufficient permissions.")
elif "Process not found" in str(e):
print(" Hint: The target process might not be running or the name is incorrect.")
except Exception as e:
print(f"[-] An unexpected error occurred: {e}")
finally:
if session:
print("[*] Detaching from process...")
session.detach()
print("[*] Exited.")