icesword?驅(qū)動(dòng)部分分析

信息來(lái)源:驅(qū)動(dòng)開(kāi)發(fā)網(wǎng)(www
. zndev . com)
文章作者:wuyanfeng

icesword
. exe?在執(zhí)行的時(shí)候會(huì)放出一個(gè)驅(qū)動(dòng)程序?ispubdrv . sys? .
icesword . exe?裝載?這個(gè)驅(qū)動(dòng),這個(gè)驅(qū)動(dòng)安裝后就不會(huì)卸載。直到系統(tǒng)重新啟動(dòng)。這可能是因?yàn)轵?qū)動(dòng)中調(diào)用了
PsSetCreateThreadNotifyRoutine?函數(shù)
. 下面是這個(gè)函數(shù)在?ddk?中的介紹。

///////////////////////////////////////////////
PsSetCreateThreadNotifyRoutine?registers?a?driver - supplied?callback?that?is?subsequently?notified?when?a? new? thread?is?created? and? when?such?a?thread?is?deleted .

NTSTATUS
PsSetCreateThreadNotifyRoutine
(
??
IN?PCREATE_THREAD_NOTIFY_ROUTINE?NotifyRoutine
??
);

Any?driver?that?successfully?registers?such?a?callback?must?remain?loaded?until?the?system?itself?is?shut?down .

//////////////////////////////////////////////

雖然ddk?中說(shuō)成功的調(diào)用PsSetCreateThreadNotifyRoutine?函數(shù)就要保留驅(qū)動(dòng)直到系統(tǒng)重新啟動(dòng),但是還是有辦法做到可以卸載的。

icesword?是如何列出隱藏進(jìn)程?

icesword?是通過(guò)?PspCidTable?這個(gè)表來(lái)遍厲進(jìn)程的,?PspCidTable?是一個(gè)沒(méi)有被?ntoskrnl
. exe?導(dǎo)出的。這就涉及到如何定位
PspCidTable?的問(wèn)題。icesword?是通過(guò)搜索特征串的方式定位?PspCidTalbe
.? PspCidTable?是一個(gè)?HANDLE_TALBE?結(jié)構(gòu) .
PsLookupProcessByProcessId?函數(shù)中會(huì)引用?PspCidTalbe?變量。icesword?從?PsLookupProcessByProcessId?函數(shù)的前幾十個(gè)字節(jié)
內(nèi)搜索?PspCidTalbe?變量。那有人就可能會(huì)想,那我把?PsLookupProcessByProcessId???這個(gè)函數(shù)給?patch?了
, 他不就找不到
PspCidTalbe?變量了嗎??對(duì)你說(shuō)的沒(méi)錯(cuò),是可以這樣。當(dāng)然我們能想到這點(diǎn)。icesword?的作者也能想到這點(diǎn)。作者為了防止你這么做
也采取了相應(yīng)的對(duì)策。他采取的對(duì)策就是運(yùn)行前效驗(yàn)恢復(fù)的方法。它在執(zhí)行關(guān)鍵的系統(tǒng)函數(shù)時(shí)會(huì)比較函數(shù)的頭幾十個(gè)字節(jié)是否被修改。
如果被修改了它就會(huì)把被修改的給恢復(fù)成系統(tǒng)原來(lái)的內(nèi)容,那我們可能就會(huì)提出一個(gè)疑問(wèn),如果我在它啟動(dòng)之前就?patch?了他要效驗(yàn)
的函數(shù),它怎么能知道系統(tǒng)原來(lái)的內(nèi)容呢?這個(gè)問(wèn)題提的好。現(xiàn)在就讓我們來(lái)看看?icesword?的作者是怎么做到這一點(diǎn)的。我們就拿
PsLookupProcessByProcessId???函數(shù)來(lái)說(shuō)吧。PsLookupProcessByProcessId???函數(shù)是?ntoskrnl
. exe?文件導(dǎo)出的。作者不是用我們通常
的方法來(lái)定位?PsLookupProcessByProcessId???函數(shù)的,也就是說(shuō)?ispubdrv
. sys?并沒(méi)有導(dǎo)入這個(gè)函數(shù)。同樣也沒(méi)有通過(guò)
MmGetSystemRoutineAddress?函數(shù)來(lái)得PsLookupProcessByProcessId的地址。那他是怎么獲的?PsLookupProcessByProcessId?的地址的呢?
那可能有的人就會(huì)想到他是通過(guò)?自己打開(kāi)ntoskrnl
. exe?文件然后分析導(dǎo)出函數(shù)做的,對(duì)icesword?的作者就是這么做的。當(dāng)然他這里還是
有技巧在的。作者操作文件也沒(méi)有用我們寫(xiě)驅(qū)動(dòng)程序時(shí)常用的操作文件的方式來(lái)訪問(wèn)文件。我們平時(shí)在驅(qū)動(dòng)程序里面打開(kāi)和讀寫(xiě)文件時(shí)
大多是使用?ZwCreateFile
, ZwOpenFile , ZwReadFile , ZwWriteFile , NtCreateFile?等等函數(shù) . 這樣的話作者就可以避免一些文件過(guò)濾程序。
作者打開(kāi)文件用?IoCreateFile?函數(shù)。在讀文件的時(shí)候作者沒(méi)有用正常的?文件相關(guān)的?API?函數(shù),而是用的?IofCallDriver?來(lái)做的。因?yàn)?br /> 本人對(duì)驅(qū)動(dòng)不熟悉,也不清楚他?IoCallDriver?做什么用,只知道調(diào)用完了這個(gè)?IoCallDriver?函數(shù)后,數(shù)據(jù)就被讀出來(lái)了。?這就防止了
通常的文件讀寫(xiě)過(guò)濾程序。自己分析?pe?文件,找出他要定位的函數(shù)的導(dǎo)出地址。然后他會(huì)把函數(shù)的前?幾十個(gè)字節(jié)讀出來(lái),當(dāng)然這里又涉及
到代碼重定位的問(wèn)題。(熟悉?pe?的人可能都會(huì)理解重定位的問(wèn)題的問(wèn)題,這里我就不講了。如果有不理解的可以自己參考?PE?文件格式的相關(guān)
文檔。)作者自己把讀出來(lái)的代碼片段自己做重定為。這樣他就得到了函數(shù)開(kāi)頭部分的原始代碼。作者通過(guò)這種方法,就獲得了原始的效驗(yàn)數(shù)據(jù)
。這樣他運(yùn)行系統(tǒng)函數(shù)的時(shí)候就保證函數(shù)沒(méi)有被?patch?過(guò)。當(dāng)然了如果你不怕麻煩的話可以把自己的?patch?放到更深的調(diào)用路徑上。
這樣即使是用?windbg?
,? softice , syser?調(diào)試器下斷點(diǎn)調(diào)試,也是斷不住的。當(dāng)然了你也不能用調(diào)試器調(diào)試,因?yàn)?icesword . exe?會(huì)在一個(gè)
timer?中不停的重新設(shè)置?
int? 1 , int? 3? 的中斷處理函數(shù)。設(shè)置成?windows?ntoskrnl . exe?中的缺省處理函數(shù)。即使你用硬件斷點(diǎn)寄存器也是不
管用的。那有的人就會(huì)說(shuō)既然設(shè)置成?windows?ntoskrnl
. exe?中的缺省處理函數(shù)就可以使用?windbg?雙機(jī)調(diào)試 . icesword?也做了處理 ,
icesword?會(huì)通過(guò)?KdDebuggerEnabled?變量判斷是否允許內(nèi)核調(diào)試。如果允許調(diào)試的話 .? icesword?會(huì)調(diào)用?KdDisableDebugger?函數(shù)禁止內(nèi)核調(diào)試。



這里順便在說(shuō)兩個(gè)分析?icesword?中遇到的反調(diào)試小陷阱?這里把代碼片段列出來(lái),希望作者原諒

. text : 000xxxF0??? mov? [ ebp + IoControlCode ],? eax
. text : 000xxxF3??? mov?eax ,?[ esp + 5Ch - 6Ch ]?;? 反調(diào)試代碼
. text : 000xxxF7??? push?eax
. text : 000xxxF8??? mov?eax ,?[ esp + 60h - 6Ch ]
.
text : 000xxxFC??? pop?ebx
. text : 000xxxFD??? cmp?eax ,? ebx
. text : 000xxxFF??? jz? short? loc_1240B? ;? 如果沒(méi)有被調(diào)試則會(huì)跳轉(zhuǎn)
. text : 000xxx01??? mov?eax ,? 200EDBh
. text : 000xxx06??? not? eax
. text : 000xxx08??? push?eax
. text : 000xxx09??? pop?edi
. text : 000xxx0A??? stosd

. text : 000xxxF3??? mov?eax ,?[ esp + 5Ch + 6Ch ]? 當(dāng)單步執(zhí)行到這條指令或者在這條指令上設(shè)置斷點(diǎn)的時(shí)候,因?yàn)楫?dāng)調(diào)試器在這條指令上彈出的時(shí)候會(huì)
用到被調(diào)試程序的堆棧來(lái)保存?EFLAGS
, CS , EIP ,? (如果? int? 1 , 或? int? 3? 處理函數(shù)用任務(wù)門(mén)就可以解決這個(gè)問(wèn)題。)例如?當(dāng)代碼執(zhí)行到這條指令時(shí)
ESP?
=? 805E4320h??? 執(zhí)行完這條指令是?eax?的值為? [ ESP + 5Ch - 6Ch ]=[ ESP - 10h ]=[ 805E4320h - 10h ]=[ 805E4310h ]? 的值。
當(dāng)單步執(zhí)行到?
. text : 000xxxF8??? mov?eax ,?[ esp + 60h - 6Ch ]? 指令的時(shí)候?ESP = 805E432Ch? 以為其中入棧了一個(gè)?eax?所以?ESP = 805E432Ch ,
執(zhí)行完? . text : 000xxxF8??? mov?eax ,?[ esp + 60h - 6Ch ]? 條指令的時(shí)候?eax? =?[ ESP + 60h - 6Ch ]=[ ESP - Ch ]=[ 805E432Ch - Ch ]=[ 805E4310h ]
如果不調(diào)試的情況下?讀的是同一個(gè)地址的值,所以?xún)蓚€(gè)值比較應(yīng)該是相同的?也就是? . text : 000xxxFD??? cmp?eax ,? ebx?這條指令的比較結(jié)果
應(yīng)該是相同的。這個(gè)指令?
. text : 000xxxFF??? jz? short? loc_1240B?執(zhí)行后直接跳轉(zhuǎn)到。
如果是被調(diào)試器調(diào)試的情況下?
. text : 000xxxFF??? jz? short? loc_1240B?不會(huì)跳轉(zhuǎn)。?如果不跳轉(zhuǎn)時(shí)下面的代碼?會(huì)覆蓋掉系統(tǒng)的當(dāng)前?ETHREAD
指針。接下來(lái)在調(diào)用很多系統(tǒng)函數(shù)都會(huì)導(dǎo)致系統(tǒng)崩潰,并且是崩潰到系統(tǒng)模塊里面,這樣給你定位錯(cuò)誤帶來(lái)誤導(dǎo)。哈哈



. text : 000xxx68??? push? 1? ;? Alignment
. text : 000xxx6A??? push? 40h? ;? Length
. text : 000xxx6C??? push?CurrentEProcessObject? ;? Address
. text : 000xxx72??? call?ds : ProbeForRead

這里是故意做個(gè)異常來(lái)實(shí)現(xiàn)跳轉(zhuǎn)。如果你在?
. text : 000xxx72??? call?ds : ProbeForRead?指令上單步執(zhí)行的時(shí)候調(diào)試器會(huì)跑飛了,
也就是說(shuō)從調(diào)試器退出了,沒(méi)有繼續(xù)跟蹤下去。


接下來(lái)說(shuō)我們的?PspCidTable?我們找到了?PspCidTable?變量后,?PspCidTable?
[ 這個(gè)?HANDLE_TABLE?的句柄表中,保存著所有進(jìn)程和線程對(duì)象的指針。
PID(進(jìn)程ID)和?ThreadID(線程ID)就是在這個(gè)句柄表中的索引。這個(gè)?HANDLE_TABLE?不屬于任何進(jìn)程,也沒(méi)有鏈在?HANDLE_TABLE?鏈上。全局變量
PspCidTable?中是指向這個(gè)?HANDLE_TABLE?的指針。這個(gè)?HANDLE_TABLE?還有一點(diǎn)和別的?HANDLE_TABLE?都不同,就是它的?HANDLE_TABLE_ENTRY?中的
第一個(gè)
32bit? 放著的是對(duì)象體指針(當(dāng)然需要轉(zhuǎn)換)而不是對(duì)象頭指針(對(duì)象指針就是對(duì)象體指針)。 ]?( 特別注明?在 [] 的話不是俺寫(xiě)的是在網(wǎng)上抄來(lái)的
這里特別感謝?“JIURL玩玩Win2k進(jìn)程線程篇?HANDLE_TABLE”?文章的作者:JIURL?
)
我們之要想到辦法遍歷這個(gè)?PspCidTable?句柄表就可以遍歷到系統(tǒng)的所有進(jìn)程。icesword?為了遍歷這個(gè)表他使用了系統(tǒng)為公開(kāi)的?ntoskrnl . exe
的導(dǎo)出函數(shù)?ExEnumHandleTable?。

icesword?定位到?ntoskrnl
. exe?導(dǎo)出的?ExEnumHandleTable函數(shù)。
這個(gè)函數(shù)是未公開(kāi)的函數(shù)。
這個(gè)函數(shù)的函數(shù)原形可能是?VOID?STDCALL?ExEnumHandleTable?
( PULONG?HandleTable ,? PVOID?Callback ,? PVOID?Param ,? PHANDLE?Handle?OPTIONAL );

其中的參數(shù)?PULONG?HandleTable?就可以用?PspCidTable?做參數(shù) .
PVOID?Callback?的類(lèi)型為? bool? (* EXENUMHANDLETABLECALLBACK )( HANDLE_TALBE_ENTRY *, DWORD?PID , PVOID?Param )? 函數(shù)指針。
PVOID?Param?參數(shù)就是傳送給回調(diào)函數(shù)的參數(shù)。
PHANDLE?Handle?OPTIONAL?這個(gè)參數(shù)俺還沒(méi)搞懂什么意思。在說(shuō)俺也用不到他,所以也不管他了隨他去吧。

當(dāng)調(diào)用?ExEnumHandleTable?函數(shù)的時(shí)候?函數(shù)在每次枚舉到表中的一個(gè)句柄時(shí)都會(huì)調(diào)用一次回調(diào)函數(shù)。

當(dāng)調(diào)用的?Callback?回調(diào)函數(shù)返回值為?
0? 時(shí)繼續(xù)枚舉句柄表,如果返回? 1? 時(shí)則停止枚舉。