驅動開發:核心解鎖與強刪檔案

2023-06-15 12:06:03

在某些時候我們的系統中會出現一些無法被正常刪除的檔案,如果想要強制刪除則需要在驅動層面對其進行解鎖後才可刪掉,而所謂的解鎖其實就是釋放掉檔案描述符(控制程式碼表)佔用,檔案解鎖的核心原理是通過呼叫ObSetHandleAttributes函數將特定控制程式碼設定為可關閉狀態,然後在呼叫ZwClose將其檔案關閉,強制刪除則是通過ObReferenceObjectByHandle在物件上提供相應的許可權後直接呼叫ZwDeleteFile將其刪除,雖此類程式碼較為普遍,但作為揭祕ARK工具來說也必須要將其分析並講解一下。

首先封裝lyshark.h通用標頭檔案,並定義好我們所需要的結構體,以及特定未匯出函數的宣告,此處的定義部分是微軟官方的規範,如果不懂結構具體含義可自行去微軟官方查閱參考資料。

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include <ntddk.h>

// -------------------------------------------------------
// 參照微軟結構
// -------------------------------------------------------
// 結構體定義
typedef struct _HANDLE_INFO
{
	UCHAR ObjectTypeIndex;
	UCHAR HandleAttributes;
	USHORT  HandleValue;
	ULONG GrantedAccess;
	ULONG64 Object;
	UCHAR Name[256];
} HANDLE_INFO, *PHANDLE_INFO;

HANDLE_INFO HandleInfo[1024];

typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
{
	USHORT  UniqueProcessId;
	USHORT  CreatorBackTraceIndex;
	UCHAR ObjectTypeIndex;
	UCHAR HandleAttributes;
	USHORT  HandleValue;
	PVOID Object;
	ULONG GrantedAccess;
} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;

typedef struct _SYSTEM_HANDLE_INFORMATION
{
	ULONG64 NumberOfHandles;
	SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

typedef enum _OBJECT_INFORMATION_CLASS
{
	ObjectBasicInformation,
	ObjectNameInformation,
	ObjectTypeInformation,
	ObjectAllInformation,
	ObjectDataInformation
} OBJECT_INFORMATION_CLASS, *POBJECT_INFORMATION_CLASS;

typedef struct _OBJECT_BASIC_INFORMATION
{
	ULONG                   Attributes;
	ACCESS_MASK             DesiredAccess;
	ULONG                   HandleCount;
	ULONG                   ReferenceCount;
	ULONG                   PagedPoolUsage;
	ULONG                   NonPagedPoolUsage;
	ULONG                   Reserved[3];
	ULONG                   NameInformationLength;
	ULONG                   TypeInformationLength;
	ULONG                   SecurityDescriptorLength;
	LARGE_INTEGER           CreationTime;
} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;

typedef struct _OBJECT_TYPE_INFORMATION
{
	UNICODE_STRING          TypeName;
	ULONG                   TotalNumberOfHandles;
	ULONG                   TotalNumberOfObjects;
	WCHAR                   Unused1[8];
	ULONG                   HighWaterNumberOfHandles;
	ULONG                   HighWaterNumberOfObjects;
	WCHAR                   Unused2[8];
	ACCESS_MASK             InvalidAttributes;
	GENERIC_MAPPING         GenericMapping;
	ACCESS_MASK             ValidAttributes;
	BOOLEAN                 SecurityRequired;
	BOOLEAN                 MaintainHandleCount;
	USHORT                  MaintainTypeList;
	POOL_TYPE               PoolType;
	ULONG                   DefaultPagedPoolCharge;
	ULONG                   DefaultNonPagedPoolCharge;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;

typedef struct _KAPC_STATE
{
	LIST_ENTRY ApcListHead[2];
	PVOID Process;
	BOOLEAN KernelApcInProgress;
	BOOLEAN KernelApcPending;
	BOOLEAN UserApcPending;
}KAPC_STATE, *PKAPC_STATE;

typedef struct _OBJECT_HANDLE_FLAG_INFORMATION
{
	BOOLEAN Inherit;
	BOOLEAN ProtectFromClose;
}OBJECT_HANDLE_FLAG_INFORMATION, *POBJECT_HANDLE_FLAG_INFORMATION;

typedef struct _LDR_DATA_TABLE_ENTRY64
{
  LIST_ENTRY64 InLoadOrderLinks;
  LIST_ENTRY64 InMemoryOrderLinks;
  LIST_ENTRY64 InInitializationOrderLinks;
  ULONG64 DllBase;
  ULONG64 EntryPoint;
  ULONG64 SizeOfImage;
  UNICODE_STRING FullDllName;
  UNICODE_STRING BaseDllName;
  ULONG Flags;
  USHORT LoadCount;
  USHORT TlsIndex;
  LIST_ENTRY64 HashLinks;
  ULONG64 SectionPointer;
  ULONG64 CheckSum;
  ULONG64 TimeDateStamp;
  ULONG64 LoadedImports;
  ULONG64 EntryPointActivationContext;
  ULONG64 PatchInformation;
  LIST_ENTRY64 ForwarderLinks;
  LIST_ENTRY64 ServiceTagLinks;
  LIST_ENTRY64 StaticLinks;
  ULONG64 ContextInformation;
  ULONG64 OriginalBase;
  LARGE_INTEGER LoadTime;
} LDR_DATA_TABLE_ENTRY64, *PLDR_DATA_TABLE_ENTRY64;

// -------------------------------------------------------
// 匯出函數定義
// -------------------------------------------------------

NTKERNELAPI NTSTATUS ObSetHandleAttributes
(
	HANDLE Handle,
	POBJECT_HANDLE_FLAG_INFORMATION HandleFlags,
	KPROCESSOR_MODE PreviousMode
);

NTKERNELAPI VOID KeStackAttachProcess
(
	PEPROCESS PROCESS,
	PKAPC_STATE ApcState
);

NTKERNELAPI VOID KeUnstackDetachProcess
(
	PKAPC_STATE ApcState
);

NTKERNELAPI NTSTATUS PsLookupProcessByProcessId
(
	IN HANDLE ProcessId,
	OUT PEPROCESS *Process
);

NTSYSAPI NTSTATUS NTAPI ZwQueryObject
(
	HANDLE  Handle,
	ULONG ObjectInformationClass,
	PVOID ObjectInformation,
	ULONG ObjectInformationLength,
	PULONG  ReturnLength OPTIONAL
);

NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation
(
	ULONG SystemInformationClass,
	PVOID SystemInformation,
	ULONG SystemInformationLength,
	PULONG  ReturnLength
);

NTSYSAPI NTSTATUS NTAPI ZwDuplicateObject
(
	HANDLE    SourceProcessHandle,
	HANDLE    SourceHandle,
	HANDLE    TargetProcessHandle OPTIONAL,
	PHANDLE   TargetHandle OPTIONAL,
	ACCESS_MASK DesiredAccess,
	ULONG   HandleAttributes,
	ULONG   Options
);

NTSYSAPI NTSTATUS NTAPI ZwOpenProcess
(
	PHANDLE       ProcessHandle,
	ACCESS_MASK     AccessMask,
	POBJECT_ATTRIBUTES  ObjectAttributes,
	PCLIENT_ID      ClientId
);

#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004

接下來將具體分析如何解鎖指定檔案的控制程式碼表,強制解鎖檔案控制程式碼表,大體步驟如下所示。

  • 1.首先呼叫ZwQuerySystemInformation的16功能號SystemHandleInformation來列舉系統裡的控制程式碼。
  • 2.通過ZwOpenProcess()開啟擁有此控制程式碼的程序,通過ZwDuplicateObject建立一個新的控制程式碼,並把此控制程式碼複製到自己的程序內。
  • 3.通過呼叫ZwQueryObject並傳入ObjectNameInformation查詢到控制程式碼的名稱,並將其放入到pNameInfo變數內。
  • 4.迴圈這個過程並在每次迴圈中通過strstr()判斷是否是我們需要關閉的檔名,如果是則呼叫ForceCloseHandle強制解除佔用。
  • 5.此時會進入到ForceCloseHandle流程內,通過KeStackAttachProcess附加到程序內,並呼叫ObSetHandleAttributes將控制程式碼設定為可關閉狀態。
  • 6.最後呼叫ZwClose關閉控制程式碼佔用,並KeUnstackDetachProcess脫離該程序。

實現程式碼流程非常容易理解,此類功能也沒有其他別的寫法了一般也就這種,但是還是需要注意這些內建函數的引數傳遞,這其中ZwQuerySystemInformation()一般用於查詢系統程序等資訊居多,但通過對SystemInformationClass變數傳入不同的引數可實現對不同結構的列舉工作,具體的定義可去查閱微軟定義規範;

NTSTATUS WINAPI ZwQuerySystemInformation(
  _In_      SYSTEM_INFORMATION_CLASS SystemInformationClass,      // 傳入不同引數則輸出不同內容
  _Inout_   PVOID                    SystemInformation,           // 輸出資料
  _In_      ULONG                    SystemInformationLength,     // 長度
  _Out_opt_ PULONG                   ReturnLength                 // 返回長度
);

函數ZwDuplicateObject(),該函數例程用於建立一個控制程式碼,該控制程式碼是指定源控制程式碼的副本,此函數的具體宣告部分如下;

NTSYSAPI NTSTATUS ZwDuplicateObject(
  [in]            HANDLE      SourceProcessHandle,    // 要複製的控制程式碼的源程序的控制程式碼。
  [in]            HANDLE      SourceHandle,           // 要複製的控制程式碼。
  [in, optional]  HANDLE      TargetProcessHandle,    // 要接收新控制程式碼的目標程序的控制程式碼。
  [out, optional] PHANDLE     TargetHandle,           // 指向例程寫入新重複控制程式碼的 HANDLE 變數的指標。
  [in]            ACCESS_MASK DesiredAccess,          // 一個ACCESS_MASK值,該值指定新控制程式碼的所需存取。
  [in]            ULONG       HandleAttributes,       // 一個 ULONG,指定新控制程式碼的所需屬性。 
  [in]            ULONG       Options                 // 一組標誌,用於控制重複操作的行為。
);

函數ZwQueryObject()其可以返回特定的一個物件引數,此函數尤為注意第二個引數,當下我們傳入的是ObjectNameInformation則代表需要取出物件名稱,而如果使用ObjectTypeInformation則是返回物件型別,該函數微軟定義如下所示;

NTSYSAPI NTSTATUS ZwQueryObject(
  [in, optional]  HANDLE                   Handle,                        // 要獲取相關資訊的物件控制程式碼。
  [in]            OBJECT_INFORMATION_CLASS ObjectInformationClass,        // 該值確定 ObjectInformation 緩衝區中返回的資訊的型別。
  [out, optional] PVOID                    ObjectInformation,             // 指向接收請求資訊的呼叫方分配緩衝區的指標。
  [in]            ULONG                    ObjectInformationLength,       // 指定 ObjectInformation 緩衝區的大小(以位元組為單位)。
  [out, optional] PULONG                   ReturnLength                   // 指向接收所請求金鑰資訊的大小(以位元組為單位)的變數的指標。 
);

而對於ForceCloseHandle函數中,需要注意的只有一個ObSetHandleAttributes該函數微軟並沒有格式化檔案,但是也並不影響我們使用它,如下最需要注意的是PreviousMode變數,該變數如果傳入KernelMode則是核心模式,傳入UserMode則代表使用者模式,為了許可權最大化此處需要寫入KernelMode模式;

NTSYSAPI NTSTATUS ObSetHandleAttributes(
  HANDLE Handle,                                        // 傳入檔案控制程式碼
  POBJECT_HANDLE_FLAG_INFORMATION HandleFlags,          // OBJECT_HANDLE_FLAG_INFORMATION標誌
  KPROCESSOR_MODE PreviousMode                          // 指定執行級別KernelMode
)

實現檔案解鎖,該驅動程式不僅可用於解鎖應用層程式,也可用於解鎖驅動,如下程式碼中我們解鎖pagefile.sys程式的控制程式碼佔用;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include "lyshark.h"

// 根據PID得到EProcess
PEPROCESS LookupProcess(HANDLE Pid)
{
	PEPROCESS eprocess = NULL;
	if (NT_SUCCESS(PsLookupProcessByProcessId(Pid, &eprocess)))
		return eprocess;
	else
		return NULL;
}

// 將uncode轉為char*
VOID UnicodeStringToCharArray(PUNICODE_STRING dst, char *src)
{
	ANSI_STRING string;
	if (dst->Length > 260)
	{
		return;
	}

	RtlUnicodeStringToAnsiString(&string, dst, TRUE);
	strcpy(src, string.Buffer);
	RtlFreeAnsiString(&string);
}

// 強制關閉控制程式碼
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);

	DbgPrint("EP = [ %d ] | HandleValue = [ %d ] 程序控制程式碼已被關閉 \n",Process,HandleValue);
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驅動解除安裝成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark.com \n");

	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 = ExAllocatePoolWithTag(NonPagedPool, BufferSize, "LyShark");
	memset(Buffer, 0, BufferSize);

	// SystemHandleInformation
	Status = ZwQuerySystemInformation(16, Buffer, BufferSize, 0);
	while (Status == STATUS_INFO_LENGTH_MISMATCH)
	{
		ExFreePool(Buffer);
		BufferSize = BufferSize * 2;
		Buffer = ExAllocatePoolWithTag(NonPagedPool, BufferSize, "LyShark");
		memset(Buffer, 0, BufferSize);
		Status = ZwQuerySystemInformation(16, Buffer, BufferSize, 0);
	}

	if (!NT_SUCCESS(Status))
	{
		return;
	}

	// 獲取系統中所有控制程式碼表
	qwHandleCount = ((SYSTEM_HANDLE_INFORMATION *)Buffer)->NumberOfHandles;

	// 得到控制程式碼表的SYSTEM_HANDLE_TABLE_ENTRY_INFO結構
	p = (SYSTEM_HANDLE_TABLE_ENTRY_INFO *)((SYSTEM_HANDLE_INFORMATION *)Buffer)->Handles;

	// 初始化HandleInfo陣列
	memset(HandleInfo, 0, 1024 * sizeof(HANDLE_INFO));

	// 開始列舉控制程式碼
	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))
		{
			continue;
		}

		// 建立一個控制程式碼,該控制程式碼是指定源控制程式碼的副本。
		ns = ZwDuplicateObject(hProcess, hHandle, NtCurrentProcess(), &hDupObj, PROCESS_ALL_ACCESS, 0, DUPLICATE_SAME_ACCESS);
		if (!NT_SUCCESS(ns))
		{
			continue;
		}

		// 查詢物件控制程式碼的資訊並放入BasicInfo
		ZwQueryObject(hDupObj, ObjectBasicInformation, &BasicInfo, sizeof(OBJECT_BASIC_INFORMATION), NULL);

		// 得到物件控制程式碼的名字資訊
		pNameInfo = ExAllocatePool(PagedPool, 1024);
		RtlZeroMemory(pNameInfo, 1024);

		// 查詢物件資訊中的物件名,並將該資訊儲存到pNameInfo中
		qost = ZwQueryObject(hDupObj, ObjectNameInformation, pNameInfo, 1024, &rtl);

		// 獲取資訊並關閉控制程式碼
		UnicodeStringToCharArray(&(pNameInfo->Name), szFile);
		ExFreePool(pNameInfo);
		ZwClose(hDupObj);
		ZwClose(hProcess);

		// 檢查控制程式碼是否被佔用,如果被佔用則關閉檔案並刪除
		if (strstr(_strlwr(szFile), "pagefile.sys"))
		{
			PEPROCESS ep = LookupProcess((HANDLE)(p[i].UniqueProcessId));

			// 佔用則強制關閉
			ForceCloseHandle(ep, p[i].HandleValue);
			ObDereferenceObject(ep);
		}
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

編譯並執行這段驅動程式,則會將pagefile.sys核心檔案進行解鎖,輸出效果如下所示;

聊完了檔案解鎖功能,接下來將繼續探討如何實現強制刪除檔案的功能,檔案強制刪除的關鍵在於ObReferenceObjectByHandle函數,該函數可在物件控制程式碼上提供存取驗證,並授予存取許可權返回指向物件的正文的相應指標,當有了指定的許可權以後則可以直接呼叫ZwDeleteFile()將檔案強制刪除。

在呼叫初始化控制程式碼前提之下需要先呼叫KeGetCurrentIrql()函數,該函數返回當前IRQL級別,那麼什麼是IRQL呢?

Windows中系統中斷請求(IRQ)可分為兩種,一種外部中斷(硬體中斷),一種是軟體中斷(INT3),微軟將中斷的概念進行了擴充套件,提出了中斷請求級別(IRQL)的概念,其中就規定了32箇中斷請求級別。

  • 其中0-2級為軟中斷,順序由小到大分別是:PASSIVE_LEVEL,APC_LEVEL,DISPATCH_LEVEL
  • 其中27-31為硬中斷,順序由小到大分別是:PROFILE_LEVEL,CLOCK1_LEVEL,CLOCK2_LEVEL,IPI_LEVEL,POWER_LEVEL,HIGH_LEVEL

我們的程式碼中開頭部分KeGetCurrentIrql() > PASSIVE_LEVEL則是在判斷當前的級別不大於0級,也就是說必須要大於0才可以繼續執行。

好開始步入正題,函數ObReferenceObjectByHandle需要傳入一個檔案控制程式碼,而此控制程式碼需要通過IoCreateFileSpecifyDeviceObjectHint對其進行初始化,檔案系統篩選器驅動程式使用IoCreateFileSpecifyDeviceObjectHint函數建立,該函數的微軟完整定義如下所示;

NTSTATUS IoCreateFileSpecifyDeviceObjectHint(
  [out]          PHANDLE            FileHandle,               // 指向變數的指標,該變數接收檔案物件的控制程式碼。
  [in]           ACCESS_MASK        DesiredAccess,            // 標誌的位掩碼,指定呼叫方需要對檔案或目錄的存取型別。
  [in]           POBJECT_ATTRIBUTES ObjectAttributes,         // 指向已由 InitializeObjectAttributes 例程初始化的OBJECT_ATTRIBUTES結構的指標。
  [out]          PIO_STATUS_BLOCK   IoStatusBlock,            // 指向 IO_STATUS_BLOCK 結構的指標,該結構接收最終完成狀態和有關所請求操作的資訊。
  [in, optional] PLARGE_INTEGER     AllocationSize,           // 指定檔案的初始分配大小(以位元組為單位)。
  [in]           ULONG              FileAttributes,           // 僅當檔案建立、取代或在某些情況下被覆蓋時,才會應用顯式指定的屬性。
  [in]           ULONG              ShareAccess,              // 指定呼叫方希望的對檔案的共用存取型別(為零或 1,或以下標誌的組合)。
  [in]           ULONG              Disposition,              // 指定一個值,該值確定要執行的操作,具體取決於檔案是否已存在。
  [in]           ULONG              CreateOptions,            // 指定要在建立或開啟檔案時應用的選項。
  [in, optional] PVOID              EaBuffer,                 // 指向呼叫方提供的 FILE_FULL_EA_INFORMATION結構化緩衝區的指標。
  [in]           ULONG              EaLength,                 // EaBuffer 的長度(以位元組為單位)。
  [in]           CREATE_FILE_TYPE   CreateFileType,           // 驅動程式必須將此引數設定為 CreateFileTypeNone。
  [in, optional] PVOID              InternalParameters,       // 驅動程式必須將此引數設定為 NULL。
  [in]           ULONG              Options,                  // 指定要在建立請求期間使用的選項。
  [in, optional] PVOID              DeviceObject              // 指向要向其傳送建立請求的裝置物件的指標。
);

當呼叫IoCreateFileSpecifyDeviceObjectHint()函數完成初始化並建立裝置後,則下一步就是呼叫ObReferenceObjectByHandle()並傳入初始化好的裝置控制程式碼到Handle引數上,

NTSTATUS ObReferenceObjectByHandle(
  [in]            HANDLE                     Handle,             // 指定物件的開啟控制程式碼。
  [in]            ACCESS_MASK                DesiredAccess,      // 指定對物件的請求存取型別。
  [in, optional]  POBJECT_TYPE               ObjectType,         // 指向物件型別的指標。
  [in]            KPROCESSOR_MODE            AccessMode,         // 指定要用於存取檢查的存取模式。 它必須是 UserMode 或 KernelMode。
  [out]           PVOID                      *Object,            // 指向接收指向物件正文的指標的變數的指標。
  [out, optional] POBJECT_HANDLE_INFORMATION HandleInformation   // 驅動程式將此設定為 NULL。
);

通過呼叫如上兩個函數將許可權設定好以後,我們再手動將ImageSectionObject也就是映像節物件填充為0,然後再將DeleteAccess刪除許可權位開啟,最後呼叫ZwDeleteFile()函數即可實現強制刪除檔案的效果,其核心程式碼如下所示;

// 署名權
// right to sign one's name on a piece of work
// PowerBy: LyShark
// Email: [email protected]

#include "lyshark.h"

// 強制刪除檔案
BOOLEAN ForceDeleteFile(UNICODE_STRING pwzFileName)
{
	PEPROCESS pCurEprocess = NULL;
	KAPC_STATE kapc = { 0 };
	OBJECT_ATTRIBUTES fileOb;
	HANDLE hFile = NULL;
	NTSTATUS status = STATUS_UNSUCCESSFUL;
	IO_STATUS_BLOCK iosta;
	PDEVICE_OBJECT DeviceObject = NULL;
	PVOID pHandleFileObject = NULL;


	// 判斷中斷等級不大於0
	if (KeGetCurrentIrql() > PASSIVE_LEVEL)
	{
		return FALSE;
	}
	if (pwzFileName.Buffer == NULL || pwzFileName.Length <= 0)
	{
		return FALSE;
	}

	__try
	{
		// 讀取當前程序的EProcess
		pCurEprocess = IoGetCurrentProcess();

		// 附加程序
		KeStackAttachProcess(pCurEprocess, &kapc);

		// 初始化結構
		InitializeObjectAttributes(&fileOb, &pwzFileName, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);

		// 檔案系統篩選器驅動程式 僅向指定裝置物件下面的篩選器和檔案系統傳送建立請求。
		status = IoCreateFileSpecifyDeviceObjectHint(&hFile,
			SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_READ_ATTRIBUTES | FILE_READ_DATA,
			&fileOb,
			&iosta,
			NULL,
			0,
			FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
			FILE_OPEN,
			FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
			0,
			0,
			CreateFileTypeNone,
			0,
			IO_IGNORE_SHARE_ACCESS_CHECK,
			DeviceObject);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		// 在物件控制程式碼上提供存取驗證,如果可以授予存取許可權,則返回指向物件的正文的相應指標。
		status = ObReferenceObjectByHandle(hFile, 0, 0, 0, &pHandleFileObject, 0);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}

		// 映象節物件設定為0
		((PFILE_OBJECT)(pHandleFileObject))->SectionObjectPointer->ImageSectionObject = 0;

		// 刪除許可權開啟
		((PFILE_OBJECT)(pHandleFileObject))->DeleteAccess = 1;

		// 呼叫刪除檔案API
		status = ZwDeleteFile(&fileOb);
		if (!NT_SUCCESS(status))
		{
			return FALSE;
		}
	}

	_finally
	{
		if (pHandleFileObject != NULL)
		{
			ObDereferenceObject(pHandleFileObject);
			pHandleFileObject = NULL;
		}
		KeUnstackDetachProcess(&kapc);

		if (hFile != NULL || hFile != (PVOID)-1)
		{
			ZwClose(hFile);
			hFile = (PVOID)-1;
		}
	}
	return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
	DbgPrint("驅動解除安裝成功 \n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
	DbgPrint("Hello LyShark.com \n");

	UNICODE_STRING local_path;
	UNICODE_STRING file_path;
	BOOLEAN ref = FALSE;
	
	// 初始化被刪除檔案
	RtlInitUnicodeString(&file_path, L"\\??\\C:\\lyshark.exe");

	// 獲取自身驅動檔案
	local_path = ((PLDR_DATA_TABLE_ENTRY64)Driver->DriverSection)->FullDllName;

	// 刪除lyshark.exe
	ref = ForceDeleteFile(file_path);
	if (ref == TRUE)
	{
		DbgPrint("[+] 已刪除 %wZ \n",file_path);
	}

	// 刪除WinDDK.sys
	ref = ForceDeleteFile(local_path);
	if (ref == TRUE)
	{
		DbgPrint("[+] 已刪除 %wZ \n", local_path);
	}

	Driver->DriverUnload = UnDriver;
	return STATUS_SUCCESS;
}

編譯並執行如上程式,則會分別將c://lyshark.exe以及驅動程式自身刪除,並輸出如下圖所示的提示資訊;