Frida Anti-Debugging Cheat-Sheet + One-Shot Bypass

33次阅读
没有评论
  1. Detection

1.1 Common Frida Detection

  • File / port
    – Look for “frida” files in /data/local/tmp
    – Check if TCP 27042 is open (default frida-server port)
  • Dual-process
    – Enumerate /proc/<pid>
  • Memory introspection
    – Scan /proc/<pid><pid>
  • D-Bus probe
    – Android normally does not use D-Bus; send a D-Bus ping to every port—if any replies “reject”, frida-server is likely running

1.2 Self-implemented Detection (svc / syscall)
Key idea: avoid libc and instead use raw syscalls or inline assembly to bypass Frida hooks that target libc.

  1. Direct syscall wrappers
#include <sys/syscall.h>
int my_open(const char *p, int f) { return syscall(SYS_open, p, f); }
ssize_t my_read(int fd, void *b, size_t c) { return syscall(SYS_read, fd, b, c); }
int my_close(int fd) { return syscall(SYS_close, fd); }
  1. Hardened syscall wrapper
ssize_t secure_read(int fd, void *buf, size_t cnt) {
    if (syscall(SYS_gettid) != syscall(SYS_getpid))   // extra thread ⇒ debugger?
        return -1;
    ssize_t n = syscall(SYS_read, fd, buf, cnt);
    /* additional integrity / XOR tricks here */
    return n;
}
  1. Runtime-generated syscall stub
typedef long (*sc_fn)(long,...);
sc_fn gen_write(void) {
    unsigned char code[] = {
        0x48,0xc7,0xc0,0x01,0x00,0x00,0x00,  /* mov rax,1 (write) */
        0x0f,0x05,                             /* syscall          */
        0xc3                                   /* ret              */
    };
    void *m = mmap(NULL, 4096, PROT_R|PROT_W|PROT_X, MAP_ANON|MAP_PRIVATE, -1, 0);
    memcpy(m, code, sizeof(code));
    return (sc_fn)m;
}
  1. Pure assembly (ARM64 example)

asm

.global my_write
my_write:
    mov x8, #64     // SYS_write
    svc #0
    ret
  1. Practical detection snippet
JNIEXPORT jboolean JNICALL
Java_com_example_SecurityCheck_detectFrida(JNIEnv *env, jobject thiz) {
    char line[256];
    int fd = syscall(SYS_open, "/proc/self/maps", O_RDONLY);
    if (fd != -1) {
        while (syscall(SYS_read, fd, line, sizeof(line)) > 0) {
            if (strstr(line, "frida") || strstr(line, "gum-js-loop")) {
                syscall(SYS_close, fd);
                return JNI_TRUE;
            }
        }
        syscall(SYS_close, fd);
    }
    return JNI_FALSE;
}

  1. Bypass

2.1 Bypassing Common Checks — One-Shot Script
Hook libc’s strstr / strcmp, force any match against Frida keywords to return 0 (not found).

JavaScript

function replace_str() {
    ['strstr', 'strcmp'].forEach(fn => {
        const addr = Module.findExportByName('libc.so', fn);
        Interceptor.attach(addr, {
            onEnter(args) {
                const s2 = args[1].readCString();
                if (s2 && /tmp|frida|gum-js-loop|gmain|gdbus|pool-frida|linjector/i.test(s2))
                    this.hook = true;
            },
            onLeave(retval) {
                if (this.hook) retval.replace(0);
            }
        });
    });
}
replace_str();

2.2 Bypassing Self-Implemented Checks
Since self-implemented detection uses raw svc instructions, we can scan executable memory for svc opcodes and then hook them with Frida’s Memory API.

Example: locate and hook ARM64 SYS_open

JavaScript

function hookSysOpen() {
    const SYS_OPEN = 56;                 // ARM64
    const SVC_HEX = "01 00 00 D4";       // svc #0
    Process.enumerateRanges('r-x').forEach(r => {
        if (!r.file || !r.file.path.endsWith('.so')) return;
        Memory.scan(r.base, r.size, SVC_HEX, {
            onMatch(addr) {
                const sysno = addr.sub(4).readU32() & 0xFFFF;
                if (sysno === SYS_OPEN) {
                    Interceptor.attach(addr, {
                        onEnter(args) {
                            const fn = args[0].readUtf8String();
                            console.log('[SYS_open]', fn);
                        }
                    });
                }
            }
        });
    });
}
hookSysOpen();

Inside onEnter you can decide whether to tamper with the return value to bypass the check.


TL;DR
Detector side: files, ports, maps, threads, D-Bus (common); svc / syscall / assembly / runtime machine code (self-implemented).
Bypasser side: one-shot script kills common checks; signature scanning + instruction-level hook defeats self-implemented checks.

正文完
 0
评论(没有评论)