码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 驱动开发:内核解锁与强删文件


    合集 - Windows 内核安全编程技术实践(97)
    1.驱动开发:运用VAD隐藏R3内存思路2021-07-162.驱动开发:应用DeviceIoContro模板精讲06-293.驱动开发:取进程模块的函数地址06-284.驱动开发:内核读写内存多级偏移06-275.驱动开发:内核物理内存寻址读写06-266.驱动开发:内核远程线程实现DLL注入06-257.驱动开发:摘除InlineHook内核钩子06-248.驱动开发:内核中进程与句柄互转06-239.驱动开发:内核注册表增删改查06-2110.驱动开发:基于事件同步的反向通信06-2011.驱动开发:文件微过滤驱动入门06-1912.驱动开发:内核RIP劫持实现DLL注入06-16
    13.驱动开发:内核解锁与强删文件06-15
    14.驱动开发:内核ShellCode线程注入06-1415.驱动开发:内核LoadLibrary实现DLL注入06-1316.驱动开发:内核遍历文件或目录06-1217.驱动开发:内核文件读写系列函数06-0918.驱动开发:内核封装WFP防火墙入门06-0819.驱动开发:PE导出函数与RVA转换06-0720.驱动开发:内核扫描SSDT挂钩状态06-0621.驱动开发:内核实现SSDT挂钩与摘钩06-0522.驱动开发:内核PE结构VA与FOA转换06-0223.驱动开发:内核解析PE结构节表06-0124.驱动开发:内核解析PE结构导出表05-3125.驱动开发:内核读写内存浮点数05-3026.驱动开发:内核解析内存四级页表05-2927.驱动开发:内核实现进程汇编与反汇编05-2328.驱动开发:通过应用堆实现多次通信05-1929.驱动开发:内核远程堆分配与销毁05-1530.驱动开发:通过MDL映射实现多次通信04-2931.驱动开发:内核使用IO/DPC定时器04-0432.驱动开发:探索DRIVER_OBJECT驱动对象04-0333.驱动开发:配置Visual Studio驱动开发环境03-1334.驱动开发:内核封装TDI网络通信接口2022-11-0335.驱动开发:内核封装WSK网络通信接口2022-11-0336.驱动开发:内核层InlineHook挂钩函数2022-10-3137.驱动开发:内核LDE64引擎计算汇编长度2022-10-3138.驱动开发:内核强制结束进程运行2022-10-2939.驱动开发:内核监控FileObject文件回调2022-10-2840.驱动开发:内核监控Register注册表回调2022-10-2741.驱动开发:内核运用LoadImage屏蔽驱动2022-10-2642.驱动开发:内核监视LoadImage映像回调2022-10-2543.驱动开发:内核无痕隐藏自身分析2022-10-2444.驱动开发:内核注册并监控对象回调2022-10-2445.驱动开发:内核监控进程与线程回调2022-10-2346.驱动开发:内核测试模式过DSE签名2022-10-2247.驱动开发:内核枚举进程与线程ObCall回调2022-10-2248.驱动开发:内核枚举Registry注册表回调2022-10-2149.驱动开发:内核枚举LoadImage映像回调2022-10-2050.驱动开发:内核枚举ShadowSSDT基址2022-10-2051.驱动开发:Win10枚举完整SSDT地址表2022-10-1952.驱动开发:Win10内核枚举SSDT表基址2022-10-1953.驱动开发:内核特征码扫描PE代码段2022-10-1854.驱动开发:内核枚举Minifilter微过滤驱动2022-10-1855.驱动开发:内核特征码搜索函数封装2022-10-1756.驱动开发:内核枚举驱动内线程(答疑篇)2022-10-1657.驱动开发:内核枚举PspCidTable句柄表2022-10-1658.驱动开发:内核枚举DpcTimer定时器2022-10-1659.驱动开发:如何枚举所有SSDT表地址2022-10-1460.驱动开发:内核枚举IoTimer定时器2022-10-1461.驱动开发:内核遍历进程VAD结构体2022-10-1362.驱动开发:内核中实现Dump进程转储2022-10-1163.驱动开发:内核R3与R0内存映射拷贝2022-10-1164.驱动开发:内核通过PEB得到进程参数2022-10-1065.驱动开发:内核取应用层模块基地址2022-10-0966.驱动开发:内核取ntoskrnl模块基地址2022-10-0967.驱动开发:判断自身是否加载成功2022-10-0868.驱动开发:应用DeviceIoContro开发模板2022-10-0369.驱动开发:通过Async反向与内核通信2022-10-0370.驱动开发:通过PIPE管道与内核层通信2022-10-0171.驱动通信:通过PIPE管道与内核层通信2022-10-0172.驱动开发:通过ReadFile与内核层通信2022-09-3073.驱动开发:内核字符串拷贝与比较2022-09-2974.驱动开发:内核字符串转换方法2022-09-2875.驱动开发:内核中的自旋锁结构2022-09-2876.驱动开发:内核CR3切换读写内存2022-09-2577.驱动开发:内核中的链表与结构体2022-09-2378.驱动开发:摘链DKOM进程隐藏2022-08-3179.驱动开发:WinDBG 枚举SSDT以及SSSDT地址2022-04-2880.驱动开发:实现驱动加载卸载工具2021-07-1681.驱动开发:断链隐藏驱动程序自身2021-07-1682.驱动开发:通过内存拷贝读写内存2021-07-1283.驱动开发:内核MDL读写进程内存2021-07-0584.驱动开发:内核监控进程与线程创建2021-05-0685.驱动开发:监控进程与线程对象操作2020-06-1486.驱动开发:WinDBG 常用调试命令总结2020-06-1087.驱动开发:通过SystemBuf与内核层通信2020-04-1388.驱动开发:对象回调监控文件访问2019-11-0189.驱动开发:内核中枚举进线程与模块2019-10-2190.驱动开发:DKOM 实现进程隐藏2019-10-1191.驱动开发:内核读取SSDT表基址2019-10-0992.驱动开发:驱动与应用的简单通信2019-09-2393.驱动开发:恢复SSDT内核钩子2019-09-2194.驱动开发:挂接SSDT内核钩子2019-09-2095.驱动开发:WinDBG 配置内核双机调试2019-09-1996.VS2013+WDK8.1 驱动开发环境配置2019-09-1897.驱动开发:派遣函数与设备对象2019-09-14
    收起

    在某些时候我们的系统中会出现一些无法被正常删除的文件,如果想要强制删除则需要在驱动层面对其进行解锁后才可删掉,而所谓的解锁其实就是释放掉文件描述符(句柄表)占用,文件解锁的核心原理是通过调用ObSetHandleAttributes函数将特定句柄设置为可关闭状态,然后在调用ZwClose将其文件关闭,强制删除则是通过ObReferenceObjectByHandle在对象上提供相应的权限后直接调用ZwDeleteFile将其删除,虽此类代码较为普遍,但作为揭秘ARK工具来说也必须要将其分析并讲解一下。

    首先封装lyshark.h通用头文件,并定义好我们所需要的结构体,以及特定未导出函数的声明,此处的定义部分是微软官方的规范,如果不懂结构具体含义可自行去微软官方查阅参考资料。

    // 署名权
    // right to sign one's name on a piece of work
    // PowerBy: LyShark
    // Email: me@lyshark.com
    
    #include 
    
    // -------------------------------------------------------
    // 引用微软结构
    // -------------------------------------------------------
    // 结构体定义
    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: me@lyshark.com
    
    #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: me@lyshark.com
    
    #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以及驱动程序自身删除,并输出如下图所示的提示信息;

  • 相关阅读:
    决策树算法中处理噪音点
    记录最近两次java内存过高的分析
    Java学习之构造器
    Springboot 项目下载资源目录下的 Word 文件
    (一)《数字电子技术基础》——引言
    我整理了近五年的开发者报告,看看国内外有什么差异?
    电脑键盘各按键的作用及常用的快捷键总结
    Java项目:SSM共享汽车租赁平台
    [DP] DP优化总结
    Ubuntu下安装Java
  • 原文地址:https://www.cnblogs.com/LyShark/p/17155477.html
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号