利用kprobe監(jiān)聽(tīng)刪除文件

場(chǎng)景來(lái)源

在使用kubevirt的場(chǎng)景中遥倦,因?yàn)槟承┊惓3腊龋瑪?shù)據(jù)盤被清理掉管挟,經(jīng)過(guò)分析disk.img在pod內(nèi)和宿主機(jī)的存儲(chǔ)路徑轿曙,基本可以排除人為刪除,軟件層面只有kubelet或者virt-handler可以去做這個(gè)清理僻孝。查看路徑导帝,可以看到數(shù)據(jù)盤使用的是kubernetes的empty-dir,極有可能是pod異常穿铆,被重啟時(shí)您单,empty-dir也會(huì)隨之被清理,現(xiàn)在要確認(rèn)到底是不是kubelet清理了該文件荞雏。

問(wèn)題分析

如果想知道一個(gè)文件是被誰(shuí)刪除的虐秦,有什么辦法呢?監(jiān)測(cè)rm命令凤优?如果是語(yǔ)言層面進(jìn)行刪除悦陋,就沒(méi)法監(jiān)測(cè)了。
我們可以想到一個(gè)辦法筑辨,從內(nèi)核層面監(jiān)聽(tīng)內(nèi)核的刪除函數(shù)俺驶。kprobe可以實(shí)現(xiàn)這個(gè)功能。
刪除文件是執(zhí)行系統(tǒng)調(diào)用unlink完成的棍辕,但是可能因?yàn)殒溄訂?wèn)題或者引用問(wèn)題暮现,這個(gè)文件并不會(huì)刪除还绘,所以我們應(yīng)該找到一個(gè)函數(shù),這個(gè)函數(shù)如果被調(diào)用到栖袋,文件肯定會(huì)被刪除蚕甥,由此可以想到,監(jiān)測(cè)底層文件系統(tǒng)的一個(gè)刪除函數(shù)栋荸。
如ext4,可以監(jiān)測(cè)ext4_unlink凭舶,xfs要監(jiān)測(cè)xfs_vn_unlink函數(shù)晌块。
inode_operations的unlink函數(shù)原型

    int (*unlink) (struct inode *,struct dentry *);

我們可以從dentry中拿到文件路徑,通過(guò)內(nèi)核的全局變量current拿到當(dāng)前的進(jìn)程帅霜,由于dentry是該函數(shù)的第二個(gè)函數(shù)匆背,我們應(yīng)該從rsi寄存器中獲取地址。下面附上完整代碼身冀。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/kprobes.h>

#define MAX_SYMBOL_LEN  64
// 要監(jiān)測(cè)的符號(hào)信息
static char symbol[MAX_SYMBOL_LEN] = "xfs_vn_unlink";
module_param_string(symbol, symbol, sizeof(symbol), 0644);

/* For each probe you need to allocate a kprobe structure */
static struct kprobe kp = {
        .symbol_name    = symbol,
};

/* kprobe pre_handler: called just before the probed instruction is executed */
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#ifdef CONFIG_X86
        // rdi,rsi,rdx,rcx,r8,r9钝尸,寄存器傳參及順序,dentry在第二個(gè)參數(shù)搂根,所以用rsi珍促。
        struct dentry *dentry = (struct dentry*)regs->si;
        //pr_info("<%s> pre_handler: p->addr = 0x%p, ip = %lx, flags = 0x%lx\n",
        //        p->symbol_name, p->addr, regs->ip, regs->flags);
        char * name = dentry->d_name.name;
        struct task_struct * acurrent =current;
        pr_info("name : %s pid : %d execname : %s\n", name, acurrent->pid, acurrent->comm);
#endif
        return 0;
}

/* kprobe post_handler: called after the probed instruction is executed */
static void handler_post(struct kprobe *p, struct pt_regs *regs,
                                unsigned long flags)
{
#ifdef CONFIG_X86
        //pr_info("<%s> post_handler: p->addr = 0x%p, flags = 0x%lx\n",
        //      p->symbol_name, p->addr, regs->flags);
#endif
}

/*
 *  * fault_handler: this is called if an exception is generated for any
 *   * instruction within the pre- or post-handler, or when Kprobes
 *    * single-steps the probed instruction.
 *     */
static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr)
{
        pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr);
        /* Return 0 because we don't handle the fault. */
        return 0;
}

static int __init kprobe_init(void)
{
        int ret;
        kp.pre_handler = handler_pre;
        kp.post_handler = handler_post;
        kp.fault_handler = handler_fault;

        ret = register_kprobe(&kp);
        if (ret < 0) {
                pr_err("register_kprobe failed, returned %d\n", ret);
                return ret;
        }
        pr_info("Planted kprobe at %p\n", kp.addr);
        return 0;
}

static void __exit kprobe_exit(void)
{
        unregister_kprobe(&kp);
        pr_info("kprobe at %p unregistered\n", kp.addr);
}

對(duì)應(yīng)的Makefile
# To build modules outside of the kernel tree, we run "make"
# # # in the kernel source tree; the Makefile these then includes this
# # # Makefile once again.
# # # This conditional selects whether we are being included from the
# # # kernel Makefile or not.
# #
# # # called from kernel build system: just declare what our modules are
#obj-m := reg_module.o 
obj-m := kprobe_test.o 
# #
CROSS_COMPILE = 
# #
CC            = gcc
# #     # Assume the source tree is where the running kernel was built
#     # You should set KERNELDIR in the environment if it's elsewhere
KERNELDIR ?= /lib/modules/4.19.0/build
    #         # The current directory is passed to sub-makes as argument
PWD := $(shell pwd)

all: 
        make -C $(KERNELDIR) M=$(PWD) modules
clean:
        rm -rf *.o *~ core .depend *.symvers .*.cmd *.ko *.mod.c .tmp_versions $(TARGET)

效果展示

加載內(nèi)核模塊后
insmod kprobe_test.ko
[256838.004884] name : kprobe_test.mod.o pid : 10749 execname : rm
[256861.029438] name : .kprobe_test.c.swx pid : 10756 execname : vim
[256861.029456] name : .kprobe_test.c.swp pid : 10756 execname : vim
[256901.147068] name : .viminfo pid : 10756 execname : vim
[256901.262790] name : .kprobe_test.c.swp pid : 10756 execname : vim
[256913.980868] name : abc pid : 10767 execname : rm
[256929.192739] name : .messages.swpx pid : 10769 execname : vim
[256929.192767] name : .messages.swp pid : 10769 execname : vim
[256933.188426] name : .viminfo pid : 10769 execname : vim
[256933.289964] name : .messages.swp pid : 10769 execname : vim
[256938.301215] name : .kprobe_test.c.swx pid : 10770 execname : vim
[256938.301236] name : .kprobe_test.c.swp pid : 10770 execname : vim
[256978.605412] name : .viminfo pid : 10770 execname : vim
[256978.714638] name : .kprobe_test.c.swp pid : 10770 execname : vim
[257128.882336] name : .kprobe_test.c.swx pid : 10798 execname : vim
[257128.882354] name : .kprobe_test.c.swp pid : 10798 execname : vim
[257382.589794] name : .viminfo pid : 10798 execname : vim
[257382.699274] name : .kprobe_test.c.swp pid : 10798 execname : vim

總結(jié)

代碼及功能雖然不多,但是需要對(duì)內(nèi)核有一些源碼層面的了解剩愧。
這個(gè)功能當(dāng)然也可以做為一個(gè)metrics來(lái)暴露出來(lái)猪叙,監(jiān)測(cè)是哪個(gè)進(jìn)程,甚至是哪個(gè)用戶仁卷,在什么時(shí)間刪除了文件穴翩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市锦积,隨后出現(xiàn)的幾起案子芒帕,更是在濱河造成了極大的恐慌,老刑警劉巖丰介,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件背蟆,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡基矮,警方通過(guò)查閱死者的電腦和手機(jī)淆储,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)家浇,“玉大人本砰,你說(shuō)我怎么就攤上這事「直” “怎么了点额?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵舔株,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我还棱,道長(zhǎng)载慈,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任珍手,我火速辦了婚禮办铡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘琳要。我一直安慰自己寡具,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布稚补。 她就那樣靜靜地躺著童叠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪课幕。 梳的紋絲不亂的頭發(fā)上厦坛,一...
    開(kāi)封第一講書(shū)人閱讀 51,541評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音乍惊,去河邊找鬼杜秸。 笑死,一個(gè)胖子當(dāng)著我的面吹牛污桦,可吹牛的內(nèi)容都是我干的亩歹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼凡橱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼小作!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起稼钩,我...
    開(kāi)封第一講書(shū)人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤顾稀,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坝撑,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體静秆,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年巡李,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了抚笔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侨拦,死狀恐怖殊橙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤膨蛮,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布叠纹,位于F島的核電站,受9級(jí)特大地震影響敞葛,放射性物質(zhì)發(fā)生泄漏誉察。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一惹谐、第九天 我趴在偏房一處隱蔽的房頂上張望持偏。 院中可真熱鬧,春花似錦氨肌、人聲如沸综液。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至檩奠,卻和暖如春桩了,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背埠戳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工井誉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人整胃。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓颗圣,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親屁使。 傳聞我的和親對(duì)象是個(gè)殘疾皇子在岂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容