SuperAPI AI Directory

re-frida

General & other

View repository

Updated 10-15 21:34

About this skill

Frida dynamic instrumentation

When to use / when not to use

  • Use: when you need to hook functions (intercept / modify arguments / modify return values), enumerate modules and classes, bypass certificate pinning / root detection, observe runtime call chains
  • Use: unified instrumentation needs across mobile (Android / iOS) and desktop (Linux / macOS / Windows)
  • Don't use: when you only need static analysis (for Android use [[re-apk]], for formats use the [[re-format-elf]] / [[re-format-macho]] series)
  • Don't use: when you need syscall-level tracing (use [[re-tracing]])
  • Don't use: when you need a full debugger experience (breakpoints / single-stepping / memory, use [[re-gdb]] / [[re-lldb]] / [[re-x64dbg]])

Tool preparation

Dynamic analysis follows the highest principle of [[re-analyze/platform-tips]]: execute mobile-side within a controlled device / emulator snapshot, and confirm the sandbox environment before desktop-side instrumentation. Verify all tools before use.

frida-tools —— host-side command-line tools

  • Cross-platform (Linux / macOS / Windows): pip install frida-tools(Python 3.8+; venv recommended: python3 -m venv venv && venv/bin/pip install frida-tools)
  • Verify: frida --version(outputs the frida version number);frida-ps -U / frida-trace -h available

frida-server —— mobile-side instrumentation agent

  • Download: GitHub release https://github.com/frida/frida/releases, choose frida-server-<版本>-android-<架构>(choose -arm64for arm64, -armfor 32-bit, -x86_64)
  • Client and frida-server must at least share the same major(compare against frida --version); to avoid feature/API differences, using exactly the same and latest version is recommended. The architecture must match the device, otherwise the connection reports a protocol error (see Pitfall 1)
  • Android push and launch:
    adb push frida-server-xxx /data/local/tmp/frida-server
    adb shell "chmod 755 /data/local/tmp/frida-server"
    adb shell "su -c /data/local/tmp/frida-server" &   # 或 adb root 后直接运行
    
  • iOS jailbroken device: add the repo https://build.frida.re in Sileo / Cydia to install the frida deb, or ssh root@<设备IP> then install
  • Verify: host frida-ps -U can list device processes (-U = USB device,-R = remote ip:port)

objection —— quick instrumentation without writing JS

  • Requires Python >=3.10 (different from the >=3.7 minimum of frida's own binding, must be satisfied separately): python -m pip install -U objection, recommended to create a separate 3.10+ venv or isolate with pipx
  • Verify: objection --version
  • Usage: objection -n <应用或 Bundle ID> start, built-in android hooking / ios hooking subcommands (e.g. android hooking list activities、ios sslpinning disable)
  • As of 1.12.x -g / explore is deprecated (hidden in the CLI, only a deprecation warning is emitted, the old syntax still runs); for new scripts use -n + start

Procedure

Execute in order, and record the result of each step.

  1. spawn vs attach choice:

    frida -U -f com.target.app              # spawn:从零启动应用,能抓启动早期逻辑(解密 / 初始化)
    frida -U com.target.app                 # attach:附加到已运行进程(不重启)
    frida-trace -U -f com.target.app -i "Java!*"   # 启动即跟踪 Java 方法调用
    

    Rule: use spawn to capture startup logic / bypass early detection; use attach to merely observe the current state. attach happens later than startup and may miss early logic that has already executed (see Pitfall 2).

  2. Writing JS hooks (intercept / modify arguments / return values):

    // hook.js —— 拦截、改参数、改返回值(Android Java 层)
    Java.perform(function () {
      var cls = Java.use("com.example.Target");
      cls.doLogin.implementation = function (user, pass) {
        console.log("doLogin(" + user + ", " + pass + ")");
        return this.doLogin("hacked", "pass123");   // 改参
      };
      cls.isLicensed.implementation = function () {
        return true;                                // 改返回值
      };
    });
    
    frida -U -f com.target.app -l hook.js
    

    For native functions use Interceptor.attach(Process.getModuleByName("libfoo.so").findExportByName("func"), { onEnter: ..., onLeave: ... }) to intercept (modify arguments in onEnter, modify the return value in onLeave with retval.replace() ). Same example on desktop:frida -p <pid> -l hook.js。

  3. Enumeration and invocation (enumerateModules / Java.perform):

    Java.perform(function () {
      Java.enumerateLoadedClasses({
        onMatch: function (c) { if (c.indexOf("target") >= 0) console.log(c); },
        onComplete: function () {}
      });
    });
    
    // 模块与导出枚举(原生层)
    Process.enumerateModules().forEach(function (m) { console.log(m.name + " " + m.base); });
    Process.getModuleByName("libfoo.so").enumerateExports().forEach(function (e) { console.log(e.name); });
    

    Run: frida -U -f com.target.app -l enum.js. After locating the target, invoke it directly and actively:Java.use("com.x").method(...) / native exported functions.

  4. Bypassing certificate pinning / detection (common templates):

    // Android SSL 绕过模板(配合抓包工具)
    Java.perform(function () {
      var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
      var TrustAll = Java.registerClass({
        name: "com.bypass.TrustAll",
        implements: [X509TrustManager],
        methods: { checkClientTrusted: function () {}, checkServerTrusted: function () {},
                   getAcceptedIssuers: function () { return []; } }
      });
      var SSLContext = Java.use("javax.net.ssl.SSLContext");
      SSLContext.init.implementation = function (km, tm, sr) {
        this.init(km, [TrustAll.$new()], sr);
      };
    });
    
    // iOS 证书绕过:hook SecTrustEvaluateWithError 返回值
    var SecTrust = Module.findGlobalExportByName("SecTrustEvaluateWithError");   // Frida 17+:全局符号静态查找
    Interceptor.attach(SecTrust, { onLeave: function (r) { this.context.x0 = 0; } }); // arm64 返回寄存器 x0;x86_64 用 rdi 场景先验证
    

    Ready-made commands:objection -n com.target.app start → android sslpinning disable / ios sslpinning disable。

  5. Anti-detection countermeasures (hide frida-server, rename):

    • Common detection points: frida-server default port 27042,/data/local/tmp/frida-server path,gum-js-loop / gmain thread name,/proc/self/maps frida signatures in,frida string
    • Countermeasures:
      # 1) 二进制改名后启动(避开路径检测)
      cp frida-server-xxx /data/local/tmp/fridad
      adb shell "su -c 'chmod 755 /data/local/tmp/fridad && /data/local/tmp/fridad' &"
      # 2) 换端口 + adb 端口转发,主机用 -H 连接
      adb shell "su -c '/data/local/tmp/fridad -l 0.0.0.0:27142' &"
      adb forward tcp:27142 tcp:27142
      frida -H 127.0.0.1:27142 -f com.target.app
      
    • Root detection countermeasures: first hook the detection function to change the return value, then instrument the target:Java.use("com.target.rootcheck").isRooted.implementation = function () { return false; };
    • Still detected → inject frida-gadget into the App process (gadget listen (interactive) mode), together with [[re-apk]]'s smali patch to load libgadget.so
  6. Script templates and countermeasure methodology: for common script skeletons see [[frida-scripts]] (TLS keylog / DEX/SO dump / JNI registration restoration / encryption interception / detection bypass table); for the crash-iteration method and detection-surface comparison table see [[re-analyze/anti-dynamic-workflow]] — run a baseline first to see bare crashes, then hook at specific points, do not preset a full bypass toolkit.

Cross-domain combinations

  • [[re-address-space]]: runtime base address conversion (Process.getModuleByName(...).base aligned with the link address)

  • [[re-mobile]]: step 3 of the workflow, dynamic instrumentation, always calls this skill

  • [[re-apk]] / [[re-ios]]: call this skill when runtime behavior is needed after static analysis (decryption / hooking / bypass / unpacking execution)

  • System call-level tracing complement → [[re-tracing]]; runtime memory extraction → [[re-memdump]]

  • This skill is called by [[re-analyze]]'s triage "mobile app analysis" path (re-mobile → re-frida)

  • Script generation: target characteristics → template selection → rewrite and verify → [[re-frida-script-author]] (template materials [[frida-scripts]])

Common pitfalls and traps

  • Version mismatch → protocol error: symptom —frida-ps -U reports unable to communicate with the frida server / protocol error; cause — the client and frida-server major versions are inconsistent, or the feature used does not exist on the target version; countermeasure — first check whether the major versions match (frida --version against the frida-server version, see tool preparation), then confirm whether that feature is supported by the corresponding version; for the upgrade side use pip install -U frida-tools
  • Frida 17 removes the static Module lookup/enumeration APIs: symptom — the script reports TypeError: Module.findExportByName is not a function(or the same kind of getExportByName / enumerateExports / findBaseAddress); cause — since 17.0.0 all static lookup and enumeration APIs have been removed, and statically only Module.load / Module.findGlobalExportByName / Module.getGlobalExportByNameremains, while the rest have changed tomodule instance methods; countermeasure — get the instance first, then look up:Process.getModuleByName("libfoo.so").findExportByName("func")(find returns null, get throws an exception), for the base address use .base, for enumeration use .enumerateExports(); for global symbols (formerly the null module parameter) use Module.findGlobalExportByName(...)instead; note that Process.getModuleByName when the module is not loadedthrows an exception(the old Module.findExportByName returned null) — when unsure, first use Process.findModuleByName to check for null before looking up; before writing, frida --version confirm the version before choosing the syntax (for the migration checklist see the version-related group in [[re-frida-script-author]])
  • spawn timing too late → miss early logic: symptom — after attach, the hook does not trigger or early decryption has already finished; cause — the app decrypts / verifies right at startup, and by the time of attach it is already past; countermeasure — use -f spawn mode to instrument from the very start; if it is still missed, hook dlopen / ClassLoader.loadClass or similar earlier execution points
  • Objection Detection frida (Ports / Signatures): Symptoms—the app crashes / freezes / behaves abnormally after spawn; Cause—the app scans port 27042, the frida-server path,frida thread names, or maps signatures; Countermeasure—rename in step 5 + change the port; if still detected, use frida-gadget injection (hidden within the process)
  • root detection blocks instrumentation: Symptoms—frida can connect but the hook doesn't take effect or it exits immediately; Cause—the app performs root / jailbreak detection first and exits directly upon detecting the environment; Countermeasure—first hook the return value of the detection function (step 5 template), and after passing detection, hook the target function
  • JS script fails silently: Symptoms—the script loads with no errors but the hook produces no output; Cause—incorrect class name,Java.perform calling the Java API from outside, or a module name with mismatched case; Countermeasure—first verify the names using the enumeration from step 3, and within the script use console.log instrumentation to locate the issue
  • Hooking exported APIs is bypassed by packers (direct syscall / API name hashing): Symptoms—successfully hooked IsDebuggerPresent/NtQueryInformationProcess and other exports, yet anti-debugging still triggers and the process exits; Cause—the packed/hardened target does not go through the import table: it implements GetProcAddressitself, stores API names as hashes, or makes direct syscalls (inline Nt* direct calls), so the hook point is never traversed; Countermeasure—hook one layer deeper (the syscall wrapper point in ntdll), trace GetProcAddress/hash resolution locations to reverse-engineer the real call sites, and hook anti-debugging and anti-VM (RegOpenKeyExA/GetSystemFirmwareTable) APIs as well (Arkana project practical template); first use Process.enumerateModules()/enumerateExports to confirm the actual call target before instrumenting
  • Detection point upgrades (memfd/JIT pool/memory strings/pipe names): Symptoms—still detected after renaming and changing the port to start frida-server; Cause—newer detection goes beyond ports and paths: it scans /proc/self/fd memfd names, JIT cache pools (pool-frida),frida_agent.so/frida_rpc and other in-memory strings and exported symbols, pipe/linjector names, and even non-standard ports are scanned (Promon-style scanning); Countermeasure—use the undetected-frida patch set (full obfuscation of strings/symbols/threads/protocol/memfd/JIT pool, in Magisk/KSU module form), or frida-gadget + a self-compiled hidden version, to reduce the fingerprint surface
  • Strong hardening (Pairipcore-type) as a whole against Frida: Symptoms—common hook scripts (certificate bypass/anti-detection/decryption) all fail, the app crashes or behaves abnormally on spawn, and community reports say frida-interception-type scripts cannot bypass it; Cause—commercial hardening performs C++/Java dual-layer integrity checks + pseudo-VM instructions + custom dlopen/dlsym/syscall dynamic import obfuscation + prctl/clone/waitpid/ptrace anti-debugging + /proc/self/maps+/proc/self/status process monitoring + non-standard port scanning, a whole defensive line rather than a single point; Countermeasure—first deal with the integrity check and process monitoring (hook the check function's return, patch the monitoring points), then get past anti-debugging, and make hooks land at the custom import resolution sites rather than exported APIs; if necessary, combine with [[re-apk]] static smali modification + Native-layer instrumentation
  • Troubleshooting hooks on egress functions with multiple processes/implementations: Symptoms—hooking SocketOutputStream yields no result, yet packet capture shows traffic; Cause—the app is multi-process (the traffic-sending logic is in a child process) or uses Netty's SocketChannelImpl(which does not go through the OutputStream path); Countermeasure—troubleshooting order: hook the outermost egress → if no result ps -e check child processes and hook each → then try SocketChannelImpl and other alternative implementations; after a successful hook, print the stack (Netty's MessageToByteEncoder chain) locate assembly/encryption code; trace plaintext objects and encrypted artifacts layer by layer upward from the "codec chain"
  • Symmetric encryption key transmission tracing: Symptom — an RC4/AES ciphertext packet is captured, but the key changes every session and cannot be found by static search; Cause — the symmetric encryption key is dynamically generated and transmitted online (in plaintext/carried after encryption/negotiated); Countermeasure — hook the key input point (the encryption function parameter bArr class) to get the current key, then trace upward to the key's source and transmission path, cross-checking with packet captures (a fixed header in the first packet such as 89 04 01 01 can serve as a protocol anchor)
  • Multi-layer TLS verification stack: Symptom — some requests still get SSL errors after a single-point hook; Cause — the App simultaneously uses multiple stacks: OkHttp/native HttpsURLConnection/Conscrypt; Countermeasure — layered coverage (hook OkHttp CertificatePinner + TrustManagerImpl.verifyChain + HostnameVerifier at the same time); only full-stack coverage counts as a completed bypass
  • Locating target classes after ProGuard obfuscation: Symptom — class names are changed to short names; Cause — obfuscation; Countermeasure — in jadx, use Find Usages to reverse-lookup who instantiates the key Builder, and infer the original class from the instantiation point (Source: reverse-skill field-journal, MIT)
  • Wrong test environment chosen → dynamic false negatives / delivery false positives: Symptom — in the same environment, hooks do not trigger, memory patches have no effect, or everything works fine on an emulator but problems appear on a real device; Cause — live Frida is unavailable on x86 translation-layer emulators (Process.arch==x64 and the target native so cannot be found), and the runtime never executes your bytes; some targets have lax verification on emulators, so "it runs on the emulator" may be a degraded illusion; Countermeasure — choose the environment by phase: static patching and rendering regression can use any emulator (including x86 translation layers), dynamic instrumentation must use native ARM, and delivery verification must use a real device matching the target; translation-layer illusions can be falsified: patch the first instruction of a known-required function to a bare ret, and if the UI still renders normally, the runtime is not executing your bytes — immediately stop live work in that environment (static patches on disk will be re-translated on the next launch and remain usable); before collecting on a real device, svc power stayon true and wake it to prevent sleep/screen-off from causing "blank" false screenshots (Source: reverse-skills (inliver233), MIT)
  • Pitfalls of setting up and using the redroid container: Symptom — the guest hangs silently / servicemanager spins idle, vold crashes, adb repeatedly disconnects, and the container cannot be restarted; Cause — incorrect binder mount method (must bind-mount /dev/binder, cannot use --device or binderfs), failure to add androidboot.use_memfd=1when the new kernel lacks ashmem, setting ro.secure=1 ro.debuggable=0 which kills root adb, the binder of a stopped container having disappeared so docker restart is impossible, and adbd crashing under high load; Countermeasure — use bind-mount for binder, force use_memfd on new kernels, retain root adb (to prevent tampering, keep the signature valid rather than hiding root), and re-inject the adb public key after recreating the container (echo $PUBKEY > /data/misc/adb/adb_keys); use docker cp rather than adb push for large files; use spawn mode for dynamic analysis (frida -U -f, attach often gets 0 hits under anti-tampering); Positioning insight — it is a data collection tool, not an iteration environment; after collecting dynamic data, return to a stable environment for static patch regression (Source: reverse-skills (inliver233), MIT)
  • Hooks silently fail to trigger on AOT targets / caller cannot be found: Symptom — the hook produces no output, the target function has static callers=0, and the guard field is unknown so no probe can be placed; Cause — the target so is loaded later than the engine (not loaded at attach time), virtual methods go through the cid dispatch wall (not called via BL, so there is no static shortcut), and the guard field's location is unknown; Countermeasure — hook after the target so has loaded: a 200ms polling findModuleByName waitForLibapp wrapper, preferring spawn mode at the start; break the cid wall: record the target function's onEnter for lr(returnAddress)+ Thread.backtrace(Backtracer.ACCURATE) the first 20 frames, and use one backtrace to locate the dispatch point; the guard-location trio: in the Stalker.follow transform, put a putCallout on ldr class instructions (the first hit PC = guard), MemoryAccessMonitor.enable and other rebuild triggers (arm64 support varies and needs a fallback), and candidate hook sweeping;Flip verification at runtime as a precondition:live writePointer flip the candidate guard field → trigger a rebuild → if the component disappears, the guard field is confirmed; after confirmation, then apply the static patch, saving repeated repackaging; since frida 17 the Java bridge is no longer built into the underlying GumJS runtime, but frida CLI/REPL and frida-trace still ship with the three bridges, and ordinary frida -l scripts need no changes; only when building your own agent / injecting scripts via bindings do you need to explicitly install frida-java-bridge(use frida-compile to package if necessary) (Source: reverse-skills (inliver233), MIT)