64位内开发第二十五讲-x64内核强删文件.
x64内核中强删文件的实现
一丶简介
说道删除文件.有各种各样的方法. 有ring3 也有ring0. 而且也有许多对抗的方法. 如ring3想删除一个文件.被占用你就无法删除.此时可以通过解除句柄进行删除 ring0中想删除一个文件.有常规方法也有非常规方法.常规方法就是 设置文件属性为删除.然后进行设置. 还有就是直接调用ZwDeleteFile 进行删除. 暴力删除就是这里所讲的 IRP删除.给底层发送IRP即可进行删除文件.
1.步骤
步骤很简单.基本上说完就可以自己写代码做出
-
1.打开文件.获取文件句柄 (IoCreateFile)
-
2.根据文件句柄,获取文件对象.(有了句柄都应该第一时间想到获取它的对象) (
ObReferenceObjectByHandle
) -
3.获取文件对象的设备对象指针.这个发送IRP的时候需要使用(
IoGetRelatedDeviceObject
) -
4.申请IRP(
IoAllocateIrp
) -
5.初始化你申请的IRP
-
6.获取IRP的下层堆栈,并且初始化信息.(
IoGetNextIrpStackLocation
) -
7.设置回调.系统完成IRP之后会调用你这个回调.所以你需要设置事件同步跟你自己同步,才知道IRP已经发送完了.(
IoSetCompletionRoutine
) -
8.获取文件对象的域指针.并且设置域指针的两个成员为0.系统以他来判断这个程序是否可以删除.如果不为0.那么则无法删除运行中的文件.
pSectionObjectPointer->ImageSectionObject = 0; pSectionObjectPointer->DataSectionObject = 0;
-
9.发送IRP(IoCallDriver)
-
10.根据 事件 来等待是否IRP完成(KeWaitForSingleObject)
-
11.做完收工.(ZwClose)
2.Nt驱动代码
#include "Driver.h"
//删除文件函数的入口
NTSTATUS RetOpenFileHandle(UNICODE_STRING uDelFileName, PHANDLE pFileHandle)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK iostu;
HANDLE hFileHandle = 0;
if (pFileHandle == NULL)
return STATUS_UNSUCCESSFUL;
if (KeGetCurrentIrql() > PASSIVE_LEVEL)
return 0;
if (uDelFileName.Length < 0 || uDelFileName.MaximumLength < 0)
{
return 0;
}
OBJECT_ATTRIBUTES ObjAttribute;
ObjAttribute.ObjectName = &uDelFileName;
ObjAttribute.SecurityDescriptor = NULL;
ObjAttribute.SecurityQualityOfService = NULL;
ObjAttribute.Attributes = OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE;
ObjAttribute.Length = sizeof(OBJECT_ATTRIBUTES);
/* InitializeObjectAttributes(
&ObjAttribute,
&uDelFileName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE,
NULL, NULL);*/
ntStatus = IoCreateFile(&hFileHandle,
FILE_READ_ATTRIBUTES,
&ObjAttribute,
&iostu,
0,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_DELETE,
FILE_OPEN,
0,
NULL,
0,
CreateFileTypeNone,
NULL,
IO_NO_PARAMETER_CHECKING);
*pFileHandle = hFileHandle;
return ntStatus;
}
//去掉文件属性
//CallBack回调
NTSTATUS
CallBackIrpCompleteionProc(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
//操作系统会设置这个回调
Irp->UserIosb->Status = Irp->IoStatus.Status;
Irp->UserIosb->Information = Irp->IoStatus.Information;
KeSetEvent(Irp->UserEvent, IO_NO_INCREMENT, FALSE);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}
NTSTATUS PassFileattribute(PFILE_OBJECT pFileObj)
{
/*
1.申请IRP,初始化IRP
2.初始化同步事件,以及设置回调.
3.设置文件属性为默认
4.发送IRP
*/
PDEVICE_OBJECT pNextDeviceObj = NULL;
PIRP pAllocIrp = NULL;
KEVENT IrpSynEvent = {0}; //Irp同步需要的事件同步
FILE_BASIC_INFORMATION fileBasciInfo = { 0 };
IO_STATUS_BLOCK iostu;
PIO_STACK_LOCATION IrpStack;
//通过文件对象.获取其设备对象指针
pNextDeviceObj = IoGetRelatedDeviceObject(pFileObj);
if (pNextDeviceObj == NULL)
return STATUS_UNSUCCESSFUL;
//通过设备对象指针.确定申请的IRP的大小,注意在完成设置里面进行释放.
pAllocIrp = IoAllocateIrp(pNextDeviceObj->StackSize,TRUE);
if (pAllocIrp == NULL)
return STATUS_UNSUCCESSFUL;
//初始化Irp
//设置为自动,设置为无信号.
KeInitializeEvent(&IrpSynEvent, SynchronizationEvent, FALSE);
fileBasciInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
pAllocIrp->AssociatedIrp.SystemBuffer = &fileBasciInfo;
pAllocIrp->UserIosb = &iostu;
pAllocIrp->UserEvent = &IrpSynEvent;
pAllocIrp->Tail.Overlay.OriginalFileObject = pFileObj;
pAllocIrp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread();
//获取下层堆栈.进行设置.
//IrpStack =
IrpStack = IoGetNextIrpStackLocation(pAllocIrp);
IrpStack->MajorFunction = IRP_MJ_SET_INFORMATION;
IrpStack->DeviceObject = pNextDeviceObj;
IrpStack->FileObject = pFileObj;
IrpStack->Parameters.SetFile.Length = sizeof(FILE_BASIC_INFORMATION);
IrpStack->Parameters.SetFile.FileObject = pFileObj;
IrpStack->Parameters.SetFile.FileInformationClass = FileBasicInformation;
//设置完成例程
IoSetCompletionRoutine(pAllocIrp, CallBackIrpCompleteionProc, &IrpSynEvent, TRUE, TRUE, TRUE);
//发送IRP
IoCallDriver(pNextDeviceObj, pAllocIrp);
//等待完成.
KeWaitForSingleObject(&IrpSynEvent, Executive, KernelMode, TRUE, NULL);
return STATUS_SUCCESS;
}
NTSTATUS FsDeleteFile(PFILE_OBJECT pFileObj)
{
/*
1.申请IRP,初始化IRP
2.初始化同步事件,以及设置回调.
3.设置文件属性为默认
4.发送IRP
核心:
核心是设置 FileObject中的域.进而删除正在运行中的文件
*/
PDEVICE_OBJECT pNextDeviceObj = NULL;
PIRP pAllocIrp = NULL;
KEVENT IrpSynEvent = { 0 }; //Irp同步需要的事件同步
FILE_DISPOSITION_INFORMATION fileBasciInfo = { 0 }; //注意此位置.已经变化为 FILE_DISPOSITION_INFORMATION
IO_STATUS_BLOCK iostu;
PIO_STACK_LOCATION IrpStack;
PSECTION_OBJECT_POINTERS pFileExe; //注意此属性要设置为0.欺骗系统进行删除
//通过文件对象.获取其设备对象指针
pNextDeviceObj = IoGetRelatedDeviceObject(pFileObj);
if (pNextDeviceObj == NULL)
return STATUS_UNSUCCESSFUL;
//通过设备对象指针.确定申请的IRP的大小,注意在完成设置里面进行释放.
pAllocIrp = IoAllocateIrp(pNextDeviceObj->StackSize, TRUE);
if (pAllocIrp == NULL)
return STATUS_UNSUCCESSFUL;
//初始化Irp
//设置为自动,设置为无信号.
KeInitializeEvent(&IrpSynEvent, SynchronizationEvent, FALSE);
fileBasciInfo.DeleteFile = TRUE; //设置标记为删除
pAllocIrp->AssociatedIrp.SystemBuffer = &fileBasciInfo;
pAllocIrp->UserIosb = &iostu;
pAllocIrp->UserEvent = &IrpSynEvent;
pAllocIrp->Tail.Overlay.OriginalFileObject = pFileObj;
pAllocIrp->Tail.Overlay.Thread = (PETHREAD)KeGetCurrentThread();
//获取下层堆栈.进行设置.
//IrpStack =
IrpStack = IoGetNextIrpStackLocation(pAllocIrp);
IrpStack->MajorFunction = IRP_MJ_SET_INFORMATION;
IrpStack->DeviceObject = pNextDeviceObj;
IrpStack->FileObject = pFileObj;
IrpStack->Parameters.SetFile.Length = sizeof(FILE_DISPOSITION_INFORMATION);
IrpStack->Parameters.SetFile.FileObject = pFileObj;
IrpStack->Parameters.SetFile.FileInformationClass = FileDispositionInformation;
//设置完成例程
IoSetCompletionRoutine(pAllocIrp, CallBackIrpCompleteionProc, &IrpSynEvent, TRUE, TRUE, TRUE);
//删除正在运行中的文件.
pFileExe = pFileObj->SectionObjectPointer;
pFileExe->DataSectionObject = 0;
pFileExe->ImageSectionObject = 0;
//发送IRP
IoCallDriver(pNextDeviceObj, pAllocIrp);
//等待完成.
KeWaitForSingleObject(&IrpSynEvent, Executive, KernelMode, TRUE, NULL);
return STATUS_SUCCESS;
}
NTSTATUS IrpDeleteFileRun(UNICODE_STRING uDelFileName)
{
KdBreakPoint();
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
/*
1.首先通过发送IRP去掉文件的属性
2.设置文件属性为删除.进行发送IRP强删文件.
*/
HANDLE hFileHandle = { 0 };
PFILE_OBJECT pFileObject = NULL;
//sep1 : OpenFile Get File Handle
ntStatus = RetOpenFileHandle(uDelFileName,&hFileHandle);
if (!NT_SUCCESS(ntStatus))
{
goto ExitAnRelease;
}
//sep2: Chang File Handle to FileObject
ntStatus = ObReferenceObjectByHandle(
hFileHandle,
GENERIC_ALL,
*IoFileObjectType,
KernelMode,
&pFileObject,
NULL);
if (!NT_SUCCESS(ntStatus))
{
goto ExitAnRelease;
}
//setp 3: Pass File Atribute
KdBreakPoint();
ntStatus = PassFileattribute(pFileObject);
if (!NT_SUCCESS(ntStatus))
{
goto ExitAnRelease;
}
//setp 4: Send Irp DeleteFile
KdBreakPoint();
ntStatus = FsDeleteFile(pFileObject);
if (!NT_SUCCESS(ntStatus))
{
goto ExitAnRelease;
}
ExitAnRelease:
if (pFileObject != NULL)
ObDereferenceObject(pFileObject);
if (hFileHandle != NULL)
ZwClose(hFileHandle);
return ntStatus;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pRegPath)
{
ULONG iCount = 0;
NTSTATUS ntStatus;
UNICODE_STRING uDelFileName = { 0 };
pDriverObj->DriverUnload = DriverUnLoad;
/*ntStatus = InitDeviceAnSybolicLinkName(pDriverObj);
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}
ntStatus = InitDisPatchFunction(pDriverObj);
if (!NT_SUCCESS(ntStatus))
{
return ntStatus;
}*/
//也可写成: \\??\\c:\\xxx.txt
RtlInitUnicodeString(&uDelFileName, L"//DosDevices//C://123.txt");
IrpDeleteFileRun(uDelFileName);
return STATUS_SUCCESS;
}
代码测试可以进行强删.
二丶驱动文件自删除实战
其原理是与强删文件一样。只不过应用于驱动自删除。抹除自身。
代码如下:
start.h
#ifdef _cplusplus
extern "C"
{
#endif
#include <ntifs.h>
#include <ntddk.h>
#ifdef _cplusplus
}
#endif // DEBUG
#include "start.h"
VOID DriverUnload(PDRIVER_OBJECT pDriverObj)
{
KdPrint(("Unload Driver\n"));
}
#define INVALID_HANDLE_VALUE ((PVOID)(-1))
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;
BOOLEAN ForceDeleteFileW(UNICODE_STRING pwzFileName)
{
PEPROCESS pCurEprocess = NULL;
KAPC_STATE kapc = {0};
OBJECT_ATTRIBUTES fileOb;
HANDLE hFile = INVALID_HANDLE_VALUE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK iosta;
PDEVICE_OBJECT DeviceObject = NULL;
PVOID pHandleFileObject = NULL;
if (KeGetCurrentIrql() > PASSIVE_LEVEL)
{
return FALSE;
}
if (pwzFileName.Buffer == NULL || pwzFileName.Length <= 0)
{
return FALSE;
}
// Start
__try
{
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;
}
((PFILE_OBJECT)(pHandleFileObject))->SectionObjectPointer->ImageSectionObject = 0;
((PFILE_OBJECT)(pHandleFileObject))->DeleteAccess = 1;
status = ZwDeleteFile(&fileOb);
if (!NT_SUCCESS(status))
{
return FALSE;
}
}
_finally
{
if (pHandleFileObject != NULL)
{
ObDereferenceObject(pHandleFileObject);
pHandleFileObject = NULL;
}
KeUnstackDetachProcess(&kapc);
if (hFile != NULL || hFile != INVALID_HANDLE_VALUE)
{
ZwClose(hFile);
hFile = INVALID_HANDLE_VALUE;
}
}
return TRUE;
}
extern "C" NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pRootDriverObj,
IN PUNICODE_STRING pRegPath)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
pRootDriverObj->DriverUnload = DriverUnload;
KdBreakPoint();
UNICODE_STRING pusDriverPath = ((PLDR_DATA_TABLE_ENTRY64)pRootDriverObj->DriverSection)->FullDllName;
ForceDeleteFileW(pusDriverPath);
return status;
}
1.3 其它代码
自己写的代码虽然可以强删,但是在某些情况下无法打开文件. 而且强删要考虑的因素很多. 比如要设置SetFileInformation等信息.
看看网络其它的优秀实现.
cpp
#include "ForceDeleteFile.h"
ForceDeleteFile::ForceDeleteFile()
{
}
ForceDeleteFile::~ForceDeleteFile()
{
}
BOOLEAN ForceDeleteFile::ForceDeleteFileW(UNICODE_STRING pwzFileName)
{
NTSTATUS status = STATUS_SUCCESS;
PFILE_OBJECT pFileObject = NULL;
IO_STATUS_BLOCK iosb = { 0 };
FILE_BASIC_INFORMATION fileBaseInfo = { 0 };
FILE_DISPOSITION_INFORMATION fileDispositionInfo = { 0 };
PVOID pImageSectionObject = NULL;
PVOID pDataSectionObject = NULL;
PVOID pSharedCacheMap = NULL;
// 发送IRP打开文件
status = IrpCreateFile(&pFileObject, GENERIC_READ | GENERIC_WRITE, &pwzFileName,
&iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(status))
{
DbgPrint("IrpCreateFile Error[0x%X]\n", status);
return FALSE;
}
// 发送IRP设置文件属性, 去掉只读属性, 修改为 FILE_ATTRIBUTE_NORMAL
RtlZeroMemory(&fileBaseInfo, sizeof(fileBaseInfo));
fileBaseInfo.FileAttributes = FILE_ATTRIBUTE_NORMAL;
status = IrpSetInformationFile(pFileObject, &iosb, &fileBaseInfo, sizeof(fileBaseInfo), FileBasicInformation);
if (!NT_SUCCESS(status))
{
return status;
}
// 清空PSECTION_OBJECT_POINTERS结构
if (pFileObject->SectionObjectPointer)
{
// 保存旧值
pImageSectionObject = pFileObject->SectionObjectPointer->ImageSectionObject;
pDataSectionObject = pFileObject->SectionObjectPointer->DataSectionObject;
pSharedCacheMap = pFileObject->SectionObjectPointer->SharedCacheMap;
// 置为空
pFileObject->SectionObjectPointer->ImageSectionObject = NULL;
pFileObject->SectionObjectPointer->DataSectionObject = NULL;
pFileObject->SectionObjectPointer->SharedCacheMap = NULL;
}
// 发送IRP设置文件属性, 设置删除文件操作
RtlZeroMemory(&fileDispositionInfo, sizeof(fileDispositionInfo));
fileDispositionInfo.DeleteFile = TRUE;
status = IrpSetInformationFile(pFileObject, &iosb, &fileDispositionInfo, sizeof(fileDispositionInfo), FileDispositionInformation);
if (!NT_SUCCESS(status))
{
return status;
}
//还原旧值
if (pFileObject->SectionObjectPointer)
{
pFileObject->SectionObjectPointer->ImageSectionObject = pImageSectionObject;
pFileObject->SectionObjectPointer->DataSectionObject = pDataSectionObject;
pFileObject->SectionObjectPointer->SharedCacheMap = pSharedCacheMap;
}
// 关闭文件对象
ObDereferenceObject(pFileObject);
return status;
}
Irp.cpp
#include "IrpFile.h"
// 完成实例, 设置事件信号量, 并释放IRP
NTSTATUS MyCompleteRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID Context)
{
*pIrp->UserIosb = pIrp->IoStatus;
// 设置事件信号
if (pIrp->UserEvent)
{
KeSetEvent(pIrp->UserEvent, IO_NO_INCREMENT, FALSE);
}
// 释放MDL
if (pIrp->MdlAddress)
{
IoFreeMdl(pIrp->MdlAddress);
pIrp->MdlAddress = NULL;
}
// 释放IRP
IoFreeIrp(pIrp);
pIrp = NULL;
return STATUS_MORE_PROCESSING_REQUIRED;
}
// 创建或者打开文件
// ZwCreateFile
NTSTATUS IrpCreateFile(
OUT PFILE_OBJECT *ppFileObject,
IN ACCESS_MASK DesiredAccess,
IN PUNICODE_STRING pustrFilePath,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength)
{
NTSTATUS status = STATUS_SUCCESS;
ULONG ulFileNameMaxSize = 512;
WCHAR wszName[100] = { 0 };
UNICODE_STRING ustrRootPath;
OBJECT_ATTRIBUTES objectAttributes = { 0 };
HANDLE hRootFile = NULL;
PFILE_OBJECT pRootFileObject = NULL, pFileObject = NULL;
PDEVICE_OBJECT RootDeviceObject = NULL, RootRealDevice = NULL;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
ACCESS_STATE accessData = { 0 };
AUX_ACCESS_DATA auxAccessData = { 0 };
IO_SECURITY_CONTEXT ioSecurityContext = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
// 打开磁盘根目录并获取句柄
wcscpy(wszName, L"\\??\\A:\\");
wszName[4] = pustrFilePath->Buffer[0];
RtlInitUnicodeString(&ustrRootPath, wszName);
DbgPrint("RootPath:%wZ\n", &ustrRootPath);
InitializeObjectAttributes(&objectAttributes, &ustrRootPath, OBJ_KERNEL_HANDLE, NULL, NULL);
status = IoCreateFile(&hRootFile, GENERIC_READ | SYNCHRONIZE,
&objectAttributes, IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0, CreateFileTypeNone,
NULL, IO_NO_PARAMETER_CHECKING);
if (!NT_SUCCESS(status))
{
DbgPrint("IoCreateFile Error[0x%X]", status);
return status;
}
// 获取磁盘根目录文件对象
status = ObReferenceObjectByHandle(hRootFile, FILE_READ_ACCESS, *IoFileObjectType, KernelMode, (PVOID*)&pRootFileObject, NULL);
if (!NT_SUCCESS(status))
{
ZwClose(hRootFile);
DbgPrint("ObReferenceObjectByHandle Error[0x%X]\n", status);
return status;
}
// 获取磁盘根目录设备对象
RootDeviceObject = pRootFileObject->Vpb->DeviceObject;
RootRealDevice = pRootFileObject->Vpb->RealDevice;
// 关闭磁盘根目录句柄和对象
ObDereferenceObject(pRootFileObject);
ZwClose(hRootFile);
// 创建IRP
pIrp = IoAllocateIrp(RootDeviceObject->StackSize, FALSE);
if (NULL == pIrp)
{
ObDereferenceObject(pFileObject);
DbgPrint("IoAllocateIrp Error!\n");
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 创建空文件对象
InitializeObjectAttributes(&objectAttributes, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL);
status = ObCreateObject(KernelMode, *IoFileObjectType, &objectAttributes, KernelMode, NULL, sizeof(FILE_OBJECT), 0, 0, (PVOID*)&pFileObject);
if (!NT_SUCCESS(status))
{
DbgPrint("ObCreateObject Error[0x%X]\n", status);
return status;
}
// 设置创建的文件对象 FILE_OBJECT
RtlZeroMemory(pFileObject, sizeof(FILE_OBJECT));
pFileObject->Type = IO_TYPE_FILE;
pFileObject->Size = sizeof(FILE_OBJECT);
pFileObject->DeviceObject = RootRealDevice;
pFileObject->Flags = FO_SYNCHRONOUS_IO;
// FILE_OBJECT中的FileName最好动态创建, 否则ObDereferenceObject文件句柄的时候会蓝屏
pFileObject->FileName.Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, ulFileNameMaxSize);
pFileObject->FileName.MaximumLength = (USHORT)ulFileNameMaxSize;
pFileObject->FileName.Length = pustrFilePath->Length - 4;
RtlZeroMemory(pFileObject->FileName.Buffer, ulFileNameMaxSize);
RtlCopyMemory(pFileObject->FileName.Buffer, &pustrFilePath->Buffer[2], pFileObject->FileName.Length);
DbgPrint("pFileObject->FileName:%wZ\n", &pFileObject->FileName);
KeInitializeEvent(&pFileObject->Lock, SynchronizationEvent, FALSE);
KeInitializeEvent(&pFileObject->Event, NotificationEvent, FALSE);
// 创建权限状态
RtlZeroMemory(&auxAccessData, sizeof(auxAccessData));
status = SeCreateAccessState(&accessData, &auxAccessData, DesiredAccess, IoGetFileObjectGenericMapping());
if (!NT_SUCCESS(status))
{
IoFreeIrp(pIrp);
ObDereferenceObject(pFileObject);
DbgPrint("SeCreateAccessState Error[0x%X]\n", status);
return status;
}
// 设置安全内容 IO_SECURITY_CONTEXT
ioSecurityContext.SecurityQos = NULL;
ioSecurityContext.AccessState = &accessData;
ioSecurityContext.DesiredAccess = DesiredAccess;
ioSecurityContext.FullCreateOptions = 0;
// 设置IRP
RtlZeroMemory(IoStatusBlock, sizeof(IO_STATUS_BLOCK));
pIrp->MdlAddress = NULL;
pIrp->AssociatedIrp.SystemBuffer = EaBuffer;
pIrp->Flags = IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API;
pIrp->RequestorMode = KernelMode;
pIrp->UserIosb = IoStatusBlock;
pIrp->UserEvent = &kEvent;
pIrp->PendingReturned = FALSE;
pIrp->Cancel = FALSE;
pIrp->CancelRoutine = NULL;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.AuxiliaryBuffer = NULL;
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_CREATE;
pIoStackLocation->DeviceObject = RootDeviceObject;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Parameters.Create.SecurityContext = &ioSecurityContext;
pIoStackLocation->Parameters.Create.Options = (CreateDisposition << 24) | CreateOptions;
pIoStackLocation->Parameters.Create.FileAttributes = (USHORT)FileAttributes;
pIoStackLocation->Parameters.Create.ShareAccess = (USHORT)ShareAccess;
pIoStackLocation->Parameters.Create.EaLength = EaLength;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(RootDeviceObject, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, TRUE, NULL);
}
// 判断IRP处理结果
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
ObDereferenceObject(pFileObject);
DbgPrint("IRP FAILED!\n");
return status;
}
InterlockedIncrement(&pFileObject->DeviceObject->ReferenceCount);
if (pFileObject->Vpb)
{
InterlockedIncrement((LONG*)&pFileObject->Vpb->ReferenceCount);
}
// 返回文件对象
*ppFileObject = pFileObject;
return status;
}
// 文件遍历
// ZwQueryDirectoryFile
NTSTATUS IrpQueryDirectoryFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN PUNICODE_STRING FileName OPTIONAL)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
PDEVICE_OBJECT pDevObj = NULL;
// 判断参数是否有效
if ((NULL == pFileObject) ||
(NULL == pFileObject->Vpb) ||
(NULL == pFileObject->Vpb->DeviceObject))
{
return STATUS_UNSUCCESSFUL;
}
// 获取设备对象
pDevObj = pFileObject->Vpb->DeviceObject;
// 创建IRP
pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
if (NULL == pIrp)
{
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 设置IRP
RtlZeroMemory(FileInformation, Length);
pIrp->UserEvent = &kEvent;
pIrp->UserIosb = IoStatusBlock;
pIrp->UserBuffer = FileInformation;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
pIrp->Overlay.AsynchronousParameters.UserApcRoutine = NULL;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_DIRECTORY_CONTROL;
pIoStackLocation->MinorFunction = IRP_MN_QUERY_DIRECTORY;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Flags = SL_RESTART_SCAN;
pIoStackLocation->Parameters.QueryDirectory.Length = Length;
pIoStackLocation->Parameters.QueryDirectory.FileName = FileName;
pIoStackLocation->Parameters.QueryDirectory.FileInformationClass = FileInformationClass;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(pDevObj, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, NULL);
}
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
return status;
}
return status;
}
// 获取文件的信息
// ZwQueryInformationFile
NTSTATUS IrpQueryInformationFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
PDEVICE_OBJECT pDevObj = NULL;
// 判断参数是否有效
if ((NULL == pFileObject) ||
(NULL == pFileObject->Vpb) ||
(NULL == pFileObject->Vpb->DeviceObject))
{
return STATUS_UNSUCCESSFUL;
}
// 获取设备对象
pDevObj = pFileObject->Vpb->DeviceObject;
// 创建IRP
pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
if (NULL == pIrp)
{
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 设置IRP
RtlZeroMemory(FileInformation, Length);
pIrp->UserEvent = &kEvent;
pIrp->UserIosb = IoStatusBlock;
pIrp->AssociatedIrp.SystemBuffer = FileInformation;
pIrp->RequestorMode = KernelMode;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
pIoStackLocation->DeviceObject = pDevObj;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Parameters.QueryFile.Length = Length;
pIoStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(pDevObj, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, NULL);
}
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
return status;
}
return status;
}
// 设置文件信息
// ZwSetInformationFile
NTSTATUS IrpSetInformationFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
PDEVICE_OBJECT pDevObj = NULL;
// 判断参数是否有效
if ((NULL == pFileObject) ||
(NULL == pFileObject->Vpb) ||
(NULL == pFileObject->Vpb->DeviceObject))
{
return STATUS_UNSUCCESSFUL;
}
// 获取设备对象
pDevObj = pFileObject->Vpb->DeviceObject;
// 创建IRP
pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
if (NULL == pIrp)
{
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 设置IRP
pIrp->UserEvent = &kEvent;
pIrp->UserIosb = IoStatusBlock;
pIrp->AssociatedIrp.SystemBuffer = FileInformation;
pIrp->RequestorMode = KernelMode;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_SET_INFORMATION;
pIoStackLocation->DeviceObject = pDevObj;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Parameters.SetFile.Length = Length;
pIoStackLocation->Parameters.SetFile.FileInformationClass = FileInformationClass;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(pDevObj, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, NULL);
}
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
return status;
}
return status;
}
// 读文件
// ZwReadFile
NTSTATUS IrpReadFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
PDEVICE_OBJECT pDevObj = NULL;
// 判断参数是否有效
if ((NULL == pFileObject) ||
(NULL == pFileObject->Vpb) ||
(NULL == pFileObject->Vpb->DeviceObject))
{
return STATUS_UNSUCCESSFUL;
}
// 调整参数
if (NULL == ByteOffset)
{
if (0 == (FO_SYNCHRONOUS_IO & pFileObject->Flags))
{
return STATUS_INVALID_PARAMETER;
}
ByteOffset = &pFileObject->CurrentByteOffset;
}
// 获取设备对象
pDevObj = pFileObject->Vpb->DeviceObject;
// 创建IRP
pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
if (NULL == pIrp)
{
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 设置IRP
RtlZeroMemory(Buffer, Length);
pIrp->MdlAddress = MmCreateMdl(NULL, Buffer, Length);
if (NULL == pIrp->MdlAddress)
{
IoFreeIrp(pIrp);
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(pIrp->MdlAddress);
pIrp->UserEvent = &kEvent;
pIrp->UserIosb = IoStatusBlock;
pIrp->Flags = IRP_READ_OPERATION;
pIrp->RequestorMode = KernelMode;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_READ;
pIoStackLocation->MinorFunction = IRP_MN_NORMAL;
pIoStackLocation->DeviceObject = pDevObj;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Parameters.Read.Length = Length;
pIoStackLocation->Parameters.Read.ByteOffset = *ByteOffset;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(pDevObj, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, NULL);
}
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
return status;
}
return status;
}
// 写文件
// ZwWriteFile
NTSTATUS IrpWriteFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL
)
{
NTSTATUS status = STATUS_SUCCESS;
PIRP pIrp = NULL;
KEVENT kEvent = { 0 };
PIO_STACK_LOCATION pIoStackLocation = NULL;
PDEVICE_OBJECT pDevObj = NULL;
// 判断参数是否有效
if ((NULL == pFileObject) ||
(NULL == pFileObject->Vpb) ||
(NULL == pFileObject->Vpb->DeviceObject))
{
return STATUS_UNSUCCESSFUL;
}
// 调整参数
if (NULL == ByteOffset)
{
if (0 == (FO_SYNCHRONOUS_IO & pFileObject->Flags))
{
return STATUS_INVALID_PARAMETER;
}
ByteOffset = &pFileObject->CurrentByteOffset;
}
// 获取设备对象
pDevObj = pFileObject->Vpb->DeviceObject;
// 创建IRP
pIrp = IoAllocateIrp(pDevObj->StackSize, FALSE);
if (NULL == pIrp)
{
return STATUS_UNSUCCESSFUL;
}
// 创建事件
KeInitializeEvent(&kEvent, SynchronizationEvent, FALSE);
// 设置IRP
pIrp->MdlAddress = MmCreateMdl(NULL, Buffer, Length);
if (NULL == pIrp->MdlAddress)
{
IoFreeIrp(pIrp);
return STATUS_INSUFFICIENT_RESOURCES;
}
MmBuildMdlForNonPagedPool(pIrp->MdlAddress);
pIrp->UserEvent = &kEvent;
pIrp->UserIosb = IoStatusBlock;
pIrp->Flags = IRP_WRITE_OPERATION;
pIrp->RequestorMode = KernelMode;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
// 获取下一个IRP的IO_STACK_LOCATION并设置
pIoStackLocation = IoGetNextIrpStackLocation(pIrp);
pIoStackLocation->MajorFunction = IRP_MJ_WRITE;
pIoStackLocation->MinorFunction = IRP_MN_NORMAL;
pIoStackLocation->DeviceObject = pDevObj;
pIoStackLocation->FileObject = pFileObject;
pIoStackLocation->Parameters.Write.Length = Length;
pIoStackLocation->Parameters.Write.ByteOffset = *ByteOffset;
// 设置完成实例, 以便通知IRP处理完成, 释放资源
IoSetCompletionRoutine(pIrp, MyCompleteRoutine, NULL, TRUE, TRUE, TRUE);
// 发送IRP
status = IoCallDriver(pDevObj, pIrp);
// 等待IRP的处理
if (STATUS_PENDING == status)
{
KeWaitForSingleObject(&kEvent, Executive, KernelMode, FALSE, NULL);
}
status = IoStatusBlock->Status;
if (!NT_SUCCESS(status))
{
return status;
}
return status;
}
各自的头文件
#pragma once
#include <ntifs.h>
#include "IrpFile.h"
#define INVALID_HANDLE_VALUE ((PVOID)(-1))
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;
class ForceDeleteFile
{
public:
ForceDeleteFile();
~ForceDeleteFile();
BOOLEAN ForceDeleteFileW(UNICODE_STRING pwzFileName);
};
irp.h
#ifndef _IRP_FILE_H_
#define _IRP_FILE_H_
#include <ntifs.h>
#ifdef __cplusplus
extern "C"
{
#endif
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(
PACCESS_STATE AccessState,
PVOID AuxData,
ACCESS_MASK DesiredAccess,
PGENERIC_MAPPING GenericMapping
);
NTSTATUS ObCreateObject(
__in KPROCESSOR_MODE ProbeMode, // 决定是否要验证参数
__in POBJECT_TYPE ObjectType, // 对象类型指针
__in POBJECT_ATTRIBUTES ObjectAttributes, // 对象的属性, 最终会转化成ObAllocateObject需要的OBJECT_CREATE_INFORMATION结构
__in KPROCESSOR_MODE OwnershipMode, // 内核对象?用户对象? 同上
__inout_opt PVOID ParseContext, // 这参数没用
__in ULONG ObjectBodySize, // 对象体大小
__in ULONG PagedPoolCharge, // ...
__in ULONG NonPagedPoolCharge, // ...
__out PVOID* Object // 接收对象体的指针
);
// 完成实例, 设置事件信号量, 并释放IRP
NTSTATUS MyCompleteRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP pIrp,
IN PVOID Context);
// 创建或者打开文件
// ZwCreateFile
NTSTATUS IrpCreateFile(
OUT PFILE_OBJECT* ppFileObject,
IN ACCESS_MASK DesiredAccess,
IN PUNICODE_STRING pustrFilePath,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG CreateDisposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength);
// 文件遍历
// ZwQueryDirectoryFile
NTSTATUS IrpQueryDirectoryFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN PUNICODE_STRING FileName OPTIONAL);
// 获取文件的信息
// ZwQueryInformationFile
NTSTATUS IrpQueryInformationFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);
// 设置文件信息
// ZwSetInformationFile
NTSTATUS IrpSetInformationFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID FileInformation,
IN ULONG Length,
IN FILE_INFORMATION_CLASS FileInformationClass
);
// 读文件
// ZwReadFile
NTSTATUS IrpReadFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
OUT PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL
);
// 写文件
// ZwWriteFile
NTSTATUS IrpWriteFile(
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN PVOID Buffer,
IN ULONG Length,
IN PLARGE_INTEGER ByteOffset OPTIONAL
);
#ifdef __cplusplus
}
#endif
#endif
坚持两字,简单,轻便,但是真正的执行起来确实需要很长很长时间.当你把坚持两字当做你要走的路,那么你总会成功. 想学习,有问题请加群.群号:725864912(收费)群名称: 逆向学习小分队 群里有大量学习资源. 以及定期直播答疑.有一个良好的学习氛围. 涉及到外挂反外挂病毒 司法取证加解密 驱动过保护 VT 等技术,期待你的进入。
详情请点击链接查看置顶博客 https://www.cnblogs.com/iBinary/p/7572603.html
本文来自博客园,作者:iBinary,未经允许禁止转载 转载前可联系本人.对于爬虫人员来说如果发现保留起诉权力.https://www.cnblogs.com/iBinary/p/11596466.html
欢迎大家关注我的微信公众号.不定期的更新文章.更新技术. 关注公众号后请大家养成 不白嫖的习惯.欢迎大家赞赏. 也希望在看完公众号文章之后 不忘 点击 收藏 转发 以及点击在看功能. QQ群: