x64驅動基礎教程 32
相信大家在 WINDOWS 系統上都遇到過想刪除一個檔案時卻被提示“無法刪除”的情況。我在初學電腦時遇到這種情況只能自認倒黴,重啟之後再刪除檔案。學習了 WINDOWS 只是之後知道了在正常情況下(即不算檔案被檔案過濾驅動或者各種 API HOOK 保護的情況)遇到這個提示只有兩種可能性:
1.你沒有刪除這個檔案的許可權;
2.有控制代碼在某個程序裡被打開了。
解決第一種情況不需要程式設計,只要把以下文字儲存成*.reg 檔案並新增到登錄檔即可:
Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\takeownership] @="Take ownership" "HasLUAShield"="" "NoWorkingDirectory"="" [HKEY_CLASSES_ROOT\*\shell\takeownership\command] @="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" "IsolatedCommand"="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" [HKEY_CLASSES_ROOT\exefile\shell\takeownership] @="Take ownership" "HasLUAShield"="" "NoWorkingDirectory"="" [HKEY_CLASSES_ROOT\exefile\shell\takeownership\command] @="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" "IsolatedCommand"="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" [HKEY_CLASSES_ROOT\dllfile\shell\takeownership] @="Take ownership" "HasLUAShield"="" "NoWorkingDirectory"="" [HKEY_CLASSES_ROOT\dllfile\shell\takeownership\command] @="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" "IsolatedCommand"="cmd.exe /c takeown /f \"%1\" && icacls \"%1\" /grant administrators:F" [HKEY_CLASSES_ROOT\Directory\shell\takeownership] @="Take ownership" "HasLUAShield"="" "NoWorkingDirectory"="" [HKEY_CLASSES_ROOT\Directory\shell\takeownership\command] @="cmd.exe /c takeown /f \"%1\" /r /d y && icacls \"%1\" /grant administrators:F /t" "IsolatedCommand"="cmd.exe /c takeown /f \"%1\" /r /d y && icacls \"%1\" /grant administrators:F /t"
當你要刪除一個檔案而遇到“無法刪除需要許可權”的提示時,只要對著檔案按下右鍵,選擇『Take ownership』再刪除檔案即可。但是第二種情況就要通過程式設計解決了,這也就是本文的核心內容。要刪除被開啟的檔案,比較好的方法是關閉此檔案在其它程序裡的控制代碼(直接解析檔案系統修改標誌位也可以,不過這個難度太大,而且不通用)。總體來說,步驟分為以下兩步:
1.呼叫 ZwQuerySystemInformation 的 16 功能號來列舉系統裡的控制代碼
2.開啟擁有此控制代碼的程序並把此控制代碼複製到自己的程序
3.用 ZwQueryObject 查詢控制代碼的型別和名稱
4.如果發現此控制代碼的型別是檔案控制代碼, 名稱和被鎖定的檔案一致,就關閉此控制代碼
5.重複 2、 3、 4 步,直到遍歷完系統裡所有的控制代碼
程式碼如下:
VOID CloseFileHandle(char *szFileName) { PVOID Buffer; ULONG BufferSize = 0x20000, rtl = 0; NTSTATUS Status, qost = 0; NTSTATUS ns = STATUS_SUCCESS; ULONG64 i = 0; ULONG64 qwHandleCount; SYSTEM_HANDLE_TABLE_ENTRY_INFO *p; OBJECT_BASIC_INFORMATION BasicInfo; POBJECT_NAME_INFORMATION pNameInfo; ULONG ulProcessID; HANDLE hProcess; HANDLE hHandle; HANDLE hDupObj; CLIENT_ID cid; OBJECT_ATTRIBUTES oa; CHAR szFile[260] = { 0 }; Buffer = kmalloc(BufferSize); memset(Buffer, 0, BufferSize); Status = ZwQuerySystemInformation(16, Buffer, BufferSize, 0); //SystemHandleInformation while (Status == 0xC0000004) //STATUS_INFO_LENGTH_MISMATCH { kfree(Buffer); BufferSize = BufferSize * 2; Buffer = kmalloc(BufferSize); memset(Buffer, 0, BufferSize); Status = ZwQuerySystemInformation(16, Buffer, BufferSize, 0); } if (!NT_SUCCESS(Status)) return; qwHandleCount = ((SYSTEM_HANDLE_INFORMATION *)Buffer)->NumberOfHandles; p = (SYSTEM_HANDLE_TABLE_ENTRY_INFO *)((SYSTEM_HANDLE_INFORMATION *)Buffer)->Handles; //clear array memset(HandleInfo, 0, 1024 * sizeof(HANDLE_INFO)); //ENUM HANDLE PROC for (i = 0; i < qwHandleCount; i++) { ulProcessID = (ULONG)p[i].UniqueProcessId; cid.UniqueProcess = (HANDLE)ulProcessID; cid.UniqueThread = (HANDLE)0; hHandle = (HANDLE)p[i].HandleValue; InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL); ns = ZwOpenProcess(&hProcess, PROCESS_DUP_HANDLE, &oa, &cid); if (!NT_SUCCESS(ns)) { KdPrint(("ZwOpenProcess : Fail ")); continue; } ns = ZwDuplicateObject(hProcess, hHandle, NtCurrentProcess(), &hDupObj,PROCESS_ALL_ACCESS, 0, DUPLICATE_SAME_ACCESS); if (!NT_SUCCESS(ns)) { KdPrint(("ZwDuplicateObject : Fail ")); continue; } //get basic information ZwQueryObject(hDupObj, ObjectBasicInformation, &BasicInfo,sizeof(OBJECT_BASIC_INFORMATION), NULL); //get name information pNameInfo = ExAllocatePoolWithTag(PagedPool, 1024, 'ONON'); RtlZeroMemory(pNameInfo, 1024); qost = ZwQueryObject(hDupObj, ObjectNameInformation, pNameInfo, 1024, &rtl); //get information and close handle UnicodeStringToCharArray(&(pNameInfo->Name), szFile); ExFreePool(pNameInfo); ZwClose(hDupObj); ZwClose(hProcess); if (!strstr(_strlwr(szFile), szFileName))//這裡只判斷了檔名 { PEPROCESS ep = LookupProcess((HANDLE)(p[i].UniqueProcessId)); ForceCloseHandle(ep, p[i].HandleValue); ObDereferenceObject(ep); } } }
接下來說說如何關閉其它程序裡的控制代碼:
1.用 KeStackAttachProcess“依附”到目標程序
2.用 ObSetHandleAttributes 設定控制代碼為“可以關閉”
3.用 ZwClose 關閉控制代碼
4.用 KeUnstackDetachProcess 脫離“依附”目標程序
程式碼如下:
VOID ForceCloseHandle(PEPROCESS Process, ULONG64 HandleValue) { HANDLE h; KAPC_STATE ks; OBJECT_HANDLE_FLAG_INFORMATION ohfi; if (Process == NULL) return; if (!MmIsAddressValid(Process)) return; KeStackAttachProcess(Process, &ks); h = (HANDLE)HandleValue; ohfi.Inherit = 0; ohfi.ProtectFromClose = 0; ObSetHandleAttributes(h, &ohfi, KernelMode); ZwClose(h); KeUnstackDetachProcess(&ks); }
1.用『lockfile.exe』鎖定檔案『lockfile.xxx』
2.刪除『lockfile.xxx』,會提示無法刪除
3.載入『UnlockFile.sys』
4.再次刪除『lockfile.xxx』則會成功
相關程式在 WIN7 X64 和 WIN8 X64 上測試通過(執行任何程式時都要以管理員許可權執行)。在 WIN32 上可以用相同的方法,但是結構體的定義並不相同,對應的結構體需要你自己去尋找。