Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[linux][kprobe]谁动了我的文件---使用kprobe找到目标进程

[linux][kprobe]谁动了我的文件---使用kprobe找到目标进程

作者头像
皮振伟
发布于 2018-04-09 02:10:53
发布于 2018-04-09 02:10:53
2.4K00
代码可运行
举报
文章被收录于专栏:皮振伟的专栏皮振伟的专栏
运行总次数:0
代码可运行

问题场景:

云计算IaaS平台上,经常使用libvirt+qemu-kvm做基础平台。libvirt会在/etc/libvirt/qemu/目录下,保存很多份qemu的配置文件,如ubuntu.xml。

作者发现其中的配置文件会在特定的场景下被修改,却不知道哪个进程是凶手。为了找到凶手,作者写下了这个debug工具。

代码分析

代码路径:https://github.com/pacepi/whotouchmyfile

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <asm/uaccess.h>

#define FILE_NAME 64//被监控的文件名最大字符数,按需求,自己改

static struct kprobe kp = {
    .symbol_name    = "vfs_write",//使用kprobe,监控vfs_write,如果修改文件,几乎逃不开这个入口。作者想到的其他情况就是使用了mmap映射文件,然后msync回写。
};

static char file_name[FILE_NAME] = {0};
static struct ctl_table_header *cth = NULL;
static struct ctl_path path = {
    .procname = "kernel",//监控点放到了/proc/sys/kernel目录下
};
static struct ctl_table table[] = {
    {
        .procname   = "who_touch_my_file",//命名是作者的一时想法。用法就是echo "file_name" > /proc/sys/kernel/who_touch_my_file
        .data       = file_name,
        .maxlen     = FILE_NAME,
        .mode       = 0644,
        .proc_handler   = proc_dostring,
    },
    {
    }
};

static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{//这里是重头戏,主要实现在这里。如果调用到了这里,说明就有进程调用了write,pwrite,writev,总之,陷入到了vfs_write这里。至于原因,留个悬念吧。后面开一篇来分析kprobe的实现
    struct file *file = (struct file *)regs->di;//因为x86的参数传递规则是di,si,dx,cx,r8,r9,所以di就是vfs_write的第一个参数。arm默认是r0,r1,r2,
r3,相应的取r0
    char *buf = NULL;
    size_t size = 0;

#if 0
    printk(KERN_INFO "pre_handler: p->addr = 0x%p, ip = %lx," " flags = 0x%lx\n", p->addr, regs->ip, regs->flags);
    if (file->f_path.dentry && file->f_path.dentry->d_name.name)
        printk(KERN_INFO "name = %s" , file->f_dentry->d_name.name);
#endif
    //简单的字符串比较。这里需要注意一下,作者实验过的3.10和4.0.4,这里的数据结构发生了变化。对于不同版本,需要自己修改适配一下。
    if (unlikely(strlen(file_name) && file && file->f_path.dentry && file->f_path.dentry->d_name.name && (strncmp(file->f_path.dentry->d_name.name, file_name, FILE_NAME) == 0)))
        printk(KERN_INFO "bingo : %s\n" , file->f_path.dentry->d_name.name);
    else
        return 0;

    size = regs->dx + 1;//vfs_write的第三个参数在dx中
    printk(KERN_INFO "process = %s , pid = %ld, file = %s, size = %ld\n" , current->comm, current->pid, file->f_path.dentry->d_name.name, size);//进程从用户态call下来,到这里都是同步的,所以current就是调用vfs_write的caller,拿到进程名称和pid就很容易了
    /*
     * should be careful ! if size is too big, maybe over flow.//文件不大的时候,可以尝试打印一下,不过注意,内核栈和thread结构体一共8k。不要overflow。
     buf = (char*)kmalloc(size, 0);
     if (buf == NULL)
     return 0;

     memset(buf, 0x00, size);
     if(copy_from_user(buf, regs->si, size))
     goto out;

     printk(KERN_INFO "%s\n" , buf);
     */

out :
    if (buf)
        kfree(buf);

    return 0;
}

static void handler_post(struct kprobe *p, struct pt_regs *regs,
        unsigned long flags)
{
    //printk(KERN_INFO "post_handler: p->addr = 0x%p, flags = 0x%lx\n", p->addr, regs->flags);
}

static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
    printk(KERN_INFO "fault_handler: p->addr = 0x%p, trap #%dn",
            p->addr, trapnr);
    return 0;
}

static int __init kprobe_init(void)
{//这里是八股文,init+exit
    int ret;
    kp.pre_handler = handler_pre;
    kp.post_handler = handler_post;
    kp.fault_handler = handler_fault;

    ret = register_kprobe(&kp);//其实这里才是入口,ko向kernel注册kprobe
    if (ret < 0) {
        printk(KERN_INFO "register_kprobe failed, returned %d\n", ret);
        goto out;
    }
    printk(KERN_INFO "register kprobe at %p\n", kp.addr);

    cth = register_sysctl_paths(&path, &table);///proc/sys/kernel/who_touch_my_file在这里注册的
    if (cth == NULL) {
        printk(KERN_INFO "register_sysctl_paths failed\n");
        ret = -EFAULT;
        goto error;
    }

    return 0;
error:
    unregister_kprobe(&kp);

out:
    return ret;
}

static void __exit kprobe_exit(void)
{
    if (cth)
        unregister_sysctl_table(cth);
    unregister_kprobe(&kp);
    printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr);
}

module_init(kprobe_init);
module_exit(kprobe_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("PiZhenwei p_ace@126.com");

其他的话

这里是否可以用systemtap?答案是可以的。本质来说,systemtap也是用kprobe实现的(不过它需要debug symbol,也就是vmlinux,不过也可以捕获更精确的代码,原因在后面的kprobe实现一起分析)。

所谓内核热补丁,也可以用kprobe实现。在不重启内核的情况下,动态加载ko,修改内核行为。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AlwaysGeek 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Linux内核调试技术——kprobe使用与实现
Linux kprobes调试技术是内核开发者们专门为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术。利用kprobes技术,内核开发人员可以在内核的绝大多数指定函数中动态的插入探测点来收集所需的调试状态信息而基本不影响内核原有的执行流程。kprobes技术目前提供了3种探测手段:kprobe、jprobe和kretprobe,其中jprobe和kretprobe是基于kprobe实现的,他们分别应用于不同的探测场景中。本文首先简单描述这3种探测技术的原理与区别,然后主要围绕其中的kprobe技术进行分析并给出一个简单的实例介绍如何利用kprobe进行内核函数探测,最后分析kprobe的实现过程(jprobe和kretprobe会在后续的博文中进行分析)。
嵌入式Linux内核
2022/09/24
2.7K0
Linux内核调试技术——kprobe使用与实现
Linux内核调试技术——kprobe使用与实现
Linux kprobes调试技术是内核开发者们专门为了便于跟踪内核函数执行状态所设计的一种轻量级内核调试技术。利用kprobes技术,内核开发人员可以在内核的绝大多数指定函数中动态的插入探测点来收集所需的调试状态信息而基本不影响内核原有的执行流程。kprobes技术目前提供了3种探测手段:kprobe、jprobe和kretprobe,其中jprobe和kretprobe是基于kprobe实现的,他们分别应用于不同的探测场景中。本文首先简单描述这3种探测技术的原理与区别,然后主要围绕其中的kprobe技术进行分析并给出一个简单的实例介绍如何利用kprobe进行内核函数探测,最后分析kprobe的实现过程(jprobe和kretprobe会在后续的博文中进行分析)。
233333
2021/09/08
6K0
Kernel调试追踪技术之 Kprobe on ARM64
kprobe 是一种动态调试机制,用于debugging,动态跟踪,性能分析,动态修改内核行为等,2004年由IBM发布,是名为Dprobes工具集的底层实现机制[1][2],2005年合入Linux kernel。probe的含义是像一个探针,可以不修改分析对象源码的情况下,获取Kernel的运行时信息。
233333
2024/04/03
4400
Kernel调试追踪技术之 Kprobe on ARM64
kprobe分析内核kworker占用CPU 100%问题总结
利用linux kernel 动态追踪技术,排查问题本身就可能会变成一个非常有趣的过程,让我们遇到线上的诡异问题就感到兴奋,就仿佛好不容易又逮着机会,可以去解一道迷人的谜题。
全栈程序员站长
2022/11/08
2.8K0
内核级防篡改
网络攻击者通常会利用被攻击网站中存在的漏洞,通过在网页中植入非法暗链对网页内容进行篡改等方式,进行非法牟利或者恶意商业攻击等活动。网页被恶意篡改会影响用户正常访问网页内容,还可能会导致严重的经济损失、品牌损失甚至是政治风险。
Laikee
2022/04/25
2.2K1
内核级防篡改
深入探究Linux Kprobe机制
kprobe机制用于在内核中动态添加一些探测点,可以满足一些调试需求。本文主要探寻kprobe的执行路径,也就是说如何trap到kprobe,以及如何回到原路径继续执行。
Linux阅码场
2020/12/14
1.6K0
Linux 内核动态追踪技术的实现
前言:之前的文章介绍了基于 tracepoint 静态追踪技术的实现,本文再介绍基于 kprobe 的动态追踪即使的实现。同样,动态追踪也是排查问题的利器。
theanarkh
2021/11/19
8230
[linux][perf]list过长导致CPU消耗过高的问题分析
某机器上网络出现时断时续的问题,网络的同事发现ovs进程的CPU消耗很高,硬件offload的规则下发卡住的问题。即通过netlink向内核发送消息卡住。
皮振伟
2019/12/15
1.8K0
Linux 内核监控在 Android 攻防中的应用
在日常分析外部软件时,遇到的反调试/反注入防护已经越来越多,之前使用的基于 frida 的轻量级沙盒已经无法满足这类攻防水位的需要,因此需要有一种更加深入且通用的方式来对 APP 进行全面的监测和绕过。本文即为对这类方案的一些探索和实践。
evilpan
2023/02/12
3.3K0
Linux 内核监控在 Android 攻防中的应用
uprobe
本章的我们来学习uprobe ,顾名思义,相对于内核函数/地址的监控,主要用于用户态函数/地址的监控。听起来是不是有点神奇,内核怎么监控用户态函数的调用呢?本章的内容包括:
233333
2023/05/07
1.1K0
uprobe
Linux内核调试利器|kprobe 原理与实现
在《Linux 内核调试利器 | kprobe 的使用》一文中,我们介绍过怎么使用 kprobe 来追踪内核函数,而本文将会介绍 kprobe 的原理和实现。
用户7686797
2022/08/24
3.6K0
Linux内核调试利器|kprobe 原理与实现
使用jprobe建设镜面层叠的原则和见解
忽然想起的回忆,那是2007上周五在冬季,我看我的老湿调试Linux堆IP层,只看到他改变路由查找的逻辑,然后直接make install上的立竿见影的效果有点,我只知道,,这种逻辑必须再次更改编译内核。再一次,他没有编译,就像刚才编译的文件…时又无聊的工作阻碍了我对Linux内核的探索进度,直到今天,我依旧对编译内核有相当的恐惧,不怕出错,而是怕磁盘空间不够,initrd的组装拆解之类,太繁琐了。我之所以知道2007年的那天是周五,是由于第二天我要加班。没有谁逼我。我自愿的,由于我想知道师父是怎么做到不又一次编译内核就能改变非模块的内核代码处理逻辑的。第二天的收获非常多,不但知道了他使用了“镜像协议栈”。还额外赚了一天的加班费。我还记得周六加完班我和老婆去吃了一家叫做石工坊的羊排火锅。人家赠送了一仅仅绿色的兔子玩偶。
全栈程序员站长
2022/07/06
7390
万字长文解读 Linux 内核追踪机制
Linux 存在众多 tracing tools,比如 ftrace、perf,他们可用于内核的调试、提高内核的可观测性。众多的工具也意味着繁杂的概念,诸如 tracepoint、trace events、kprobe、eBPF 等,甚至让人搞不清楚他们到底是干什么的。本文尝试理清这些概念。
刘盼
2023/08/22
2.6K0
万字长文解读 Linux 内核追踪机制
android rootfs的挂载流程[通俗易懂]
out/host/linux-x86/bin/mkbootfs out/target/product//root | out/host/linux-x86/bin/minigzip > out/target/product//ramdisk.img 上述命令分两步进行: 1.out/host/linux-x86/bin/mkbootfs out/target/product/*/root 生成一个cpio文件,利用cpio 可将文件或目录从文件库获取出来或将散列文件拷贝到文件库。 2.out/host/linux-x86/bin/minigzip 将生成的cpio文件压缩成一个gzip格式的文件“out/target/product/*/ramdisk.img“
全栈程序员站长
2022/11/08
2.7K0
Linux内核Crash分析
在工作中经常会遇到一些内核crash的情况,本文就是根据内核出现crash后的打印信息,对其进行了分析,使用的内核版本为:Linux2.6.32。
用户5807183
2019/10/15
4.7K0
Linux内核Crash分析
【调试】kprobes(一)基本概念
开发人员在内核或者模块的调试过程中,往往会需要要知道其中的一些函数有无被调用、何时被调用、执行是否正确以及函数的入参和返回值是什么等等。
嵌入式与Linux那些事
2023/03/24
1.2K0
【调试】kprobes(一)基本概念
LCD驱动程序分析
/* LCD驱动程序分析 和别的驱动程序一样,首先找到模块函数的入口处 */ #include "s3c2410fb.h" /* Debugging stuff */ #ifdef CONFIG_FB_S3C2410_DEBUG static int debug = 1; #else static int debug = 0; #endif #define dprintk(msg...) if (debug) { printk(KERN_DEBUG "s3c2410fb: " msg); }
DragonKingZhu
2022/05/08
1.6K0
Linux|IO|File IO源码剖析
文件的open、close、read、write是最基本的文件抽象,描述了对于设备的操作。本文将结合用户态的接口以及内核态的实现剖析文件IO。
朝闻君
2021/11/22
3.9K0
Linux|IO|File IO源码剖析
15.linux-LCD层次分析(详解)
本文主要介绍了如何为树莓派开发一款支持200万像素摄像头的LCD驱动程序。首先介绍了LCD驱动的整体流程,然后重点讲解了树莓派上的LCD驱动实现。在实现过程中,详细描述了LCD驱动程序的框架设计、寄存器配置、中断处理、视频处理流程等内容。最后通过实际案例演示了该驱动程序的具体使用方法。
诺谦
2018/01/03
1.8K0
15.linux-LCD层次分析(详解)
Linux驱动之I2C子系统剖析
源码中会涉及到一部分SMBus相关内容,SMBus是Intel在I2C的基础上开发的类似I2C的总线,本文不探讨SMBus相关内容(其实说白了,还是懒QAQ)。笔者会大体上对I2C子系统的源码进行分析,如若分析的有出入,还望指出。
菜菜cc
2022/11/15
6.9K0
Linux驱动之I2C子系统剖析
相关推荐
Linux内核调试技术——kprobe使用与实现
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验