操作被佔用的檔案-unlocker機理分析
====================================================================
以下為轉帖
操作被佔用的檔案-unlocker機理分析
*[標題]: 操作被佔用的檔案-unlocker機理分析
*[作者]: gz1X [gz1x(at)tom(dot)com]
EagleNet [hklt594(at)163(dot)com]
*[來自]: 中國黑客聯盟 [CHU]
*[原始連結]: http://blog.china-pub.com/more.asp?name=zzs0405&id=41832
[前言]
——————————————————————
之前給原作者發過郵件, 不過沒能得到unlocker的原始碼, 所以自己逆向了一份;
逆向的很匆忙, 因為近來事情太多, 而且unlocker本身也是版權所有, 所以不好做的太露骨;
本人也還有不少地方不是很明白, 有機會和我聯絡, 一起探討 :-)
感謝EagleNet的討論.
[關於unlocker]
——————————————————————
Unlocker是一個免費的工具, 原作者的網站是: http://ccollomb.free.fr/unlocker
當使用者發現有某個檔案或目錄無法刪除時, 只要按下滑鼠右鍵中的"Unlocker",
程式會顯示出是哪一些程式佔用了該目錄或檔案, 接著只要按下"Unlock"就能夠為你的檔案解鎖.
==============================Here we start=================================
[大致流程]
——————————————————————
主程式Unlocker.exe通過ZwQuerySystemInformation查詢當前系統的所有控制程式碼資訊, 然後呼叫OpenProcess獲取目標程式控制程式碼,
遍歷當前所有程式, 根據程式ID, 得到此程式開啟的所有控制程式碼資訊, 接下來用DuplicateHandle複製Handle到本地程式,
然後把檔案控制程式碼發給驅動UnlockerDriver5.sys進行名字的查詢, sys將返回檔案控制程式碼對應的核心檔案物件的完整名字.
確定檔名後, 如果要刪除檔案, 則呼叫OpenProcess與DuplicateHandle關閉控制程式碼, 然後ZwDeleteFile刪除檔案.
[UnlockerAssistant.exe]
[UnlockerHook.dll]
——————————————————————
UnlockerAssistant.exe主要是實現系統托盤等輔助功能, 同時安裝鉤子:
.text:00403AC9 public start
.text:00403AC9 start:
//...
.text:00403AD5 call sub_40391E
跟進, 能看到主要的實現程式碼:
.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分析]
——————————————————————
反彙編驅動檔案, 跳到入口點:
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
經典的, 處理MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)]等等.
我們跟蹤這兩個函式, 先跟sub_401240, 如下:
PAGE:0040124D call ds:RtlInitUnicodeString
//...
PAGE:00401257 call ds:IoDeleteSymbolicLink
//...
PAGE:00401265 call ds:IoDeleteDevice
很明顯的DriverUnload函式;
接著跟sub_401090, 這個函式就是類似於DispatchControl(IRP_MJ_DEVICE_CONTROL)了;
我們挑關鍵的看, RtlInitUnicodeString函式填充UNICODE_STRING結構就不多做糾纏:
PAGE:004010FA call ds:ObReferenceObjectByHandle
獲取由控制程式碼描述的物件的指標, 也就是獲取FILE_OBJECT物件;
這裡, 想想核心級檔案的Read和Write, 通過HANDLE執行就要先用ObReferenceObjectByHandle函式來獲得Handle對應的FileObject,
然後我們再給FileObject傳送IRP進行實質操作.
略過分配記憶體, 接著往下走:
PAGE:00401158 call ds:ObQueryNameString
這個函式將獲取裝置名, 然後和FILE_OBJECT的FileName構成完整的名字返回(應用層在登錄檔中儲存裝置名+目錄名);
PAGE:00401214 call ds:ObfDereferenceObject
再一次呼叫ObfDereferenceObject, 將物件的引用計數器恢復到先前的值, 防止洩漏;
PAGE:0040122D call ds:IofCompleteRequest
MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], 完成操作.
回頭看看這個驅動, 實際上是很經典也很簡單的驅動程式, 實現的功能也很簡單:
返回檔案控制程式碼對應的核心檔案物件的完整名字, 傳給主程式進行檔案"刪除""移動"等操作.
[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 ; 建立執行緒, 網路升級, 線上輔助
//...
.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地址後, 呼叫其中的函式:
1)ZwQuerySystemInformation函式獲得系統當前所以程式的所建立的控制程式碼及其相關資訊;
2)ZwQueryObject獲取控制程式碼所代表物件資訊, 查出目標檔案(裝置名+目錄名);
3)ZwDeleteFile刪除目標檔案;
4)NtLoadDriver載入驅動, 當然還需要後面的登錄檔修改;
===========================================================================
再往下:
.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函式, 發現作用是將驅動資訊寫入登錄檔;
.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
建立一個對話方塊視窗, 顯示所有列舉出的相關程式;
sub_411A59函式將獲取物件檔案的完整名, 並返回, 我們跟進去:
.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
之前是程式和模組遍歷, 找到相關聯的所有程式和模組, OpenProcess開啟需要操作的檔案;
.text:00411F93 call ds:GetCurrentProcess
//...
.text:00411FA2 call ds:DuplicateHandle
獲取本地程式, 將物件程式的控制程式碼複製到本地程式(控制程式碼程式相關);
.text:00411FBB push offset s_?Unlockerdriv ; "\\\\?\\UnlockerDriver5"
.text:00411FC0 call ds:CreateFileW
//...
將控制程式碼傳送給驅動程式, 驅動將返回檔案控制程式碼對應的核心檔案物件的完整名字;
[檔案操作選擇]
——————————————————————
現在退出sub_411A59函式返回主線, 我們走到這裡:
.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這個函式的交叉引用(實際上你不用看引用也很容易就會發現), 會發現:
.text:00412478 sub_412478 proc near //-->GuiControlDeal
這裡是一個對話方塊, 也就是我們右鍵unlocker時產生的介面, 它將生成unlocker的主介面,
也將處理各種使用者操作的訊息, 發給各個子程式去處理;
回到:
.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
呼叫wsprintfW格式化路徑後, 填充SHFILEOPSTRUCT結構, 由SHFileOperationW來重新命名, 完成後呼叫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 ; 迴圈移動目錄下的所有檔案
.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
到這裡就完成了這兩項功能, 不過注意函式裡壓棧時的引數:
.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
這段程式碼的意思是, 如果暫時處理不了使用者請求的檔案操作, 那就在系統重新啟動時實行操作;
========================================================================
接著:
.text:004135DE cmp [ebp+var_9], 0
.text:004135E2 jnz loc_413514
返回, 重新獲取檔案的完整名, 重繪主視窗;
.text:004135E8 call sub_40D51D
.text:004135ED call sub_413B8F
ds:GlobalFree釋放記憶體後, 呼叫了sub_40D51D, 它刪除了登錄檔裡的sys服務, 然後sub_413B8F休眠, 繼續等待操作觸發;
b)
現在我們回到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函式的作用就是實現刪除檔案, 我們看:
.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實現;
.text:00411944 push offset s_Freelibrary ; "FreeLibrary"
//...
.text:00411966 push offset s_Closehandle ; "CloseHandle"
然後通過FreeLibrary來釋放, 呼叫GetModuleHandleW查詢控制程式碼後, 用CloseHandle將其關閉;
注意這一段程式碼:
.text:00411888 push 0Eh
.text:0041188A pop ecx
也就是說迴圈將進行0EH(14)次, FreeLibrary也將執行14次直到dll被釋放;
.text:00411921 call esi ; WriteProcessMemory
.text:00411933 call edi ; VirtualAllocEx
.text:004119A8 call esi ; WriteProcessMemory
.text:004119B5 call ds:CreateRemoteThread
想必您已經注意到了這些程式碼, 對, 思路就是用執行緒注入到目標程式去呼叫FreeLibrary來釋放dll;
現在我們來到loc_4119F8處, 也就是上文提到的如果檔案是exe檔案時的處理方法:
.text:00411A07 call ds:OpenProcess
.text:00411A1B call ds:GetCurrentProcess
.text:00411A28 call ds:DuplicateHandle
.text:00411A3C call ds:CloseHandle
還是先開啟檔案, 然後呼叫DuplicateHandle, 但是這裡傳遞的引數是DUPLICATE_CLOSE_SOURCE標誌, 將強制關閉控制程式碼;
===========================EOF===But to be modified========================
[後語]
——————————————————————
突然發現沒結尾, 被老大姐罵了, 補充一個吧.
unlocker最主要的功能實現都在unlocker.exe和一個.sys驅動裡, 值得關注的是它怎麼實現的關閉控制程式碼和刪除檔案.
關鍵的函式 (部分也是未公開的API) , 比如:
ZwQuerySystemInformation、 ZwQueryObject、ZwDeleteFile、ObReferenceObjectByHandle、ObQueryNameString、 SHFileOperationW、MoveFileExW、DuplicateHandle.
這裡尤其是DuplicateHandle和MoveFileExW比較有意思.
詳細的介面構造沒有去研究, 就是這句:
.text:00412478 sub_412478 proc near //-->GuiControlDeal
跟進去分析就是介面的相關操作了, 太煩, 本人時間有限, 如果有人願意繼續, 記得把結果分享一份給我, 謝謝.
*[參考資料]:
——————————————————————
1. Windows平臺核心級檔案訪問 baiyuanfan
2. unlocker1.8.5 http://ccollomb.free.fr/unlocker
3. Kmd教程-全功能的驅動程式分析 羅雲彬
4. http://forum.sysinternals.com/forum_posts.asp?TID=7974
==========================================================================
另一篇: http://windknown.spaces.live.com/blog/cns!627D8DB6EC5BD4A7!435.entry
April 07
UnLocker帶來的思考
這幾個禮拜已經是忙得焦頭爛額了, 這2天還偏偏和unlocker這個軟體耗上了, 我的睡眠時間啊~~
UnLocker是一款可以用來刪除已被佔用檔案的軟體, http://ccollomb.free.fr/unlocker/
通常的刪除檔案是通過呼叫win32 api的DeleteFile來執行的, 該函式會檢查是否有引用該檔案的控制程式碼, 因此若有程式開啟了該檔案是無法刪除的. 另外說一下該api的執行過程, 開啟檔案-〉設定為刪除-〉關閉檔案, 此時系統會自動刪除該檔案. 設定是通過主控制號為IRP_MJ_SET_INFORMATION的IRP進行的, IO 棧的Parameters.SetFile.FileInformationClass值為 FileDispositionInformation, AssociatedIrp.SystemBuffer指向結構 FILE_DISPOSITION_INFORMATION, 其成員DeleteFile設定為TRUE. 於是乎, 如果在自己的檔案過濾驅動裡過濾該 IRP, 只需要改為FASLE就可以防止檔案被刪除了.
說一下另一種刪除檔案的方法, 也就是unlocker使用的, ntdll匯出的ZwDeleteFile, 該函式的原理沒有細究, 但是同樣也是會檢查控制程式碼的.
因此, 要刪除檔案的關鍵是需要關閉開啟的檔案控制程式碼, 而控制程式碼是與程式相關的. 今天仔細逆向了一下unlocker, 包括主程式和一個sys檔案.
sys檔案的作用是返回檔案控制程式碼對應的核心檔案物件的完整名字, 例如: \Device\HarddiskVolum1\mytest.doc, \Device\HarddiskVolum1就是C:這個卷的裝置名. 大致流程就是呼叫ObReferenceObjectByHandle通過物件獲取FILE_OBJECT物件, 然後通過ObQuerNameString查詢FILE_OBJECT中的DeviceObject指標獲取裝置名, 再與 FILE_OBJECT的FileName構成完整的名字返回.
主程式是通過 ZwQuerySystemInformation 查詢 handle 資訊, 型別為 SystemHandleInformation, 然後呼叫 OpenProcess 獲取目標程式控制程式碼, 再用 DuplicateHandle 複製 Handle 為本程式, 再通過驅動查詢名字, 確定檔名. 一定記住控制程式碼是程式相關的, MSDN裡描述, 例如在DriverEntry中開啟的控制程式碼是在system程式下, 因此在驅動DispatchFunction中不可用, 因為DispatchFunction的程式上下文為與驅動通訊的程式. 當要刪除檔案時, 對於開啟的檔案, unlocker仍然是通過OpenProcess與DuplicateHandle, 只是指定了 DUPLICATE_CLOSE_SOURCE標誌, 真是大開眼界阿, 如此便關閉了控制程式碼, 高. 而對於dll就沒有那麼方便了, unlocker會用執行緒注入到目標程式去呼叫FreeLibrary來釋放dll, 因此可能不夠穩定, 而且逆向時發現注入的程式碼只嘗試0x10次Free, 呵呵我load20次自己就可以搞定撈.
然後看MSDN過程中發現了另一個nb函式, SetHandleInformation, 設定這個標誌 HANDLE_FLAG_PROTECT_FROM_CLOSE , 那麼 unlocker 呼叫 DuplicateHandle 也關不掉你的控制程式碼了, 試驗了一下, 果然說刪不了撈, hoho
整這些鳥東西弄了我一天時間, 唉, 許久不逆向, 生疏了不少~~而且因為在虛擬機器裡除錯, 老破機器這個卡啊~~睡覺去!
順便一提, 今天發現國內搞安全的兄弟們共享精神不如國外阿, 唉, 環境使然, 人嗎首先還是要自己生存的. 想想自己也快畢業了, 有點迷惘, 畢業了幹啥工作去捏……生存阿生存
============================================================================
第三篇: http://blog.vckbase.com/windowssky/archive/2007/04/18/25565.html
小議檔案保護和鎖定技術
近1年來互連網上的木馬越來越多, 有的還刪除不掉, 要切換到dos才可行, 如: CNNC, 3721等, 實現技術也五花八門, 但就檔案不可刪除的實現技術可分三類:
1 Attach file system; 這種技術和Filemon/sfilter查不多, 就是掛一個 filter 驅動到 fs 上, 其他函式都是 passthru 下去, 只處理 IRP_MJ_SET_INFORMATION, 當發現有刪除需保護檔案的 IRP, 就
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
根本不讓 Fs 去處理, 從而達到檔案不可刪除的作用!
有什麼方法可以刪除呢? 自己寫個驅動自己填充 irp 包 (見 OSR 文件 Rolling Your Own) , 直接傳送 IRP 到 File System Device 上去就 ok 啦!
2 修改 file system 的 dispatch 函式表; 首先得到 Fs 的 DriverObject (根據驅動名得到驅動裝置物件 (ObReferenceObjectByName(IoDriverObjectType))), pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = MySetInformation, 然後在 MySetInformation 中再呼叫原來的排程函式, 類似於 HookApi; 發現有刪除需保護檔案的 IRP, 就直接 IoCompleteRequest, 根本不讓原來的 FsSetInformation 處理!
有什麼方法可以刪除呢? 自己寫個驅動來修復 Fs 的 dispatch 函式表, 讀 Fs 的原始檔案, 根據 PE 檔案得到 Fs 的 Entrypoint, dispatch 函式表的填充都在 EntryPoint 後面, 我們可以根據 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 實現的!
有什麼方法可以刪除呢?
step1: 通過 QuerySystemInformation(SystemHandleInformation) 得到當前系統的所有控制程式碼資訊
step2: 遍歷當前所有程式, 根據程式 ID, 得到此程式開啟的所有控制程式碼資訊
Step3: 把控制程式碼傳送給我們的驅動程式, 驅動程式根據 ObQueryNameString 得到控制程式碼的路徑資訊, 然後再傳給我們的應用程式
Step4: 如果是我們要刪除檔案的路徑, 應用程式呼叫 DuplicateHandle(DUPLICATE_CLOSE_SOURCE), 控制程式碼被關閉了, 現在可以刪除檔案了!
注: QuerySystemInformation 的使用說明見 The Undocumented Functions, 或者 http://undocumented.ntinternals.net, 第三種解除檔案鎖定的方法是我反彙編 Unlocker 軟體學習到的
最後感謝 7cat 的幫助!
====================================================================
以下為原創
free2000fly 按:
unlocker 軟體真是搞的百轉千回, 還用到了驅動, 其實不必要, 在ring3是可以通過檔案控制程式碼得到檔名的, 以下是刪除已被鎖定檔案的程式碼:
PS. 最近幾天試圖實現列舉已開啟所有檔案的功能, 我卻一而再,再而三的在函式 NtQueryObject 和 ZwQueryInformationFile 上遭遇掛起的現象, 這兩個函式在被呼叫後有可能不再返回, 軟體失去響應, 這是相當要命的事, 後來的解決方法是, 用一個獨立的執行緒來呼叫這兩個函式, 如果超時則立即返回, 但在需要講求效率的場合這個解決方法行不通. 查遍網路才發現這可能算是 Windows 系列作業系統的一個 BUG. 因此, 最後, 還是回到了必須使用驅動的老路上去了. 2009.12.05
參考:
http://www.codeproject.com/KB/shell/OpenedFileFinder.aspx
http://www.codeproject.com/Articles/35202/GetFinalPathNameByHandle-API-Hangs.aspx
http://www.codeguru.com/forum/showthread.php?t=359606
以下為轉帖
操作被佔用的檔案-unlocker機理分析
*[標題]: 操作被佔用的檔案-unlocker機理分析
*[作者]: gz1X [gz1x(at)tom(dot)com]
EagleNet [hklt594(at)163(dot)com]
*[來自]: 中國黑客聯盟 [CHU]
*[原始連結]: http://blog.china-pub.com/more.asp?name=zzs0405&id=41832
[前言]
——————————————————————
之前給原作者發過郵件, 不過沒能得到unlocker的原始碼, 所以自己逆向了一份;
逆向的很匆忙, 因為近來事情太多, 而且unlocker本身也是版權所有, 所以不好做的太露骨;
本人也還有不少地方不是很明白, 有機會和我聯絡, 一起探討 :-)
感謝EagleNet的討論.
[關於unlocker]
——————————————————————
Unlocker是一個免費的工具, 原作者的網站是: http://ccollomb.free.fr/unlocker
當使用者發現有某個檔案或目錄無法刪除時, 只要按下滑鼠右鍵中的"Unlocker",
程式會顯示出是哪一些程式佔用了該目錄或檔案, 接著只要按下"Unlock"就能夠為你的檔案解鎖.
==============================Here we start=================================
[大致流程]
——————————————————————
主程式Unlocker.exe通過ZwQuerySystemInformation查詢當前系統的所有控制程式碼資訊, 然後呼叫OpenProcess獲取目標程式控制程式碼,
遍歷當前所有程式, 根據程式ID, 得到此程式開啟的所有控制程式碼資訊, 接下來用DuplicateHandle複製Handle到本地程式,
然後把檔案控制程式碼發給驅動UnlockerDriver5.sys進行名字的查詢, sys將返回檔案控制程式碼對應的核心檔案物件的完整名字.
確定檔名後, 如果要刪除檔案, 則呼叫OpenProcess與DuplicateHandle關閉控制程式碼, 然後ZwDeleteFile刪除檔案.
[UnlockerAssistant.exe]
[UnlockerHook.dll]
——————————————————————
UnlockerAssistant.exe主要是實現系統托盤等輔助功能, 同時安裝鉤子:
.text:00403AC9 public start
.text:00403AC9 start:
//...
.text:00403AD5 call sub_40391E
跟進, 能看到主要的實現程式碼:
.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分析]
——————————————————————
反彙編驅動檔案, 跳到入口點:
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
經典的, 處理MajorFunction[IRP_MJ_DEVICE_CONTROL*(sizeof PVOID)]等等.
我們跟蹤這兩個函式, 先跟sub_401240, 如下:
PAGE:0040124D call ds:RtlInitUnicodeString
//...
PAGE:00401257 call ds:IoDeleteSymbolicLink
//...
PAGE:00401265 call ds:IoDeleteDevice
很明顯的DriverUnload函式;
接著跟sub_401090, 這個函式就是類似於DispatchControl(IRP_MJ_DEVICE_CONTROL)了;
我們挑關鍵的看, RtlInitUnicodeString函式填充UNICODE_STRING結構就不多做糾纏:
PAGE:004010FA call ds:ObReferenceObjectByHandle
獲取由控制程式碼描述的物件的指標, 也就是獲取FILE_OBJECT物件;
這裡, 想想核心級檔案的Read和Write, 通過HANDLE執行就要先用ObReferenceObjectByHandle函式來獲得Handle對應的FileObject,
然後我們再給FileObject傳送IRP進行實質操作.
略過分配記憶體, 接著往下走:
PAGE:00401158 call ds:ObQueryNameString
這個函式將獲取裝置名, 然後和FILE_OBJECT的FileName構成完整的名字返回(應用層在登錄檔中儲存裝置名+目錄名);
PAGE:00401214 call ds:ObfDereferenceObject
再一次呼叫ObfDereferenceObject, 將物件的引用計數器恢復到先前的值, 防止洩漏;
PAGE:0040122D call ds:IofCompleteRequest
MajorFunction[IRP_MJ_CLOSE*(sizeof PVOID)], 完成操作.
回頭看看這個驅動, 實際上是很經典也很簡單的驅動程式, 實現的功能也很簡單:
返回檔案控制程式碼對應的核心檔案物件的完整名字, 傳給主程式進行檔案"刪除""移動"等操作.
[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 ; 建立執行緒, 網路升級, 線上輔助
//...
.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地址後, 呼叫其中的函式:
1)ZwQuerySystemInformation函式獲得系統當前所以程式的所建立的控制程式碼及其相關資訊;
2)ZwQueryObject獲取控制程式碼所代表物件資訊, 查出目標檔案(裝置名+目錄名);
3)ZwDeleteFile刪除目標檔案;
4)NtLoadDriver載入驅動, 當然還需要後面的登錄檔修改;
===========================================================================
再往下:
.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函式, 發現作用是將驅動資訊寫入登錄檔;
.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
建立一個對話方塊視窗, 顯示所有列舉出的相關程式;
sub_411A59函式將獲取物件檔案的完整名, 並返回, 我們跟進去:
.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
之前是程式和模組遍歷, 找到相關聯的所有程式和模組, OpenProcess開啟需要操作的檔案;
.text:00411F93 call ds:GetCurrentProcess
//...
.text:00411FA2 call ds:DuplicateHandle
獲取本地程式, 將物件程式的控制程式碼複製到本地程式(控制程式碼程式相關);
.text:00411FBB push offset s_?Unlockerdriv ; "\\\\?\\UnlockerDriver5"
.text:00411FC0 call ds:CreateFileW
//...
將控制程式碼傳送給驅動程式, 驅動將返回檔案控制程式碼對應的核心檔案物件的完整名字;
[檔案操作選擇]
——————————————————————
現在退出sub_411A59函式返回主線, 我們走到這裡:
.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這個函式的交叉引用(實際上你不用看引用也很容易就會發現), 會發現:
.text:00412478 sub_412478 proc near //-->GuiControlDeal
這裡是一個對話方塊, 也就是我們右鍵unlocker時產生的介面, 它將生成unlocker的主介面,
也將處理各種使用者操作的訊息, 發給各個子程式去處理;
回到:
.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
呼叫wsprintfW格式化路徑後, 填充SHFILEOPSTRUCT結構, 由SHFileOperationW來重新命名, 完成後呼叫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 ; 迴圈移動目錄下的所有檔案
.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
到這裡就完成了這兩項功能, 不過注意函式裡壓棧時的引數:
.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
這段程式碼的意思是, 如果暫時處理不了使用者請求的檔案操作, 那就在系統重新啟動時實行操作;
========================================================================
接著:
.text:004135DE cmp [ebp+var_9], 0
.text:004135E2 jnz loc_413514
返回, 重新獲取檔案的完整名, 重繪主視窗;
.text:004135E8 call sub_40D51D
.text:004135ED call sub_413B8F
ds:GlobalFree釋放記憶體後, 呼叫了sub_40D51D, 它刪除了登錄檔裡的sys服務, 然後sub_413B8F休眠, 繼續等待操作觸發;
b)
現在我們回到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函式的作用就是實現刪除檔案, 我們看:
.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實現;
.text:00411944 push offset s_Freelibrary ; "FreeLibrary"
//...
.text:00411966 push offset s_Closehandle ; "CloseHandle"
然後通過FreeLibrary來釋放, 呼叫GetModuleHandleW查詢控制程式碼後, 用CloseHandle將其關閉;
注意這一段程式碼:
.text:00411888 push 0Eh
.text:0041188A pop ecx
也就是說迴圈將進行0EH(14)次, FreeLibrary也將執行14次直到dll被釋放;
.text:00411921 call esi ; WriteProcessMemory
.text:00411933 call edi ; VirtualAllocEx
.text:004119A8 call esi ; WriteProcessMemory
.text:004119B5 call ds:CreateRemoteThread
想必您已經注意到了這些程式碼, 對, 思路就是用執行緒注入到目標程式去呼叫FreeLibrary來釋放dll;
現在我們來到loc_4119F8處, 也就是上文提到的如果檔案是exe檔案時的處理方法:
.text:00411A07 call ds:OpenProcess
.text:00411A1B call ds:GetCurrentProcess
.text:00411A28 call ds:DuplicateHandle
.text:00411A3C call ds:CloseHandle
還是先開啟檔案, 然後呼叫DuplicateHandle, 但是這裡傳遞的引數是DUPLICATE_CLOSE_SOURCE標誌, 將強制關閉控制程式碼;
===========================EOF===But to be modified========================
[後語]
——————————————————————
突然發現沒結尾, 被老大姐罵了, 補充一個吧.
unlocker最主要的功能實現都在unlocker.exe和一個.sys驅動裡, 值得關注的是它怎麼實現的關閉控制程式碼和刪除檔案.
關鍵的函式 (部分也是未公開的API) , 比如:
ZwQuerySystemInformation、 ZwQueryObject、ZwDeleteFile、ObReferenceObjectByHandle、ObQueryNameString、 SHFileOperationW、MoveFileExW、DuplicateHandle.
這裡尤其是DuplicateHandle和MoveFileExW比較有意思.
詳細的介面構造沒有去研究, 就是這句:
.text:00412478 sub_412478 proc near //-->GuiControlDeal
跟進去分析就是介面的相關操作了, 太煩, 本人時間有限, 如果有人願意繼續, 記得把結果分享一份給我, 謝謝.
*[參考資料]:
——————————————————————
1. Windows平臺核心級檔案訪問 baiyuanfan
2. unlocker1.8.5 http://ccollomb.free.fr/unlocker
3. Kmd教程-全功能的驅動程式分析 羅雲彬
4. http://forum.sysinternals.com/forum_posts.asp?TID=7974
==========================================================================
另一篇: http://windknown.spaces.live.com/blog/cns!627D8DB6EC5BD4A7!435.entry
April 07
UnLocker帶來的思考
這幾個禮拜已經是忙得焦頭爛額了, 這2天還偏偏和unlocker這個軟體耗上了, 我的睡眠時間啊~~
UnLocker是一款可以用來刪除已被佔用檔案的軟體, http://ccollomb.free.fr/unlocker/
通常的刪除檔案是通過呼叫win32 api的DeleteFile來執行的, 該函式會檢查是否有引用該檔案的控制程式碼, 因此若有程式開啟了該檔案是無法刪除的. 另外說一下該api的執行過程, 開啟檔案-〉設定為刪除-〉關閉檔案, 此時系統會自動刪除該檔案. 設定是通過主控制號為IRP_MJ_SET_INFORMATION的IRP進行的, IO 棧的Parameters.SetFile.FileInformationClass值為 FileDispositionInformation, AssociatedIrp.SystemBuffer指向結構 FILE_DISPOSITION_INFORMATION, 其成員DeleteFile設定為TRUE. 於是乎, 如果在自己的檔案過濾驅動裡過濾該 IRP, 只需要改為FASLE就可以防止檔案被刪除了.
說一下另一種刪除檔案的方法, 也就是unlocker使用的, ntdll匯出的ZwDeleteFile, 該函式的原理沒有細究, 但是同樣也是會檢查控制程式碼的.
因此, 要刪除檔案的關鍵是需要關閉開啟的檔案控制程式碼, 而控制程式碼是與程式相關的. 今天仔細逆向了一下unlocker, 包括主程式和一個sys檔案.
sys檔案的作用是返回檔案控制程式碼對應的核心檔案物件的完整名字, 例如: \Device\HarddiskVolum1\mytest.doc, \Device\HarddiskVolum1就是C:這個卷的裝置名. 大致流程就是呼叫ObReferenceObjectByHandle通過物件獲取FILE_OBJECT物件, 然後通過ObQuerNameString查詢FILE_OBJECT中的DeviceObject指標獲取裝置名, 再與 FILE_OBJECT的FileName構成完整的名字返回.
主程式是通過 ZwQuerySystemInformation 查詢 handle 資訊, 型別為 SystemHandleInformation, 然後呼叫 OpenProcess 獲取目標程式控制程式碼, 再用 DuplicateHandle 複製 Handle 為本程式, 再通過驅動查詢名字, 確定檔名. 一定記住控制程式碼是程式相關的, MSDN裡描述, 例如在DriverEntry中開啟的控制程式碼是在system程式下, 因此在驅動DispatchFunction中不可用, 因為DispatchFunction的程式上下文為與驅動通訊的程式. 當要刪除檔案時, 對於開啟的檔案, unlocker仍然是通過OpenProcess與DuplicateHandle, 只是指定了 DUPLICATE_CLOSE_SOURCE標誌, 真是大開眼界阿, 如此便關閉了控制程式碼, 高. 而對於dll就沒有那麼方便了, unlocker會用執行緒注入到目標程式去呼叫FreeLibrary來釋放dll, 因此可能不夠穩定, 而且逆向時發現注入的程式碼只嘗試0x10次Free, 呵呵我load20次自己就可以搞定撈.
然後看MSDN過程中發現了另一個nb函式, SetHandleInformation, 設定這個標誌 HANDLE_FLAG_PROTECT_FROM_CLOSE , 那麼 unlocker 呼叫 DuplicateHandle 也關不掉你的控制程式碼了, 試驗了一下, 果然說刪不了撈, hoho
整這些鳥東西弄了我一天時間, 唉, 許久不逆向, 生疏了不少~~而且因為在虛擬機器裡除錯, 老破機器這個卡啊~~睡覺去!
順便一提, 今天發現國內搞安全的兄弟們共享精神不如國外阿, 唉, 環境使然, 人嗎首先還是要自己生存的. 想想自己也快畢業了, 有點迷惘, 畢業了幹啥工作去捏……生存阿生存
============================================================================
第三篇: http://blog.vckbase.com/windowssky/archive/2007/04/18/25565.html
小議檔案保護和鎖定技術
近1年來互連網上的木馬越來越多, 有的還刪除不掉, 要切換到dos才可行, 如: CNNC, 3721等, 實現技術也五花八門, 但就檔案不可刪除的實現技術可分三類:
1 Attach file system; 這種技術和Filemon/sfilter查不多, 就是掛一個 filter 驅動到 fs 上, 其他函式都是 passthru 下去, 只處理 IRP_MJ_SET_INFORMATION, 當發現有刪除需保護檔案的 IRP, 就
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
根本不讓 Fs 去處理, 從而達到檔案不可刪除的作用!
有什麼方法可以刪除呢? 自己寫個驅動自己填充 irp 包 (見 OSR 文件 Rolling Your Own) , 直接傳送 IRP 到 File System Device 上去就 ok 啦!
2 修改 file system 的 dispatch 函式表; 首先得到 Fs 的 DriverObject (根據驅動名得到驅動裝置物件 (ObReferenceObjectByName(IoDriverObjectType))), pDriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = MySetInformation, 然後在 MySetInformation 中再呼叫原來的排程函式, 類似於 HookApi; 發現有刪除需保護檔案的 IRP, 就直接 IoCompleteRequest, 根本不讓原來的 FsSetInformation 處理!
有什麼方法可以刪除呢? 自己寫個驅動來修復 Fs 的 dispatch 函式表, 讀 Fs 的原始檔案, 根據 PE 檔案得到 Fs 的 Entrypoint, dispatch 函式表的填充都在 EntryPoint 後面, 我們可以根據 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 實現的!
有什麼方法可以刪除呢?
step1: 通過 QuerySystemInformation(SystemHandleInformation) 得到當前系統的所有控制程式碼資訊
step2: 遍歷當前所有程式, 根據程式 ID, 得到此程式開啟的所有控制程式碼資訊
Step3: 把控制程式碼傳送給我們的驅動程式, 驅動程式根據 ObQueryNameString 得到控制程式碼的路徑資訊, 然後再傳給我們的應用程式
Step4: 如果是我們要刪除檔案的路徑, 應用程式呼叫 DuplicateHandle(DUPLICATE_CLOSE_SOURCE), 控制程式碼被關閉了, 現在可以刪除檔案了!
注: QuerySystemInformation 的使用說明見 The Undocumented Functions, 或者 http://undocumented.ntinternals.net, 第三種解除檔案鎖定的方法是我反彙編 Unlocker 軟體學習到的
最後感謝 7cat 的幫助!
====================================================================
以下為原創
free2000fly 按:
unlocker 軟體真是搞的百轉千回, 還用到了驅動, 其實不必要, 在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;
}
其中函式 GetFilePathFromHandle 的實現請看我的 另一篇博文 "
從檔案控制程式碼得到檔案路徑的函式 "{
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;
}
PS. 最近幾天試圖實現列舉已開啟所有檔案的功能, 我卻一而再,再而三的在函式 NtQueryObject 和 ZwQueryInformationFile 上遭遇掛起的現象, 這兩個函式在被呼叫後有可能不再返回, 軟體失去響應, 這是相當要命的事, 後來的解決方法是, 用一個獨立的執行緒來呼叫這兩個函式, 如果超時則立即返回, 但在需要講求效率的場合這個解決方法行不通. 查遍網路才發現這可能算是 Windows 系列作業系統的一個 BUG. 因此, 最後, 還是回到了必須使用驅動的老路上去了. 2009.12.05
參考:
http://www.codeproject.com/KB/shell/OpenedFileFinder.aspx
http://www.codeproject.com/Articles/35202/GetFinalPathNameByHandle-API-Hangs.aspx
http://www.codeguru.com/forum/showthread.php?t=359606
http://bbs.pediy.com/showthread.php?t=60190
最後附上csdn論壇上的一個討論帖:http://bbs.csdn.net/topics/230089113
相關文章
- C#讀取被程式佔用的檔案C#
- win10怎樣檢視電腦檔案被程式佔用_win10檔案被其他程式佔用的解決方法Win10
- 電腦檔案被佔用無法刪除?Win10系統檔案被佔用無法刪除的解決方法Win10
- tgp檔案佔用怎麼回事win10_win10系統提示tgp檔案被佔用如何解決Win10
- win10檔案被佔用無法刪除怎麼辦_win10如何解除檔案佔用Win10
- win10資料夾被佔用無法刪除怎麼辦_win10檔案被佔用刪不了的解決方法Win10
- Windows10系統下檔案被佔用無法刪除的解決方法Windows
- windows刪除檔案的批處理操作Windows
- 用JDOM操作XML檔案XML
- Python批處理:檔案操作Python
- 被誤刪的檔案正確處理方法,快速找回誤刪的檔案
- Solaris中無法找到硬碟佔用的檔案硬碟
- windows 80埠被佔用Windows
- IDEAweb專案報錯8080埠被佔用IdeaWeb
- 443和80埠被佔用win10如何解決_443和80埠被佔用win10怎麼處理Win10
- 07 Windows批處理之檔案操作Windows
- 檔案系統被破壞時的處理方法(轉)
- 關閉tomact被佔用的程式Mac
- C檔案與檔案的操作
- ORACLE UTL_FILE檔案包的應用,檔案I/O操作Oracle
- Valet 80 埠被佔用 It works
- jboss 8083 埠被佔用
- 搬運工:temp表空間被過多佔用處理方法
- AIX檢視某個埠被哪個程式佔用並處理AI
- 測試環境控制檔案被誤刪的故障處理
- linux查詢佔用空間較大的檔案Linux
- windows 80埠被佔用的解決方法Windows
- win10埠被佔用如何檢視?win10快速檢視被佔用埠的方法教程Win10
- windows檢視檔案/夾被哪個程序佔用以及解除方法Windows
- GC機制+字元編碼+檔案操作GC字元
- win10 datastore目錄下檔案不能刪除提示被romote access佔用怎麼辦Win10AST
- Python中檔案I/O高效操作處理的技巧Python
- 用SpringMVC來簡單的操作Excel檔案SpringMVCExcel
- Cmd命令 檢視埠被佔用
- Java檢測埠是否被佔用Java
- 解決linux埠被佔用Linux
- 使用graphicsmagick時gm命令被佔用
- tomcat埠被佔用異常Tomcat