本文为看雪论精华文章
看雪论坛作者ID:低调putchar
一、概述
Windows操作系统中,文件系统过滤驱动的过滤设备绑定在文件系统(FSD)设备之上,监视和过滤我们的文件访问。
typedef struct _VPB {
CSHORT Type;
CSHORT Size;
USHORT Flags;
USHORT VolumeLabelLength; // in bytes
struct _DEVICE_OBJECT *DeviceObject; //文件系统卷设备
struct _DEVICE_OBJECT *RealDevice; //卷管理器生成的卷设备
ULONG SerialNumber;
ULONG ReferenceCount;
WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)];
} VPB, *PVPB;
-
文件的生成/打开(IRP_MJ_CREATE)
-
文件的关闭(IRP_MJ_CLEANUP: 表示句柄数为0,IRP_MJ_CLOSE: 表示文件对象引用计数为0,即将销毁)
-
文件读/写(IRP_MJ_READ/IRP_MJ_WRITE)
-
文件设置(IRP_MJ_SET_INFORMATION)
-
文件查询(IRP_MJ_QUERY_INFORMATION)
-
目录下项查询(MajorFuncton: IRP_MJ_DIRECTORY_CONTROL, MinorFunction: IRP_MN_QUERY_DIRECTORY)
每类操作对应的内核函数如下表所示:
二、自己发送IRP请求操作文件的封装
(1)解锁并释放MDL链
//解锁并释放MDL
VOID MyFreeMdl(
IN PMDL pMdl
)
{
PMDL pMdlCurrent = pMdl, pMdlNext;
while (pMdlCurrent != NULL) {
pMdlNext = pMdlCurrent->Next;
if (pMdlCurrent->MdlFlags&MDL_PAGES_LOCKED) {
MmUnlockPages(pMdlCurrent);
}
IoFreeMdl(pMdlCurrent);
pMdlCurrent = pMdlNext;
}
return;
}
(2)文件操作完成例程:
//文件操作完成例程
NTSTATUS FileOperationCompletion(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp,
IN PVOID pContext OPTIONAL
)
{
PKEVENT pkEvent = pIrp->UserEvent;
ASSERT(pkEvent != NULL);
//趁机拷贝完成状态
RtlCopyMemory(
pIrp->UserIosb,
&pIrp->IoStatus,
sizeof(IO_STATUS_BLOCK)
);
//检查并释放MDL
if (pIrp->MdlAddress != NULL) {
MyFreeMdl(pIrp->MdlAddress);
pIrp->MdlAddress = NULL;
}
//释放IRP
IoFreeIrp(pIrp);
//设置事件
KeSetEvent(pkEvent,
IO_NO_INCREMENT,
FALSE
);
return STATUS_MORE_PROCESSING_REQUIRED;
}
2. 文件的打开/生成: 与其它IRP不同,在发送IRP_MJ_CREATE请求前就需要创建并填写好文件对象,FSD的卷设备可以从根目录pRootFileObject->Vpb->DeviceObject得到,对应的卷管理器生成的卷设备可以从: pRootFileObject->Vpb->RealDevice得到。
//未公开的函数,直接声明下就可以用了
//创建对象
NTKERNELAPI
NTSTATUS NTAPI ObCreateObject(
IN KPROCESSOR_MODE ObjectAttributesAccessMode OPTIONAL,
IN POBJECT_TYPE Type,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN KPROCESSOR_MODE AccessMode,
IN OUT PVOID ParseContext OPTIONAL,
IN ULONG ObjectSize,
IN ULONG PagedPoolCharge OPTIONAL,
IN ULONG NonPagedPoolCharge OPTIONAL,
OUT PVOID * Object);
//设置安全状态
NTKERNELAPI
NTSTATUS NTAPI SeCreateAccessState(
__out PACCESS_STATE AccessState,
__out PAUX_ACCESS_DATA AuxData,
__in ACCESS_MASK DesiredAccess,
__in_opt PGENERIC_MAPPING GenericMapping
);
未公开的数据结构
//SeCreateAccessState第2个参数
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;
//自己发送IRP请求生成/打开文件(IRP_MJ_CREATE)
NTSTATUS IrpCreateFile(
OUT PFILE_OBJECT *ppFileObject,
OUT PDEVICE_OBJECT *ppDeviceObject, //如果成功,这里保存:(*ppFileObject)->Vpb->DeviceObject
IN ACCESS_MASK DesiredAccess,
IN PUNICODE_STRING punsFilePath, //必须以"盘符:"开头, 比如: "C:..."
OUT PIO_STATUS_BLOCK pIoStatusBlock,
IN PLARGE_INTEGER AllocationSize OPTIONAL,
IN ULONG FileAttributes,
IN ULONG ShareAccess,
IN ULONG Disposition,
IN ULONG CreateOptions,
IN PVOID EaBuffer OPTIONAL,
IN ULONG EaLength
)
{
PAUX_ACCESS_DATA pAuxData = NULL;
HANDLE hRoot = NULL;
PFILE_OBJECT pRootObject = NULL, pFileObject = NULL;
PDEVICE_OBJECT pDeviceObject = NULL, pRealDeviceObject = NULL;
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
ACCESS_STATE AccessState;
KEVENT kEvent;
IO_SECURITY_CONTEXT IoSecurityContext;
OBJECT_ATTRIBUTES objAttrs;
UNICODE_STRING unsRootPath;
IO_STATUS_BLOCK userIosb;
WCHAR wRootPath[8]; //"??C:"
//路径长度检查(最短: "C:")
if (punsFilePath->Length < 3 * sizeof(CHAR)) {
return STATUS_INVALID_PARAMETER;
}
RtlZeroMemory(pIoStatusBlock, sizeof(IO_STATUS_BLOCK));
//根目录符号链接
ntStatus = RtlStringCbPrintfW(
wRootPath,
sizeof(wRootPath),
L"\??\%c:\",
punsFilePath->Buffer[0]);
ASSERT(NT_SUCCESS(ntStatus));
RtlInitUnicodeString(&unsRootPath, (PCWSTR)wRootPath);
......完整代码请点击阅读原文查看
//自己发送IRP请求关闭文件的第1步(IRP_MJ_CLEANUP),表示文件对象句柄数为0
NTSTATUS IrpCleanupFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
KEVENT kEvent;
IO_STATUS_BLOCK userIosb;
//卷参数块检查
if (pFileObject->Vpb == NULL || pFileObject->Vpb->DeviceObject == NULL) {
return STATUS_INVALID_PARAMETER;
}
//
// IRP_MJ_CLEANUP
//
//分配IRP
pIrp = IoAllocateIrp(pDeviceObject->StackSize,
FALSE);
if (pIrp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//填写IRP
pIrp->MdlAddress = NULL;
pIrp->AssociatedIrp.SystemBuffer = NULL;
pIrp->UserBuffer = NULL;
pIrp->Flags = IRP_CLOSE_OPERATION | IRP_SYNCHRONOUS_API;
pIrp->RequestorMode = KernelMode;
pIrp->UserIosb = &userIosb;
KeInitializeEvent(&kEvent,
NotificationEvent,
FALSE);
pIrp->UserEvent = &kEvent;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
//得到下层栈空间
pIrpSp = IoGetNextIrpStackLocation(pIrp);
ASSERT(pIrpSp != NULL);
//填写下层栈空间
pIrpSp->MajorFunction = IRP_MJ_CLEANUP;
pIrpSp->FileObject = pFileObject;
pIrpSp->DeviceObject = pDeviceObject;
//设置完成例程: IoSetCompletionRoutine(pIrp,FileOperationCompletion,NULL,TRUE,TRUE,TRUE);
pIrpSp->CompletionRoutine = FileOperationCompletion;
pIrpSp->Context = NULL;
pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
//下发请求
ntStatus = IoCallDriver(pDeviceObject,
pIrp);
//等待请求结束
KeWaitForSingleObject(&kEvent,
Executive,
KernelMode,
FALSE,
NULL);
//得到完成状态
ntStatus = userIosb.Status;
return ntStatus;
}
//自己发送IRP请求关闭文件的第2步(IRP_MJ_CLOSE),表示引用计数数为0
NTSTATUS IrpCloseFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
KEVENT kEvent;
IO_STATUS_BLOCK userIosb;
//检查并设置文件打开取消标志
if (pFileObject->Vpb != NULL && !(pFileObject->Flags & FO_DIRECT_DEVICE_OPEN)) {
InterlockedDecrement(&pFileObject->Vpb->ReferenceCount);
pFileObject->Flags |= FO_FILE_OPEN_CANCELLED;
}
//分配IRP
pIrp = IoAllocateIrp(pDeviceObject->StackSize,
FALSE);
if (pIrp == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//填写IRP
pIrp->MdlAddress = NULL;
pIrp->AssociatedIrp.SystemBuffer = NULL;
pIrp->UserBuffer = NULL;
pIrp->Flags = IRP_CLOSE_OPERATION | IRP_SYNCHRONOUS_API;
pIrp->RequestorMode = KernelMode;
pIrp->UserIosb = &userIosb;
KeInitializeEvent(&kEvent,
NotificationEvent,
FALSE);
pIrp->UserEvent = &kEvent;
pIrp->Tail.Overlay.Thread = PsGetCurrentThread();
pIrp->Tail.Overlay.OriginalFileObject = pFileObject;
//得到IRP下层栈空间
pIrpSp = IoGetNextIrpStackLocation(pIrp);
ASSERT(pIrpSp != NULL);
//填写IRP下层栈空间
pIrpSp->MajorFunction = IRP_MJ_CLOSE;
pIrpSp->FileObject = pFileObject;
pIrpSp->DeviceObject = pDeviceObject;
//设置完成例程: IoSetCompletionRoutine(pIrp,FileOperationCompletion,NULL,TRUE,TRUE,TRUE);
pIrpSp->CompletionRoutine = FileOperationCompletion;
pIrpSp->Context = NULL;
pIrpSp->Control = SL_INVOKE_ON_SUCCESS | SL_INVOKE_ON_ERROR | SL_INVOKE_ON_CANCEL;
//下发IRP请求
ntStatus = IoCallDriver(pDeviceObject,
pIrp);
//等待请求结束
KeWaitForSingleObject(&kEvent,
Executive,
KernelMode,
FALSE,
NULL);
//得到完成状态
ntStatus = userIosb.Status;
return ntStatus;
}
//文件读/写(IRP_MJ_READ/IRP_MJ_WRITE)
NTSTATUS IrpReadOrWriteFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
IN OUT PVOID pBuffer,
IN ULONG ulLength,
IN PLARGE_INTEGER pliByteOffset OPTIONAL,
IN BOOLEAN bRead
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK userIosb;
KEVENT kEvent;
//分配IRP
pIrp = IoAllocateIrp(
pDeviceObject->StackSize,
FALSE);
RtlZeroMemory(pIoStatusBlock,
sizeof(IO_STATUS_BLOCK));
if (pIrp == NULL) {
pIoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
return STATUS_INSUFFICIENT_RESOURCES;
}
......完整代码请点击阅读原文查看
//文件设置(IRP_MJ_SET_INFORMATION)
NTSTATUS IrpSetInformationFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject,
IN PFILE_OBJECT pTargetFileObject OPTIONAL,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
IN PVOID pFileInformation,
IN ULONG ulLength,
IN FILE_INFORMATION_CLASS FileInformationClass
)
{
PIRP pIrp = NULL;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK userIosb;
KEVENT kEvent;
//分配IRP
pIrp = IoAllocateIrp(
pDeviceObject->StackSize,
FALSE);
RtlZeroMemory(pIoStatusBlock,
sizeof(IO_STATUS_BLOCK));
if (pIrp == NULL) {
pIoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
return STATUS_INSUFFICIENT_RESOURCES;
}
......完整代码请点击阅读原文查看
//文件查询(IRP_MJ_QUERY_INFORMATION)
NTSTATUS IrpQueryInformationFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
OUT PVOID pFileInformation,
IN ULONG ulLength,
IN FILE_INFORMATION_CLASS FileInformationClass
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK userIosb;
KEVENT kEvent;
//分配IRP
pIrp = IoAllocateIrp(pDeviceObject->StackSize,
FALSE);
RtlZeroMemory(pIoStatusBlock,
sizeof(IO_STATUS_BLOCK));
if (pIrp == NULL) {
pIoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
return STATUS_INSUFFICIENT_RESOURCES;
}
......完整代码请点击阅读原文查看
//获取目录下的文件及子目录(MajorFunction: IRP_MJ_DIRECTORY_CONTROL, MinorFunction: IRP_MN_QUERY_DIRECTORY)
NTSTATUS IrpQueryDirectoryFile(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject,
OUT PIO_STATUS_BLOCK pIoStatusBlock,
OUT PVOID pFileInformation,
IN ULONG ulLength,
IN FILE_INFORMATION_CLASS FileInformationClass,
IN BOOLEAN bReturnSingleEntry,
IN PUNICODE_STRING punsFileName OPTIONAL,
IN BOOLEAN bRestartScan
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
KEVENT kEvent;
IO_STATUS_BLOCK userIosb;
//分配IRP
pIrp = IoAllocateIrp(pDeviceObject->StackSize,
FALSE);
RtlZeroMemory(pIoStatusBlock,
sizeof(IO_STATUS_BLOCK));
if (pIrp == NULL) {
pIoStatusBlock->Status = STATUS_INSUFFICIENT_RESOURCES;
return STATUS_INSUFFICIENT_RESOURCES;
}
......完整代码请点击阅读原文查看
三、文件IRP操作测试
这里实现常用的几个功能就可以把前面的文件操作涵盖。
1. 文件删除
//自己发送IRP请求删除文件
NTSTATUS RootkitDeleteFile(
IN PDEVICE_OBJECT pDeviceObject, //如果指定为pFileObject->Vpb->DeviceObject,可绕过文件系统过滤驱动
IN PFILE_OBJECT pFileObject
)
{
PFILE_BASIC_INFORMATION pFileBasicInfo = NULL;
PFILE_DISPOSITION_INFORMATION pFileDispInfo = NULL;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK iosb;
SECTION_OBJECT_POINTERS stSectionObjPointers;
PAGED_CODE();
ASSERT(pDeviceObject != NULL && pFileObject != NULL);
do {
//分配属性缓冲区
pFileBasicInfo = (PFILE_BASIC_INFORMATION)ExAllocatePoolWithTag(PagedPool,
sizeof(FILE_BASIC_INFORMATION),
MY_TAG);
if (pFileBasicInfo == NULL) {
ntStatus = STATUS_INSUFFICIENT_RESOURCES;
break;
}
//下发IRP_MJ_QUERY_INFORMATION请求查询文件属性
ntStatus = IrpQueryInformationFile(pDeviceObject,
pFileObject,
&iosb,
......完整代码请阅读原文
1) nt!NtSetInformationFile分析关键处
.text:00000001400EC230 NtSetInformationFile proc near ; DATA XREF: .pdata:00000001404EDDC4o
....
.text:00000001400ECD3C loc_1400ECD3C: ; CODE XREF: NtSetInformationFile+141DACj
.text:00000001400ECD3C cmp word ptr [r8+14h], 5Ch ; pFileLinkOrRenameInfo->FileName[0]!=L'\' or not
.text:00000001400ECD42 jnz loc_1400ECF74 ; pFileLinkOrRenameInfo->RootDirectory!=NULL or not
.text:00000001400ECD48
.text:00000001400ECD48 loc_1400ECD48: ; CODE XREF: NtSetInformationFile+D49j
.text:00000001400ECD48 mov r9, [rsp+138h+var_100] ; pFileObject
.text:00000001400ECD4D mov rdx, rdi ; pIrp
.text:00000001400ECD50 lea rcx, [rsp+138h+var_A8] ; &TargetHandle
.text:00000001400ECD58 call IopOpenLinkOrRenameTarget ; pFileLinkOrRenameInfo->FileName[0]==L'\'或pFileLinkOrRenameInfo->RootDirectory!=NULL时会执行
.text:00000001400ECD5D mov r13d, eax
.text:00000001400ECD60 movzx r14d, [rsp+138h+var_107]
.text:00000001400ECD66 test eax, eax
.text:00000001400ECD68 jns loc_1400EC93E
.text:00000001400ECD6E mov [rdi+30h], eax
.text:00000001400ECD71 jmp loc_1400EC95F
...
.text:00000001400ECF74 loc_1400ECF74: ; CODE XREF: NtSetInformationFile+B12j
.text:00000001400ECF74 cmp qword ptr [r8+8], 0 ; pFileLinkOrRenameInfo->RootDirectory!=NULL or not
.text:00000001400ECF79 jnz loc_1400ECD48 ; pFileObject
.text:00000001400ECF7F jmp loc_1400EC938
....
NTSTATUS IopOpenLinkOrRenameTarget(
OUT PHANDLE pTargetFileHandle,
IN PIRP pIrp,
IN PVOID pFileLinkOrRenameInfo, //PFILE_RENAME_INFORMATION 或者 PFILE_LINK_INFORMATION
IN PFILE_OBJECT pSrcFileObject
);
typedef struct _FILE_RENAME_INFORMATION {#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS1) union {
BOOLEAN ReplaceIfExists; // FileRenameInformation
ULONG Flags; // FileRenameInformationEx
} DUMMYUNIONNAME;#else
BOOLEAN ReplaceIfExists;#endif
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
typedef struct _FILE_LINK_INFORMATION {#if (_WIN32_WINNT >= _WIN32_WINNT_WIN10_RS5) union {
BOOLEAN ReplaceIfExists; // FileLinkInformation
ULONG Flags; // FileLinkInformationEx
} DUMMYUNIONNAME;#else
BOOLEAN ReplaceIfExists;#endif
HANDLE RootDirectory;
ULONG FileNameLength;
WCHAR FileName[1];
} FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION;
当pFileLinkOrRenameInfo->FileName[0]==L'\'或pFileLinkOrRenameInfo->RootDirectory!=NULL时都会nt!NtSetInformationFile调用nt!IopOpenLinkOrRenameTarget,这里考虑: pFileLinkOrRenameInfo->FileName包含完整路径的情况就可以实现后面的功能了。
PAGE:00000001405C03F0 IopOpenLinkOrRenameTarget proc near ; CODE XREF: NtSetInformationFile+B28p
...
;指定目标文件打开权限
PAGE:00000001405C0417 xor edi, edi
PAGE:00000001405C0419 mov r15, r9
PAGE:00000001405C041C test dword ptr [r9+50h], 800h ; pFileObject->Flags: FO_DIRECT_DEVICE_OPEN or not
PAGE:00000001405C0424 mov rbx, r8
PAGE:00000001405C0427 mov r13, rdx
PAGE:00000001405C042A mov [rbp+90h+var_110], rdi
PAGE:00000001405C042E mov r12, rcx
PAGE:00000001405C0431 lea esi, [rdi+2] ; DesiredAccess初始化: FILE_WRITE_DATA
PAGE:00000001405C0434 jnz short loc_1405C0454
PAGE:00000001405C0436 lea rdx, [rbp+90h+var_78]
PAGE:00000001405C043A mov rcx, r9
PAGE:00000001405C043D call IopGetBasicInformationFile
PAGE:00000001405C0442 test eax, eax
PAGE:00000001405C0444 js loc_1405C05C2
PAGE:00000001405C044A test [rbp+90h+var_58], 10h ; pFileBasicInfo->FileAttributes: FILE_ATTRIBUTE_DIRECTORY or not
PAGE:00000001405C044E lea eax, [rdi+4] ; FILE_ADD_SUBDIRECTORY
PAGE:00000001405C0451 cmovnz esi, eax ; 后续打开权限: 如果是目录则为FILE_ADD_SUBDIRECTORY,否则FILE_WRITE_DATA
...完整代码请点击阅读原文
//自己发送IRP请求更名文件
//wNewFileNameOrPath: 如果只是文件名,则简单进行更名。如果是文件路径,则表示文件移动到相同卷的不同目录下,比如:移到回收站是最常见的操作
NTSTATUS RootkitRenameFile(
IN PDEVICE_OBJECT pDeviceObject, //如果指定为pFileObject->Vpb->DeviceObject,可绕过文件系统过滤驱动
IN PFILE_OBJECT pFileObject,
IN LPCWSTR wNewFileNameOrPath,
IN BOOLEAN bReplaceIfExists
)
{
LPWSTR lpwNewFileNameOrPath = NULL;
PFILE_OBJECT pRootFileObject = NULL, pTargetFileObject = NULL;
PDEVICE_OBJECT pTargetDeviceObject = NULL;
PFILE_RENAME_INFORMATION pFileRenameInfo = NULL;
HANDLE hRoot = NULL, hTargetFile = NULL;
SIZE_T nRenameInfoLen;
NTSTATUS ntStatus = STATUS_NOT_SUPPORTED;
UNICODE_STRING unsNewFileNameOrPath, unsRootPath;
OBJECT_ATTRIBUTES objAttrs;
IO_STATUS_BLOCK iosb;
BOOLEAN bNeedPrefix = TRUE;
PAGED_CODE();
ASSERT(pDeviceObject != NULL && pFileObject != NULL && wNewFileNameOrPath != NULL);
......
完整代码请点击阅读原文
//拷贝文件
NTSTATUS RootkitCopyFile(
IN PDEVICE_OBJECT pSrcDeviceObject,
IN PFILE_OBJECT pSrcFileObject,
IN PDEVICE_OBJECT pDestDeviceObject,
IN PFILE_OBJECT pDestFileObject,
IN BOOLEAN bDeleteSrcFile
)
{
PVOID lpTransBuf;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
IO_STATUS_BLOCK iosb;
LARGE_INTEGER liBytesOffset;
PAGED_CODE();
//分配文件读写缓冲区
lpTransBuf = ExAllocatePoolWithTag(NonPagedPool,
FILE_TRANS_BUF_MAXLEN,
MY_TAG);
if (lpTransBuf == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
......
完整代码请点击阅读原文
4. 模拟FindFirstFile/FindNextFile/FindClose实现目录下文件及子目录的枚举
//
//查询目录下的文件相关数据结构(与应用层交互)
//
//被查询的目录打开信息
typedef struct _FIND_FILE_HANDLE_INFO {
PVOID pDeviceObject; //卷设备(内部用)
PVOID pFileObject; //目录文件对象(内部用)
}FIND_FILE_HANDLE_INFO, *PFIND_FILE_HANDLE_INFO, *LPFIND_FILE_HANDLE_INFO;
//发现项信息
typedef struct _FIND_FILE_OUTPUT {
LARGE_INTEGER CreationTime; //创建时间
LARGE_INTEGER LastAccessTime; //最近访问时间
LARGE_INTEGER LastWriteTime; //最近写入时间
LARGE_INTEGER ChangeTime; //变更时间
LARGE_INTEGER EndOfFile; //文件大小
LARGE_INTEGER AllocationSize; //占用空间大小
ULONG ulFileAttributes; //属性
WCHAR wShortFileName[14]; //8.3 格式名
WCHAR wFileName[MAX_PATH]; //名称
} FIND_FILE_OUTPUT, *PFIND_FILE_OUTPUT, *LPFIND_FILE_OUTPUT;
//首次查询的输出缓冲区结构
typedef struct _FIND_FIRST_FILE_OUTPUT {
FIND_FILE_HANDLE_INFO stFileFileHandleInfo; //被查询的目录打开信息
FIND_FILE_OUTPUT stFindFileItem; //发现项信息
}FIND_FIRST_FILE_OUTPUT,*PFIND_FIRST_FILE_OUTPUT,*LPFIND_FIRST_FILE_OUTPUT;
(2)实现:
//自己发送IRP请求查询目录下的1项封装
NTSTATUS RootkitFindFileItem(
IN PDEVICE_OBJECT pDeviceObject,
IN PFILE_OBJECT pFileObject,
IN BOOLEAN bRestartScan,
OUT PFIND_FILE_OUTPUT pFindFileOut
)
{
PFILE_BOTH_DIR_INFORMATION pFileBothDirInfo;
IO_STATUS_BLOCK iosb;
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PAGED_CODE();
ASSERT(pDeviceObject != NULL && pFileObject != NULL && pFindFileOut != NULL);
//分配缓冲区
pFileBothDirInfo = (PFILE_BOTH_DIR_INFORMATION)ExAllocatePoolWithTag(PagedPool,
PAGE_SIZE,
MY_TAG);
if (pFileBothDirInfo == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory(pFileBothDirInfo,
PAGE_SIZE);
//发送IRP请求查询目录下的1项
......
完整代码请点击阅读原文
//
// 设备控制请求
//
#define BASE_DEVICE_TYPE FILE_DEVICE_UNKNOWN
#define IOCTL_IRP_DELETEFILE CTL_CODE(BASE_DEVICE_TYPE, 0x101, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_IRP_RENAMEFILE CTL_CODE(BASE_DEVICE_TYPE, 0x102, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_IRP_COPYFILE CTL_CODE(BASE_DEVICE_TYPE, 0x103, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_IRP_FINDFIRSTFILE CTL_CODE(BASE_DEVICE_TYPE, 0x104, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCTL_IRP_FINDNEXTFILE CTL_CODE(BASE_DEVICE_TYPE, 0x105, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define IOCLT_IRP_FINDCLOSE CTL_CODE(BASE_DEVICE_TYPE, 0x106, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
//不可换页的节,确保:有关自己发送IRP请求的封装函数Irpxxx是不可换页的,因为前面的完成例程FileOperationCompletion中要访问
//pIrp->UserIosb及pIrp->UserEvent,而这2个指针成员指向的是Irpxxx栈上的局部变量, 其余对其调用的外围函数不受此限制
#pragma alloc_text(PAGELK,IrpCreateFile)
#pragma alloc_text(PAGELK,IrpCleanupFile)
#pragma alloc_text(PAGELK,IrpCloseFile)
#pragma alloc_text(PAGELK,IrpQueryInformationFile)
#pragma alloc_text(PAGELK,IrpSetInformationFile)
#pragma alloc_text(PAGELK,IrpReadOrWriteFile)
#pragma alloc_text(PAGELK,IrpQueryDirectoryFile)
//
// 设备控制请求
//
//
//交互数据结构
//
//文件重命名数据结构(与应用层交互)
typedef struct _IOCTL_RENAME_FILE_INFO {
WCHAR wOldFilePath[MAX_PATH]; //更名前的文件路径
WCHAR wNewFileNameOrPath[MAX_PATH]; //更名后的文件名或路径
BOOLEAN bReplaceIfExists; //文件名如果存在是否替换该文件
}IOCTL_RENAME_FILE_INFO, *PIOCTL_RENAME_FILE_INFO, *LPIOCTL_RENAME_FILE_INFO;
//文件拷贝数据结构(与应用层交互)
typedef struct _IOCTL_COPY_FILE_INFO {
WCHAR wSrcFilePath[MAX_PATH]; //源文件路径
WCHAR wDestFilePath[MAX_PATH]; //目标文件路径
BOOLEAN bDeleteSrcFile; //拷贝成功后是否删除源文件
}IOCTL_COPY_FILE_INFO, *PIOCTL_COPY_FILE_INFO, *LPIOCTL_COPY_FILE_INFO;
//
//查询目录下的文件相关数据结构(与应用层交互)
//
//被查询的目录打开信息
typedef struct _FIND_FILE_HANDLE_INFO {
PVOID pDeviceObject; //卷设备(内部用)
PVOID pFileObject; //目录文件对象(内部用)
}FIND_FILE_HANDLE_INFO, *PFIND_FILE_HANDLE_INFO, *LPFIND_FILE_HANDLE_INFO;
//发现项信息
typedef struct _FIND_FILE_OUTPUT {
LARGE_INTEGER CreationTime; //创建时间
LARGE_INTEGER LastAccessTime; //最近访问时间
LARGE_INTEGER LastWriteTime; //最近写入时间
LARGE_INTEGER ChangeTime; //最近更改时间
LARGE_INTEGER EndOfFile; //文件大小
LARGE_INTEGER AllocationSize; //占用空间大小
ULONG ulFileAttributes; //属性
WCHAR wShortFileName[14]; //8.3 格式名
WCHAR wFileName[MAX_PATH]; //名称
} FIND_FILE_OUTPUT, *PFIND_FILE_OUTPUT, *LPFIND_FILE_OUTPUT;
//首次查询的输出缓冲区结构
typedef struct _FIND_FIRST_FILE_OUTPUT {
FIND_FILE_HANDLE_INFO stFileFileHandleInfo; //被查询的目录打开信息
FIND_FILE_OUTPUT stFindFileItem; //发现项信息
}FIND_FIRST_FILE_OUTPUT, *PFIND_FIRST_FILE_OUTPUT, *LPFIND_FIRST_FILE_OUTPUT;
...
//
//功能函数
//
//文件强删
BOOL RootkitDeleteFile(
IN HANDLE hDevice,
IN LPCTSTR szFilePath
)
{
LPCWSTR lpwFilePath;
BOOL bSuc;
DWORD dwBytesRet;
//指定输入缓冲区,UNICODE字符集与多字节字符集分别考虑
lpwFilePath = (LPCWSTR)szFilePath;
WCHAR wFilePath[MAX_PATH] = { L'0' };
::MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)szFilePath,
-1,
wFilePath,
sizeof(wFilePath)/sizeof(WCHAR));
wFilePath[sizeof(wFilePath) / sizeof(WCHAR) - 1] = L'0';
lpwFilePath = &wFilePath[0];
//发送设备控制请求
bSuc = ::DeviceIoControl(hDevice,
IOCTL_IRP_DELETEFILE,
(LPVOID)lpwFilePath,
(::wcslen(lpwFilePath)+1)*sizeof(WCHAR),
NULL,
0,
&dwBytesRet,
NULL);
return bSuc;
}
//文件更名
BOOL RootkitRenameFile(
IN HANDLE hDevice,
IN LPCTSTR szOldFilePath,
IN LPCTSTR szNewFileNameOrPath, //如果只包含文件名,则进行简单更名。如果是文件全路径,则移动到相同卷的不同目录下, 比如:移到回收站是最常见的操作
IN BOOLEAN bReplaceIfExists
)
{
LPIOCTL_RENAME_FILE_INFO lpIoctlRenameFileInfo;
BOOL bSuc;
DWORD dwBytesRet, dwErr;
//分配输入缓冲区
lpIoctlRenameFileInfo = new IOCTL_RENAME_FILE_INFO;
if (lpIoctlRenameFileInfo == NULL) {
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
::ZeroMemory(lpIoctlRenameFileInfo, sizeof(IOCTL_RENAME_FILE_INFO));
//填写输入缓冲区,UNICODE字符集与多字节字符集分别考虑
::wcscpy_s(lpIoctlRenameFileInfo->wOldFilePath, sizeof(lpIoctlRenameFileInfo->wOldFilePath) / sizeof(WCHAR), (LPCWSTR)szOldFilePath);
::wcscpy_s(lpIoctlRenameFileInfo->wNewFileNameOrPath, sizeof(lpIoctlRenameFileInfo->wNewFileNameOrPath) / sizeof(WCHAR), (LPCWSTR)szNewFileNameOrPath);
::MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)szOldFilePath,
-1,
lpIoctlRenameFileInfo->wOldFilePath,
sizeof(lpIoctlRenameFileInfo->wOldFilePath)/sizeof(WCHAR));
lpIoctlRenameFileInfo->wOldFilePath[sizeof(lpIoctlRenameFileInfo->wOldFilePath) / sizeof(WCHAR) - 1] = L'0';
::MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)szNewFileNameOrPath,
-1,
lpIoctlRenameFileInfo->wNewFileNameOrPath,
sizeof(lpIoctlRenameFileInfo->wNewFileNameOrPath) / sizeof(WCHAR));
lpIoctlRenameFileInfo->wNewFileNameOrPath[sizeof(lpIoctlRenameFileInfo->wNewFileNameOrPath) / sizeof(WCHAR) - 1] = L'0';
lpIoctlRenameFileInfo->bReplaceIfExists = bReplaceIfExists;
//发送设备控制请求
if ((bSuc = ::DeviceIoControl(hDevice,
IOCTL_IRP_RENAMEFILE,
(LPVOID)lpIoctlRenameFileInfo,
sizeof(IOCTL_RENAME_FILE_INFO),
NULL,
0,
&dwBytesRet,
NULL))){
dwErr = ERROR_SUCCESS;
}else {
dwErr = ::GetLastError();
}
//释放输入缓冲区
delete lpIoctlRenameFileInfo;
::SetLastError(dwErr);
return bSuc;
}
//文件复制/移动(虽然RootkitRenameFile也能移动文件,但不同的是: RootkitCopyFile可以把文件移动到不同的卷下)
BOOL RootkitCopyFile(
IN HANDLE hDevice,
IN LPCTSTR szSrcFilePath,
IN LPCTSTR szDestFilePath,
IN BOOLEAN bDeleteSrcFile //为TRUE: 表示移动文件
)
{
LPIOCTL_COPY_FILE_INFO lpIoctlCopyFileInfo;
BOOL bSuc;
DWORD dwBytesRet, dwErr;
//分配输入缓冲区
lpIoctlCopyFileInfo = new IOCTL_COPY_FILE_INFO;
if (lpIoctlCopyFileInfo == NULL) {
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return FALSE;
}
::ZeroMemory(lpIoctlCopyFileInfo, sizeof(IOCTL_COPY_FILE_INFO));
//填写输入缓冲区,UNICODE字符集与多字节字符集分别考虑
::wcscpy_s(lpIoctlCopyFileInfo->wSrcFilePath, sizeof(lpIoctlCopyFileInfo->wSrcFilePath) / sizeof(WCHAR), (LPCWSTR)szSrcFilePath);
::wcscpy_s(lpIoctlCopyFileInfo->wDestFilePath, sizeof(lpIoctlCopyFileInfo->wDestFilePath) / sizeof(WCHAR), (LPCWSTR)szDestFilePath);
::MultiByteToWideChar(
CP_ACP,
0,
(LPCSTR)szSrcFilePath,
-1,
lpIoctlCopyFileInfo->wSrcFilePath,
sizeof(lpIoctlCopyFileInfo->wSrcFilePath)/sizeof(WCHAR));
lpIoctlCopyFileInfo->wSrcFilePath[sizeof(lpIoctlCopyFileInfo->wSrcFilePath) / sizeof(WCHAR) - 1] = L'0';
::MultiByteToWideChar(
CP_ACP,
0,
(LPCSTR)szDestFilePath,
-1,
lpIoctlCopyFileInfo->wDestFilePath,
sizeof(lpIoctlCopyFileInfo->wDestFilePath) / sizeof(WCHAR));
lpIoctlCopyFileInfo->wDestFilePath[sizeof(lpIoctlCopyFileInfo->wDestFilePath) / sizeof(WCHAR) - 1] = L'0';
lpIoctlCopyFileInfo->bDeleteSrcFile = bDeleteSrcFile;
//发送设备控制请求
if (bSuc = ::DeviceIoControl(hDevice,
IOCTL_IRP_COPYFILE,
(LPVOID)lpIoctlCopyFileInfo,
sizeof(IOCTL_COPY_FILE_INFO),
NULL,
0,
&dwBytesRet,
NULL)) {
dwErr = ERROR_SUCCESS;
}else {
dwErr = ::GetLastError();
}
//释放输入缓冲区
delete lpIoctlCopyFileInfo;
::SetLastError(dwErr);
return bSuc;
}
//查询目录下的第1项
HANDLE RootkitFindFirstFile(
IN HANDLE hDevice,
IN LPCTSTR szDirPath,
OUT LPFIND_FILE_OUTPUT lpFindFileOutput
)
{
HANDLE hFindFile = INVALID_HANDLE_VALUE;
LPFIND_FIRST_FILE_OUTPUT lpFindFirstFileOutput;
LPCWSTR lpwDirPath;
DWORD dwBytesRet, dwErr;
WCHAR wDirPath[MAX_PATH] = {L'0'};
//分配输出缓冲区
lpFindFirstFileOutput = new FIND_FIRST_FILE_OUTPUT;
if (lpFindFirstFileOutput == NULL) {
::SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return INVALID_HANDLE_VALUE;
}
//指定输入缓冲区, UNICODE字符集与多字节字符集分别考虑
lpwDirPath = (LPCWSTR)szDirPath;
::MultiByteToWideChar(CP_ACP,
0,
(LPCSTR)szDirPath,
-1,
wDirPath,
sizeof(wDirPath) / sizeof(WCHAR));
wDirPath[sizeof(wDirPath) / sizeof(WCHAR) - 1] = L'0';
lpwDirPath = &wDirPath[0];
//发送设备控制请求
if (::DeviceIoControl(hDevice,
IOCTL_IRP_FINDFIRSTFILE,
(LPVOID)lpwDirPath,
(::wcslen(lpwDirPath) + 1) * sizeof(WCHAR),
(LPVOID)lpFindFirstFileOutput,
sizeof(FIND_FIRST_FILE_OUTPUT),
&dwBytesRet,
NULL)) {
hFindFile = (HANDLE)lpFindFirstFileOutput; //将第1次查询目录的输出缓冲区地址作为返回句柄值
dwErr = ERROR_SUCCESS;
}else {
dwErr = ::GetLastError();
}
if (hFindFile != INVALID_HANDLE_VALUE) {
//成功时的处理
::CopyMemory(lpFindFileOutput,
&lpFindFirstFileOutput->stFindFileItem,
sizeof(FIND_FILE_OUTPUT)); //文件项信息保存
}else {
//失败时的处理
delete lpFindFirstFileOutput;
}
return hFindFile;
}
//查询目录的下1项
BOOL RootkitFindNextFile(
IN HANDLE hDevice,
IN HANDLE hFindFile,
OUT LPFIND_FILE_OUTPUT lpFindFileOutput
)
{
LPFIND_FIRST_FILE_OUTPUT lpFindFirstFileOutput;
BOOL bSuc;
DWORD dwBytesRet;
//检查参数合法性
if (hFindFile == INVALID_HANDLE_VALUE || hFindFile == NULL) {
::SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
//发送设备控制请求
lpFindFirstFileOutput = (LPFIND_FIRST_FILE_OUTPUT)hFindFile;
bSuc = ::DeviceIoControl(hDevice,
IOCTL_IRP_FINDNEXTFILE,
(LPVOID)&lpFindFirstFileOutput->stFileFileHandleInfo,
sizeof(FIND_FILE_HANDLE_INFO),
(LPVOID)lpFindFileOutput,
sizeof(FIND_FILE_OUTPUT),
&dwBytesRet,
NULL);
return bSuc;
}
//停止查询目录
BOOL RootkitFindClose(
IN HANDLE hDevice,
IN HANDLE hFindFile
)
{
LPFIND_FIRST_FILE_OUTPUT lpFindFirstFileOutput;
BOOL bSuc;
DWORD dwBytesRet, dwErr;
//检查参数合法性
if (hFindFile == INVALID_HANDLE_VALUE || hFindFile == NULL) {
::SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
//发送设备控制请求
lpFindFirstFileOutput = (LPFIND_FIRST_FILE_OUTPUT)hFindFile;
if (bSuc = ::DeviceIoControl(hDevice,
IOCLT_IRP_FINDCLOSE,
(LPVOID)&lpFindFirstFileOutput->stFileFileHandleInfo,
sizeof(FIND_FILE_HANDLE_INFO),
NULL,
0,
&dwBytesRet,
NULL)) {
dwErr = ERROR_SUCCESS;
delete lpFindFirstFileOutput;
}else {
dwErr = ::GetLastError();
}
::SetLastError(dwErr);
return bSuc;
}
看雪ID:低调putchar
https://bbs.pediy.com/user-home-817607.htm
*本文由看雪论坛 低调putchar 原创,转载请注明来自看雪社区。
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!
本文始发于微信公众号(看雪学院):内核层自己发送IRP请求操作文件全面总结
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论