bpftrace各维度捕捉SIGKILL信号

2023-05-16

一、问题

Ftrace(几乎适配任何主流内核版本) 和 bpftrace(要求内核版本4.1以上)中都有两个现成的脚本execsnoop(.bt)、killsnoop(.bt), 我经常用他们从外部(不去读代码)观察几个关系紧密的进程之间是如何相互配合的,比如用execsnoop追踪一个大的系统(往往有多个进程)是如何逐一启动的,用killsnoop看他们相互之间信号的发送(进程间交互的一种方式)。
killsnoop是从tracepoint角度写的,今天我准备从各个角度重写此功能,包括:

  1. 用户空间追踪kill executable
  2. 用户空间追踪libc
  3. 发信号端内核空间追踪
  4. 接受信号端内核空间追踪

二、用户空间追踪kill executable

系统管理员一般使用/usr/bin/kill -9 xxx的方式去结束某进程,这样我们只要使用uprobe hook到main函数或其它函数然后把参数打出来即可。
为了简单直接对main下手,而且只考虑kill -9 xxx这种参数。不过出人意料的是,/usr/bin/kill竟然是strip的,没有main符号。

[root]# file /usr/bin/kill
/usr/bin/kill: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=2eaa68ee706e53cb99b1f07f6508dae7656c5a61, stripped

所以只好用offset

[root]# objdump -D /usr/bin/kill |grep main
0000000000001f70 <textdomain@plt>:
0000000000001fb0 <bindtextdomain@plt>:
    2353:       e8 58 fc ff ff          callq  1fb0 <bindtextdomain@plt>
    235f:       e8 0c fc ff ff          callq  1f70 <textdomain@plt>

[root]# objdump -D /usr/bin/kill |grep 00000000000022f0 -A10
00000000000022f0 <.text>:
    22f0:       f3 0f 1e fa             endbr64
    22f4:       41 57                   push   %r15
    22f6:       66 0f ef c0             pxor   %xmm0,%xmm0
    22fa:       41 56                   push   %r14
    22fc:       41 55                   push   %r13
    22fe:       41 54                   push   %r12
    2300:       55                      push   %rbp
    2301:       89 fd                   mov    %edi,%ebp
    2303:       bf 06 00 00 00          mov    $0x6,%edi
    2308:       53                      push   %rbx

随便从上面选一个offset 0x2301

[root]# bpftrace -e 'struct argvarr {long a1; long a2; long a3;} uprobe:/usr/bin/kill:0x2301 {$tar=((struct argvarr*)reg("si"))->a3; $sig=((struct argvarr*)reg("si"))->a2; printf("%-6d(%s) -> %s, sig:%s", pid, comm, str($tar), str($sig));}'
Attaching 1 probe...
ERROR: Could not resolve address: /usr/bin/kill:0x2301

遇到不能解析地址的错误,根据文档加上unsafe即可

[root]# sleep 1000 &
[1] 2293884
[root]# kill -9 2293884
[root]# bpftrace -e 'struct argvarr {long a1; long a2; long a3;} uprobe:/usr/bin/kill:0x2301 {$tar=((struct argvarr*)reg("si"))->a3; $sig=((struct argvarr*)reg("si"))->a2; printf("%-6d(%s) -> %s, sig:%s", pid, comm, str($tar), str($sig));}' --unsafe
Attaching 1 probe...
WARNING: Could not determine instruction boundary for uprobe:/usr/bin/kill:8961 (binary appears stripped). Misaligned probes can lead to tracee crashes!
2293895(kill) -> 2293884, sig:-9

sig:-9 看着不顺的,自己改改吧。当然这种方式不能追踪直接调用kill函数的情况。

三、用户空间追踪libc

即使/usr/bin/kill也是最终调用的libc中的kill系统函数,所以hook libc能撒出更大的网。
首先查下libc在哪?

[root]# ldd /usr/bin/kill
        linux-vdso.so.1 (0x00007ffdf93fe000)
        libc.so.6 => /lib64/libc.so.6 (0x00007f3bb35a2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f3bb3b70000)
[root]# ls -l /lib64/libc.so.6
lrwxrwxrwx. 1 root root 12 Mar 11  2021 /lib64/libc.so.6 -> libc-2.28.so

然后查看kill的函数原型,两个参数分别是pid和sig_no,

#include <signal.h>
int kill(pid_t pid, int sig);

所以,这个大网根据调用约定可以这样编写

bpftrace -e 'uprobe:/lib64/libc-2.28.so:kill {printf("%d(%s) -> %d, sig:%d\n", pid, comm, reg("di"), reg("si"));}'

bpftrace还给我们提供了快速访问参数的方便:argN

bpftrace -e 'uprobe:/lib64/libc-2.28.so:kill {printf("%d(%s) -> %d, sig:%d\n", pid, comm, arg0, arg1);}'

结果如下:

[root]# bpftrace -e 'uprobe:/lib64/libc-2.28.so:kill {printf("%d(%s) -> %d, sig:%d\n", pid, comm, arg0, arg1);}'
Attaching 1 probe...
2261306(bash) -> 2319005, sig:9

四、发信号端内核空间追踪

内核中有个tracepoint - sys_enter_kill,如果读过bpftrace中的样例killsnoop.bt, 就能知道内核中有这么个tracepoint。
如果不知道,也可以按关键词kill盲猜,猜出个大概后查看参数详情。

[root]# bpftrace -l |grep kill
tracepoint:syscalls:sys_enter_kill
[root]# bpftrace -lv sys_enter_kill*
tracepoint:syscalls:sys_enter_kill
    int __syscall_nr
    pid_t pid
    int sig

结果如下(下文都略掉了kill -9.。。。) :

[root]# bpftrace -e 'tracepoint:syscalls:sys_enter_kill {printf("%d(%s) -> %d, sig:%d\n", pid, comm, args->pid, args->sig);}'
Attaching 1 probe...
2258577(bash) -> 2300678, sig:9

五、接受信号端内核空间追踪

以上三种办法都是在发送端截获,信号接收端也是一种办法。Google了下内核在接收端是如何处理SIGKILL的,如有兴趣请参考这儿。
直接对do_group_exit下手,并额外打印了内核调用栈:

[root]# bpftrace -lv do_group_exit*
kfunc:do_group_exit
    int exit_code
kprobe:do_group_exit
[root]# bpftrace -e 'kprobe:do_group_exit {printf("pid:%-6d(%s) Got sig:%d, ks:%s, us:%s\n", pid, comm, arg0, kstack(), ustack());}'

pid:2283764(sleep) Got sig:9, ks:
        do_group_exit+1
        get_signal+344
        do_signal+54
        exit_to_usermode_loop+137
        do_syscall_64+408
        entry_SYSCALL_64_after_hwframe+101
, us:
        0x7fe1a96f3d68

你可能怀疑bpftrace给出的参数说明命名是exit_code啊,看下面的内核源码(4.18)或上面链接的源码

2450                 if (sig_kernel_coredump(signr)) {
2451                         if (print_fatal_signals)
2452                                 print_fatal_signal(ksig->info.si_signo);
2453                         proc_coredump_connector(current);
2454                         /*
2455                          * If it was able to dump core, this kills all
2456                          * other threads in the group and synchronizes with
2457                          * their demise.  If we lost the race with another
2458                          * thread getting here, it set group_exit_code
2459                          * first and our do_group_exit call below will use
2460                          * that value and ignore the one we pass it.
2461                          */
2462                         do_coredump(&ksig->info);
2463                 }
2464
2465                 /*
2466                  * Death signals, no core dump.
2467                  */
2468                 do_group_exit(ksig->info.si_signo);
2469                 /* NOTREACHED */

此处传给do_group_exit的参数是signo, 恰好作为退出码exit_code使用。
从exit_code可以看出此脚本还能作为检测程序退出码使用,留给读者验证。

通过内核调用栈,能清晰的看到接收端处理SIGKILL的过程。而且也能看出在上层get_signal、do_signal下probe也是可行的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

bpftrace各维度捕捉SIGKILL信号 的相关文章

随机推荐

  • FreeRTOS —— 4.队列管理

    4 1 本章介绍与适用范围 队列 提供了任务到任务 xff0c 任务到中断以及中断到任务的通信机制 范围 本章旨在使读者更好地理解 xff1a 如何创建队列 队列如何管理其包含的数据 如何将数据发送到队列 如何从队列接收数据 阻塞队列意味着
  • LSTM一般最多堆叠多少层

    一 LSTM一般最多堆叠多少层 在大规模翻译任务的经验中 简单的堆叠LSTM层最多可以工作4层 很少工作6层 超过8层就很差了 Redisual connection有助于梯度的反向传播 xff0c 能够帮助lstm堆叠更多层 xff0c
  • 华为机试在线训练-牛客网(23)判断两个IP是否属于同一子网

    题目描述 子网掩码是用来判断任意两台计算机的IP地址是否属于同一子网络的根据 子网掩码与IP地址结构相同 xff0c 是32位二进制数 xff0c 其中网络号部分全为 1 和主机号部分全为 0 利用子网掩码可以判断两台主机是否中同一子网中
  • APISIX Dashboard中文文档(二)

    2022年7月6日14 31 51 APISIX Dashboard中文文档 一 APISIX Dashboard中文文档 二 APISIX Dashboard中文文档 三 基本部署 在 Linux 上安装 Apache APISIX Da
  • FreeRTOS 任务调度原理(基于cortexM内核)

    目录 默认FreeRTOS调度策略 xff08 单核 xff09 FreeRTOS调度策略的实现 任务创建 任务调度的4种情景 xff1a 1 第一次启动任务调度器 2 任务主动触发调度 3 SystemTick时钟触发调度 4 因为中断而
  • Centos7命令行安装图形用户界面

    安装完成Centos7后 xff0c 重启后发现是命令行界面 xff0c 于是就想改成图形用户界面 安装了图形用户界面的话 xff1a 1 查看系统里是否已经安装了图形用户界面 使用ctrl 43 alt 43 fx xff0c x为123
  • STM32G070 DMA+SPI+LCD显示

    SPI HandleTypeDef hspi1 DMA HandleTypeDef hdma spi1 tx 描述 xff1a LCD的SPI引脚初始化 参数 xff1a 无 返回 xff1a 无 void LCD SPI Init voi
  • Linux 开启VNCSERVER

    尽管我们可以使用 SSH连接远程通过字符界面来操作Linux xff0c 但是对于更多熟悉图形人来说是很不方便的 xff0c 因此开启Linux的远程桌面还是很有必要的 目前有两种比较流行的方式 xff1a XDM X display ma
  • Ubuntu 代号引发的“崩溃”

    写这篇文章主要是因为在前几天 xff0c 因为向来不关心ubuntu代号的我而引发的一次 崩溃 xff08 人崩溃 xff09 xff0c 正如我们所知Ubuntu 每半年都会更新一个版本 xff0c 每两年都会发布一个TLS xff08
  • Prometheus(二)部署Prometheus和node_exporter

    软件包列表 Prometheus安装 解压部署 rm rf prometheus 2 28 1 linux amd64 tar xvf prometheus 2 28 1 linux amd64 tar gz rm usr local pr
  • Python学习之路_day_02(编程语言介绍及变量)

    一 编程语言介绍 1 机器语言 直接用二进制编程 xff0c 直接控制硬件 xff0c 需要掌握硬件的操作细节 优点 xff1a 执行效率高 缺点 xff1a 开发效率低 2 汇编语言 xff1a 用英文标签取代二进制指令去编写程序 xff
  • 解决linux系统read only system 解决办法

    首先确认系统属于非硬盘物理坏道引起 其次确认是否有root权限 下面我要阐述一个恢复实例 xff1a 一现象 xff1a 1 没有root权限 2 由于磁盘空间满溢导致分区表损坏 xff08 非物理坏道引起 xff09 3 重启后已经无法进
  • 哈希查找效率及应用场景

    数组的特点是 xff1a 寻址容易 xff0c 插入和删除困难 xff1b 而链表的特点是 xff1a 寻址困难 xff0c 插入和删除容易 那么我们能不能综合两者的特性 xff0c 做出一种寻址容易 xff0c 插入删除也容易的数据结构
  • 四位比较器

    四位比较器 一 xff0c 实验目的 通过使用比较四位二进制判断它的相对大小 二 xff0c 实验内容 四位比较器的实验 三 xff0c 实验代码 module Comp 2 str output A gt B A lt B A eq B
  • 程序员玩游戏之三--天天爱消除非暴力脚本

    评论 xff1a 此款游戏成功在其好友排名上 好友的分数超过了你无疑会增加你的斗志 中级策略 xff1a 七手八脚多人一起点 这相当于多个CPU处理一个大任务了 xff0c 哈哈 终极策略 xff1a 自动化 机器总是比人快的多 你两个人一
  • 程序员玩游戏之四--娱网棋牌大连打滚子记牌器

    话说大连人都爱打滚子 xff0c 所以本人就做了一个打滚子记牌器 基本原理同 程序员玩游戏之一 自动对对碰 xff0c 故此处不再赘述 xff0c 只留下一张截图吧 代码请见资源地址 xff1a http download csdn net
  • 为SIGSEGV设置handler有用吗?

    背景 最近几天看到先辈们30年前留下了一块代码 xff0c 为SIGSEGV设置了handler xff0c 所以心中有了两个疑问 xff1a 为SIGSEGV设置handler有没有用 xff1f 能否跳过引起崩溃的那一句指令 xff1f
  • GDB调试技巧实战--为优化版release版本的函数寻找参数值

    在上一篇 GDB调试技巧实战 为release版本的函数寻找参数值 中 xff0c 我们探讨了一种为函数找参数的办法 xff0c 但是 xff0c 那是最理想的情况 编译时没有使用 fomit frame pointer 编译时没有开启优化
  • 通过实例了解uprobe及其对性能的影响

    前言 uprobe是用户空间探针的意思 xff0c 可以用来给用户程序的任何地方下探针 xff0c 不仅仅是函数粒度层级的 所以异常灵活 如果不熟悉ftrace uprobe 可参考以下文档 xff1a https www kernel o
  • bpftrace各维度捕捉SIGKILL信号

    一 问题 Ftrace 几乎适配任何主流内核版本 xff09 和 bpftrace xff08 要求内核版本4 1以上 xff09 中都有两个现成的脚本execsnoop bt killsnoop bt 我经常用他们从外部 xff08 不去