电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

《Undocumented Windows 2000 Secrets》翻譯 --- 第五章(3)

瀏覽:111日期:2023-08-27 10:31:57

第五章 監(jiān)控 Native API 調用

翻譯: Kendiv( fcczj@263.net )

更新: Thursday, March 24, 2005

聲明:轉載請注明出處,并保證文章的完整性,本人保留譯文的所有權利。

本書設計的 hook 機制的最大特色就是它是完全數(shù)據(jù)驅動的( data-driven )。只需簡單的增加一個新的 API 符號表,該 hook dispatcher 就可適應 Windows 2000 的新版本。而且,通過向 apdSdtFormats[] 數(shù)組中加入新的 API 函數(shù)的格式化字符串就可在任何時候記錄對這些附加的 API 函數(shù)的調用。這并不需要編寫任何附加的代碼 ---API Spy 的動作可完全由一組字符串來確定!不過,在定義新的格式化字符串是必須要小心,因為 w2k_spy.sys 是運行于內核模式的驅動程序。因為在這一系統(tǒng)層次上,系統(tǒng)不能溫和的處理發(fā)生錯誤。給 Win32 API 函數(shù)提供了一個無效的參數(shù)并不是問題 ----- 你會收到一個錯誤提示窗口,同時程序會被系統(tǒng)自動終止。在內核模式下,一個微小的訪問違規(guī)都會引發(fā)系統(tǒng)藍屏。因此,一定要小心。在需要的地方如果沒有出現(xiàn)一個正確的格式化控制 ID 或缺失了這一 ID 都會使你的系統(tǒng)徹底崩潰。即使一個簡單的字符串有時都是致命的!

現(xiàn)在僅剩 SpyHookInitializeEx() 中的那一大塊 ASM 代碼還未討論,這段代碼由 SpyHook2 和 SpyHook9 標識。這段代碼的一個有趣的特性是:在 SpyHookInitializeEx() 被調用的時候,它們從來都不會被執(zhí)行。在進入 SpyHookInitializeEx() 后,函數(shù)代碼將跳過這一整段代碼,然后在 SpyHook9 標簽處開始恢復執(zhí)行,此處包含 aSpyHooks[] 數(shù)組的初始化代碼。這一大塊 ASM 代碼只有通過 aSpyHooks[] 數(shù)組中的 Handler 成員才能進入。稍候,我將展示這些進入點是如何連接到 SDT 的。

在設計這段 ASM 代碼時,我的重要目標之一就是使其是完全非侵入式的。截獲操作系統(tǒng)調用非常危險,因為你從來不會知道被調用的代碼是否會依賴調用上下文( calling context )的某些未知特性。理論上來說,這些 ASM 代碼完全符合 __stdcall 約定,但仍存在出錯的可能性。我不得不選擇將原始的 Native API 處理例程放入幾乎完全相同的環(huán)境中,這意味著這些原始函數(shù)將使用最初的參數(shù)堆棧并且可以訪問所有的 CPU 寄存器,就像它們被正常調用一樣。當然,必須接受由于插入 hook 所帶來的最低限度的危險,否則,監(jiān)控將不可能實現(xiàn)。在這里,有意義的改動就是維護堆棧中的返回地址。如果你翻回到 5-3 ,你會發(fā)現(xiàn)在進入函數(shù)時,調用者的返回地址并不位于堆棧的頂部。 SpyHookInitializeEx() 中的 hook dispatcher 占用了此地址,將它自己的 SpyHook6 標簽的地址寫在了這里。因此,原始 Native API 處理例程將被打斷,然后進入 SpyHook6 中,這樣 hook dispatcher 才能檢查原始 Native API 處理例程的參數(shù)和它要返回的數(shù)據(jù)。

在調用原始處理例程之前, dispatcher 將建立一個 SPY_CALL (參見 列表 5-3 )控制塊,該控制塊中包含它稍候將會用到的參數(shù)。其中的一些參數(shù)在正確記錄 API 調用時會用到,另外一些則提供了有關調用者的信息,因此 dispatcher 可以在寫完 log 后,把控制返回給調用者,就像什么都沒有發(fā)生一樣。 Spy 設備在它的全局數(shù)據(jù)塊 DEVICE_CONTEXT 中維護著一個 SPY_CALL 結構的數(shù)組,可通過全局變量 gpDeviceContext 來訪問。 Hook Dispatcher 通過檢查 SPY_CALL 結構中的 InUse 成員來在數(shù)組中找到一個空的 SPY_CALL 。 Hook Dispatcher 使用 CPU 的 XCHG 指令來加載和設置該成員的值(譯注: XCHG 指令可以保證此操作為原子操作)。這一點非常重要,因為當代碼運行于多線程環(huán)境中時,讀寫全局數(shù)據(jù)時必須采取保護措施以避免條件競爭。如果在數(shù)組中找到了一個空的 SPY_CALL , dispatcher 就會將調用者的線程 ID (通過 PsGetCurrentThreadId() 獲取)、與當前 API 函數(shù)相關的 SPY_HOOK_ENTRY 結構的地址以及整個參數(shù)堆棧保存到該 SPY_CALL 結構中。需要復制的參數(shù)的字節(jié)數(shù)取自 KiArqumentTable 數(shù)組,該數(shù)組保存在系統(tǒng)的 SDT 中。如果所有的 SPY_CALL 都被使用了,原始的 API 函數(shù)處理例程將被調用而不會產(chǎn)生任何日志記錄。

必須采用 SPY_CALL 數(shù)組是因為 Windows 2000 的多線程本性。當 Native API 函數(shù)被暫停( suspended )時,這種情況就會經(jīng)常出現(xiàn) ---- 此時,另一個線程將獲得控制權,然后在它自己的時間片( time slice )內調用另一個 Native API 函數(shù)。這意味著 Spy 設備的 Hook Dispatcher 必須允許在任何時間和任何執(zhí)行點上的重進入( reenter )。如果 Hook Dispatcher 有單一的全局 SPY_CALL 存儲區(qū)域,它就可能在處于等待狀態(tài)的線程使用完之前被當前運行的線程覆寫( overwritten )。而這種情況正是藍屏的最佳候選人。為了進一步了解 Native API 的嵌套,我在 Spy 的 DEVICE_CONTEXT 結構中增加了 dLevel 和 dMisses 成員。無論何時只要重進入 hook dispatcher (如,向 SPY_CALL 數(shù)組中增加一個新的 SPY_CALL ) dLevel 都不會累加一個 1 。如果超過了最大嵌套層數(shù)(如, SPY_CALL 數(shù)組已滿), dMisses 就會累加一個 1 ,來標識丟失了一個日志記錄。根據(jù)我的觀察,在實際環(huán)境下,可以很容易的發(fā)現(xiàn)嵌套層達到 4 。這表示即時在高負載( heavy-load )的情況下, Native API 也會被重進入,因此,我將嵌套層數(shù)的上限設為 256 。

在調用原始的 API 處理例程之前, Hook Dispatcher 會保存所有的 CPU 寄存器(包括 EFLAGS ),隨后執(zhí)行路徑將導向函數(shù)的進入點。這會在 列表 5-3 中的 SpyHook5 標簽之前立即完成。此時, SpyHook6 將位于棧頂,僅隨其后的是調用者的參數(shù)。一旦 API 處理例程推出了,控制將被傳回到 hook dispatcher 的 SpyHook6 標簽。從此處開始執(zhí)行的代碼也被設計為非入侵的。此時,主要目標是允許調用者可以看到調用上下文,這和原始 API 函數(shù)建立的上下文幾乎完全一致。 Dispatcher 的主要問題是要能立即找到保存有當前 API 調用信息的 SPY_CALL 結構。唯一可以依賴的就是調用者的線程 ID ,該 ID 保存在 SPY_CALL 結構的 hThread 成員中。因此, Dispatcher 循環(huán)遍歷整個 SPY_CALL 數(shù)組以尋找匹配的線程 ID 。注意,代碼不會關心 fmuse 標志的值;這并不是必須的,因為數(shù)組中所有未使用的 SPY_CALL 結構的 hThread 都被設為了 0 ,這是系統(tǒng)空閑線程的 ID 。循環(huán)會在到達數(shù)組結尾時終止。否則的話(譯注:即沒有找到匹配的線程 ID ), Dispatcher 不會將控制返回給調用者,因為這樣做將是致命的。在這種情況下,代碼的選擇余地很小,因此,它會進入 KeBugCheck() ,這樣做的結果當然是使系統(tǒng)以受控的方式終止。不過這種情況應該從來不會發(fā)生,但如果它發(fā)生了,那表示系統(tǒng)必然出現(xiàn)了很嚴重的錯誤,因此,使系統(tǒng)終止是最佳解決方案。

如果發(fā)現(xiàn)了匹配的 SPY_CALL , Hook Dispatcher 將結束它的工作。最后的動作是調用日志記錄函數(shù) SpyHookProtocol() ,需要給該函數(shù)傳入一個指向 SPY_CALL 結構的指針。日志記錄所需的信息都保存在該結構中。當 SpyHookProtocol() 返回后, Dispatcher 就釋放它剛才使用的 SPY_CALL ,恢復所有的 CPU 寄存器,然后返回到調用者。

API HOOK 協(xié)議

一個好的 API Spy 應該可以在原始函數(shù)被調用后還能察看它使用的參數(shù),因為函數(shù)可能會通過傳入的緩沖區(qū)返回附加的數(shù)據(jù)。因此,日志函數(shù) SpyHookProtocol() 在 hook 例程結束時將被調用,而此時 API 函數(shù)還未返回到調用者。在討論它的實現(xiàn)秘訣之前,請先看看下面給出的兩個示例性的協(xié)議( Protocol ),它們會為你提供一個大概的方向。 5-6 是在命令行下執(zhí)行 dir c: 時產(chǎn)生的日志文件的快照。

請對比 5-6 中列出的日志項和 列表 5-6 給出的協(xié)議格式化字符串。在 示列 5-1 中, NtOpenFile() 和 NtClose() 的格式化字符串分別對應 5-6 中的第一行和第四行。它們有著驚人的相似處;每一個格式化控制 ID 都緊隨在一個 % 號后(參考 5-2 ),與其相關的參數(shù)項將包含在協(xié)議中。不過,協(xié)議還包含一些附加的信息,這些信息明顯不屬于格式字符串。稍后我將解釋這種差異的原因。

示例 5-2 給出了一個協(xié)議項的一般格式。每一項包含相同個數(shù)的域,這些域采用分隔符隔開。這樣分隔可以使程序很容易的解析它。這些域按照如下的一組簡單的基本規(guī)則來構建:

l 所有的數(shù)字都已十六進制表示,沒有 0 前綴或常見的前綴“ 0x ”

l 函數(shù)的多個參數(shù)由逗號隔開

l 字符串參數(shù)將位于一對雙引號中

l 結構體成員的值由“ . ”符號隔開

圖 5-6. 命令 dir c: 的示列協(xié)議

'%s=NtOpenFile(%+,%n,%o,%i,%n, %n) '

18:sO=NtOpenFile(+46C.18,nl00001,o'??C:',i0.1,n3,n4021)lBFEE5AE05B6710,278,2

'%s=NtClose(%-l)'

lB:sO=NtClose(-46C.18='??C:')lBFEE5AE05B6710,278,l

示列 5-1. 比較格式化字符串和協(xié)議項

<#> : <status>=<function> (<arguments>) <time> , <thread>, <handles>

示列 5-2. 協(xié)議項的一般格式

l 與句柄相關的對象名稱和句柄的值采用“ = ”進行分割。

l 日期 / 時間的 stamp 為 1601-01-01 至今逝去的毫秒數(shù),其格式依賴 Windows 2000 的基本時間格式,精度可達到 1/10 毫秒。

l 線程 ID 是調用 API 函數(shù)的線程的唯一數(shù)字標識。

l 句柄計數(shù)的狀態(tài)表示當前注冊到 Spy 設備句柄列表中的句柄的數(shù)量。協(xié)議函數(shù)使用該列表查找與對象名稱相關的句柄。

圖 5-7. 命令 type c:boot.ini 的示列協(xié)議

圖 5-7 是在控制臺中執(zhí)行: type c:boot.ini 命令產(chǎn)生的 API Spy 協(xié)議結果。下面給出日志項中的某些列的含義:

l 在 0x31 行,調用了 NtCreateFile() 來打開 ??c:boot.ini 文件。( o”??c:boot.ini” )該函數(shù)返回的 NTSTATUS 的值為 0 ( s0 ),即 STATUS_SUCCESS ,并分配了一個新的文件句柄,其值為 0 小 8 ,該句柄屬于進程 0x46c ( +46C.18 )。因此,句柄計數(shù)從 1 增加到 2 。

l 在 0x36 行, type 命令將文件 ??c:boot.ini 的前 512 個字節(jié)( n200 )讀入位于線性地址 0x0012F5B4 處的緩沖區(qū)中,并把從 NtCreateFile() 獲取的句柄解析給 NtReadFile() 函數(shù)。系統(tǒng)成功的返回 512 字節(jié)( io.200 )。

l 在 0x39 行,將處理另一塊 512 個字節(jié)的文件塊。這一次,將到達文件的末尾,因此 NtReadFile() 僅返回了 75 個字節(jié)( io.4B )。顯然,我的 boot.ini 文件的大小為: 512+75=587 字節(jié)。

l 在 0x3C 行, NtClose() 成功的釋放了指向 ??c:boot.ini 的文件句柄( -46.18=”??c:boot.ini” ),因此,句柄計數(shù)將從 2 減少為 1 。

現(xiàn)在,你應該已經(jīng)明白 Spy 協(xié)議的 API 是如何構建的了,這會幫助你掌握協(xié)議生成機制的細節(jié),接下來我們將討論這一機制。在前面我曾提及過,用于日志記錄的主要 API 函數(shù)是 SpyHookProtocol() 。 列表 5-7 給出了該函數(shù),它將使用 SPY_CALL 結構中的數(shù)據(jù)來為每個 API 函數(shù)生成一個協(xié)議記錄并將其寫入一個環(huán)形緩沖區(qū)中,這里的 SPY_CALL 結構由 Hook Dispatcher 傳入。一個 Spy 設備的客戶端可以通過 IOCTL 調用來讀去這一協(xié)議。每個記錄項都是一行文本,每行都由單個行結束符(即 C 語言中的 ”n” )表示行的結束。通過使用內核的 Mutext KMUTEX kmProtcol 來實現(xiàn)串行讀去協(xié)議緩沖區(qū), kmProtocol 位于 Spy 設備的全局結構 DEVICE_CONTEXT 中。 列表 5-7 中的 SpyHookWait() 和 SpyHookRelease() 函數(shù)用于請求和釋放此 Mutext 對象。所有對協(xié)議緩沖區(qū)的訪問都必須由 SpyHookWait() 預處理并在結束時由 SpyHookRelease() 處理, SpyHookProtocol() 函數(shù)展示了這種行為。

NTSTATUS SpyHookWait (void)

{

return MUTEX_WAIT (gpDeviceContext->kmProtocol);

}

// -----------------------------------------------------------------

LONG SpyHookRelease (void)

{

return MUTEX_RELEASE (gpDeviceContext->kmProtocol);

}

// -----------------------------------------------------------------

// <#>:<status>=<function>(<arguments>)<time>,<thread>,<handles>

void SpyHookProtocol (PSPY_CALL psc)

{

LARGE_INTEGER liTime;

PSPY_PROTOCOL psp = &gpDeviceContext->SpyProtocol;

KeQuerySystemTime (&liTime);

SpyHookWait ();

if (SpyWriteFilter (psp, psc->pshe->pbFormat,

psc->adParameters,

psc->dParameters))

{

SpyWriteNumber (psp, 0, ++(psp->sh.dCalls)); // <#>:

SpyWriteChar (psp, 0, ':');

// <status>=

SpyWriteFormat (psp, psc->pshe->pbFormat, // <function>

psc->adParameters); // (<arguments>)

SpyWriteLarge (psp, 0, &liTime); // <time>,

SpyWriteChar (psp, 0, ',');

SpyWriteNumber (psp, 0, (DWord) psc->hThread); // <thread>,

SpyWriteChar (psp, 0, ',');

SpyWriteNumber (psp, 0, psp->sh.dHandles); // <handles>

SpyWriteChar (psp, 0, 'n');

}

SpyHookRelease ();

return;

}

列表 5-7. 主要的 Hook 協(xié)議函數(shù) SpyHookProtocol()

如果你比較一下 列表 5-7 給出的 SpyHookProtocol() 函數(shù)的主要部分和 示列 5-2 給出的協(xié)議項的一般格式,將很容易找出那個語句生成了協(xié)議項中的哪一個域( fIEld )。這樣一來一切就很清楚了為什么 列表 5-6 中的協(xié)議字符串沒有說明整個數(shù)據(jù)項 --- 有些獨立于功能的數(shù)據(jù)將由 SpyHookProtocol() 添加,而這將不需要格式字符串的幫助。 SpyHookProtocl() 的核心調用是 SpyWriteFormat() ,該函數(shù)生成 <status>=<function>[<arguments>] 部分,這依賴于與要記錄的當前 API 函數(shù)相關的格式字符串。請參考位于隨書光盤的 srcw2k_spy 目錄下的源文件 w2k_spy.c 和 w2k_spy.h ,以獲取 Spy 設備驅動程序中使用的 SpyWrite*() 函數(shù)的更多實現(xiàn)信息。

請注意,這些代碼稍微有些危險。這些代碼編寫與 1997 年是針對 Windows NT 4.0 的。在移植到 Windows 2000 之后,當 hook 工作一段較長時間后會偶爾引發(fā)藍屏。更糟糕的是,有些特殊的操作將立即引發(fā)藍屏,例如,在 My Favoriter 文本編輯器的 FileOpen 對話框中打開我的電腦時。在分析過多過 crash dump 后,我發(fā)現(xiàn)是由于將 NULL 指針傳遞給了某些函數(shù)從而導致了系統(tǒng)崩潰。一但 Spy 設備試圖使用這些指針中的某個來記錄該指針引用的數(shù)據(jù)時,系統(tǒng)就會崩潰。典型的就是,指向 IO_STATUS_BLOCK 結構的指針,在 UNICODE_STRING 和 OBJECT_ATTRIBUTES 結構中存在無效的字符串指針。我還發(fā)現(xiàn)某些有 Buffer 成員的 UNICODE_STRING 結構沒有 結束符。因此,我再次強調你不應該假定所有的 UNICODE_STRING 結構都以 結束。在不能確定時,請使用 Length 成員,它總能正確地告訴你在 Buffer 中存放的有效的字節(jié)數(shù)。

為了修正這一問題,我為所有使用客戶指針的日志函數(shù)增加了指針有效性檢查。在結束時,我使用第四章討論過的 SpyMemoryTestAddress() 函數(shù)來檢驗一個線性地址指針是否指向一個有效的頁表項( PTE )。更詳細的信息請參考 列表 4-22 列表 4-24 。另一種可能的替代方案是使用結構化異常( __try/__except )。

主站蜘蛛池模板: 高尔夫球杆_高尔夫果岭_高尔夫用品-深圳市新高品体育用品有限公司 | 动库网动库商城-体育用品专卖店:羽毛球,乒乓球拍,网球,户外装备,运动鞋,运动包,运动服饰专卖店-正品运动品网上商城动库商城网 - 动库商城 | 贵州成人高考网_贵州成考网 | 广东风淋室_广东风淋室厂家_广东风淋室价格_广州开源_传递窗_FFU-广州开源净化科技有限公司 | 台湾Apex减速机_APEX行星减速机_台湾精锐减速机厂家代理【现货】-杭州摩森机电 | jrs高清nba(无插件)直播-jrs直播低调看直播-jrs直播nba-jrs直播 上海地磅秤|电子地上衡|防爆地磅_上海地磅秤厂家–越衡称重 | 非甲烷总烃分析仪|环控百科| H型钢切割机,相贯线切割机,数控钻床,数控平面钻,钢结构设备,槽钢切割机,角钢切割机,翻转机,拼焊矫一体机 | 电梯乘运质量测试仪_电梯安全评估测试仪-武汉懿之刻 | 保温杯,儿童婴童奶瓶,运动水壶「广告礼品杯定制厂家」超朗保温杯壶 | 档案密集架_电动密集架_移动密集架_辽宁档案密集架-盛隆柜业厂家现货批发销售价格公道 | 北京企业宣传片拍摄_公司宣传片制作-广告短视频制作_北京宣传片拍摄公司 | 登车桥动力单元-非标液压泵站-非标液压系统-深圳市三好科技有限公司 | 石家庄网站建设|石家庄网站制作|石家庄小程序开发|石家庄微信开发|网站建设公司|网站制作公司|微信小程序开发|手机APP开发|软件开发 | 美甲贴片-指甲贴片-穿戴美甲-假指甲厂家--薇丝黛拉 | 深圳美安可自动化设备有限公司,喷码机,定制喷码机,二维码喷码机,深圳喷码机,纸箱喷码机,东莞喷码机 UV喷码机,日期喷码机,鸡蛋喷码机,管芯喷码机,管内壁喷码机,喷码机厂家 | 精密模具制造,注塑加工,吹塑和吹瓶加工,EPS泡沫包装生产 - 济南兴田塑胶有限公司 | 一体化隔油提升设备-餐饮油水分离器-餐厨垃圾处理设备-隔油池-盐城金球环保产业发展有限公司 | 权威废金属|废塑料|废纸|废铜|废钢价格|再生资源回收行情报价中心-中废网 | 浩方智通 - 防关联浏览器 - 跨境电商浏览器 - 云雀浏览器 | 臭氧发生器_臭氧消毒机 - 【同林品牌 实力厂家】 | 生产自动包装秤_颗粒包装秤_肥料包装秤等包装机械-郑州鑫晟重工科技有限公司 | 水稻烘干机,小麦烘干机,大豆烘干机,玉米烘干机,粮食烘干机_巩义市锦华粮食烘干机械制造有限公司 水环真空泵厂家,2bv真空泵,2be真空泵-淄博真空设备厂 | 隧道风机_DWEX边墙风机_SDS射流风机-绍兴市上虞科瑞风机有限公司 | 武汉创亿电气设备有限公司_电力检测设备生产厂家 | 写方案网_方案策划方案模板下载 事迹材料_个人事迹名人励志故事 | 散热器厂家_暖气片_米德尔顿散热器| 淄博不锈钢无缝管,淄博不锈钢管-鑫门物资有限公司 | 华禹护栏|锌钢护栏_阳台护栏_护栏厂家-华禹专注阳台护栏、楼梯栏杆、百叶窗、空调架、基坑护栏、道路护栏等锌钢护栏产品的生产销售。 | 上海小程序开发-上海小程序制作公司-上海网站建设-公众号开发运营-软件外包公司-咏熠科技 | 杭州成人高考_浙江省成人高考网上报名| 耐压仪-高压耐压仪|徐吉电气 | 造价工程师网,考试时间查询,报名入口信息-网站首页 | 房间温控器|LonWorks|海思| 超声波破碎仪-均质乳化机(供应杭州,上海,北京,广州,深圳,成都等地)-上海沪析实业有限公司 | 杭州荣奥家具有限公司-浙江办公家具,杭州办公家具厂 | 橡胶膜片,夹布膜片,橡胶隔膜密封,泵阀设备密封膜片-衡水汉丰橡塑科技公司网站 | 不锈钢轴流风机,不锈钢电机-许昌光维防爆电机有限公司(原许昌光维特种电机技术有限公司) | 清水-铝合金-建筑模板厂家-木模板价格-铝模板生产「五棵松」品牌 | 澳门精准正版免费大全,2025新澳门全年免费,新澳天天开奖免费资料大全最新,新澳2025今晚开奖资料,新澳马今天最快最新图库 | 刮板输送机,粉尘加湿搅拌机,螺旋输送机,布袋除尘器 |