← Back to list

Bypassing Frida: Advanced Frida Detection Bypass — Part 5

Table of contents :

Haxymad in System Weakness · 2026-03-23 17:30 · 5 claps · 5.9 min read
#cybersecurity #hacking #reverse-engineering #frida #rasp
Open on Medium ↗
Wiki topics: 🔒 · Cybersecurity

Bypassing Frida: Advanced Frida Detection Bypass — Part 5

Table of contents :

  1. Overview of Frida How it works (Must Read).
  2. Kernel Hooking Basics.
  3. Bypassing Port detection
  4. Bypassing agent detection
  5. Bypassing Thread detection
  6. Bypassing RWX detection
  7. Kerenel Code for Ultimate Bypass
  8. Stealth Frida : A modifed version Frida.
  9. Advanced Bypass : Stealth hooking framework (FridaZygote+Kerenel)

Hey Guys It took me a while . Here is the Full Kernel Module .

Bypasses

  1. Port Detection .
  2. Agent Detection
  3. Thread Detection
  4. RWX detection
  5. Ptrace Detection

// comprehensive_bypass.c – Combined kernel module for x86_64
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kprobes.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/net.h>
#include <net/checksum.h>
#include <net/ip.h>
#include <linux/dirent.h>
#include <linux/pid.h>
#include <asm/ptrace.h>

/* ================= CONFIGURATION ================= */
static int target_uid = 10132;
module_param(target_uid, int, 0644);
MODULE_PARM_DESC(target_uid, "Target UID to protect");

#define MAX_PATH 256
#define TASK_DIR_NAME "task"
#define WATCH_PORT 27042
#define DUMMY_PORT 55555

/* ================= NETFILTER PORT MIRROR ================= */
static inline void fix_ip_checksum(struct iphdr *ip)
{
    ip->check = 0;
    ip->check = ip_fast_csum((u8 *)ip, ip->ihl);
}

static inline void fix_tcp_checksum(struct sk_buff *skb,
                                    struct iphdr *ip,
                                    struct tcphdr *tcp)
{
    unsigned int tcp_len = skb->len - ip_hdrlen(skb);
    tcp->check = 0;
    tcp->check = csum_tcpudp_magic(ip->saddr, ip->daddr, tcp_len,
                                   IPPROTO_TCP,
                                   csum_partial((char *)tcp, tcp_len, 0));
}

static unsigned int hook_out(void *priv,
                             struct sk_buff *skb,
                             const struct nf_hook_state *state)
{
    struct iphdr  *ip;
    struct tcphdr *tcp;

    if (!skb) return NF_ACCEPT;
    ip = ip_hdr(skb);
    if (!ip || ip->protocol != IPPROTO_TCP) return NF_ACCEPT;
    if (ip->daddr != htonl(INADDR_LOOPBACK)) return NF_ACCEPT;

    if (skb_try_make_writable(skb, skb->len)) return NF_DROP;

    tcp = tcp_hdr(skb);
    if (!tcp) return NF_ACCEPT;

    if (tcp->dest == htons(WATCH_PORT)) {
        tcp->dest = htons(DUMMY_PORT);
        fix_tcp_checksum(skb, ip, tcp);
        fix_ip_checksum(ip);
        pr_info("[PORT-MIRROR][OUT] 127.0.0.1:%d → %d\n", WATCH_PORT, DUMMY_PORT);
    }
    return NF_ACCEPT;
}

static unsigned int hook_in(void *priv,
                            struct sk_buff *skb,
                            const struct nf_hook_state *state)
{
    struct iphdr  *ip;
    struct tcphdr *tcp;

    if (!skb) return NF_ACCEPT;
    ip = ip_hdr(skb);
    if (!ip || ip->protocol != IPPROTO_TCP) return NF_ACCEPT;
    if (ip->saddr != htonl(INADDR_LOOPBACK)) return NF_ACCEPT;

    if (skb_try_make_writable(skb, skb->len)) return NF_DROP;

    tcp = tcp_hdr(skb);
    if (!tcp) return NF_ACCEPT;

    if (tcp->source == htons(DUMMY_PORT)) {
        tcp->source = htons(WATCH_PORT);
        fix_tcp_checksum(skb, ip, tcp);
        fix_ip_checksum(ip);
        pr_info("[PORT-MIRROR][IN ] 127.0.0.1:%d ← %d\n", WATCH_PORT, DUMMY_PORT);
    }
    return NF_ACCEPT;
}

static struct nf_hook_ops nf_out = {
    .hook     = hook_out,
    .pf       = NFPROTO_IPV4,
    .hooknum  = NF_INET_LOCAL_OUT,
    .priority = NF_IP_PRI_FIRST,
};
static struct nf_hook_ops nf_in = {
    .hook     = hook_in,
    .pf       = NFPROTO_IPV4,
    .hooknum  = NF_INET_LOCAL_IN,
    .priority = NF_IP_PRI_FIRST,
};

/* ================= AGENT BYPASS (maps/status) ================= */
static char *hidden_keywords[] = {
    "frida", "libinjector", "linjector", "gadget",
    "gum", "frida-agent", "frida-gadget", NULL
};

struct read_ctx {
    struct file *file;
    char __user *ubuf;
    int is_maps;
    int is_status;
    char path_cache[MAX_PATH];
};

static int contains_hidden_keyword(const char *str)
{
    for (int i = 0; hidden_keywords[i]; i++)
        if (strstr(str, hidden_keywords[i]))
            return 1;
    return 0;
}

static int vfs_read_entry(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct read_ctx *ctx = (struct read_ctx *)ri->data;
    struct file *file = (struct file *)regs->di;   // 1st arg: file pointer
    char *tmp, *path;
    int path_len;

    ctx->file = file;
    ctx->ubuf = (char __user *)regs->si;           // 2nd arg: buffer
    ctx->is_maps = 0;
    ctx->is_status = 0;
    ctx->path_cache[0] = '\0';

    if (__kuid_val(current_uid()) != target_uid)
        return 0;

    tmp = (char *)__get_free_page(GFP_ATOMIC);
    if (!tmp) return 0;

    path = d_path(&file->f_path, tmp, PAGE_SIZE);
    if (!IS_ERR(path)) {
        path_len = strlen(path);
        if (path_len < MAX_PATH) {
            strncpy(ctx->path_cache, path, MAX_PATH - 1);
            ctx->path_cache[MAX_PATH - 1] = '\0';
            if (strstr(path, "/maps"))
                ctx->is_maps = 1;
            else if (strstr(path, "/status"))
                ctx->is_status = 1;
        }
    }
    free_page((unsigned long)tmp);
    return 0;
}

static int vfs_read_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct read_ctx *ctx = (struct read_ctx *)ri->data;
    ssize_t ret = regs_return_value(regs);
    char *kbuf = NULL;
    char *line_start, *line_end, *current_pos;
    char *tracer_loc;
    char *perm_ptr;
    size_t line_len, remaining_len;
    int modified = 0;

    if (ret <= 0) goto cleanup;
    if (!ctx->is_maps && !ctx->is_status) goto cleanup;
    if (__kuid_val(current_uid()) != target_uid) goto cleanup;

    kbuf = kzalloc(ret + 1, GFP_ATOMIC);
    if (!kbuf) goto cleanup;
    if (copy_from_user(kbuf, ctx->ubuf, ret)) goto cleanup;

    if (ctx->is_maps) {
        current_pos = kbuf;
        while (current_pos < kbuf + ret && *current_pos != '\0') {
            line_start = current_pos;
            line_end = strchr(line_start, '\n');
            if (!line_end)
                line_end = kbuf + ret;
            else
                line_end++;
            line_len = line_end - line_start;
            if (contains_hidden_keyword(line_start)) {
                remaining_len = (kbuf + ret) - line_end;
                memmove(line_start, line_end, remaining_len);
                ret -= line_len;
                kbuf[ret] = '\0';
                modified = 1;
                current_pos = line_start;
            } else {
                current_pos = line_end;
            }
        }
        perm_ptr = kbuf;
        while ((perm_ptr = strstr(perm_ptr, "rwx")) != NULL) {
            if (perm_ptr - kbuf < ret) {
                perm_ptr[2] = '-';
                modified = 1;
            }
            perm_ptr++;
        }
    }

    if (ctx->is_status) {
        tracer_loc = strstr(kbuf, "TracerPid:");
        if (tracer_loc && (tracer_loc - kbuf < ret)) {
            char *value_start = tracer_loc + 10;
            while (*value_start && (*value_start == ' ' || *value_start == '\t'))
                value_start++;
            if (value_start < kbuf + ret && *value_start != '0') {
                *value_start = '0';
                char *value_end = value_start + 1;
                while (*value_end && *value_end != '\n' && value_end < kbuf + ret)
                    value_end++;
                if (value_end > value_start + 1)
                    memset(value_start + 1, 0, value_end - (value_start + 1));
                modified = 1;
            }
        }
    }

    if (modified) {
        if (copy_to_user(ctx->ubuf, kbuf, ret) == 0)
            regs->ax = ret;   // set return value in ax (x86_64)
    }

    cleanup:
    if (kbuf) kfree(kbuf);
    return 0;
}

static struct kretprobe probe_vfs_read = {
    .kp.symbol_name = "vfs_read",
    .entry_handler  = vfs_read_entry,
    .handler        = vfs_read_ret,
    .data_size      = sizeof(struct read_ctx),
    .maxactive      = 32,
};

/* ================= THREAD HIDER (task/ directory) ================= */
static char *ghost_threads[] = {
    "frida", "linjector", "pool-frida", "gadget",
    "gmain", "gum-js-loop", "gdbus", "thread_injector", NULL
};

struct getdents_ctx {
    struct linux_dirent64 __user *dirent;
    char path_cache[MAX_PATH];
    int is_task_dir;
};

static int is_ghost_thread(int tid)
{
    struct pid *pid_struct;
    struct task_struct *task;
    int is_ghost = 0;

    rcu_read_lock();
    pid_struct = find_vpid(tid);
    if (pid_struct) {
        task = pid_task(pid_struct, PIDTYPE_PID);
        if (task) {
            for (int i = 0; ghost_threads[i]; i++) {
                if (strstr(task->comm, ghost_threads[i])) {
                    is_ghost = 1;
                    break;
                }
            }
        }
    }
    rcu_read_unlock();
    return is_ghost;
}

static int getdents64_entry(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct getdents_ctx *ctx = (struct getdents_ctx *)ri->data;
    int fd = (int)regs->di;                     // 1st arg: fd
    struct file *file;
    char *buf, *path;

    ctx->dirent = (struct linux_dirent64 __user *)regs->si; // 2nd arg: dirent
    ctx->is_task_dir = 0;
    ctx->path_cache[0] = '\0';

    if (__kuid_val(current_uid()) != target_uid)
        return 0;

    file = fget(fd);
    if (!file) return 0;

    buf = (char *)__get_free_page(GFP_ATOMIC);
    if (buf) {
        path = d_path(&file->f_path, buf, PAGE_SIZE);
        if (!IS_ERR(path)) {
            strncpy(ctx->path_cache, path, MAX_PATH - 1);
            ctx->path_cache[MAX_PATH - 1] = '\0';
            if (strstr(ctx->path_cache, TASK_DIR_NAME))
                ctx->is_task_dir = 1;
        }
        free_page((unsigned long)buf);
    }
    fput(file);
    return 0;
}

static int getdents64_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
{
    struct getdents_ctx *ctx = (struct getdents_ctx *)ri->data;
    int nread = regs_return_value(regs);
    struct linux_dirent64 *kdirent, *d;
    unsigned long offset = 0;
    int modified = 0;
    long tid;

    if (nread <= 0) goto cleanup;
    if (!ctx->is_task_dir) goto cleanup;
    if (__kuid_val(current_uid()) != target_uid) goto cleanup;

    kdirent = kmalloc(nread, GFP_ATOMIC);
    if (!kdirent) goto cleanup;
    if (copy_from_user(kdirent, ctx->dirent, nread)) {
        kfree(kdirent);
        goto cleanup;
    }

    while (offset < nread) {
        d = (struct linux_dirent64 *)((char *)kdirent + offset);
        if (kstrtol(d->d_name, 10, &tid) == 0 && is_ghost_thread((int)tid)) {
            unsigned long reclen = d->d_reclen;
            unsigned long next_off = offset + reclen;
            unsigned long remaining = nread - next_off;
            memmove((char *)d, (char *)kdirent + next_off, remaining);
            nread -= reclen;
            modified = 1;
            continue;
        }
        offset += d->d_reclen;
    }

    if (modified) {
        if (copy_to_user(ctx->dirent, kdirent, nread) == 0)
            regs->ax = nread;   // set return value in ax
    }
    kfree(kdirent);

    cleanup:
    return 0;
}

static struct kretprobe probe_getdents64 = {
    .kp.symbol_name = "ksys_getdents64",
    .entry_handler  = getdents64_entry,
    .handler        = getdents64_ret,
    .data_size      = sizeof(struct getdents_ctx),
    .maxactive      = 64,
};

/* ================= MODULE INIT/EXIT ================= */
static int __init comprehensive_bypass_init(void)
{
    int ret;

    pr_info("[COMPREHENSIVE_BYPASS] Loading for UID: %d\n", target_uid);

    // Register netfilter hooks
    ret = nf_register_net_hook(&init_net, &nf_out);
    if (ret) {
        pr_err("Failed to register NF_OUT hook: %d\n", ret);
        return ret;
    }
    ret = nf_register_net_hook(&init_net, &nf_in);
    if (ret) {
        pr_err("Failed to register NF_IN hook: %d\n", ret);
        nf_unregister_net_hook(&init_net, &nf_out);
        return ret;
    }

    // Register vfs_read probe
    ret = register_kretprobe(&probe_vfs_read);
    if (ret < 0) {
        pr_err("Failed to register vfs_read probe: %d\n", ret);
        nf_unregister_net_hook(&init_net, &nf_in);
        nf_unregister_net_hook(&init_net, &nf_out);
        return ret;
    }

    // Register getdents64 probe (optional, warn on failure)
    ret = register_kretprobe(&probe_getdents64);
    if (ret < 0) {
        pr_warn("Failed to register getdents64 probe: %d (continuing)\n", ret);
    }

    pr_info("[COMPREHENSIVE_BYPASS] Loaded successfully\n");
    pr_info("  - Port mirror: 127.0.0.1:%d ⇄ %d\n", WATCH_PORT, DUMMY_PORT);
    pr_info("  - Agent bypass active for UID %d\n", target_uid);
    pr_info("  - Thread hider active for UID %d\n", target_uid);

    return 0;
}

static void __exit comprehensive_bypass_exit(void)
{
    unregister_kretprobe(&probe_getdents64);
    unregister_kretprobe(&probe_vfs_read);
    nf_unregister_net_hook(&init_net, &nf_in);
    nf_unregister_net_hook(&init_net, &nf_out);
    pr_info("[COMPREHENSIVE_BYPASS] Unloaded\n");
}

module_init(comprehensive_bypass_init);
module_exit(comprehensive_bypass_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Syscall0x");
MODULE_DESCRIPTION("Combined bypass: port mirroring, agent detection hiding, thread hiding");

Steps :

  1. copy
  2. Build According to your kernel

Note : if you are facing any issue with building ask gpts they will convert to you kernel version

KPM Version Release soon .


메타데이터
post_id
a23cce2a6da8
slug
bypassing-frida-advanced-frida-detection-bypass-part-5-a23cce2a6da8
url
https://systemweakness.com/bypassing-frida-advanced-frida-detection-bypass-part-5-a23cce2a6da8
canonical_url
https://systemweakness.com/bypassing-frida-advanced-frida-detection-bypass-part-5-a23cce2a6da8
author_url
https://medium.com/@haxymad
status
ok
fetched_at
2026-06-21 07:44:09