systemtap内核监控发起域名请求的进程

systemtap内核监控发起域名请求的进程

在日常的应急响应中经常会出现Linux主机发起恶意的dnslog请求,但是找不到对应触发的进程。在用户态下没有好的监控方式,这里用到systemtap来通过内核的方式监控。

SystemTap是什么

systemtap 是利用Kprobe 提供的API来实现动态地监控和跟踪运行中的Linux内核的工具,相比Kprobe,systemtap更加简单,提供给用户简单的命令行接口,以及编写内核指令的脚本语言,类似C语言、D语言。

systemtap原理

SystemTap 基本思想是命名事件,并为它们提供处理程序。每当发生指定的事件时,内核都会将处理程序视为子例程运行,然后继续运行。有一系列的事件,例如进入或退出函数,计时器到期或整个SystemTap会话的开始和停止。处理程序是一系列脚本语言语句,用于指定事件发生时要完成的工作。这项工作通常包含从事件上下文中提取数据,将其存储到内部变量或打印结果。

SystemTap 的工作原理是将脚本翻译成C语言,执行C编译器创建一个内核模块。当模块被加载后,通过挂载到内核来激活所有的探测事件。然后,当事件发生再任何处理器上时,编译后的处理程序就运行,最终,SystemTap会话停止,Hook取消,内核模块被移除,整个过程由命令行程序stap驱动。

原理如下图所示:

systemtap安装

以较老的系统(CentOS6.8 x64为例),并下载与内核版本完全一致的调试依赖包。systemtap主要依赖下面的3个内核模块。

  • kernel-debuginfo-common
  • kernel-debuginfo
  • kernel-devel

查看当前系统的内核版本

[root@VM-0-4-centos ~]# uname -r
2.6.32-642.6.2.el6.x86_64

安装systemtap

yum install systemtap systemtap-runtime -y

查看依赖的内核信息包

[root@VM-0-4-centos ~]# stap-prep
Need to install the following packages:
kernel-devel-2.6.32-642.6.2.el6.x86_64
kernel-debuginfo-2.6.32-642.6.2.el6.x86_64
Loaded plugins: fastestmirror, security
Setting up Install Process
Loading mirror speeds from cached hostfile
No package kernel-devel-2.6.32-642.6.2.el6.x86_64 available.
No package kernel-debuginfo-2.6.32-642.6.2.el6.x86_64 available.
Error: Nothing to do
package kernel-devel-2.6.32-642.6.2.el6.x86_64 is not installed
package kernel-debuginfo-2.6.32-642.6.2.el6.x86_64 is not installed
problem installing rpm(s) kernel-devel-2.6.32-642.6.2.el6.x86_64 kernel-debuginfo-2.6.32-642.6.2.el6.x86_64

stap-prep会打印依赖的包及其版本。注意:不要通过yum去安装,yum不会去匹配对应的内核版本,即使安装了也无法使用。

Google搜索对应的依赖版本

注意:systemtap对内核版本要求比较严格,一定要下载安装完全匹配的版本。

依次下载对应的包。

rpm安装对应的内核信息包

[root@VM-0-4-centos ~]# rpm -ivh kernel-debuginfo-2.6.32-642.6.2.el6.x86_64.rpm
error: Failed dependencies:
        kernel-debuginfo-common-x86_64 = 2.6.32-642.6.2.el6 is needed by kernel-debuginfo-2.6.32-642.6.2.el6.x86_64

报错提示kernel-debuginfo的安装依赖kernel-debuginfo-common,所以还要先下载安装kernel-debuginfo-common。同样通过谷歌找到对应的包。

[root@VM-0-4-centos ~]# rpm -ivh kernel-debuginfo-common-x86_64-2.6.32-642.6.2.el6.x86_64.rpm
warning: kernel-debuginfo-common-x86_64-2.6.32-642.6.2.el6.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID ec551f03: NOKEY
Preparing...                ########################################### [100%]
   1:kernel-debuginfo-common########################################### [100%]
[root@VM-0-4-centos ~]# rpm -ivh kernel-debuginfo-2.6.32-642.6.2.el6.x86_64.rpm 
Preparing...                ########################################### [100%]
   1:kernel-debuginfo       ########################################### [100%]
[root@VM-0-4-centos ~]# rpm -ivh kernel-devel-2.6.32-642.6.2.el6.x86_64.rpm
Preparing...                ########################################### [100%]
        package kernel-devel-2.6.32-754.35.1.el6.x86_64 (which is newer than kernel-devel-2.6.32-642.6.2.el6.x86_64) is already installed

注意踩坑:之前我们通过yum安装systemtap时,自动安装的kernel-devel和内核版本并不匹配。这里需要卸载自动安装的kernel-devel。

[root@VM-0-4-centos ~]# yum remove -y kernel-devel-2.6.32-754.35.1.el6.x86_64
[root@VM-0-4-centos ~]# rpm -ivh kernel-devel-2.6.32-642.6.2.el6.x86_64.rpm
Preparing...                ########################################### [100%]
   1:kernel-devel           ########################################### [100%]

检查systemtap是否安装完成

[root@VM-0-4-centos ~]# stap-prep

执行stap-prep没有任何输出,就表明所以依赖都安装完成,可以正常使用systemtap了。但是卸载kernel-devel会导致systemtap本身有问题所以需要重新yum安装。

yum install systemtap systemtap-runtime -y

systemtap测试

[root@VM-0-4-centos ~]# stap -e 'probe begin{printf("Hello, World\n"); exit();}'
Hello, World
[root@VM-0-4-centos ~]# stap -ve 'probe begin{printf("Hello, World\n"); exit();}'
Pass 1: parsed user script and 111 library script(s) using 206796virt/34536res/3220shr/31672data kb, in 80usr/10sys/97real ms.
Pass 2: analyzed script: 1 probe(s), 1 function(s), 0 embed(s), 0 global(s) using 207456virt/35524res/3524shr/32332data kb, in 10usr/0sys/3real ms.
Pass 3: using cached /root/.systemtap/cache/3e/stap_3e793d5afad1dc01d547399a819b8745_979.c
Pass 4: using cached /root/.systemtap/cache/3e/stap_3e793d5afad1dc01d547399a819b8745_979.ko
Pass 5: starting run.
Hello, World
Pass 5: run completed in 0usr/0sys/327real ms.

可以看到systemtap打印了字符串,通过-v参数显示了stap脚本的执行过程。查看转换后的C语言代码,可知systemtap是先将stap脚本转化为了C,然后将C编译为ko模块加载到内核中执行的。

工作流程解释

重要文件和目录

/lib/modules/KERNEL_VERSION/systemtap/保存 SystemTap 工具模块。
/usr/share/systemtap/tapset/保存标准的 tapset 库。
/usr/share/doc/packages/systemtap/examples保存用于各种目的的多个示例 SystemTap 脚本。仅当已安装 systemtap-docs 软件包时才可用。
~/.systemtap/cache缓存的 SystemTap 文件的数据目录。
/tmp/stap*SystemTap 文件的临时目录,包含已转换的 C 代码和内核对象。

systemtap脚本默认文件后缀为.stp,文件内容有固定的格式,有完整的语法(变量、常量、注释、条件、循环、数组等等)。begin/end, 分别是systemtap会话的起始和结尾,IDE推荐VSCode。如下图所示:

脚本

systemtap脚本编写参考: https://sourceware.org/systemtap/tutorial/

可探测事件

Kprobes允许你为任何内核指令以及函数入口和函数返回处理程序安装预处理程序和后处理程序。

常用的可探测事件如下:

tid()当前线程的 ID。
pid()当前线程的进程 ID。
uid()当前用户的 ID。
cpu()当前 CPU 编号。
execname()当前进程的名称。
gettimeofday_s()自 Unix 纪元(1970 年 1 月 1 日)起经过的秒数。
ctime()将时间转换为字符串。
pp()用于描述当前正在处理的探测点的字符串。

函数大全:https://sourceware.org/systemtap/tapsets/index.html

systemtap应用

监控DNS请求

脚本原理:判断外联目标端口为53。

#! /usr/bin/env stap

global the_dport = 53

probe netfilter.ip.local_out {

if (the_dport == dport)

printf("%s[PID:%d,TID:%d] sent packet to %s:%d\n", execname(), pid(),  tid(), daddr, dport)

}

效果:

监控ICMP请求

脚本原理: netfilter.ip.local_out 为所有协议外联,ICMP的目标端口为0

#! /usr/bin/env stap

probe netfilter.ip.local_out {

if (0 == dport)

printf("%s[PID:%d,TID:%d] sent %d to %s:%d\n", execname(), pid(),  tid(),length, daddr, dport)

}

probe netfilter.ip.local_in {

if (0 == sport)

printf("%s recv %d from %s:%d\n", execname(),length, saddr, sport)

}

监控ping得到的效果:

总结

systemtap的功能非常强大,用途远不止于此。但是安装systemtap的过程过于麻烦,易用性不高。后续考虑用其他的方式(ebpf)代替。

赞赏

微信赞赏支付宝赞赏

Zgao

愿有一日,安全圈的师傅们都能用上Zgao写的工具。

发表评论