Frida Native Hooks
Use this skill when the target is a native function or memory behavior.
Discovery First
- Identify process architecture and module:
console.log(Process.arch, Process.platform, Process.pointerSize); for (const m of Process.enumerateModules()) console.log(m.name, m.base, m.size); - Resolve addresses through Frida APIs when possible:
const mod = Process.getModuleByName("libtarget.so"); const fn = mod.getExportByName("target_function"); - If names are stripped, search imports/exports first, then symbols, strings, and byte patterns.
- On ARM/Thumb, prefer addresses returned by Frida APIs so instruction-set details are handled correctly.
Hook Pattern
const target = Process.getModuleByName("libc.so").getExportByName("open");
Interceptor.attach(target, {
onEnter(args) {
this.path = args[0].readUtf8String();
console.log("[open] path=", this.path);
},
onLeave(retval) {
console.log("[open] fd=", retval.toInt32());
}
});
Replacement Rules
- Use
Interceptor.attach()for tracing and light argument/return mutation. - Use
Interceptor.replace()when fully replacing behavior. - Use
Interceptor.replaceFast()only for hot functions where lower overhead is required; call the original through the returned pointer. - Wrap original functions with
NativeFunctionusing the correct ABI, return type, and argument types. - Call
Interceptor.flush()only when a just-installed hook must be invoked immediately from the same script.
Practitioner Patterns
- Hook boundaries before internals: exported API, imported libc/crypto/TLS calls, file/network/syscall wrappers, then app-specific stripped functions.
- When names are stripped, combine static strings, imports, cross-references from a disassembler, runtime module load logs, and Stalker/caller summaries.
- Use
Thread.backtrace()early to find who called a generic boundary such asopen,connect,SSL_write,CCCrypt,strcmp, ormemcmp. - Use Stalker for bounded questions only: a specific thread, during a specific function call, with call/block summaries before instruction-level tracing.
- Treat anti-Frida checks as native work when they involve
/proc,ptrace, loaded module scans, thread names, ports, or symbol/string scans.
Memory Safety
- Do not write a longer string into an existing buffer unless the buffer size is proven.
- Prefer allocating a new string and replacing the pointer argument.
- Keep allocated replacement strings reachable for the required lifetime:
const keepalive = []; Interceptor.attach(target, { onEnter(args) { const s = Memory.allocUtf8String("replacement"); keepalive.push(s); args[0] = s; } }); - Copy
retval/argument data inside callbacks if it is needed later.
Native Investigation Checklist
- Module loaded before resolving address.
- Function signature verified from headers, decompiler, debug symbols, or observed calling convention.
- Pointer reads guarded for null and readability.
- Logs include enough context to prove the right call path.
- Mutation has a rollback path: detach, revert, restart, or remove Gadget config.
References
Read references/native-patterns.md for templates covering exports, imports, pattern scans, backtraces, replacements, and performance notes.