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í)則停止枚舉。