x64驱动基础教程 09

栏目: 服务器 · Linux · 发布时间: 7年前

内容简介:RING0 操作文件和 RING3 操作文件在流程上没什么大的区别,也是“获得文件句柄->读/ 写/删/改->关闭文件句柄”的模式。当然了,只能用内核 API,不能用 WIN32API。在讲解具体的代码之前,先讲解一下文件系统的流程,让大家对整个文件系统有个大概的了解。假设我们要读写一个文件,无论在 RING3 调用 ReadFile,还是在 RING0 调用 NtReadFile,它们最终会转换为 IRP,发送到文件系统驱动(具体哪个驱动和分区类型相关,如果是 FAT32分区,则是 FASTFAT.SY

RING0 操作文件和 RING3 操作文件在流程上没什么大的区别,也是“获得文件句柄->读/ 写/删/改->关闭文件句柄”的模式。当然了,只能用内核 API,不能用 WIN32API。在讲解具体的代码之前,先讲解一下文件系统的流程,让大家对整个文件系统有个大概的了解。

假设我们要读写一个文件,无论在 RING3 调用 ReadFile,还是在 RING0 调用 NtReadFile,它们最终会转换为 IRP,发送到文件系统驱动(具体哪个驱动和分区类型相关,如果是 FAT32

分区,则是 FASTFAT.SYS;如果是 NTFS 分区,则是 NTFS.SYS)的 IRP_MJ_READ 分发函数里。文件系统驱动经过一定处理后,就把 IRP 传给磁盘类驱动(通常是 CLASSPNP.SYS,此驱动的源码在 WDK 里有)的 IRP_MJ_READ 分发函数处理。磁盘类驱动处理完毕后,又把 IRP 传给磁盘小端口驱动的 IRP_MJ_SCSI 分发函数处理。磁盘小端口驱动太多了,网上有人用 ATAPI.SYS 来指代磁盘小端口驱动,是极端错误的说法。ATAPI.SYS 是磁盘小端口驱动,但磁盘小端口驱动绝非只能是 ATAPI.SYS,常见的磁盘小端口驱动还有 LSI_SAS.SYS 等。如果安装了芯片组驱动,磁盘小端口驱动通常会被替换成主板厂商的驱动。比安装了英特尔 P67、 HM77 的芯片组驱动后,磁盘小端口驱动就会变成 iaStroV.sys。在磁盘小端口驱动里,无论是读还是写,用的都是 IRP_MJ_SCSI 的分发函数。IRP 被磁盘小端口驱动处理完之后,就要依靠 HAL.DLL 进行端口 IO,此时数据就真的从硬盘里读取了出来。接下来再按照相反的方向把数据返回到调用者。另外,在内核里,文件夹和文件没啥本质的区别。比如 ZwDeleteFile 既可以删除文件,也可以删除文件夹。接下来举几个例子,让大家了解内核里读写、删除、重命名和枚举文件,以及获取文件信息。

1.复制文件(包括了读文件、写文件):

BOOLEAN ZwCopyFile
    (
    IN PUNICODE_STRING ustrDestFile, // \??\c:\1.txt
    IN PUNICODE_STRING ustrSrcFile // \??\c:\0.txt
    ) {
        HANDLE hSrcFile, hDestFile;
        PVOID buffer = NULL;
        ULONG length = 0;
        LARGE_INTEGER offset = {0};
        IO_STATUS_BLOCK Io_Status_Block = {0};
        OBJECT_ATTRIBUTES obj_attrib;
        NTSTATUS status;
        BOOLEAN bRet = FALSE;
        do
        {
            // 打开源文件
            InitializeObjectAttributes( &obj_attrib,
                ustrSrcFile,
                OBJ_CASE_INSENSITIVE |
                OBJ_KERNEL_HANDLE,
                NULL,
                NULL);
            status = ZwCreateFile( &hSrcFile,
                GENERIC_READ,
                &obj_attrib,
                &Io_Status_Block,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_READ,
                FILE_OPEN,
                FILE_NON_DIRECTORY_FILE |
                FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0 );
            if (!NT_SUCCESS(status))
            {
                bRet = FALSE;
                goto END;
            }
            // 打开目标文件
            InitializeObjectAttributes( &obj_attrib,
                ustrDestFile,
                OBJ_CASE_INSENSITIVE |
                OBJ_KERNEL_HANDLE,
                NULL,
                NULL);
            status = ZwCreateFile( &hDestFile,
                GENERIC_WRITE,
                &obj_attrib,
                &Io_Status_Block,
                NULL,
                FILE_ATTRIBUTE_NORMAL,
                FILE_SHARE_READ,
                FILE_OPEN_IF,
                FILE_NON_DIRECTORY_FILE |
                FILE_SYNCHRONOUS_IO_NONALERT,
                NULL,
                0 );
            if (!NT_SUCCESS(status))
            {
                bRet = FALSE;
                goto END;
            }
            // 为 buffer 分配 4KB 空间
            buffer = ExAllocatePool(NonPagedPool, 1024 * 4);
            if (buffer == NULL)
            {
                bRet = FALSE;
                goto END;
            }
            // 复制文件
            while (1)
            {
                length = 4 * 1024;
                // 读取源文件
                status = ZwReadFile(hSrcFile,
                    NULL,
                    NULL,
                    NULL,
                    &Io_Status_Block,
                    buffer,
                    length,
                    &offset,
                    NULL);
                if (!NT_SUCCESS(status))
                {
                    // 如果状态为 STATUS_END_OF_FILE,说明文件已经读取到末尾
                    if (status == STATUS_END_OF_FILE)
                    {
                        bRet = TRUE;
                        goto END;
                    }
                }
                // 获得实际读取的长度
                length = (ULONG)Io_Status_Block.Information;
                // 写入到目标文件
                status = ZwWriteFile( hDestFile,
                    NULL,
                    NULL,
                    NULL,
                    &Io_Status_Block,
                    buffer,
                    length,
                    &offset,
                    NULL);
                if (!NT_SUCCESS(status))
                {
                    bRet = FALSE;
                    goto END;
                }
                // 移动文件指针
                offset.QuadPart += length;
            }
        }
        while (0);
END:
        if (hSrcFile)
        {
            ZwClose(hSrcFile);
        }
        if (hDestFile)
        {
            ZwClose(hDestFile);
        }
        if (buffer != NULL)
        {
            ExFreePool(buffer);
        }
        return bRet;
}

2. 删除文件 / 文件夹:

void ZwDeleteFileFolder(WCHAR *wsFileName)
{
    NTSTATUS st;
    OBJECT_ATTRIBUTES ObjectAttributes;
    UNICODE_STRING UniFileName;
    //把 WCHAR*转化为 UNICODE_STRING
    RtlInitUnicodeString(&UniFileName, wsFileName);
    //设置包 OBJECT 对象并使用 ZwDeleteFile 删除
    InitializeObjectAttributes(&ObjectAttributes,
        &UniFileName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL);
    st=ZwDeleteFile(&ObjectAttributes);
}

3. 重命名文件 / 文件夹

typedef struct _FILE_RENAME_INFORMATION
{
    BOOLEAN ReplaceIfExists;
    HANDLE RootDirectory;
    ULONG FileNameLength;
        WCHAR FileName[1];
} FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
NTSTATUS
    ZwRenameFile
    (
    IN PWSTR SrcFileName, // \??\x:\xxx\...\xxx.xxx
    IN PWSTR DstFileName // \??\x:\xxx\...\xxx.xxx
    ) {
#define RN_MAX_PATH 2048
#define SFLT_POOL_TAG 'fuck'
        HANDLE FileHandle = NULL;
        OBJECT_ATTRIBUTES ObjectAttributes;
        IO_STATUS_BLOCK IoStatus;
        NTSTATUS Status;
        PFILE_RENAME_INFORMATION RenameInfo = NULL;
        UNICODE_STRING ObjectName;
        //设置重命名的信息
        RenameInfo = (PFILE_RENAME_INFORMATION)ExAllocatePoolWithTag(NonPagedPool,
            sizeof(FILE_RENAME_INFORMATION) + RN_MAX_PATH * sizeof(WCHAR), SFLT_POOL_TAG);
        if (RenameInfo == NULL)
        {
            return STATUS_INSUFFICIENT_RESOURCES;
        }
        RtlZeroMemory(RenameInfo, sizeof(FILE_RENAME_INFORMATION) + RN_MAX_PATH *
            sizeof(WCHAR));
        RenameInfo->FileNameLength = wcslen(DstFileName) * sizeof(WCHAR);
        wcscpy(RenameInfo->FileName, DstFileName);
        RenameInfo->ReplaceIfExists = 0;
        RenameInfo->RootDirectory = NULL;
        //设置源文件信息并获得句柄
        RtlInitUnicodeString(&ObjectName, SrcFileName);
        InitializeObjectAttributes(&ObjectAttributes,
            &ObjectName,
            OBJ_CASE_INSENSITIVE,
            NULL,
            NULL);
        Status = ZwCreateFile(&FileHandle,
            SYNCHRONIZE | DELETE,
            &ObjectAttributes,
            &IoStatus,
            NULL,
            0,
            FILE_SHARE_READ,
            FILE_OPEN,
            FILE_SYNCHRONOUS_IO_NONALERT |
            FILE_NO_INTERMEDIATE_BUFFERING,
            NULL,
            0);
        if (!NT_SUCCESS(Status))
        {
            ExFreePoolWithTag(RenameInfo, SFLT_POOL_TAG);
            return Status;
        }
        //最关键一步,利用 ZwSetInformationFile 来设置文件信息
        Status = ZwSetInformationFile(FileHandle,
            &IoStatus,
            RenameInfo,
            sizeof(FILE_RENAME_INFORMATION) +
            RN_MAX_PATH * sizeof(WCHAR),
            FileRenameInformation);
        if (!NT_SUCCESS(Status))
        {
            ExFreePoolWithTag(RenameInfo, SFLT_POOL_TAG);
            ZwClose(FileHandle);
            return Status;
        }
        ZwClose(FileHandle);
        return Status;
}

4. 获取文件大小:

//这里传入的是文件句柄不是文件名, 大家尝试把这里改成传入文件名
ULONG64 GetFileSize(HANDLE hfile)
{
    IO_STATUS_BLOCK iostatus= {0};
    NTSTATUS ntStatus=0;
    FILE_STANDARD_INFORMATION fsi= {0};
    ntStatus=ZwQueryInformationFile(hfile,
        &iostatus,
        &fsi,
        sizeof(FILE_STANDARD_INFORMATION),
        FileStandardInformation);
    if(!NT_SUCCESS(ntStatus))
        return 0;
    return fsi.EndOfFile.QuadPart;
}

5.枚举文件(RING3 的 FindFirstFile 和 FindNextFile 内部就是用 ZwQueryDirectoryFile 实现的为了方便大家以后抄代码,我就把 ZwQueryDirectoryFile 封装成了 RING0 版的 FindFirstFile 和FindNextFile):

NTKERNELAPI NTSTATUS ZwQueryDirectoryFile //最关键的 API
    (
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID FileInformation,
    ULONG Length,
    FILE_INFORMATION_CLASS FileInformationClass,
    BOOLEAN ReturnSingleEntry,
    PUNICODE_STRING FileName,
    BOOLEAN RestartScan
    );
//几个常量
#define INVALID_HANDLE_VALUE (HANDLE)-1
#define MAX_PATH2 4096
#define kmalloc(_s)ExAllocatePoolWithTag(NonPagedPool, _s, 'SYSQ')
#define kfree(_p) ExFreePool(_p)
//枚举文件用到的结构体
typedef struct _FILE_BOTH_DIR_INFORMATION
{
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    CCHAR ShortNameLength;
    WCHAR ShortName[12];
    WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;
//山寨版 MyFindFirstFile
HANDLE MyFindFirstFile(LPSTR lpDirectory,PFILE_BOTH_DIR_INFORMATION pDir,ULONG uLength)
{
    char strFolder[MAX_PATH2]= {0};
    STRING astrFolder;
    UNICODE_STRING ustrFolder;
    OBJECT_ATTRIBUTES oa;
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS ntStatus;
    HANDLE hFind = INVALID_HANDLE_VALUE;
    memset(strFolder,0,MAX_PATH2);
    strcpy(strFolder,"\\??\\");
    strcat(strFolder,lpDirectory);
    RtlInitString(&astrFolder,strFolder);
    if (RtlAnsiStringToUnicodeString(&ustrFolder,&astrFolder,TRUE)==0)
    {
        InitializeObjectAttributes(&oa,&ustrFolder,OBJ_CASE_INSENSITIVE,NULL,NULL);
        ntStatus = IoCreateFile(
            &hFind,
            FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_ANY_ACCESS,
            &oa,
            &ioStatus,
            NULL,
            FILE_ATTRIBUTE_NORMAL,
            FILE_SHARE_READ | FILE_SHARE_WRITE,
            FILE_OPEN, //FILE_OPEN
            FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT |
            FILE_OPEN_FOR_BACKUP_INTENT,
            NULL,
            0,
            CreateFileTypeNone,
            NULL,
            IO_NO_PARAMETER_CHECKING);
        RtlFreeUnicodeString(&ustrFolder);
        if (ntStatus==0 && hFind!=INVALID_HANDLE_VALUE)
        {
            ntStatus=ZwQueryDirectoryFile(
                hFind, // File Handle
                NULL, // Event
                NULL, // Apc routine
                NULL, // Apc context
                &ioStatus, // IoStatusBlock
                pDir, // FileInformation
                uLength, // Length
                FileBothDirectoryInformation, // FileInformationClass
                TRUE, // ReturnSingleEntry
                NULL, // FileName
                FALSE //RestartScan
                );
            if (ntStatus!=0)
            {
                ZwClose(hFind);
                hFind=INVALID_HANDLE_VALUE;
            }
        }
    }
    return hFind;
} 
//山寨版 MyFindNextFile
BOOLEAN MyFindNextFile(HANDLE hFind, PFILE_BOTH_DIR_INFORMATION pDir, ULONG
    uLength)
{
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS ntStatus;
    ntStatus=ZwQueryDirectoryFile(
        hFind, // File Handle
        NULL, // Event
        NULL, // Apc routine
        NULL, // Apc context
        &ioStatus, // IoStatusBlock
        pDir, // FileInformation
        uLength, // Length
        FileBothDirectoryInformation, // FileInformationClass
        FALSE, // ReturnSingleEntry
        NULL, // FileName
        FALSE //RestartScan
        );
    if (ntStatus==0)
        return TRUE;
    else
        return FALSE;
} 
//枚举文件夹内容的函数,输入路径,返回目录下的文件和文件夹数目
ULONG SearchDirectory(LPSTR lpPath)
{
    ULONG muFileCount=0;
    HANDLE hFind=INVALID_HANDLE_VALUE;
    PFILE_BOTH_DIR_INFORMATION pDir;
    char *strBuffer = NULL,*lpTmp=NULL;
    char strFileName[255*2];
        ULONG uLength=MAX_PATH2*2 + sizeof(FILE_BOTH_DIR_INFORMATION);
    strBuffer = (PCHAR)kmalloc(uLength);
    pDir = (PFILE_BOTH_DIR_INFORMATION)strBuffer;
    hFind=MyFindFirstFile(lpPath,pDir,uLength);
    if (hFind!=INVALID_HANDLE_VALUE)
    {
        kfree(strBuffer);
        uLength=(MAX_PATH2*2 + sizeof(FILE_BOTH_DIR_INFORMATION)) * 0x2000;
        strBuffer = (PCHAR)kmalloc(uLength);
        pDir = (PFILE_BOTH_DIR_INFORMATION)strBuffer;
        if (MyFindNextFile(hFind,pDir,uLength))
        {
            while (TRUE)
            {
                memset(strFileName,0,255*2);
                memcpy(strFileName,pDir->FileName,pDir->FileNameLength);
                if (strcmp(strFileName,"..")!=0 && strcmp(strFileName,".")!=0)
                {
                    if (pDir->FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
                    {
                        DbgPrint("[目录]%S\n",strFileName);
                    }
                    else
                    {
                        DbgPrint("[文件]%S\n",strFileName);
                    }
                    muFileCount++;
                }
                if (pDir->NextEntryOffset==0) break;
                pDir = (PFILE_BOTH_DIR_INFORMATION)((char
                    *)pDir+pDir->NextEntryOffset);
            }
            kfree(strBuffer);
        }
        ZwClose(hFind);
    }
    return muFileCount;
}

6.创建文件夹(其实用 IoCreateFile 也能实现 ZwCreateFile 的功能, ZwCreateFile 不过是IoCreateFile 的 stub 而已。 下面利用 IoCreateFile 创建文件夹):

void ZwCreateFolder(char *FolderPath)
{
    NTSTATUS st;
    HANDLE FileHandle;
    OBJECT_ATTRIBUTES ObjectAttributes;
    IO_STATUS_BLOCK IoStatusBlock;
    UNICODE_STRING UniFileName;
    WCHAR wsFileName[2048]= {0};
    CharToWchar(FolderPath,wsFileName);
    RtlInitUnicodeString(&UniFileName, wsFileName);
    InitializeObjectAttributes(&ObjectAttributes,
        &UniFileName,
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
        NULL,
        NULL);
    st=IoCreateFile(&FileHandle,
        GENERIC_READ,
        &ObjectAttributes,
        &IoStatusBlock,
        0,
        FILE_ATTRIBUTE_NORMAL,
        0,
        FILE_CREATE,
        FILE_DIRECTORY_FILE,
        NULL,
        0,
        0,
        NULL,
        IO_NO_PARAMETER_CHECKING);
    if(NT_SUCCESS(st))
        ZwClose(FileHandle);
}

最后总结一下几个常见的、和文件相关的 Zw 函数的功能:

ZwCreateFile

创建文件 / 文件夹、获得文件句柄

ZwDeleteFile

删除文件 / 文件夹

ZwOpenFile

获得文件句柄

ZwWriteFile

写入文件

ZwQueryDirectoryFile

枚举文件

ZwQueryInformationFile

查询文件信息

ZwReadFile

读文件

ZwSetInformationFile

设置文件信息

ZwWriteFile

写文件

ZwFlushBuffersFile

把磁盘缓存的内容写入到磁盘扇区里

本文链接地址: https://www.dbgpro.com/archives/4802.html

——版权声明——


以上所述就是小编给大家介绍的《x64驱动基础教程 09》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Java程序员修炼之道

Java程序员修炼之道

[英] Benjamin J. Evans、[荷兰] Martijn Verburg / 吴海星 / 人民邮电出版社 / 2013-7 / 89.00元

本书分为四部分,第一部分全面介绍Java 7 的新特性,第二部分探讨Java 关键编程知识和技术,第三部分讨论JVM 上的新语言和多语言编程,第四部分将平台和多语言编程知识付诸实践。从介绍Java 7 的新特性入手,本书涵盖了Java 开发中最重要的技术,比如依赖注入、测试驱动的开发和持续集成,探索了JVM 上的非Java 语言,并详细讲解了多语言项目, 特别是涉及Groovy、Scala 和Cl......一起来看看 《Java程序员修炼之道》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具