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的特徵值來掃描內存時,一定要選取不能輕易篡改的值,否則很容易被木馬對抗.