Ubuntu20.04+Linux5.8.8 添加系统调用实现进程隐藏

Ubuntu20.04+Linux5.8.8 添加系统调用实现进程隐藏

虚拟机版本主机信息
VMware 15Thinkpad carbon X1 2020 + win10

1 目标

  • 实现系统调用int hide(pid_t pid, int on),在进程pid有效的前提下,如果on置1,进程被隐藏,用户无法通过ps或top观察到进程状态;如果on置0且此前为隐藏状态,则恢复正常状态。
  • 考虑权限问题,根用户才能隐藏进程。
  • 设计一个新的系统调用int hide_user_processes(uid_t uid, char *binname),参数uid为用户ID号,当binname参数为NULL时,隐藏该用户的所有进程;否则,隐藏二进制映像名为binname的用户进程。该系统调用应与hide系统调用共存。

2. 方法与原理

2.1 修改proc_pid_readdir
  • 安全的方法
  • 原理
  1. proc目录中保存着正在运行的全部进程的相关信息文件,可以调用proc_pid_readdir逐一读取。
  2. 在进程的task_struct结构体中自定义一个hide变量,proc_pid_readdir()函数中加入判断条件,如果hide=1就跳过对该进程信息的读取。
  3. 自添加的系统调用通过修改该变量,实现进程的查看隐藏与恢复显示
2.2 pid赋值为0
  • 投机取巧的危险的方法。
  • 只对Linux早期版本(如2.x)有效。但是之后的版本无效,非常不建议使用,有太多bug。
  • 原理:0号init进程不会被ps和top指令显示。
  • Bug:隐藏的进程如果不恢复,重启后无法进入正确的0号init进程,导致死机。
2.3 原理

关键是理解和linux进程相关的结构体task_struct、结构体pid和结构体cred等,用于创建进程的fork函数的源码的基本实现。
详见我精选的参考博文:

3 遇到的困难

3.1 虚拟机开机黑屏
  • 最有效的方法:使用快照备份关键节点
  • 相关博文推荐:link,基本可以解决90%的Ubuntu系统崩溃问题。

4 流程

  1. 按照官网的步骤,完成对kernel代码修改Adding a New System Call
  • 在kernel/sys.c文件里面末尾添加两个系统调用,使用SYSCALL_DEFINE2
SYSCALL_DEFINE2(hide, pid_t, pid,int,on)
{
	printk("hide process invoked with params: pid=%d on=%d\n",pid,on);
	printk("current uid = %d\n", get_current_cred()->uid.val);
	//if( get_current_cred()->uid.val != 0) //判断是否为root用户
	if(!uid_eq(current_euid(),GLOBAL_ROOT_UID))
	{
	        printk("you aren't the root user! try to use sudo!\n");
		return -1;
	}
	
	
	struct task_struct *p;
	struct task_struct * me = NULL;
	p = &init_task;
	do
	{
		printk("check proc with pid = %d and old_pid = %d\n", p->pid,p->old_pid);
		if( pid == p->old_pid )      //pid_t == int   old_pid is global pid
		{
			me = p;
			break;
		}	
	}while( ( p = next_task(p) ) && ( p != &init_task ) );  
	//next_task get the next PCB pointer, 
	//it's a loop list, so the stop signal is p != &init_task!
	
	
	if(me == NULL ) //判断pid是否有效
	{
	        printk("the target pid doesn't exist!\n");
		return -2;
	}
	
	if( on == 1 )
	{  
		printk("hide proccess with pid=%d\n",me->pid);
		
		me->hide = 1;  //置隐藏标志为1
	}
	else
	{
		if( me->hide == 1 )
		{
			printk("reveal proccess with pid=%d\n",me->pid);
			me->hide = 0;
		}
	}
	printk("nice system call! goodbye!\n");
	return 0;
}


SYSCALL_DEFINE2(hide_user_process,uid_t,uid,char*,binname){

	struct task_struct *p;
	struct user_namespace *ns = current_user_ns();
	struct pid *thread_pid;
	char buf[TASK_COMM_LEN];
	char buf2[TASK_COMM_LEN];
	int hide,i;
	kuid_t kuid;

	kuid = make_kuid(ns, uid);


	printk("hide_user_proc invoked");

	printk(" uid=%d ",uid);
	if(binname!=NULL){
		copy_from_user(buf2,binname,TASK_COMM_LEN);
		printk(" binname=%s",buf2);
	}else printk(" binname=NULL");


	for_each_process(p){
		if(uid_eq(task_uid(p),kuid)){
			hide=1;
			get_task_comm(buf, p);

			if(binname!=NULL){
				for(i=0;buf2[i];i++){
					if(!buf[i]||buf[i]!=buf2[i]){
						hide=0;
						break;
					}
				}
				if(buf[i])hide=0;
			}
			printk("scan on '%s' hide it? =%d",buf,hide);

			if(hide){
				p -> hide = 1;
				thread_pid = get_pid(p->thread_pid);
				proc_flush_pid(thread_pid);
			}
		}
	}

	return 0;
}

  • 在include/linux/syscalls.h中的对应位置添加函数声明,注意返回值都是long,因为是64位系统
asmlinkage long sys_hide( pid_t pid, int on );                        //my system call
asmlinkage long sys_hide_user_process( uid_t uid, char* binname );    //my system call
  • 在include/uapi/asm-generic/unistd.h中的对应位置添加函数声明,同时注意修改 __NR_syscalls 440为__NR_syscalls 442
#define __NR_xyzzy 440
__SYSCALL(__NR_hide, sys_hide)
#define __NR_xyzzy 441
__SYSCALL(__NR_hide_user_process, sys_hide_user_process)
  • 修改arch/x86/entry/syscalls/syscall_64.tbl,该表是x86_64的系统调用表,对应位置添加如下信息
440 64      hide       sys_hide
441 64      hide_user_process  sys_hide_user_process
  • 在fs/proc/base.c中修改函数 proc_pid_readdir,在3343行之后添加如下一行
if (iter.task->hide==1) continue;
  • 修改结构体task_struct,在include/linux/sched.h中636行之后添加如下
int hide;
  • 修改进程派生的fork函数,在kernel/fork.c的2443行之后添加
p->hide = 0;
  1. 编译修改后的内核
# 从官网下载5.8.8的linux内核的压缩包,解压并在终端打开
# 依次修改对应位置的源码之后,执行如下指令

sudo make mrproper 
 
sudo make clean 

#进入界面后,直接exit,再yes,一般这个步骤的调整会影响微内核的大小,进而影响开机时间
sudo make menuconfig

# 我给ubuntu虚拟机的配置是,1个处理器,6个内核
#本机的CPU为一个CORE i7-10710U CPU, 6核12线程
sudo make -j12  # 同时允许最多12线程并行,约1h20min


sudo make modules_install        //安装内核模块
 
sudo make install      //安装内核

reboot
  1. 重启后,使用测试程序测试系统调用的准确性

待续

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值