基于内存搜索的进程检测方法
搜索內存檢測進程的原理
1) 問題的提出——進程檢測
Rootkit等后門為了在系統中長期駐留,需要隱藏相應的信息。這些信息包括自啟動項、文件、進程、模塊、端口、注冊表、服務等。其中,以進程隱藏特別突出(因為用戶經常會打開任務管理器看看是不是有異常的進程在自己的機器上運行)。
隱藏進程的方法有多種,例如掛鉤NtQuerySystemInformation函數;從內核EPROCESS結構的ActiveProcessLinks等雙向鏈表上摘除自身;從csrss.exe進程的句柄表上摘除自身;從PspCidTable上摘除自身等等。如果Rootkit用到了其中的某種方法,那么基于此的檢測將會失敗。比如,hxdef100(黑客守護者)掛鉤NtQuerySystemInformation實現隱藏,如果想通過NtQuerySystemInformation函數來獲取全部進程信息就會失敗。再比如,FU_rootkit通過從內核EPROCESS結構的ActiveProcessLinks雙向鏈表上摘除自身實現隱藏,如果想通過遍歷該鏈表來檢測隱藏的FU_rootkit就會失敗。現在問題來了,如果上面隱藏進程的方法都用到了,該怎么檢測呢?
2)解決的思路之一——搜索內存
在Windows系統中,進程由內存空間、進程打開的各種對象和進程中運行的線程所組成。線程僅僅是一個執行上下文,系統調度的最基本單位,但每一個線程的運行都必須依附(attach)一個進程。
在Windbg中可以使用dt命令來查看線程ETHREAD的結構,命令格式為“dt _ETHREAD 線程地址”。例如:
kd> dt _ETHREAD 8238F3B8
nt!_ETHREAD
+0x000 Tcb????????????? : _KTHREAD
+0x1c0 CreateTime?????? : _LARGE_INTEGER 0xe4d5cd5`36b71830
……
+0x21c DeviceToVerify?? : (null)
+0x220 ThreadsProcess?? : 0x8234f718 _EPROCESS
+0x224 StartAddress???? : 0x7c810867
+0x228 Win32StartAddress : 0x010027f2
……
+0x254 ForwardClusterOnly : 0 ''
+0x255 DisablePageFaultClustering : 0 ''
在線程控制塊的偏移0x220處是一個ThreadsProcess指針,指向一個EPROCESS結構,即一個進程EPROCESS。
在Windbg中可以使用“!process”命令來查看進程的線程摘要,命令格式為“!process 進程地址
3”。我們看看上面顯示的地址0x8234f718對應進程的線程摘要信息。
kd> !process 0x8234f718 3
PROCESS 8234f718? SessionId: 0? Cid: 05bc??? Peb: 7ffd6000? ParentCid: 0424
DirBase: 07a00280? ObjectTable: e1975848? HandleCount:? 38.
Image: wscntfy.exe
……
BasePriority?????? 8
CommitCharge???? 164
THREAD 8238f3b8? Cid 05bc.05c4? Teb: 7ffdf000 Win32Thread: e1d88da8 WAIT:
(UserRequest) UserMode Non-Alertable
8258cef8? SynchronizationEvent
8259b7b8? SynchronizationEvent
從命令輸出可以看出該進程名為wscntfy.exe,有一個線程地址為8238f3b8,這個地址就是最開始的那個線程控制塊的地址。
現在思路有了,既然每一個線程都要依附一個進程,每一個線程控制塊中有一個指向該進程的進程控制塊的指針,那么,我們就可以通過搜索系統中所有線程得到所有進程。
搜索內存檢測進程的具體實現
1) 問題
問題1,如何判斷一個內存塊是線程的ETHREAD結構?
Windows系統中存在兩個系統描述符表,一個是KeServiceDescriptorTable,一個是KeServiceDescriptorTableShadow。兩個系統描述符表中的KeServiceDescriptorTable描述系統基本服務,我們稱之為主表;KeServiceDescriptorTableShadow除了描述系統基本服務之外,還描述GUI和USER服務,我們稱之為副表。每個線程都有自己指向系統服務描述符表的指針,這個指針指向兩個系統描述符表中的任意一個。線程ETHREAD偏移為0的位置是一個KTHREAD結構,這個結構的偏移0x0e0處就是這樣一個指向系統描述符表的32位指針ServiceTable。
kd> dt _KTHREAD
nt!_KTHREAD
+0x000 Header?????????? : _DISPATCHER_HEADER
+0x010 MutantListHead?? : _LIST_ENTRY
……
+0x0df InitialNode????? : UChar
+0x0e0 ServiceTable???? : Ptr32 Void
+0x0e4 Queue??????????? : Ptr32 _KQUEUE
……
+0x1bb DisableBoost???? : Uchar
假定我們讀取地址Addr的信息,如果Addr偏移0x0e0處的內容是系統服務調度表的地址,就可以初步判定這個Addr很有可能就是一個線程ETHREAD的入口。
問題2,如何獲得系統服務調度表的地址?
任何一個系統的系統服務描述符表在系統運行時是不會改變的,而且從前面的分析可以看出,這個表的位置線程是知道的,這樣線程在運行時才能成功找到自己要調用的服務。
可是不同版本的Windows系統的描述符表地址并不一樣,同一版本的各個系統上描述符表的地址也不一樣,因此最好能夠有一種動態獲得系統描述符表地址的方法。
基于前面對于主表和副表的分析,找到一個需要系統基本服務和GUI/USER服務的進程,獲取其所有線程中的ServiceTable內容,就可以得到系統描述符表的地址。而通過這種方法得到的地址是在程序運行過程中動態獲取的,可以滿足我們的需求。
我們通過遍歷EPROCESS中偏移0x088
處的ActiveProcessLinks來找到擬采用的進程,比如explorer.exe(偏移0x174處為ImageFileName,映像名稱)。
kd> dt _EPROCESS 81405560
nt!_EPROCESS
+0x000 Pcb????????????? : _KPROCESS
……
+0x084 UniqueProcessId? : 0x000005fc
+0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x815c0c98 - 0x815a6b00 ]
……
+0x170 Session????????? : 0xf9ebc000
+0x174 ImageFileName??? : [16]? "explorer.exe"
+0x184 JobLinks???????? : _LIST_ENTRY [ 0x0 - 0x0 ]
+0x18c LockedPagesList? : (null)
+0x190 ThreadListHead?? : _LIST_ENTRY [ 0x81405424 - 0x813adadc ]
+0x198 SecurityPort???? : (null)
……
+0x255 WorkingSetAcquiredUnsafe : 0 ''
+0x258 Cookie?????????? : 0x70f74bdb
在找到這個進程之后,我們接著要遍歷它所有的線程,以獲取我們需要的系統服務描述符表的地址。EPROCESS結構偏移0x190
處的ThreadListHead是一個類型為_LIST_ENTRY的雙向鏈表,通過它可以得到線程鏈表的頭。
kd> dt _ETHREAD
nt!_ETHREAD
+0x000 Tcb????????????? : _KTHREAD
……
+0x220 ThreadsProcess?? : Ptr32 _EPROCESS
+0x224 StartAddress???? : Ptr32 Void
+0x228 Win32StartAddress : Ptr32 Void
+0x228 LpcReceivedMessageId : Uint4B
+0x22c ThreadListEntry? : _LIST_ENTRY
+0x234 RundownProtect?? : _EX_RUNDOWN_REF
……
+0x254 ForwardClusterOnly : UChar
+0x255 DisablePageFaultClustering : UChar
而線程ETHREAD結構的偏移0x22c處也是這樣一個雙向鏈表,通過它可以遍歷所有線程。問題好像已經解決了,地址“EPROCESS+0x190-0x22c+0x0e0”中的內容即是ServiceTable,亦即系統描述符表的地址,其中0x190是ThreadListHead相對EPROCESS的偏移,0x22c是ThreadListEntry相對ETHREAD的偏移,0x0e0是ServiceTable相對ETHREAD的偏移。
圖1給出了系統中EPROCESS和ETHREAD結構的關系,我們可以從這個圖中看出上面所描述的過程(注:ServiceTable在Tcb結構中,為了使結構清晰明了,圖1沒有給出)。
?
圖1 EPROCESS結構與ETHREAD結構的關系
問題3,如何獲取系統中所有進程的EPROCESS結構?
通過圖1我們可以看出,在內存中搜索線程之后,通過EPROCESS與ETHREAD的關系,可以得到線程依附的進程。“ETHREAD+
ThreadsProcess”中即是EPROCESS的地址,其中“ETHREAD”為線程ETHREAD結構的地址,“ThreadsProcess”為ThreadsProcess指針相對ETHREAD的偏移量。獲得EPROCESS地址,便對應上了進程。一個線程會依附一個進程,而一個進程則可以對應多個線程。用這個方法會得到很多重復的進程地址,我們只需要把重復部分刪除即可。
2)實現
獲取系統服務描述符表的部分代碼如下,通過遍歷,能夠獲得兩個描述符表的地址。
currlist=currlist->Flink;
ssdt=*(PULONG)((ULONG)currlist-ThreadListEntry+ServiceTable);
if(s[0]==0)
s[0]=ssdt;
else
{
if((s[1]==0)&&(s[0]!=ssdt))
s[1]=ssdt;
}
if((s[0]!=0)&&(s[1]!=0))
break;
count++;
采用鏈表的形式記錄所有進程EPROCESS結構地址的部分代碼如下,需要在內核中申請非分頁的地址空間。
{
if((r=(process *)ExAllocatePool(NonPagedPool,sizeof(process)))==NULL)
{
return;
}
p->next=r;
r->addr=EPROCaddr;
r->next=NULL;
p=r;
}
具體細節的補充說明
我所給出的基本結構和原理是Windows NT架構下所有系統都采用的,因而在其他系統下這個方法依然可以實現進程檢測。但由于在Winodws
2000/XP/2003下相關數據結構有一定的差別,所以在其他系統下需要對相應系統數據偏移量等值進行修改。我所給出的驅動程序在Windows XP
SP2下由Windows DDK編譯,可以使用rootkit.com提供的InstDrv進行加載,使用Dbgview查看運行結果,在Windows XP
SP2下測試通過,可以成功檢測FU_rootkit、hxdef100(黑客守護者)和FUTo_enhanced隱藏的進程。
目前給出的方法具有一定的不準確性。比如內核空間地址范圍,線程、進程的判定,系統服務描述符表的獲取等都具有一定的局限性,這些都會影響檢測的準確性。雖然目前的測試結果都是準確的,但是一種從方法上能夠確保準確的思路是以后努力的方向。
?
總結
以上是生活随笔為你收集整理的基于内存搜索的进程检测方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于RT-Thread的两轮平衡小车设计
- 下一篇: cmd输入cl提示不是内部命令