常见的内核监控方式

2024-07-03 316 0

背景

监控进程创建是系统管理和安全监控中的一个关键方面。本文将介绍四种常见的进程监控技术:So preload、Netlink Connector、Audit 和 Syscall hook的实现方式和适用场景。主要参考这篇文章,改文章已经归纳得很好了:https://www.freebuf.com/column/208928.html,

So preload

在 Linux 系统中,通过设置动态链接库预加载(LD_PRELOAD),可以在程序启动时优先加载指定的库。这使得用户可以覆盖库中已有的函数实现,从而实现对某些关键函数(如execve)的监控。

编写一个demo示例,如hook.c

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

typedef int (*orig_execve_func)(const char *filename, char *const argv[], char *const envp[]);
static orig_execve_func orig_execve = NULL;

// 我们的自定义 execve 函数
int execve(const char *filename, char *const argv[], char *const envp[]) {
    // 打印将要执行的程序名称
    printf("Intercepted execve call: %s\n", filename);

    // 动态获取原始的 execve 函数指针
    orig_execve = (orig_execve_func)dlsym(RTLD_NEXT, "execve");

    // 调用原始的 execve 函数
    return orig_execve(filename, argv, envp);
}

然后执行以下命令进行编译

gcc -shared -fPIC hook.c -o hook.so -ldl

编译完成之后,将上面生成的动态链接库注册成 preload

echo "/usr/lib/hook.so" | sudo tee -a /etc/ld.so.preload

在这个示例中,我们通过定义一个新的execve函数,先打印出被执行的命令,然后调用原始的 execve函数。通过设置 LD_PRELOAD环境变量,我们的库会在程序启动时被加载,从而允许我们的 execve覆盖标准库中的同名函数。

退出当前 shell 并重新登录(下面会讲原因),执行命令即可看到我们编写的代码已被执行:

常见的内核监控方式插图

优缺点

  • 优点:轻量级,不需修改内核代码。
  • 缺点:不能监控静态链接的程序;通过系统调用(如 int80h)可绕过。

使用条件

该方法没有什么条件限制,只需有 root 权限即可(做入侵监控程序 root 权限是必需的,后面的几种方法默认也都是在 root 权限下)

适用场景

适用于需要监控动态链接应用程序的创建过程,不涉及内核修改,适用于权限受限的环境。

Netlink Connector

Netlink Connector 是一种特殊的基于 Netlink 协议的通信机制,它构建在 Linux 内核中,用于内核与用户空间应用之间的通信。Netlink 本身是一种灵活的IPC(进程间通信)机制,主要用于网络配置和管理,但其使用范围已经扩展到了各种系统事件的通知。Netlink Connector 则专门用于传递事件和消息,包括进程事件,如进程创建和终止。

Netlink Connector 使用标准的 Netlink 套接字和通信机制,但它专注于事件的传递。它允许内核组件注册事件源,并将这些事件广播给订阅这些事件的用户空间应用程序。

关键组件

  1. 连接器模块(Connector Module)

    • 内核模块,负责管理事件的注册和广播。
    • 它处理来自用户空间的订阅请求,并在相应的事件发生时向订阅者广播通知。
  2. 用户空间应用程序

    • 通过创建 Netlink 套接字并绑定到特定的 Netlink 协议(如 NETLINK_CONNECTOR)来与内核模块通信。
    • 应用程序发送订阅请求到内核,并监听来自内核的事件通知。

实现步骤

  1. 内核端配置

    • 确保内核配置中启用了 Netlink Connector 支持(通常是 CONFIG_CONNECTOR 和 CONFIG_PROC_EVENTS)。
  2. 用户空间监听程序

    • 创建一个 Netlink 套接字。
    • 绑定到 Netlink Connector 协议。
    • 发送订阅消息给内核,表明它希望接收特定类型的事件(例如,进程创建和终止)。
    • 监听套接字并处理接收到的事件通知。

代码示例

下面是一个简单的用户空间程序示例,通过 Netlink Connector 接口监听内核发出的进程事件:

// 引入必要的头文件
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

// 定义Netlink协议中使用的CONNECTOR类型
#define NETLINK_CONNECTOR 11

int main() {
// 套接字描述符
int sock;
// Netlink套接字地址结构
struct sockaddr_nl sa;
// 数据接收缓冲区
char buffer[8192];
// 接收函数的返回值
int ret;

// 创建一个Netlink套接字,使用NETLINK_CONNECTOR协议
sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (sock == -1) {
    perror("socket");  // 如果创建套接字失败,则打印错误信息
    return 1;          // 并退出程序
}

// 初始化sockaddr_nl结构体
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;  // 设置地址类型为AF_NETLINK
sa.nl_groups = CN_IDX_PROC; // 加入到进程事件组,监听进程相关事件
sa.nl_pid = getpid();       // 设置Netlink套接字的端口号为当前进程ID

// 将套接字绑定到刚刚设置的地址
if (bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
    perror("bind");  // 如果绑定失败,打印错误信息
    close(sock);     // 关闭套接字
    return 1;        // 并退出程序
}

// 循环接收消息
while (1) {
    ret = recv(sock, buffer, sizeof(buffer), 0); // 从套接字接收数据
    if (ret == -1) {
        perror("recv");  // 如果接收失败,打印错误信息
        close(sock);     // 关闭套接字
        return 1;        // 并退出程序
    }

    // 打印接收到的消息提示
    printf("Received process event\n");
}

// 关闭套接字,虽然由于无限循环,实际上这行代码不会被执行
close(sock);
return 0;
}

这段代码主要通过 Netlink Connector 来接收来自内核的进程事件通知。它首先创建了一个 Netlink 套接字,然后将其绑定到一个特定的地址,该地址订阅了进程事件。通过无限循环,程序不断接收并处理来自内核的消息。每当接收到一个消息时,程序会输出一条信息表示已接收到进程事件。这个简单的示例没有对接收到的数据进行解析,只是简单地通知已接收到数据。

常见的内核监控方式插图1

优缺点

  • 优点:实现简单,直接从内核获取信息。
  • 缺点:获取的信息有限,可能需要额外步骤来完整获取进程信息。

适用场景

非常适合需要实时监控进程活动且对性能要求不是非常高的场合。

Audit

Linux Audit 系统是一个内核集成的审计系统,能够记录系统中发生的详细安全相关事件,包括系统调用、文件访问和网络活动。

具体架构如下 :

常见的内核监控方式插图2

  • 用户通过用户态的管理进程配置规则(例如图中的 go-audit ,也可替换为常用的 auditd ),并通过 Netlink 套接字通知给内核。
  • 内核中的 kauditd 通过 Netlink 获取到规则并加载。
  • 应用程序在调用系统调用和系统调用返回时都会经过 kauditd ,kauditd 会将这些事件记录下来并通过 Netlink 回传给用户态进程。
  • 用户态进程解析事件日志并输出。

从上面的架构图可知,整个框架分为用户态和内核态两部分,内核空间的 kauditd 是不可变的,用户态的程序是可以定制的,目前最常用的用户态程序就是 auditd ,除此之外知名的 osquery在底层也是通过与 Audit 交互来获取进程事件。下面我们就简单介绍一下如何通过 auditd 来监控进程创建。

apt update && apt install auditd
systemctl start auditd && systemctl status auditd
sudo auditctl -a always,exit -F arch=b64 -S execve -k exec-monitor //创建一个对execve这个系统调用的监控

再通过 auditd 软件包中的 ausearch来检索 auditd 产生的日志:

sudo ausearch -k exec-monitor

这里使用auditctl工具来添加一个监控所有 execve调用的审计规则。-k exec-monitor是为这个规则设置的一个关键字,使得查找这个规则生成的日志更容易。通过 ausearch工具,我们可以根据关键字 exec-monitor搜索相关的审计日志。至于其他的使用方法可以通过 man auditd和 man auditctl来查看。

使用条件

内核开启 Audit

  • cat/boot/config-$(uname-r)|grep^CONFIG_AUDIT

优缺点

优点

  • 组件完善,使用 auditd 软件包中的工具即可满足大部分需求,无需额外开发代码。
  • 相比于 Netlink Connector ,获取的信息更为全面,不仅仅是 pid 。

缺点

  • 性能消耗随着进程数量提升有所上升,需要通过添加白名单等配置来限制其资源占用。

关于性能消耗:

开启了osquery的审计功能之后,会在两个方面存在性能损耗:

  1. 当开启了内核中审计功能之后并且存在审计规则,那么内核每次都会比对审计规则和实际产生的审计事件。
  2. 这个audit consumer(在本例中是osquery)将会从内核netlink socket中接受数据,然后将数据解析为了其内部定义的表的格式(socket_eventsprocess_events)并保存在早RocksDB中。最后一旦这个数据被查询,那么这个数据就会被写入到文件或者是通过日志插件发送。

每一次在内核中产生系统调用,就会产生相应的审计事件。如果这样的系统调用越多,那么内核产生这些系统调用的审计事件的工作量就越大,同时osquery解析这些审计事件并且保存在数据库中的工作量也越大。

Syscall hook

上面的 Netlink Connector 和 Audit 都是 Linux 本身提供的监控系统调用的方法,如果我们想拥有更大程度的可定制化,我们就需要通过安装内核模块的方式来对系统调用进行 hook 。

目前常用的 hook 方法是通过修改 sys_call_table( Linux 系统调用表)来实现,具体原理就是系统在执行系统调用时是通过系统调用号在 sys_call_table中找到相应的函数进行调用,所以只要将 sys_call_table中 execve对应的地址改为我们安装的内核模块中的函数地址即可。

上述具体的实现细节可参考 YSRC 的这篇关于驭龙 HIDS 如何实现进程监控的文章:https://mp.weixin.qq.com/s/ntE5FNM8UaXQFC5l4iKUUw,这里贴出文章里的一张图方便大家对整个流程有个直观地了解:

代码示例

修改sys_call_table来挂钩 execve系统调用。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/syscalls.h>
#include <linux/kallsyms.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");

void **sys_call_table;

asmlinkage int (*original_execve)(const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp);

asmlinkage int hook_execve(const char __user *filename, const char __user *const __user *argv, const char __user *const __user *envp) {
    printk(KERN_INFO "Execve hooked: %s\n", filename);
    return original_execve(filename, argv, envp);
}

static int __init hook_init(void) {
    // Find the system call table address
    sys_call_table = (void **)kallsyms_lookup_name("sys_call_table");

    // Store the original pointer of execve
    original_execve = sys_call_table[__NR_execve];

    // Change the page protection settings so we can write to the table
    write_cr0(read_cr0() & (~0x10000));
    sys_call_table[__NR_execve] = hook_execve;
    write_cr0(read_cr0() | 0x10000);

    printk(KERN_INFO "Module loaded: execve hooked\n");
    return 0;
}

static void __exit hook_exit(void) {
    // Restore the original execve function in the syscall table
    write_cr0(read_cr0() & (~0x10000));
    sys_call_table[__NR_execve] = original_execve;
    write_cr0(read_cr0() | 0x10000);

    printk(KERN_INFO "Module unloaded: execve restored\n");
}

module_init(hook_init);
module_exit(hook_exit);

这个代码通过修改系统调用表来挂钩 execve系统调用。模块初始化时,我们查找系统调用表的地址,并替换掉 execve系统调用的入口点,使其指向我们自定义的函数 hook_execve。当任何进程尝试执行 execve时,都会先调用 hook_execve,在这里我们仅打印出被执行文件的名称。

编译内核模块

创建一个makefile

obj-m += syscall_hook.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

加载模块

sudo insmod syscall_hook.ko

查看消息

dmesg

使用条件

  • 可以安装内核模块。
  • 需针对不同 Linux 发行版和内核版本进行定制。

优缺点

优点

  • 高定制化,从系统调用层面获取完整信息。

缺点

  • 开发难度大。
  • 兼容性差,需针对不同发行版和内核版本进行定制和测试。

总结

So preload :Hook 库函数,不与内核交互,轻量但易被绕过。
Netlink Connector :从内核获取数据,监控系统调用,轻量,仅能直接获取 pid ,其他信息需要通过读取 proc/<pid>/来补全。
Audit :从内核获取数据,监控系统调用,功能多,不只监控进程创建,获取的信息相对全面。
Syscall hook :从内核获取数据,监控系统调用,最接近实际系统调用,定制度高,兼容性差。

单纯地看监控进程创建这方面,更推荐使用 Netlink Connector 的方式,这种方式在保证从内核获取数据的前提下又足够轻量,方便进行定制化开发。如果是想要进行全方面的监控包括进程、网络和文件,Audit 是一个不错的选择。

参考:

https://blog.spoock.com/2019/01/13/auditing-with-osquery/

https://www.freebuf.com/column/208928.html


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

webpack打包站点,js文件名批量获取思路
加密对抗靶场enctypt——labs通关
【论文速读】| 注意力是实现基于大语言模型的代码漏洞定位的关键
蓝队技术——Sysmon识别检测宏病毒
内网渗透学习|powershell上线cs
LLM attack中的API调用安全问题及靶场实践

发布评论