Linux下监控所有进程的退出事件(x86

您所在的位置:网站首页 如何退出centumvp监控 Linux下监控所有进程的退出事件(x86

Linux下监控所有进程的退出事件(x86

2024-07-15 14:23:17| 来源: 网络整理| 查看: 265

本文详细展示Linux下进阶HOOK技术(inline hook),对于普通系统调用的hook文章可以看下面这几篇:

hook系统调用完整实例

Linux ARM64平台上Hook系统调用(以openat为例)

Linux MIPS64下hook系统调用(kylin server v10)

相信看过前面相关文章的朋友,都可以看出,hook系统调用其实还是比较简单(以open为例):

1、获取系统调用表首地址,可以看这篇;

2、替换系统调用为自定义的函数(my_hook_open)地址:

sys_call_table[__NR_open]=my_hook_open;

注:本文适用于linux(x86-64平台)内核版本为4.17以下版本,若要适配4.17及以上内核可以看这篇文章及这篇。

但是,事情往往不是都那么简单,这种只能hook到系统调用表里的系统调用,即那些可以通过__NR_xxxx在系统调用表中找到地址并替换的系统调用。如果无法通过__NR_xxx找到系统调用地址,则无法完成hook。

比如我们想捕获到每个程序启动的信息,则就要hook系统调用execve,我们很容易想到了通过__NR_execve来完成,在x86_64上,如果你这么做了,那么会很不幸的造成系统崩溃,为何会这样呢,查看内核源码会发现__NR_execve对应的其实是stub_execve,不能简单的替换成sys_execve,还需要处理栈平衡,那么该如何做呢,本文暂且不讲,后面会另起文章介绍如何hook系统调用execve。

言归正传,如何捕获到进程退出信息呢。这里我写了个简单的打印hello world程序,我们可以strace查看到该程序从启动到退出所调用的系统调用:

可以看到正常的程序退出调用的是exit_group,其实exit_group是进程正常退出的系统调用,如果只捕获正常程序退出则hook下sys_exit_group即可。

但是进程的终止的方式不止一种,最常见的信号机制,比如通过kill命令终止进程,或者是进程崩溃退出,那么这些异常退出的信息如何捕获到呢。

其实,所有进程的终止最终都是调用了do_exit来处理的,不管是进程正常退出,还是进程接收到信号异常退出,都要经过do_exit最终处理,它主要是从操作系统中删除对当前进程的所有引用(对于所有非共享资源)。调用关系如下图所示:

这样一来,我们的问题就好解决了,我们可以hook掉do_exit来捕获进程退出信息。但是do_exit在系统调用表中并没有__NR_xxx来获取,无法通过文章刚开始提到的方法来hook。

本文介绍一种方法来解决:通过修改offset实现跳转到自定义函数内,不会在原系统调用中添加或覆盖任何新指令。具体实现且看下文分解。

本文中实验环境为centos7.3,内核版本为3.10.0-514.el7.x86_64。

我们来看下do_exit部分源码(kernel\exit.c):

void do_exit(long code) { struct task_struct *tsk = current; int group_dead; profile_task_exit(tsk); WARN_ON(blk_needs_flush_plug(tsk)); ...... ptrace_event(PTRACE_EVENT_EXIT, code); validate_creds_for_do_exit(tsk); ....... exit_signals(tsk); /* sets PF_EXITING */ ...... exit_sem(tsk); exit_shm(tsk); exit_files(tsk); exit_fs(tsk); if (group_dead) disassociate_ctty(1); exit_task_namespaces(tsk); exit_task_work(tsk); check_stack_usage(); exit_thread(); ...... if (tsk->io_context) exit_io_context(tsk); if (tsk->splice_pipe) free_pipe_info(tsk->splice_pipe); if (tsk->task_frag.page) put_page(tsk->task_frag.page); validate_creds_for_do_exit(tsk); preempt_disable(); if (tsk->nr_dirtied) __this_cpu_add(dirty_throttle_leaks, tsk->nr_dirtied); exit_rcu(); ...... }

可以看出do_exit中有调用profile_task_exit,而profile_task_exit也是系统导出函数,可以获取到它的函数地址。我们可以获取到do_exit跟profile_task_exit的入口地址,从do_exit入口地址开始,查看call指令的机器码,再往下找出跟profile_task_exit地址相符的,替换offset为我们自定义的my_profile_task_exit的offset,这样所有调do_exit里profile_task_exit的地方都被跳转到了my_profile_task_exit的地方,这样就达到了劫持进程退出信息的目的了。

上面这段可能看起来有点不太好理解,下面我具体通过代码来看下如何做的。

1、首先找到do_exit\profile_task_exit的声明 grep -nr do_exit /usr/src/ grep -nr profile_task_exit /usr/src/

可以在系统/proc/kallsyms中,查看到这俩系统调用的地址:

然后在代码中可以如下声明:

void do_exit(long error_code); void profile_task_exit(struct task_struct * task); 2、根据函数声明来定义待替换的系统调用函数指针 typedef void (*profile_task_exit_t)(struct task_struct * task); void my_profile_task_exit(struct task_struct * task); unsigned long old_do_exit_func = 0; profile_task_exit_t orig_profile_task_exit = NULL; 3、获取do_exit\profile_task_exit系统调用入口地址 old_do_exit_func = kallsyms_lookup_name("do_exit"); orig_profile_task_exit = kallsyms_lookup_name("profile_task_exit"); 4、修改offset static int replace_kernel_func(unsigned long handler,  unsigned long orig_func, unsigned long my_func) { unsigned char *tmp_addr = (unsigned char*)handler; int i = 0;   do{    /* in x86_64 the call instruction opcode is 0x8e,      * occupy 1+4 bytes(E8+offset) totally      */       if(*tmp_addr == 0xe8){       unsigned int* offset = (unsigned int*)(tmp_addr+1);       if(((unsigned long)tmp_addr+5 + *offset) == orig_func){ printk("call:0x%08x, offset:%08x, old_func:%08x.\n", (unsigned int)tmp, *offset, orig_func);          /* replace with my_func relative addr(offset) */         *offset=my_func-(unsigned long)tmp_addr-5;         printk("call:0x%08x, offset:%08x, new_func:%08x.\n",          (unsigned int)tmp_addr, *offset, my_func); return 1; } }     tmp_addr++;   }while(i++ tgid,    current->real_parent->tgid, current->group_leader->comm);      /* call the origin system call */ orig_profile_task_exit(task);   return ; } 6、在uninit_module中 if(old_do_exit_func && orig_profile_task_exit){ disable_write_protection();   patch_kernel_func(old_do_exit_func, (unsigned long)my_profile_task_exit, orig_profile_task_exit);   enable_write_protection();   printk("profile_task_exit is unpatched!\n");  }

其中disable_write_protection()、enable_write_protection()在这里介绍过

本方法的关键是在第四步,读者可以自行细细体味。

到处捕获进程退出代码就完成了,这里不止可以捕获进程退出,也可以捕获到线程的退出。

该示例代码运行结果截图如下:

关于在linux的基于x86_64的各内核版本里,如何hook应用程序的启动行为,可以看这篇文章。

感兴趣的话可以关注我的微信公众号【大胖聊编程】,我的公众号中有更多文章分享,也可以在公众号中联系到我,加好友一起交流学习。



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭