linux 隐藏进程
来源:互联网 发布:如何生成短信淘宝链接 编辑:程序博客网 时间:2024/06/10 15:50
转自http://blog.csdn.net/billpig/article/details/6038330
本文在不修改ps或top命令的任何代码与采用将进程号置0的方法的前提下,实现隐藏进程,本程序在CRUX 2.2上实现
1、原理
Linux中,可以通过/proc文件系统访问到许多内核的内部信息。/proc文件系统最初的设计也是用于方便地访问进程相关的信息,因此命名为proc。现在这个文件系统已用于反映系统中方方面面的信息,例如/proc/modules是模块的列表,/proc/meminfo则是内存使用的统计。/proc文件系统中的目录并非持久存储的信息,也就是说,其目录并不“真实”地存在于磁盘,而是在访问时动态生成。
本项目中我们感兴趣的是/proc文件系统中关于进程的信息。每一个进程在/proc文件系统中有一个目录即(/proc/<pid>),目录名即进程号。self目录是一个链接,指向当前进程。
ps命令和top命令从/proc文件系统中读取进程信息并显示出来。因此,如果一个进程的进程号没有在/proc文件系统中反映出来,则这个进程被“隐藏”了,“隐藏”进程在ps或top命令的输出不出现。
2、实现
2.1 为task_struct添加变量hide如下:(include/linux/sched.h)
- struct task_struct {
- unsigned did_exec:1;
- pid_t pid;
- pid_t tgid;
- ...(省略)
- char hide;(在最后一行添加该句)
- }
2.2 哪里修改proc代码
在linux代码树中,所有文件系统的代码都放在linux/fs/目录中,其中,proc文件系统的原始码在linux/fs/proc中,下面我简单介绍一下proc目录中的源文件。
在目录中共有11个相关文件,他们是:
procfs_syms.c inode.c generic.c base.c
array.c root.c proc_tty.c proc_misc.c
kmsg.c kcore.c proc_devtree.c
这么多的文件我们如何去找呢,借鉴网上的资料,对各个文件进行查找可知:
其中,procfs_syms.c,generic.c及inode.c和proc文件系统的管理相关,包括proc文件系统的注册,及向内核其他子系统提供的例程等等。
源文件root.c和proc文件系统的根结点的管理相关。
而base.c,array.c则用来处理/proc目录中进程的信息,包括命令行,进程状态,内存状态等等和进程相关的内容。proc_tty.c用来处理/proc/tty信息,proc_misc.c则用来管理和/proc目录中的大多数文件。
除此之外,更有两个非常重要的头文件proc_fs.h,proc_fs_i.h,我们能在/linux/include/linux/目录中找到。
同时,最重要的是,我们确定了我们所要需要的相关信息在base.c文件中,主要是在这2个函数之间:proc_pid_readdir,proc_task_readdir,但是是哪个函数呢?由于网上的资料很少,没办法一个一个试。通过查看着2个看书的源代码:
- nt proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
- {
- unsigned int tgid_array[PROC_MAXPIDS];
- char buf[PROC_NUMBUF];
- unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
- unsigned int nr_tgids, i;
- int next_tgid;
- if (!nr) {
- ino_t ino = fake_ino(0,PROC_TGID_INO);
- if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0)
- return 0;
- filp->f_pos++;
- nr++;
- }
- /* f_version caches the tgid value that the last readdir call couldn't
- * return. lseek aka telldir automagically resets f_version to 0.
- */
- next_tgid = filp->f_version;
- filp->f_version = 0;
- for (;;) {
- nr_tgids = get_tgid_list(nr, next_tgid, tgid_array);
- if (!nr_tgids) {
- /* no more entries ! */
- break;
- }
- next_tgid = 0;
- /* do not use the last found pid, reserve it for next_tgid */
- if (nr_tgids == PROC_MAXPIDS) {
- nr_tgids--;
- next_tgid = tgid_array[nr_tgids];
- }
- for (i=0;i<nr_tgids;i++) {
- int tgid = tgid_array[i];
- ino_t ino = fake_ino(tgid,PROC_TGID_INO);
- unsigned long j = PROC_NUMBUF;
- do
- buf[--j] = '0' + (tgid % 10);
- while ((tgid /= 10) != 0);
- if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) {
- /* returning this tgid failed, save it as the first
- * pid for the next readir call */
- filp->f_version = tgid_array[i];
- goto out;
- }
- filp->f_pos++;
- nr++;
- }
- }
- out:
- return 0;
- }
- /* for the /proc/TGID/task/ directories */
- static int proc_task_readdir(struct file * filp, void * dirent, filldir_t filldir)
- {
- unsigned int tid_array[PROC_MAXPIDS];
- char buf[PROC_NUMBUF];
- unsigned int nr_tids, i;
- struct dentry *dentry = filp->f_dentry;
- struct inode *inode = dentry->d_inode;
- int retval = -ENOENT;
- ino_t ino;
- unsigned long pos = filp->f_pos; /* avoiding "long long" filp->f_pos */
- if (!pid_alive(proc_task(inode)))
- goto out;
- retval = 0;
- switch (pos) {
- case 0:
- ino = inode->i_ino;
- if (filldir(dirent, ".", 1, pos, ino, DT_DIR) < 0)
- goto out;
- pos++;
- /* fall through */
- case 1:
- ino = parent_ino(dentry);
- if (filldir(dirent, "..", 2, pos, ino, DT_DIR) < 0)
- goto out;
- pos++;
- /* fall through */
- }
- nr_tids = get_tid_list(pos, tid_array, inode);
- inode->i_nlink = pos + nr_tids;
- for (i = 0; i < nr_tids; i++) {
- unsigned long j = PROC_NUMBUF;
- int tid = tid_array[i];
- ino = fake_ino(tid,PROC_TID_INO);
- do
- buf[--j] = '0' + (tid % 10);
- while ((tid /= 10) != 0);
- if (filldir(dirent, buf+j, PROC_NUMBUF-j, pos, ino, DT_DIR) < 0)
- break;
- pos++;
- }
- out:
- filp->f_pos = pos;
- return retval;
通过观察,我们可以大胆的猜测,filldir()函数 就是往/proc添加<pid>目录的函数,我们可以做个实验,即,在filldir()下面再添加一个filldir()函数,这样的话,一个进程在/proc目录下就会有2个目录,同时,ps -ax显示的同一个进程也会有2个。
修改完后编译内核:
cd /usr/src/linux-<版本号>
make bzImage
cp arch/i386/boot/bzImage /boot/vmlinuz-0.1
cp System.map /boot/System.map-0.1
修改grub,为修改的内核添加相应的启动项
vim /boot/grub/menu.lst
reboot重新启动,在启动后选择CRUX_TEST选项,使用我们修改完的内核,
输入ps -ax或者查看/proc文件便会出现我们预先猜测的结果,同时,经过验证可知,ps, top命令调用的是proc_pid_readdir函数,而不是proc_task_readdir函数。嘿,在这,我们就发现新大陆了!
2.3 怎么修改proc
由2.2可知,我们需要修改在函数为proc_pid_readdir,仔细阅读相关代码可知,我们只需在if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) 前判断进程是否是否有设置为隐藏,而在之前,我们要先获取该进程的task_struct,具体代码如下(注释处即为添加的代码):
- int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
- {
- unsigned int tgid_array[PROC_MAXPIDS];
- char buf[PROC_NUMBUF];
- unsigned int nr = filp->f_pos - FIRST_PROCESS_ENTRY;
- unsigned int nr_tgids, i;
- int next_tgid;
- task_t *task; //声明一个 task_struct
- if (!nr) {
- ino_t ino = fake_ino(0,PROC_TGID_INO);
- if (filldir(dirent, "self", 4, filp->f_pos, ino, DT_LNK) < 0)
- return 0;
- filp->f_pos++;
- nr++;
- }
- /* f_version caches the tgid value that the last readdir call couldn't
- * return. lseek aka telldir automagically resets f_version to 0.
- */
- next_tgid = filp->f_version;
- filp->f_version = 0;
- for (;;) {
- nr_tgids = get_tgid_list(nr, next_tgid, tgid_array);
- if (!nr_tgids) {
- /* no more entries ! */
- break;
- }
- next_tgid = 0;
- /* do not use the last found pid, reserve it for next_tgid */
- if (nr_tgids == PROC_MAXPIDS) {
- nr_tgids--;
- next_tgid = tgid_array[nr_tgids];
- }
- for (i=0;i<nr_tgids;i++) {
- int tgid = tgid_array[i];
- ino_t ino = fake_ino(tgid,PROC_TGID_INO);
- unsigned long j = PROC_NUMBUF;
- //获得进程的pid
- task = find_task_by_pid(tgid);
- do
- buf[--j] = '0' + (tgid % 10);
- while ((tgid /= 10) != 0);
- printk(KERN_DEBUG "pid:%d, hide:%d/n", task->pid, task->hide);
- //判断进程是否为隐藏
- if(task != NULL && task->hide == 0)
- {
- printk(KERN_DEBUG "task:%d no hide/n", task->pid);
- if (filldir(dirent, buf+j, PROC_NUMBUF-j, filp->f_pos, ino, DT_DIR) < 0) {
- /* returning this tgid failed, save it as the first
- * pid for the next readir call */
- filp->f_version = tgid_array[i];
- goto out;
- }
- }
- filp->f_pos++;
- nr++;
- }
- }
- out:
- return 0;
- }
另外需要在kernel/fork.c的copy_process()函数中添加初始化hide代码(虽可不用,但为了保证逻辑,还是添加吧)
- ...省略...
- if (p->binfmt && !try_module_get(p->binfmt->module))
- goto bad_fork_cleanup_put_domain;
- p->did_exec = 0;
- copy_flags(clone_flags, p);
- p->pid = pid;
- p->hide = 0;
- retval = -EFAULT;
- ...省略...
重新编译内核
2.4 添加系统调用
为了验证我们2.3所修改的内核代码,我们需添加相应的系统调用,设置task_struct的hide,为此,我们添加2个系统调用,修改如下
2.4.1 kernel/sys.c添加源代码
- asmlinkage long sys_hide()
- {
- if(current->hide == 1)
- return -EFAULT;
- else
- current->hide = 1;
- return 0;
- }
- asmlinkage long sys_unhide()
- {
- if(current->hide == 0)
- return -EFAULT;
- else
- current->hide = 0;
- return 0;
- }
2.4.2 include/asm-i386/unistd.h添加系统调用号及系统的调用总数
- #define __NR_inotify_add_watch 292
- #define __NR_inotify_rm_watch 293
- #define __NR_hide 294
- #define __NR_unhide 295
- #define NR_syscalls 296 //系统调用总数
2.4.3 arch/i386/kernel/syscall_table.S在系统调用表中添加相应项,在最后一行添加
- .long sys_hide
- .long sys_unhide
2.4.4 在include/linux/syscalls.h添加函数声明(貌似这个可有可无)
- asmlinkage long hide();
- asmlinkage long unhide();
至此,添加系统调用完成,重新编译内核
3 测试
我们编写了一个测试函数,用来我们修改的内核是否成功,代码hide.c如下:
- #include <stdio.h>
- #include <sys/types.h>
- #include <linux/unistd.h>
- #define __NR_hide 294
- #define __NR_unhide 295
- int main(int argc, char **argv)
- {
- int i=0;
- printf("original/n");
- system("ps");
- int i = syscall(__NR_hide);//调用系统号为294的系统调用
- printf("hide:/n");
- system("ps");
- i = syscall(__NR_unhide);
- printf("unhide:/n");
- system("ps");
- return 0;
- }
gcc hide.c -o hide后,执行 ./hide ,查看结果如图
测试成功,成功隐藏进程!
4、结束语
这只是实现隐藏的一种方式,网上看到的一个是拦截系统调用,我之前想的是拦截中断,直接在中断里面修改,这个有待验证,以后有机会实现的话也会贴出来和大家分享。
这个程序从开始到完成大概花了我6,7个小时的时间,说起来难度也不大,内核代码的修改量很小,问题的关键处是找到在何处修改代码,这个俺也是边查资料边做实验才找到的。
[2011-2-25]现已实现通过拦截系统调用隐藏进程的方式,分享与大家:http://blog.csdn.net/billpig/archive/2011/02/20/6196163.aspx
- linux 隐藏进程
- Linux隐藏进程
- Linux进程隐藏
- (cc) Linux下隐藏进程
- linux 隐藏进程 - crux实现
- linux 隐藏进程 - crux实现
- Linux隐藏多个进程
- Linux 2.6 劫持系统调用 隐藏进程
- linux下如何隐藏进程(ps/top)
- linux下如何隐藏进程(ps/top)
- Linux 2.6 劫持系统调用 隐藏进程
- Linux 2.6 劫持系统调用 隐藏进程
- Linux 2.6 劫持系统调用 隐藏进程
- Linux 2.6 劫持系统调用 隐藏进程
- linux下如何隐藏进程(ps/top)
- Linux进程隐藏的一种实现思路
- 隐藏进程
- 隐藏进程
- java FTP 报错 Permission denied: recv failed 解决方法
- struct 互转 xml
- POJ 1426-Find The Multiple【搜索】
- Android Handler 异步消息处理机制的妙用 创建强大的图片加载类
- linux 让root用户可以telnet
- linux 隐藏进程
- PHP中“简单工厂模式”实例讲解
- Android 性能优化 二 TraceView工具的使用
- C++带符号类型和无符号类型
- 1355: [Baltic2009]Radio Transmission
- ajax提交表单方式
- JavaScript的函数举例
- 关于android中一种简单的分享方式
- javascript日期时间操作总结