Bypassing Frida: Advanced Frida Detection Bypass — Part 5
Table of contents :
Wiki topics:
🔒 · Cybersecurity
Bypassing Frida: Advanced Frida Detection Bypass — Part 5

Table of contents :
- Overview of Frida How it works (Must Read).
- Kernel Hooking Basics.
- Bypassing Port detection
- Bypassing agent detection
- Bypassing Thread detection
- Bypassing RWX detection
- Kerenel Code for Ultimate Bypass
- Stealth Frida : A modifed version Frida.
- Advanced Bypass : Stealth hooking framework (FridaZygote+Kerenel)
Hey Guys It took me a while . Here is the Full Kernel Module .
Bypasses
- Port Detection .
- Agent Detection
- Thread Detection
- RWX detection
- 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 :
- copy
- 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