日本免费全黄少妇一区二区三区-高清无码一区二区三区四区-欧美中文字幕日韩在线观看-国产福利诱惑在线网站-国产中文字幕一区在线-亚洲欧美精品日韩一区-久久国产精品国产精品国产-国产精久久久久久一区二区三区-欧美亚洲国产精品久久久久

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

第五章 監(jiān)控 Native API 調(diào)用
翻譯: Kendiv( fcczj@263.net)
更新: Thursday, March 24, 2005
聲明:轉(zhuǎn)載請注明出處,并保證文章的完整性,本人保留譯文的所有權(quán)利 。
本書設(shè)計的 hook 機制的最大特色就是它是完全數(shù)據(jù)驅(qū)動的( data-driven ) 。只需簡單的增加一個新的 API 符號表,該 hook dispatcher 就可適應(yīng) Windows 2000 的新版本 。而且,通過向 apdSdtFormats[] 數(shù)組中加入新的 API 函數(shù)的格式化字符串就可在任何時候記錄對這些附加的 API 函數(shù)的調(diào)用 。這并不需要編寫任何附加的代碼 ---API Spy 的動作可完全由一組字符串來確定!不過,在定義新的格式化字符串是必須要小心,因為 w2k_spy.sys 是運行于內(nèi)核模式的驅(qū)動程序 。因為在這一系統(tǒng)層次上,系統(tǒng)不能溫和的處理發(fā)生錯誤 。給 Win32 API 函數(shù)提供了一個無效的參數(shù)并不是問題 ----- 你會收到一個錯誤提示窗口,同時程序會被系統(tǒng)自動終止 。在內(nèi)核模式下,一個微小的訪問違規(guī)都會引發(fā)系統(tǒng)藍屏 。因此,一定要小心 。在需要的地方如果沒有出現(xiàn)一個正確的格式化控制 ID 或缺失了這一 ID 都會使你的系統(tǒng)徹底崩潰 。即使一個簡單的字符串有時都是致命的!
現(xiàn)在僅剩 SpyHookInitializeEx() 中的那一大塊 ASM 代碼還未討論,這段代碼由 SpyHook2 和 SpyHook9 標識 。這段代碼的一個有趣的特性是:在 SpyHookInitializeEx() 被調(diào)用的時候,它們從來都不會被執(zhí)行 。在進入 SpyHookInitializeEx() 后,函數(shù)代碼將跳過這一整段代碼,然后在 SpyHook9 標簽處開始恢復(fù)執(zhí)行,此處包含 aSpyHooks[] 數(shù)組的初始化代碼 。這一大塊 ASM 代碼只有通過 aSpyHooks[] 數(shù)組中的 Handler 成員才能進入 。稍候,我將展示這些進入點是如何連接到 SDT 的 。
在設(shè)計這段 ASM 代碼時,我的重要目標之一就是使其是完全非侵入式的 。截獲操作系統(tǒng)調(diào)用非常危險,因為你從來不會知道被調(diào)用的代碼是否會依賴調(diào)用上下文( calling context )的某些未知特性 。理論上來說,這些 ASM 代碼完全符合 __stdcall 約定,但仍存在出錯的可能性 。我不得不選擇將原始的 Native API 處理例程放入幾乎完全相同的環(huán)境中,這意味著這些原始函數(shù)將使用最初的參數(shù)堆棧并且可以訪問所有的 CPU 寄存器,就像它們被正常調(diào)用一樣 。當(dāng)然,必須接受由于插入 hook 所帶來的最低限度的危險,否則,監(jiān)控將不可能實現(xiàn) 。在這里,有意義的改動就是維護堆棧中的返回地址 。如果你翻回到 圖 5-3 ,你會發(fā)現(xiàn)在進入函數(shù)時,調(diào)用者的返回地址并不位于堆棧的頂部 。SpyHookInitializeEx() 中的 hook dispatcher 占用了此地址,將它自己的 SpyHook6 標簽的地址寫在了這里 。因此,原始 Native API 處理例程將被打斷,然后進入 SpyHook6 中,這樣 hook dispatcher 才能檢查原始 Native API 處理例程的參數(shù)和它要返回的數(shù)據(jù) 。
在調(diào)用原始處理例程之前,dispatcher 將建立一個 SPY_CALL (參見 列表 5-3 )控制塊,該控制塊中包含它稍候?qū)玫降膮?shù) 。其中的一些參數(shù)在正確記錄 API 調(diào)用時會用到,另外一些則提供了有關(guān)調(diào)用者的信息,因此 dispatcher 可以在寫完 log 后,把控制返回給調(diào)用者,就像什么都沒有發(fā)生一樣 。Spy 設(shè)備在它的全局數(shù)據(jù)塊 DEVICE_CONTEXT 中維護著一個 SPY_CALL 結(jié)構(gòu)的數(shù)組,可通過全局變量 gpDeviceContext 來訪問 。Hook Dispatcher 通過檢查 SPY_CALL 結(jié)構(gòu)中的 InUse 成員來在數(shù)組中找到一個空的 SPY_CALL。Hook Dispatcher 使用 CPU 的 XCHG 指令來加載和設(shè)置該成員的值(譯注: XCHG 指令可以保證此操作為原子操作) 。這一點非常重要,因為當(dāng)代碼運行于多線程環(huán)境中時,讀寫全局數(shù)據(jù)時必須采取保護措施以避免條件競爭 。如果在數(shù)組中找到了一個空的 SPY_CALL ,dispatcher 就會將調(diào)用者的線程 ID (通過 PsGetCurrentThreadId() 獲?。?、與當(dāng)前 API 函數(shù)相關(guān)的 SPY_HOOK_ENTRY 結(jié)構(gòu)的地址以及整個參數(shù)堆棧保存到該 SPY_CALL 結(jié)構(gòu)中 。需要復(fù)制的參數(shù)的字節(jié)數(shù)取自 KiArqumentTable 數(shù)組,該數(shù)組保存在系統(tǒng)的 SDT 中 。如果所有的 SPY_CALL 都被使用了,原始的 API 函數(shù)處理例程將被調(diào)用而不會產(chǎn)生任何日志記錄 。

推薦閱讀