Skip to content

Instantly share code, notes, and snippets.

@vadim-a-yegorov
Last active October 1, 2026 10:58
Show Gist options
  • Select an option

  • Save vadim-a-yegorov/6dad7d8a1e73ad5b211032f6e6bbeead to your computer and use it in GitHub Desktop.

Select an option

Save vadim-a-yegorov/6dad7d8a1e73ad5b211032f6e6bbeead to your computer and use it in GitHub Desktop.
Hook, intercept, trace, or patch a live binary across Mach-O, ELF, and PE — Frida Interceptor/Stalker/Memory, ELF LD_PRELOAD + dlsym(RTLD_NEXT) + GOT/PLT + LD_AUDIT, Mach-O DYLD_INSERT_LIBRARIES + __interpose + fishhook, Windows Detours + MinHook + IAT/EAT, ObjC/Java swizzle, inline trampolines, eBPF uprobes. Plus a cheat-code block of fast hook…
frida
Attach: frida -U -n App -l s.js | frida -U -f com.x.app --no-pause -l s.js | frida -p PID.
1 Frida Interceptor.attach — read/modify args + return [cross]
inspect or tweak inputs/outputs of a known function without replacing it.
1 resolve the address (Module.getExportByName(null,...) searches all modules; pass a module name to scope).
2 onEnter runs before the body: args is a NativePointer[]; read with .readUtf8String()/.readU32()/.readPointer(); edit by assigning args[i]=ptr(x) (writes the register/stack slot).
3 stash per-call state on this (a fresh object per invocation) to use it in onLeave.
4 this.context is the CpuContext (this.context.rdi / .x0 ...) for register reads and backtraces.
5 onLeave gets the return value; retval.replace(ptr) overwrites it after the body ran.
const open = Module.getExportByName(null, 'open');
Interceptor.attach(open, {
onEnter(args) { // args[i] = NativePointer of the i-th arg
this.path = args[0].readUtf8String();
this.flags = args[1].toInt32();
if (this.path && this.path.includes('secret')) args[1] = ptr(this.flags & ~2); // O_RDWR->drop write
console.log('open', this.path, this.flags, 'from\n' +
Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
},
onLeave(retval) { // retval is a NativePointer; .replace() to change it
if (this.path === '/etc/blocked') retval.replace(ptr(-1));
}
});
attaching to a tiny leaf that is inlined misses callers -> attach the caller or use Stalker. On arm64 PAC targets Frida handles signing; on a stripped binary use T4 to find the address. Interceptor cannot attach mid-instruction; it needs a function entry or a valid instruction boundary. Verify: trigger the call path; the console.log fires with the live args + backtrace.
2 Frida Interceptor.replace — swap the whole implementation [cross]
fully control a function's behavior, or stub it.
1 build a NativeFunction over the original FIRST (before replacing) so you keep a call-through.
2 NativeCallback(fn, retType, argTypes) wraps a JS fn as native; the signature MUST match the ABI.
3 Interceptor.replace swaps the entry; keep a reference to the NativeCallback alive (store it globally) or GC frees it and the target crashes.
const t = Module.getExportByName(null, 'strcmp');
const orig = new NativeFunction(t, 'int', ['pointer','pointer']);
Interceptor.replace(t, new NativeCallback((a, b) => {
const r = orig(a, b); // call-through when you still want the real result
console.log('strcmp', a.readUtf8String(), b.readUtf8String(), '=', r);
return 0; // or force a verdict
}, 'int', ['pointer','pointer']));
replace is heavier than attach; use attach if you only need to observe/patch args. Signature mismatch = stack corruption. Revert with Interceptor.revert(t).
verify: the call returns your forced value; call-through log matches.
3 Frida call native code + allocate args [cross]
invoke target functions directly (decrypt a buffer, call a getter, resolve an object).
1 Memory.alloc(n) returns a NativePointer to GC-managed memory; Memory.allocUtf8String for C strings.
2 wrap the target with NativeFunction(addr, ret, [args]); addr = Module.findBaseAddress('lib').add(off) for a non-exported fn.
3 call like a JS function; read results from the out pointer.
const malloc = new NativeFunction(Module.getExportByName(null,'malloc'), 'pointer', ['size_t']);
const dec = new NativeFunction(ptr('0x100abc'), 'int', ['pointer','int','pointer']); // off from base below
const buf = Memory.alloc(64); buf.writeByteArray([0xde,0xad]); // Frida-owned, auto-freed
const cstr = Memory.allocUtf8String('/bin/sh');
const out = malloc(0x100);
const n = dec(buf, 2, out); // call it
console.log('dec->', n, out.readByteArray(n));
a non-exported fn needs base+offset (rebased per run; compute from Module base, never a hardcoded VA). 'int64'/'uint64' args take a Frida Int64. Calling a fn that expects a live this/context will fault without it.
verify: compare the output to a known plaintext/expected result.
4 Frida find functions (export / symbol / signature) [cross]
resolve an address on a stripped or dynamically-loaded target.
1 prefer exports (getExportByName) > debug symbols (DebugSymbol) > signature scan > ObjC/Java reflection.
2 for a signature: take ~8-16 bytes of the prologue from your disasm, wildcard the relocated/variable bytes (??).
3 Memory.scan is async; act in onMatch.
Module.getExportByName('libssl.so','SSL_read'); // exported
Process.getModuleByName('libfoo.so').enumerateExports().filter(e=>e.name.includes('parse'));
DebugSymbol.fromName('mangled_or_dwarf_name').address; // if symbols present
// signature scan for a stripped fn:
const m = Process.getModuleByName('app');
Memory.scan(m.base, m.size, '55 48 89 e5 ?? ?? ?? e8', { onMatch(a){ console.log('hit', a); }, onComplete(){} });
// ObjC/Java resolvers: ObjC.classes / Java.enumerateLoadedClasses (T7/T8)
exports are rebased at runtime -> always relative to Module.base. A signature that includes a relocated operand breaks across builds; wildcard it. Multiple matches -> tighten the pattern.
verify: attach to the resolved address and confirm the expected args appear.
5 Frida Memory read/write/scan/patchCode [cross]
dump/edit process memory, or patch instructions in place.
1 read: p.readByteArray(n) / p.readU64() / hexdump(p,{length}).
2 write code: Memory.patchCode handles W^X + i-cache flush for you; use the arch Writer to emit instructions.
3 raw data write: Memory.protect(p,n,'rw'); p.writeByteArray([...]).
const p = Module.findBaseAddress('app').add(0x4000);
console.log(hexdump(p, {length:64}));
Memory.protect(p, 16, 'rwx');
Memory.patchCode(p, 8, code => { const w = new Arm64Writer(code, {pc:p}); w.putRet(); w.flush(); }); // arm64
// x86: new X86Writer(code,{pc:p}); w.putNop(); ... w.flush();
const found = Memory.scanSync(p, 0x1000, '41 42 43 44'); // sync variant returns [{address,size}]
patchCode's callback gets a scratch buffer mapped at the right PC; pass {pc:p} so relative branches assemble correctly. On hardened-runtime iOS, code pages may resist rwx -> use a tracer/breakpoint (dynamic-memory-access) instead.
verify: re-hexdump; the bytes changed; the patched path takes the new branch.
6 Frida Stalker — coverage + call trace [cross]
record executed basic blocks (drcov for a fuzzer) or trace calls in a region.
1 follow the thread doing the work; events.compile gives block edges (feed to a coverage map).
2 transform() lets you insert putCallout before/after chosen instructions (bl/blr/ret/cmp).
3 unfollow + flush to drain buffered events.
const mod = Process.getModuleByName('app');
Stalker.follow(Process.getCurrentThreadId(), {
events: { compile: true }, // block-granularity coverage
onReceive(ev) { /* parse Stalker.parse(ev) -> [{type:'compile',start,end}] */ },
transform(iterator) { // per-instruction instrumentation
let insn; while ((insn = iterator.next()) !== null) {
if (insn.mnemonic === 'bl') iterator.putCallout(ctx => console.log('call', ctx.pc));
iterator.keep();
}
}
});
// ... run target work ...
Stalker.unfollow(); Stalker.flush();
Stalker is heavy (JIT re-compile) -> scope to one module/thread and the smallest region. Excludes self; do not Stalker.follow during heavy GC. For drcov output use frida-drcov.py and load in lighthouse.
verify: block count rises as new input exercises new paths (coverage feedback for fuzz-target-synth).
7 Frida Objective-C hooking [macOS/iOS]
hook app logic / crypto / auth written in ObjC.
1 method pointer = ObjC.classes.Cls['- selector:'] (note the '- '/'+ ' and trailing ':').
2 args in an ObjC method: a[0]=self, a[1]=_cmd(selector), a[2..]=real args; wrap objects with new ObjC.Object(ptr).
3 to replace: set .implementation = ObjC.implement(method, jsfn); keep the method object alive.
4 ObjC.choose scans the heap for live instances of a class -> call their methods / read ivars.
if (ObjC.available) {
const NSURL = ObjC.classes.NSURL;
Interceptor.attach(NSURL['+ URLWithString:'].implementation, {
onEnter(a){ this.s = new ObjC.Object(a[2]).toString(); console.log('URL', this.s); }
});
// swizzle via setImplementation:
const m = ObjC.classes.MyAuth['- verify:'];
const orig = m.implementation;
m.implementation = ObjC.implement(m, (self, sel, token) => { console.log('verify', new ObjC.Object(token)); return 1; });
// enumerate live instances:
ObjC.choose(ObjC.classes.Session, { onMatch(o){ console.log('sess', o.token()); }, onComplete(){} });
}
Swift without @objc has no ObjC method -> hook the lower mangled native symbol (T4) or inline-trampoline. Autorelease: do not over-retain returned objects.
verify: the log prints decoded ObjC objects; forced verify:1 lets you in.
8 Frida Java/ART hooking [Android]
hook app Java/Kotlin (crypto, root checks, cert pinning).
1 all ART work runs inside Java.perform (attaches to the VM thread).
2 Java.use('fqcn') yields a wrapper; override .method.implementation; disambiguate with .overload('argType', ...).
3 $new() constructs; static methods call directly on the wrapper.
4 arg types: '[B'=byte[], 'int', 'java.lang.String', etc.
Java.perform(() => {
const C = Java.use('com.x.Crypto');
C.decrypt.overload('[B').implementation = function(b) { // pick the overload by arg types
const out = this.decrypt(b); // call-through
console.log('pt=', Java.use('java.lang.String').$new(out));
return out;
};
// defeat a root check:
Java.use('com.x.Security').isRooted.implementation = () => false;
// construct objects + call statics:
const URL = Java.use('java.net.URL'); const u = URL.$new('https://x');
// find loaded classes:
Java.enumerateLoadedClasses({ onMatch(n){ if(n.includes('Pinner')) console.log(n); }, onComplete(){} });
});
a method with overloads throws unless you pick one with .overload(...). Early classes (before load) -> hook the classloader or use Java.enumerateClassLoaders. Obfuscated names change per build -> match by structure/strings.
verify: plaintext prints at the crypto boundary; the pinning/root check returns your value.
9 Frida host<->agent control (rpc + messages) [cross]
drive the target from python (replay, bulk extract, fuzz loop).
1 export agent functions via rpc.exports; call from python as script.exports_sync.name(...).
2 send()/recv() for async events/backpressure; post() from host to the agent.
(agent.js):
rpc.exports = {
read(addr, n){ return Array.from(new Uint8Array(ptr(addr).readByteArray(n))); },
call(addr, x){ return new NativeFunction(ptr(addr),'int',['int'])(x); }
};
send({event:'ready'}); recv('go', m => { /* ... */ });
(host.py):
import frida; s=frida.get_usb_device().attach('App'); a=s.create_script(open('agent.js').read())
a.on('message', lambda m,d: print(m)); a.load(); print(a.exports_sync.read(0x100,16)); a.post({'type':'go'})
rpc return values must be JSON-serializable (convert byte arrays to Array). Large transfers: use send(obj, data) with a binary payload. frida-trace -i "CCCrypt*" auto-generates per-fn handlers for quick coverage.
verify: python receives the bytes/return; the loop drives the target.
10 Capture plaintext at a crypto/unpack boundary [cross]
recover decrypted data without breaking the crypto (pairs with unpack-runtime-protected).
1 identify the decrypt fn and which arg is the output buffer + the written-length out-param (from the prototype/disasm).
2 stash out ptr + len-ptr onEnter; read the buffer onLeave after it's filled.
// hook the decrypt routine; dump its OUTPUT buffer at onLeave
const dec = Module.getExportByName(null,'CCCrypt'); // or the app's own fn (T4)
Interceptor.attach(dec, {
onEnter(a){ this.out = a[8]; this.outLen = a[9]; /* CCCrypt(..., dataOut, dataOutAvail, *dataOutMoved) */ this.moved = a[10]; },
onLeave(r){ const n = this.moved.readU32(); console.log(hexdump(this.out.readByteArray(n))); }
});
some APIs write length to a pointer (deref it onLeave), others return it. For a streaming cipher, hook the final/flush call or accumulate per chunk.
verify: the dumped bytes are valid plaintext (entropy drops, structure appears).
11 ELF LD_PRELOAD interpose + call-through [Linux]
wrap a libc/shared-lib call process-wide without Frida.
1 define a same-signature symbol; resolve the genuine one with dlsym(RTLD_NEXT,"name") on first call (cache it).
2 build -shared -fPIC; export it ahead of libc via LD_PRELOAD.
// gcc -shared -fPIC -o hook.so hook.c -ldl ; LD_PRELOAD=./hook.so ./target
#define _GNU_SOURCE
#include <dlfcn.h>
#include <string.h>
ssize_t read(int fd, void *b, size_t n){
static ssize_t (*real)(int,void*,size_t);
if(!real) real = dlsym(RTLD_NEXT, "read");
ssize_t r = real(fd,b,n);
/* inspect/modify b here */
return r;
}
statically-linked or SUID targets ignore LD_PRELOAD (SUID drops it). Infinite recursion if you call the libc name instead of the RTLD_NEXT pointer. Constructor: __attribute__((constructor)) void i(){} runs at load for setup.
verify: ltrace/strace shows your wrapper; logs fire on the call.
12 ELF GOT/PLT overwrite — redirect an import [Linux]
intercept one module's external call with no preload, or from inside the process.
1 find the GOT slot: parse .rela.plt (binary-format-anatomy) for the symbol's R_*_JUMP_SLOT -> r_offset = slot RVA; add the runtime base.
2 mprotect the slot's page RW (full RELRO makes .got.plt read-only -> not viable; then hook the caller or use a trampoline).
3 save the old pointer, write the detour; call *orig from the detour to chain.
// after locating the .got.plt slot for "malloc" (readelf -r / from the rela.plt R_X86_64_JUMP_SLOT)
#include <sys/mman.h>
extern void *__start; // module base known at runtime via dladdr/link_map
void hook_got(void **slot, void *detour, void **orig){
*orig = *slot;
long pg = sysconf(_SC_PAGESIZE);
void *base = (void*)((uintptr_t)slot & ~(pg-1));
mprotect(base, pg, PROT_READ|PROT_WRITE);
*slot = detour; // next call to the import goes to detour
}
full RELRO -> GOT read-only (check checksec). Partial RELRO or lazy binding -> writable. The plthook library automates slot discovery across formats.
verify: the import's calls land in the detour; *orig call-through returns normally.
13 Mach-O DYLD interpose + fishhook [macOS]
wrap symbols in a dylib you inject (DYLD_INSERT_LIBRARIES) or from a loaded dylib.
1 interpose: a __DATA,__interpose array of {replacement,original} pairs; dyld swaps them at load (only for the inserted image's view).
2 fishhook: rebind_symbols rewrites lazy/non-lazy symbol pointers at runtime (struct rebinding {name, replacement, &orig}).
(interpose):
// clang -dynamiclib -o hook.dylib hook.c ; DYLD_INSERT_LIBRARIES=./hook.dylib ./target
#include <stdio.h>
static int my_open(const char*p,int f,...){ printf("open %s\n",p); return open(p,f); }
__attribute__((used)) static struct { const void*r; const void*o; }
_interpose_open __attribute__((section("__DATA,__interpose"))) = { (const void*)my_open, (const void*)open };
(fishhook, runtime rebind):
#include "fishhook.h"
static int (*orig_close)(int);
static int my_close(int fd){ return orig_close(fd); }
rebind_symbols((struct rebinding[1]){{"close", my_close, (void**)&orig_close}}, 1);
hardened-runtime / library-validation blocks DYLD_INSERT unless the target lacks the flag or you co-sign. SIP protects system binaries. fishhook only touches the indirect symbol tables (imports), not direct calls.
verify: the wrapped symbol logs; orig call-through works.
14 Windows inline hook — MinHook / Detours [Windows]
hook an API or internal fn in a loaded module.
1 MinHook: Initialize once; CreateHook(target, detour, &trampoline); EnableHook. Call through the trampoline (Real_*).
2 Detours: wrap Attach/Detach in a transaction + UpdateThread so in-flight threads are fixed up.
(MinHook):
// link minhook
static int (WINAPI *Real_MessageBoxA)(HWND,LPCSTR,LPCSTR,UINT) = MessageBoxA;
int WINAPI My_MessageBoxA(HWND h,LPCSTR t,LPCSTR c,UINT u){ return Real_MessageBoxA(h,"hooked",c,u); }
MH_Initialize();
MH_CreateHook(&MessageBoxA, &My_MessageBoxA, (LPVOID*)&Real_MessageBoxA);
MH_EnableHook(MH_ALL_HOOKS);
(Detours):
DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)Real_MessageBoxA, My_MessageBoxA); DetourTransactionCommit();
match calling convention (WINAPI/__stdcall). CFG/CET on newer Windows can trip indirect-call guards -> prefer trampoline hooks that keep valid targets. ASLR -> resolve via GetProcAddress, never a hardcoded VA.
verify: the hooked API shows your behavior; trampoline call-through returns the original result.
15 Windows IAT hook — redirect a module's import [Windows]
intercept a specific module's call to an imported API (lighter than inline).
1 parse the import directory (binary-format-anatomy) to find the resolved thunk for dll!fn.
2 VirtualProtect the thunk RW; save + overwrite the pointer; restore protection.
// walk DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; for the target dll+fn, patch FirstThunk
PIMAGE_THUNK_DATA th = (PIMAGE_THUNK_DATA)(base + imp->FirstThunk);
for (; th->u1.Function; th++){
if ((FARPROC)th->u1.Function == (FARPROC)target){
DWORD old; VirtualProtect(&th->u1.Function, sizeof(PVOID), PAGE_READWRITE, &old);
*orig = (FARPROC)th->u1.Function; th->u1.Function = (ULONG_PTR)detour;
VirtualProtect(&th->u1.Function, sizeof(PVOID), old, &old); break;
}
}
only intercepts calls that go through that module's IAT (not GetProcAddress-resolved or inlined). Bound imports / delay-load need the matching table.
verify: the module's calls to the import hit the detour.
16 Inline trampoline mechanics (any native) [cross]
no framework; overwrite a prologue and keep the original callable.
1 disassemble the prologue; copy enough whole instructions (>= jump size: 5 bytes x86 rel32, or 16 bytes arm64 for a far branch) into a gateway buffer (RWX).
2 append a jump from the gateway back to target+stolen_len.
3 overwrite target's prologue with a jump to your detour (x86: E9 rel32; arm64: a far branch via ADRP+BR xN or LDR xN,#8;BR xN;.quad dest).
4 relocate PC-relative operands in the stolen bytes (RIP-relative on x86; ADR/ADRP/B on arm64).
5 the detour calls the gateway to run the original.
arm64 B reaches +-128MB only -> use the register far-branch for distant detours. Patch under mprotect/VirtualProtect rwx + i-cache flush (__builtin___clear_cache / FlushInstructionCache). A self-checksum guard detects the prologue change -> use a hardware breakpoint instead (dynamic-memory-access).
verify: the detour runs; the gateway call-through executes the original behavior.
17 Code injection (load your module into a target) [cross]
get your agent/dylib/.so running inside another process.
1 write the module path into the target's memory.
2 make a target thread call the loader (LoadLibraryA / dlopen) with that path.
3 the module's constructor/DllMain runs your setup inside the target.
Linux (ptrace): attach; set regs to call __libc_dlopen_mode(remote_path, RTLD_NOW); restore + detach.
(or: gdb -p PID ; call (void*)dlopen("/tmp/e.so",2))
macOS: task_for_pid + thread_create_running a bootstrap that calls dlopen (or use Frida).
Windows: p=VirtualAllocEx(h,0,len,MEM_COMMIT,PAGE_READWRITE); WriteProcessMemory(h,p,path,len,0);
CreateRemoteThread(h,0,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,p,0,0);
// stealth: QueueUserAPC((PAPCFUNC)LoadLibraryA,hAlertableThread,p)
cross-process needs elevation (root / SeDebugPrivilege / an entitlement) — when a privileged op is blocked, wait for re-authorization, no blind retries. Hardened-runtime/WDAC can block unsigned modules -> co-sign or use a tracer.
verify: the module appears in the target's module list; its constructor log fires.
18 System tracing (no target rebuild, read-only fast paths)
uses: trace an interface's args ; force a check by rewriting the return (1/2) ; capture plaintext at a decrypt boundary (10) ; coverage for a fuzzer (6) ; stub a syscall to feed a corpus.
bpftrace -e 'uprobe:/bin/t:parse { printf("%s\n", str(arg0)) }'
perf probe -x /lib/libc.so.6 'malloc size=%di' ; perf record -e probe_libc:malloc -p PID
echo 'p:myp vfs_read' > /sys/kernel/debug/tracing/kprobe_events ; echo 1 > events/kprobes/myp/enable ; cat trace_pipe
dtrace -n 'pid$target::CCCrypt:entry{ ustack(); }' -p PID (macOS)
strace -f -e trace=network -p PID ; ltrace -e 'malloc+free' ./t
19 Attach fast:
frida -n proc -l h.js; spawn: frida -f ./x -l h.js --no-pause; trace: frida-trace -i "recv*" -i "strcmp" -n proc → editable JS stubs per matched function.
20 One-liner read-arg hook:
Interceptor.attach(Module.getExportByName(null,'strcmp'),{onEnter(a){console.log(a[0].readUtf8String(),a[1].readUtf8String())}}).
21 Force a return:
Interceptor.replace(p,new NativeCallback(()=>1,'int',['pointer','pointer'])); or inside a hook: onLeave(r){r.replace(ptr(0))}.
22 Scan + patch bytes:
Memory.scan(base,sz,'74 ?? 90',{onMatch(a){Memory.patchCode(a,1,c=>c.writeU8(0xeb))}}) (je→jmp).
23 Coverage via Stalker:
Stalker.follow(tid,{events:{compile:true}}); or: frida-drcov → open the .drcov in Lighthouse (IDA/Ghidra/Binja).
24 ELF preload wrap:
LD_PRELOAD=./h.so ./x with a same-signature symbol + dlsym(RTLD_NEXT,...); trace binds with: LD_AUDIT=./audit.so ./x (la_symbind hooks every PLT bind, no target patch).
25 Mac interpose:
DYLD_INSERT_LIBRARIES=./h.dylib ./x (needs a non-hardened target); check with: otool -l h.dylib | grep interpose.
26 GOT flip in gdb (no source):
set {long}&'x'::got_fn = detour_addr; or: plthook_replace lib; verify with pwndbg got.
27 Windows quick:
Detours withdll.exe /d:h.dll x.exe; MinHook for inline; API Monitor / Frida for tracing; Detours syelog for call logs.
28 ObjC one-liner:
ObjC.classes.NSURLSession['- dataTaskWithRequest:completionHandler:'].implementation = ...; frida-trace -m '-[NSString stringWithUTF8String:]'.
29 Android/Java:
Java.perform(()=>{Java.use('com.x.Y').chk.implementation=function(){return true}}); objection -g pkg explore for speed; frida-trace -j 'com.x.*!*'.
30 Syscall-level trace (no libc / packer-agnostic):
strace -f -e trace=network ./x; ltrace -S ./x; macOS: dtruss -f ./x.
31 eBPF uprobe any userland function, no patching:
sudo bpftrace -e 'uprobe:/lib/x86_64-linux-gnu/libc.so.6:getenv{printf("%s\n",str(arg0))}'.
32 eBPF kernel tracepoint:
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_openat{printf("%s %s\n",comm,str(args->filename))}'.
33 Capture a decrypt output:
hook the crypto/unpack routine, onLeave(){ console.log(hexdump(outPtr.readByteArray(len))) } → plaintext at the boundary.
34 Frida RPC to drive from python:
agent rpc.exports={rd:(a,n)=>Array.from(new Uint8Array(ptr(a).readByteArray(n)))}; host: script.exports_sync.rd(addr,64).
35 Patch a function to no-op:
overwrite its prologue with ret — gdb set {char}fn=0xc3 (x86) / frida: Memory.patchCode(fn,4,c=>c.writeU32(0xd65f03c0)) (arm64 ret).
36 Trace malloc/free sizes live:
bpftrace -e 'uprobe:libc:malloc{printf("%d\n",arg0)}'; or frida: Interceptor.attach(Module.getExportByName(null,'malloc'),{onEnter(a){this.n=a[0].toInt32()},onLeave(r){console.log(this.n,r)}}).
Sources: frida.re docs (Interceptor / Stalker / Memory / gum / frida-trace); facebook/fishhook fishhook.h (BSD); microsoft/Detours samples (MIT); TsudaKageyu/minhook (BSD-2 / MIT); Apple objc runtime (APSL); bpftrace reference + Linux uprobe docs; glibc rtld-audit (LD_AUDIT, la_symbind). API names + invocations verified against these.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment