Rootkit通常会隐藏进程,隐藏文件和网络连接。这里主要记录几种对隐藏进程的检测方法
一. 隐藏进程的方法
1.1 用户级Rootkit 通过LD_PRELOAD来hook libc库,从而过滤/proc/pid目录
1.2 内核级rootkit 通过hook系统调用getdents/getdents64或者hook 文件file_operation的iterate
1.3 内核级rootkit把task_struct 从相关链表摘除(init_task,pidlist)
二 检测进程的方法
2.1 前两种隐藏检测方法
因为进程还在内核链表中,可以通过pid找到task_struct .
第一步:读/proc/pid目录,收集系统所有进程
第二步: 给0到pid_max进程发送任意信号,信号发送成功,表示进程存在,从而收集系统所有进程
for(pid=0;pid<PID_MAX;pid++)
kill -20 pid
第三步:对比第一步和第二步收集进程的差异,即可确认隐藏进程(要排除已经退出的进程)
2.2 第三种隐藏方法的检测
因为在链表中找不到,所以不能通过PID找到task_struct.这里可以基于task_strcut的特征,在内存中查找task_struct结构体
直接上代码:
/*通过PID获取task_struct检测进程是否存在,也可以读/proc/确认 */
static struct task_struct *get_task(pid_t nr)
{
struct task_struct *task = NULL;
struct pid *pid_group = NULL;
pid_group = find_get_pid(nr);
task = get_pid_task(pid_group,PIDTYPE_PID);
if(IS_ERR_OR_NULL(pid_group)||IS_ERR_OR_NULL(task)){
//printk("Can't Get pid =%d,pid=%px,task=%px\n",nr,pid_group,task);
return NULL;
}
return task;
}
/*这里假设隐藏进程使用CFS调度,其他调度类也可采用类似方法比较 */
static int scan_task(struct page *page)
{
struct task_struct *task = NULL ;
unsigned long start = page_to_virt(page);
unsigned long end = start+PAGE_SIZE;
while(start<end){
task = (struct task_struct *)start;
/*找到task_struct结构体,当然也可以用其他task_struct特征值
进程再怎么隐藏和篡改,也不会修改sched_class的值,因为还需要运行。
*/
if(task->sched_class==current->sched_class&&(task->state!=TASK_DEAD)){
get_task_struct(task);
/*如果get_task找不到,而扫描内存能找到,证明进程隐藏了 */
if(!get_task(task->pid))
printk("We get Hide task %s,pid=%d\n",task->comm,task->pid);
start += sizeof(*task);
put_task_struct(task);
continue ;
}
start++;
}
return 0;
}
static void scan_memory(void)
{
int i = 0;
/*遍历系统所有内存 */
for_each_online_node(i) {
unsigned long start_pfn = node_start_pfn(i);
unsigned long end_pfn = node_end_pfn(i);
unsigned long pfn;
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
struct page *page = pfn_to_online_page(pfn);
if (!page)
continue;
/* only scan pages belonging to this node */
if (page_to_nid(page) != i)
continue;
/* only scan if page is in use */
if (page_count(page) == 0)
continue;
/*因为task_struct是通过slab机制分配 */
if(!PageSlab(page))
continue ;
scan_task(page);
if (!(pfn & 63))
cond_resched();
}
}
}
效果:
把firefox进程3126从内核链表中摘除,从/proc/看不到
ls: cannot access '/proc/3126': No such file or directory
加载内存扫描模块:
[ 2452.997816] We get Hide task firefox,pid=3126
可以看到能够找到被隐藏的进程.
通过task_struct的特征值来扫描内存时,一定要选取不能轻易篡改的值,否则很容易被木马对抗.