操作被占用的文件-unlocker機理分析
2009-10-11 21:54
操作被占用的文件-unlocker機理分析
==================================================================== 以下為轉(zhuǎn)帖 [前言] —————————————————————— 之前給原作者發(fā)過郵件, 不過沒能得到unlocker的源代碼, 所以自己逆向了一份; 逆向的很匆忙, 因為近來事情太多, 而且unlocker本身也是版權所有, 所以不好做的太露骨; 本人也還有不少地方不是很明白, 有機會和我聯(lián)系, 一起探討 :-) 感謝EagleNet的討論. [關于unlocker] —————————————————————— Unlocker是一個免費的工具, 原作者的網(wǎng)站是: http://ccollomb.free.fr/unlocker 當使用者發(fā)現(xiàn)有某個文件或目錄無法刪除時, 只要按下鼠標右鍵中的"Unlocker", 程序會顯示出是哪一些程序占用了該目錄或文件, 接著只要按下"Unlock"就能夠為你的文件解鎖. ==============================Here we start================================= [大致流程] —————————————————————— 主程序Unlocker.exe通過ZwQuerySystemInformation查詢當前系統(tǒng)的所有句柄信息, 然后調(diào)用OpenProcess獲取目標進程句柄, 遍歷當前所有進程, 根據(jù)進程ID, 得到此進程打開的所有句柄信息, 接下來用DuplicateHandle復制Handle到本地進程, 然后把文件句柄發(fā)給驅(qū)動UnlockerDriver5.sys進行名字的查詢, sys將返回文件句柄對應的內(nèi)核文件對象的完整名字. 確定文件名后, 如果要刪除文件, 則調(diào)用OpenProcess與DuplicateHandle關閉句柄, 然后ZwDeleteFile刪除文件. [UnlockerAssistant.exe] [UnlockerHook.dll] —————————————————————— UnlockerAssistant.exe主要是實現(xiàn)系統(tǒng)托盤等輔助功能, 同時安裝鉤子: .text:00403AC9?????????? public start .text:00403AC9 start: //... .text:00403AD5?????????? call?? sub_40391E 跟進, 能看到主要的實現(xiàn)代碼: .text:00403925?????????? call?? sub_402E83 //... .text:00403944?????????? push?? offset LibFileName ; "UnlockerHook.dll" .text:00403949?????????? call?? ds:LoadLibraryA //... .text:0040398A?????????? push?? offset ProcName ; "HookInstall" .text:00403994?????????? call?? edi ; GetProcAddress 注冊窗口消息, 初始化控件, 安裝鉤子; .text:00403A1A?????????? call?? ds:Shell_NotifyIconA .text:00403A20?????????? call?? sub_40359E?? ;RegOpenKeyExA... 設置托盤圖標, 寫入注冊表啟動項; .text:00403A69?????????? push?? offset s_Hookuninstall ; "HookUninstall" 卸載鉤子. 當然也有綠化版本只寫入右鍵, 這些大家自己看反匯編的代碼, 詳細的鉤子過程反匯編UnlockerHook.dll, 也不再做糾纏. 我們只看UnlockerHook.dll里一處: .text:10001102 sub_10001102?? proc near?????????? ; //... .text:10001181?????????? call?? ds:GetModuleFileNameW .text:1000118E?????????? call?? ds:PathRemoveFileSpecW //... .text:100011A7?????????? push?? offset s_SUnlocker_exe ; "\"%s\\Unlocker.exe\"" //... .text:100011E0?????????? call?? ds:ShellExecuteExW 呼出主程序, 進行文件處理. [UnlockerDriver5.sys分析] —————————————————————— 反匯編驅(qū)動文件, 跳到入口點: INIT:00402000 ; int __stdcall start(PDRIVER_OBJECT DriverObject,int) INIT:00402000?????????? public start INIT:00402000 start?????? proc near //... INIT:0040203D?????????? call?? ds:IoCreateDevice //... INIT:004020E3?????????? call?? ds:IoCreateSymbolicLink //... 上面就是DriverEntry了. 注意這一段: INIT:004020AB?????????? mov?? dword ptr [ecx], offset loc_401000 INIT:004020B1?????????? mov?? dword ptr [esi+40h], offset loc_401000 INIT:004020B8?????????? mov?? dword ptr [esi+44h], offset sub_401090 INIT:004020BF?????????? mov?? dword ptr [esi+48h], offset loc_401020 INIT:004020C6?????????? mov?? dword ptr [esi+34h], offset sub_401240 經(jīng)典的, 處理MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)]等等. 我們跟蹤這兩個函數(shù), 先跟sub_401240, 如下: PAGE:0040124D?????????? call?? ds:RtlInitUnicodeString //... PAGE:00401257?????????? call?? ds:IoDeleteSymbolicLink //... PAGE:00401265?????????? call?? ds:IoDeleteDevice 很明顯的DriverUnload函數(shù); 接著跟sub_401090, 這個函數(shù)就是類似于DispatchControl(IRP_MJ_DEVICE_CONTROL)了; 我們挑關鍵的看, RtlInitUnicodeString函數(shù)填充UNICODE_STRING結構就不多做糾纏: PAGE:004010FA?????????? call?? ds:ObReferenceObjectByHandle 獲取由句柄描述的對象的指針, 也就是獲取FILE_OBJECT對象; 這里, 想想內(nèi)核級文件的Read和Write, 通過HANDLE執(zhí)行就要先用ObReferenceObjectByHandle函數(shù)來獲得Handle對應的FileObject, 然后我們再給FileObject發(fā)送IRP進行實質(zhì)操作. 略過分配內(nèi)存, 接著往下走: PAGE:00401158?????????? call?? ds:ObQueryNameString 這個函數(shù)將獲取設備名, 然后和FILE_OBJECT的FileName構成完整的名字返回(應用層在注冊表中保存設備名+目錄名); PAGE:00401214?????????? call?? ds:ObfDereferenceObject 再一次調(diào)用ObfDereferenceObject, 將對象的引用計數(shù)器恢復到先前的值, 防止泄漏; PAGE:0040122D?????????? call?? ds:IofCompleteRequest MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], 完成操作. 回頭看看這個驅(qū)動, 實際上是很經(jīng)典也很簡單的驅(qū)動程序, 實現(xiàn)的功能也很簡單: 返回文件句柄對應的內(nèi)核文件對象的完整名字, 傳給主程序進行文件"刪除""移動"等操作. [Unlocker.exe分析] —————————————————————— 直接跳到入口點: .text:004135FB?????????? public start .text:004135FB start?????? proc near //... .text:00413607?????????? call?? sub_412EDC 跟進去, 挑重點: .text:00412EEF?????????? call?? sub_40D1FF???? ; 命令行用法說明 .text:00412EFC?????????? call?? sub_40D78C???? ; 查詢注冊表 .text:00412F0C?????????? push?? offset Caption ; "Unlocker 1.8.5" .text:00412F11?????????? call?? sub_413DEF???? ; 創(chuàng)建線程, 網(wǎng)絡升級, 在線輔助 //... .text:00412F1C?????????? mov?? ecx, eax .text:00412F1E?????????? call?? sub_40FA79???? ; 這里開始! ============================================================================= 我把sub_40FA79列出來(有省略), 如下: .text:0040FA79 sub_40FA79???? proc near?????????? ; CODE XREF: sub_412EDC+42 p .text:0040FA79?????????? push?? ebx .text:0040FA7A?????????? push?? esi .text:0040FA7B?????????? push?? offset LibFileName ; "ntdll.dll" .text:0040FA82?????????? call?? ds:LoadLibraryA //... .text:0040FA8E?????????? push?? edi .text:0040FA8F?????????? mov?? edi, ds:GetProcAddress .text:0040FA95?????????? push?? offset ProcName ; "ZwQuerySystemInformation" .text:0040FA9D?????????? push?? offset s_Zwqueryobject ; "ZwQueryObject" .text:0040FAA7?????????? push?? offset s_Zwdeletefile ; "ZwDeleteFile" .text:0040FAB2?????????? push?? offset s_Rtlinitunicod ; "RtlInitUnicodeString" .text:0040FABD?????????? push?? offset s_Rtladjustpriv ; "RtlAdjustPrivilege" .text:0040FAC8?????????? push?? offset s_Ntloaddriver ; "NtLoadDriver" .text:0040FAD3?????????? push?? offset s_Ntunloaddrive ; "NtUnloadDriver" .text:0040FAD8?????????? push?? ebx???????? ; hModule .text:0040FAD9?????????? mov?? [esi+14h], eax .text:0040FADC?????????? call?? edi ; GetProcAddress 這一段的代碼是unlocker的重點部分, 獲取ntdll.dll地址后, 調(diào)用其中的函數(shù): 1)ZwQuerySystemInformation函數(shù)獲得系統(tǒng)當前所以進程的所建立的句柄及其相關信息; 2)ZwQueryObject獲取句柄所代表對象信息, 查出目標文件(設備名+目錄名); 3)ZwDeleteFile刪除目標文件; 4)NtLoadDriver加載驅(qū)動, 當然還需要后面的注冊表修改; =========================================================================== 再往下: .text:00412F9E?????????? call?? ds:PathRemoveFileSpecW .text:00412FAB?????????? push?? offset s_SUnlocker_cfg ; "%s\\Unlocker.cfg" .text:00412FB5?????????? call?? ds:wsprintfW //... .text:00412FC2?????????? call?? sub_40D3F4 跟進sub_40D3F4函數(shù), 發(fā)現(xiàn)作用是將驅(qū)動信息寫入注冊表; .text:00413008?????????? push?? dword ptr [eax+8] ; lpFileName .text:0041300B?????????? call?? sub_410E28 //... .text:004133E8?????????? call?? ds:QueryDosDeviceA //... .text:004134B7?????????? push?? offset s_DeviceLanmanr ; "\\Device\\LanmanRedirector" .text:004134BC?????????? push?? eax???????? ; LPWSTR .text:004134BD?????????? mov?? [ebp+lpSrch], eax .text:004134C0?????????? call?? ds:wsprintfW 遍歷查詢DOS設備, 進行重定向; .text:00413504?????????? mov?? esi, ds:DialogBoxParamA???? .text:0041350A?????????? mov?? edi, ds:GetModuleHandleA //... .text:00413529?????????? call?? sub_411A59?? //--->getfullname 創(chuàng)建一個對話框窗口, 顯示所有枚舉出的相關進程; sub_411A59函數(shù)將獲取對象文件的完整名, 并返回, 我們跟進去: .text:00411F63?????????? push?? dword ptr [esi] ; dwProcessId .text:00411F65?????????? mov?? edi, ds:OpenProcess .text:00411F6B?????????? push?? ebx???????? ; bInheritHandle .text:00411F6C?????????? push?? 450h???????? ; dwDesiredAccess .text:00411F71?????????? call?? edi ; OpenProcess 之前是進程和模塊遍歷, 找到相關聯(lián)的所有進程和模塊, OpenProcess打開需要操作的文件; .text:00411F93?????????? call?? ds:GetCurrentProcess //... .text:00411FA2?????????? call?? ds:DuplicateHandle 獲取本地進程, 將對象進程的句柄復制到本地進程(句柄進程相關); .text:00411FBB?????????? push?? offset s_?Unlockerdriv ; "\\\\?\\UnlockerDriver5" .text:00411FC0?????????? call?? ds:CreateFileW //... 將句柄發(fā)送給驅(qū)動程序, 驅(qū)動將返回文件句柄對應的內(nèi)核文件對象的完整名字; [文件操作選擇] —————————————————————— 現(xiàn)在退出sub_411A59函數(shù)返回主線, 我們走到這里: .text:00413536?????????? cmp?? byte ptr [eax+3], 0 .text:0041353A?????????? jz???? short loc_41357B .text:0041353A .text:0041353C?????????? mov?? eax, cInitial .text:00413541?????????? test?? eax, eax .text:00413543?????????? jz???? short loc_413571?? ; "移動"或者"重命名"操作; .text:00413543 .text:00413545?????????? xor?? ebx, ebx .text:00413547?????????? test?? eax, eax .text:00413549?????????? jbe?? short loc_41355F?? ; "刪除"操作 這里將進行文件處理的選擇, 是無動作? 刪除? 還是移動? 重命名? .text:0041353A?????????? jz???? short loc_41357B //... .text:0041357B?????????? xor?? ebx, ebx .text:0041357D?????????? cmp?? cInitial, ebx .text:00413583?????????? jz???? short loc_4135AE cInitial存放的值代表當前的窗口是否為初始窗口; a) 我們先看是派生窗口時的處理, loc_4135AE: .text:004135B5?????????? push?? offset sub_412D62 ; lpDialogFunc //... .text:00412DA4?????????? push?? [esp+800h+hDlg] ; hDlg .text:00412DAB?????????? call?? sub_412371?? //-->toMoveFile 生成一個瀏覽對話框供選擇路徑保存文件: .text:004123AB?????????? call?? sub_410064 //---> .text:00410082?????????? call?? ds:CoInitialize .text:004100D6?????????? call?? ds:SHBrowseForFolderW .text:004100E4?????????? call?? ds:SHGetPathFromIDListW .text:004100F4?????????? call?? ds:CoUninitialize //<--- .text:004123DE?????????? call?? ds:PathIsDirectoryW .text:0041242C?????????? call?? ds:GetSaveFileNameW .text:00412451?????????? call?? sub_4115AE???? //-->inject 實際上查看sub_412371這個函數(shù)的交叉引用(實際上你不用看引用也很容易就會發(fā)現(xiàn)), 會發(fā)現(xiàn): .text:00412478 sub_412478???? proc near???? //-->GuiControlDeal 這里是一個對話框, 也就是我們右鍵unlocker時產(chǎn)生的界面, 它將生成unlocker的主界面, 也將處理各種用戶操作的消息, 發(fā)給各個子程序去處理; 回到: .text:004135C7?????????? call?? esi ; DialogBoxParamA .text:004135CF?????????? call?? sub_410F86?? //-->MoveFile 由于cInitial值為0, 所以此時必定是用戶選擇了"移動"或"重命名"; ========================================================================= 跟進sub_410F86, 看到: .text:00410F96?????????? jnz?? loc_4110C2???? ; 重命名 //... .text:00411145?????????? call?? esi ; wsprintfW //... .text:00411173?????????? call?? edi ; SHFileOperationW //... .text:00411166?????????? mov?? [ebp+FileOp.wFunc], 4?? ;ReName text:00411217???????????? call?? edi ; SHFileOperationW 調(diào)用wsprintfW格式化路徑后, 填充SHFILEOPSTRUCT結構, 由SHFileOperationW來重命名, 完成后調(diào)用MessageBoxA通知完成; 否則就是"移動"操作: .text:00411048?????????? call?? sub_410BB2?? //-->getdirfile //---> .text:00410C20?????????? call?? ds:FindFirstFileW .text:00410C95?????????? call?? ds:FindNextFileW //<--- .text:00411064?????????? call?? ds:MoveFileExW .text:00411076?????????? jnz?? short loc_41105F ; 循環(huán)移動目錄下的所有文件 .text:00411093?????????? jb???? loc_410FBF???? //-->deleteDir //... .text:004113E4?????????? push?? eax???????? ; int .text:004113E5?????????? mov?? eax, lpSrch .text:004113EA?????????? push?? [ebp+var_8]?? ; int .text:004113ED?????????? add?? eax, esi .text:004113EF?????????? push?? eax???????? ; lpExistingFileName .text:004113F0?????????? call?? sub_410462?? //-->movefile 到這里就完成了這兩項功能, 不過注意函數(shù)里壓棧時的參數(shù): .text:00411281?????????? call?? getdirfile //... .text:00411302?????????? push?? 4?????????? ; MOVEFILE_DELAY_UNTIL_REBOOT .text:00411304?????????? lea?? eax, [ebp+NewFileName] .text:0041130A?????????? push?? eax???????? ; lpNewFileName .text:0041130B?????????? push?? dword ptr [edi] ; lpExistingFileName .text:0041130D?????????? call?? ds:MoveFileExW 這段代碼的意思是, 如果暫時處理不了用戶請求的文件操作, 那就在系統(tǒng)重新啟動時實行操作; ======================================================================== 接著: .text:004135DE?????????? cmp?? [ebp+var_9], 0 .text:004135E2?????????? jnz?? loc_413514 返回, 重新獲取文件的完整名, 重繪主窗口; .text:004135E8?????????? call?? sub_40D51D .text:004135ED?????????? call?? sub_413B8F ds:GlobalFree釋放內(nèi)存后, 調(diào)用了sub_40D51D, 它刪除了注冊表里的sys服務, 然后sub_413B8F休眠, 繼續(xù)等待操作觸發(fā); b) 現(xiàn)在我們回到a)處, 考慮另一種情況, cInitial==1: .text:00413586?????????? push?? offset GuiControlDeal ; lpDialogFunc .text:00413596?????????? call?? esi ; DialogBoxParamA //... .text:00413568?????????? push?? [ebp+hMem] .text:0041356B?????????? call?? sub_41178F?? //--->CloseFileHandle 此時的文件操作是"刪除"(或者"無動作"); sub_41178F函數(shù)的作用就是實現(xiàn)刪除文件, 我們看: .text:004117FB?????????? call?? ds:OpenProcess //... .text:0041180D?????????? call?? ds:TerminateProcess //... .text:00411819?????????? cmp?? eax, 0FFFFFFFFh .text:0041181C?????????? jnz?? loc_4119F8 判斷目標文件是哪種類型文件, exe還是dll? 是exe則跳到loc_4119F8處; .text:0041182E?????????? call?? ds:OpenProcess //... .text:00411868?????????? call?? GetModuleFileNameExW //... .text:00411873?????????? push?? offset s__dll?? ; ".DLL" .text:00411878?????????? push?? eax???????? ; pszPath .text:00411879?????????? call?? sub_40FC27 文件是dll類型, 獲取其路徑; .text:0041189F?????????? push?? offset s_SUS?? ; "/s /u \"%s\"" //... .text:004118B3?????????? mov?? [ebp+ExecInfo.lpVerb], offset s_Open ; "open" .text:004118BA?????????? mov?? [ebp+ExecInfo.lpFile], offset s_Regsvr32_exe ; "regsvr32.exe" //... .text:004118DA?????????? call?? ds:ShellExecuteExW 先將模塊注銷掉, 使用regsvr32.exe /s /u實現(xiàn); .text:00411944?????????? push?? offset s_Freelibrary ; "FreeLibrary" //... .text:00411966?????????? push?? offset s_Closehandle ; "CloseHandle" 然后通過FreeLibrary來釋放, 調(diào)用GetModuleHandleW查詢句柄后, 用CloseHandle將其關閉; 注意這一段代碼: .text:00411888?????????? push?? 0Eh .text:0041188A?????????? pop?? ecx 也就是說循環(huán)將進行0EH(14)次, FreeLibrary也將執(zhí)行14次直到dll被釋放; .text:00411921?????????? call?? esi ; WriteProcessMemory .text:00411933?????????? call?? edi ; VirtualAllocEx .text:004119A8?????????? call?? esi ; WriteProcessMemory .text:004119B5?????????? call?? ds:CreateRemoteThread 想必您已經(jīng)注意到了這些代碼, 對, 思路就是用線程注入到目標進程去調(diào)用FreeLibrary來釋放dll; 現(xiàn)在我們來到loc_4119F8處, 也就是上文提到的如果文件是exe文件時的處理方法: .text:00411A07?????????? call?? ds:OpenProcess .text:00411A1B?????????? call?? ds:GetCurrentProcess .text:00411A28?????????? call?? ds:DuplicateHandle .text:00411A3C?????????? call?? ds:CloseHandle 還是先打開文件, 然后調(diào)用DuplicateHandle, 但是這里傳遞的參數(shù)是DUPLICATE_CLOSE_SOURCE標志, 將強制關閉句柄; ===========================EOF===But to be modified======================== [后語] —————————————————————— 突然發(fā)現(xiàn)沒結尾, 被老大姐罵了, 補充一個吧. unlocker最主要的功能實現(xiàn)都在unlocker.exe和一個.sys驅(qū)動里, 值得關注的是它怎么實現(xiàn)的關閉句柄和刪除文件. 關鍵的函數(shù) (部分也是未公開的API) , 比如: ZwQuerySystemInformation、 ZwQueryObject、ZwDeleteFile、ObReferenceObjectByHandle、ObQueryNameString、 SHFileOperationW、MoveFileExW、DuplicateHandle. 這里尤其是DuplicateHandle和MoveFileExW比較有意思. 詳細的界面構造沒有去研究, 就是這句: .text:00412478 sub_412478???? proc near???? //-->GuiControlDeal 跟進去分析就是界面的相關操作了, 太煩, 本人時間有限, 如果有人愿意繼續(xù), 記得把結果分享一份給我, 謝謝. *[參考資料]: —————————————————————— 1. Windows平臺內(nèi)核級文件訪問?? baiyuanfan 2. unlocker1.8.5?????? http://ccollomb.free.fr/unlocker 3. Kmd教程-全功能的驅(qū)動程序分析?? 羅云彬 4. http://forum.sysinternals.com/forum_posts.asp?TID=7974 ========================================================================== 另一篇: http://windknown.spaces.live.com/blog/cns!627D8DB6EC5BD4A7!435.entry April 07 UnLocker帶來的思考 這幾個禮拜已經(jīng)是忙得焦頭爛額了, 這2天還偏偏和unlocker這個軟件耗上了, 我的睡眠時間啊~~ UnLocker是一款可以用來刪除已被占用文件的軟件, http://ccollomb.free.fr/unlocker/ 通常的刪除文件是通過調(diào)用win32 api的DeleteFile來執(zhí)行的, 該函數(shù)會檢查是否有引用該文件的句柄, 因此若有進程打開了該文件是無法刪除的. 另外說一下該api的執(zhí)行過程, 打開文件-〉設置為刪除-〉關閉文件, 此時系統(tǒng)會自動刪除該文件. 設置是通過主控制號為IRP_MJ_SET_INFORMATION的IRP進行的, IO 棧的Parameters.SetFile.FileInformationClass值為 FileDispositionInformation, AssociatedIrp.SystemBuffer指向結構 FILE_DISPOSITION_INFORMATION, 其成員DeleteFile設置為TRUE. 于是乎, 如果在自己的文件過濾驅(qū)動里過濾該 IRP, 只需要改為FASLE就可以防止文件被刪除了. 說一下另一種刪除文件的方法, 也就是unlocker使用的, ntdll導出的ZwDeleteFile, 該函數(shù)的原理沒有細究, 但是同樣也是會檢查句柄的. 因此, 要刪除文件的關鍵是需要關閉打開的文件句柄, 而句柄是與進程相關的. 今天仔細逆向了一下unlocker, 包括主程序和一個sys文件. sys文件的作用是返回文件句柄對應的內(nèi)核文件對象的完整名字, 例如: \Device\HarddiskVolum1\mytest.doc,? \Device\HarddiskVolum1就是C:這個卷的設備名. 大致流程就是調(diào)用ObReferenceObjectByHandle通過對象獲取FILE_OBJECT對象, 然后通過ObQuerNameString查詢FILE_OBJECT中的DeviceObject指針獲取設備名, 再與 FILE_OBJECT的FileName構成完整的名字返回. 主程序是通過 ZwQuerySystemInformation 查詢 handle 信息, 類型為 SystemHandleInformation, 然后調(diào)用 OpenProcess 獲取目標進程句柄, 再用 DuplicateHandle 復制 Handle 為本進程, 再通過驅(qū)動查詢名字, 確定文件名. 一定記住句柄是進程相關的, MSDN里描述, 例如在DriverEntry中打開的句柄是在system進程下, 因此在驅(qū)動DispatchFunction中不可用, 因為DispatchFunction的進程上下文為與驅(qū)動通信的進程. 當要刪除文件時, 對于打開的文件, unlocker仍然是通過OpenProcess與DuplicateHandle, 只是指定了 DUPLICATE_CLOSE_SOURCE標志, 真是大開眼界阿, 如此便關閉了句柄, 高. 而對于dll就沒有那么方便了, unlocker會用線程注入到目標進程去調(diào)用FreeLibrary來釋放dll, 因此可能不夠穩(wěn)定, 而且逆向時發(fā)現(xiàn)注入的代碼只嘗試0x10次Free, 呵呵我load20次自己就可以搞定撈. 然后看MSDN過程中發(fā)現(xiàn)了另一個nb函數(shù), SetHandleInformation, 設置這個標志 HANDLE_FLAG_PROTECT_FROM_CLOSE , 那么 unlocker 調(diào)用 DuplicateHandle 也關不掉你的句柄了, 試驗了一下, 果然說刪不了撈, hoho 整這些鳥東西弄了我一天時間, 唉, 許久不逆向, 生疏了不少~~而且因為在虛擬機里調(diào)試, 老破機器這個卡啊~~睡覺去! 順便一提, 今天發(fā)現(xiàn)國內(nèi)搞安全的兄弟們共享精神不如國外阿, 唉, 環(huán)境使然, 人嗎首先還是要自己生存的. 想想自己也快畢業(yè)了, 有點迷惘, 畢業(yè)了干啥工作去捏……生存阿生存 ============================================================================ 第三篇: http://blog.vckbase.com/windowssky/archive/2007/04/18/25565.html 小議文件保護和鎖定技術 近1年來互連網(wǎng)上的木馬越來越多, 有的還刪除不掉, 要切換到dos才可行, 如: CNNC, 3721等, 實現(xiàn)技術也五花八門, 但就文件不可刪除的實現(xiàn)技術可分三類: 1 Attach file system; 這種技術和Filemon/sfilter查不多, 就是掛一個 filter 驅(qū)動到 fs 上, 其他函數(shù)都是 passthru 下去, 只處理 IRP_MJ_SET_INFORMATION, 當發(fā)現(xiàn)有刪除需保護文件的 IRP, 就 Irp->IoStatus.Status = STATUS_SUCCESS;??????????? Irp->IoStatus.Information = 0;??????????? IoCompleteRequest(Irp, IO_NO_INCREMENT); 根本不讓 Fs 去處理, 從而達到文件不可刪除的作用! 有什么方法可以刪除呢? 自己寫個驅(qū)動自己填充 irp 包 (見 OSR 文檔 Rolling Your Own) , 直接發(fā)送 IRP 到 File System Device 上去就 ok 啦! 2 修改 file system 的 dispatch 函數(shù)表; 首先得到 Fs 的 DriverObject (根據(jù)驅(qū)動名得到驅(qū)動設備對象 (ObReferenceObjectByName(IoDriverObjectType))), pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = MySetInformation, 然后在 MySetInformation 中再調(diào)用原來的調(diào)度函數(shù), 類似于 HookApi; 發(fā)現(xiàn)有刪除需保護文件的 IRP, 就直接 IoCompleteRequest, 根本不讓原來的 FsSetInformation 處理! 有什么方法可以刪除呢? 自己寫個驅(qū)動來修復 Fs 的 dispatch 函數(shù)表, 讀 Fs 的原始文件, 根據(jù) PE 文件得到 Fs 的 Entrypoint, dispatch 函數(shù)表的填充都在 EntryPoint 后面, 我們可以根據(jù) Opcode 查找, XX XX XX XX 就是我們要找的 dispatch 的原始地址; 找到后 pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = XX XX XX XX, 然后就能刪除文件啦! FunOpc=MajorFunction*4+0x38 C7 46 FunOpc[<80]? XX XX XX XX? mov???? dword ptr [esi+50h], offset _NtfsFsdSetInformation C7 86 FunOpc[>=80] XX XX XX XX C7 43 FunOpc[<80]? XX XX XX XX? mov???? dword ptr [ebx+50h], offset _NtfsFsdSetInformation C7 83 FunOpc[>=80] XX XX XX XX 3 通過 ZwCreateFile 把文件鎖定; 刪除時報告 "文件正在使用, 禁止刪除", 具體原理自己摸索吧, 反正是通過 ZwCreateFile 實現(xiàn)的! 有什么方法可以刪除呢? step1: 通過 QuerySystemInformation(SystemHandleInformation) 得到當前系統(tǒng)的所有句柄信息 step2: 遍歷當前所有進程, 根據(jù)進程 ID, 得到此進程打開的所有句柄信息 Step3: 把句柄發(fā)送給我們的驅(qū)動程序, 驅(qū)動程序根據(jù) ObQueryNameString 得到句柄的路徑信息, 然后再傳給我們的應用程序 Step4: 如果是我們要刪除文件的路徑, 應用程序調(diào)用 DuplicateHandle(DUPLICATE_CLOSE_SOURCE), 句柄被關閉了, 現(xiàn)在可以刪除文件了! 注: QuerySystemInformation 的使用說明見 The Undocumented Functions, 或者 http://undocumented.ntinternals.net, 第三種解除文件鎖定的方法是我反匯編 Unlocker 軟件學習到的 最后感謝 7cat 的幫助! ==================================================================== 以下為原創(chuàng) free2000fly 按: unlocker 軟件真是搞的百轉(zhuǎn)千回, 還用到了驅(qū)動, 其實不必要, 在ring3是可以通過文件句柄得到文件名的, 以下是刪除已被鎖定文件的代碼: BOOL DeleteLockedFile(DWORD dwProcessID, HANDLE hFile) { TCHAR szTargetName[MAX_PATH] = { 0 }; HANDLE hTargeFile = INVALID_HANDLE_VALUE; HANDLE hProcess = NULL; BOOL bResult = FALSE; do { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessID); if (NULL == hProcess) { break; } if (FALSE == DuplicateHandle(hProcess, hFile, GetCurrentProcess(), &hTargeFile, 0, FALSE, DUPLICATE_SAME_ACCESS)) { break; } if (INVALID_HANDLE_VALUE==hTargeFile || NULL==hTargeFile) { break; } if (FALSE == GetFilePathFromHandle(hTargeFile, szTargetName, _countof(szTargetName))) { break; } CloseHandle(hTargeFile); hTargeFile = INVALID_HANDLE_VALUE; if (0 == lstrlen(szTargetName)) { break; } if (FALSE == DuplicateHandle(hProcess, hFile, GetCurrentProcess(), &hTargeFile, 0, FALSE, DUPLICATE_SAME_ACCESS|DUPLICATE_CLOSE_SOURCE)) { break; } if (INVALID_HANDLE_VALUE==hTargeFile || NULL==hTargeFile) { break; } CloseHandle(hTargeFile); hTargeFile = INVALID_HANDLE_VALUE; bResult = DeleteFile(szTargetName); } while (FALSE); if (INVALID_HANDLE_VALUE != hTargeFile && NULL != hTargeFile) { CloseHandle(hTargeFile); } if (hProcess) { CloseHandle(hProcess); } return bResult; } 其中函數(shù) GetFilePathFromHandle 的實現(xiàn)請看我的 另一篇博文 " 從文件句柄得到文件路徑的函數(shù) " 1.運行中的程序或DLL,任何運行中的程序或DLL都可以被刪除,包括FileKill360本身 系統(tǒng)文件也可以刪除,不過系統(tǒng)的文件檢查會將其自動恢復,若關閉自動恢復或?qū)llcache中文件一起刪除,也可以刪除之 這點超越金山粉碎機、Unlocker、PowerRMv等使用MoveFileEx等不穩(wěn)定或容易失敗技巧的文件刪除工具 被運行中的文件進程功能可能受到影響,但如果是來刪除流氓軟件或木馬的DLL或文件,卻是再好不過了 2.被獨占打開的文件,被獨占(拒絕讀寫共享)的文件,也可以刪除,包括MY123系列驅(qū)動 這點目前金山粉碎機、Unlocker、PowerRMv都無法做到 獨占打開的文件被刪除后,不會消失,但任何軟件都無法將其打開,這樣可以防止惡意軟件在后臺重建該文件 等關機或獨占該文件的進程退出后,文件會自動消失 3.被IFS文件過濾驅(qū)動保護的文件,比如百度搜霸的被保護文件、彩信通的被保護文件等 4.被Fsd Dispatch Hook保護的文件,如萬能搜索保護的文件等 5.被fsd inline hook保護的文件,如cnnic保護的文件等 直接發(fā)IRP刪除文件 然后Hook fastfat.sys和Ntfs.sys上IAT的MmFlushImageSection 在NewMmFlushImageSection中判斷其&SectionObjectPointer->DataSectionObject->Fileobject是否為當前要刪除的文件的FileObject,若是,返回SUCCESS 即可刪除運行中的文件 刪除句柄被鎖文件可以用360IceBreaker里 No Handle的辦法 刪除增加自己Ref的辦法:找到FileObject的Object_Header中PointCount,清0之 再結合360IceBreaker干凈的fsd image和fsd dispatch routine,基本文件系統(tǒng)層面上沒有刪除不了的文件了 無論是正在運行的程序或文件,或者是獨占打開的文件,包括其他任何FSD驅(qū)動保護的文件等 |