zoukankan      html  css  js  c++  java
  • 10内核重载

    内核重载需求的产生

    在内核中有很多HOOK, 例如:KiFastCallEntry, SSDT,IDT,OBJECT HOOK,甚至是内核API的内联HOOK , 有些HOOK很容易找到,并还原, 有些HOOK就很难找到. 在某些时候(例如一个病毒HOOK了int3中断,这样调试器就无法得到断点事件),清除HOOK是一种反反调试的技术. 也是防护与反防护的技术. —总之内核层的对抗非常的激烈, 为了能够一举将所有的HOOK或者其它防护技术统统屏蔽, 内核重载技术应运而生. 简单来说, 内核重载就是将文件中的内核(ntkrnlpa.exe)重新加载到内存中, 这样一来, 所有对内核中的修改都会被还原.

    但是, 直接加载并覆盖到原来的内存的话会产生很多错误,例如, 系统开机之后,会对很多的数据进行初始化, 这些数据被初始化之后系统才能正常运行, 如果直接将内核文件的数据覆盖了,就没有这些数据了. 因此, 可以只将原始内核的代码段加载到内存 , 并通过一些设置, 让原始

    内核重载的目标

    1. 破掉所有的KiFastCallEntry HOOK 1.1 替换KiFastCallEntry函数.

    2. 破掉所有SSDT HOOK 2.1 使用新的SSDT表(需要修复非常多的数据)

    3. 破掉所有的Inline HOOK 3.1 直接使用新的内核代码段(需要修复非常非常多的数据)

    kernelReload.c

    #include <ntifs.h>
    #include <ntimage.h>
    #include <ntddk.h>
    #include "kernelFunction.h"


    ////////////////////////////////////////////
    ///////////// 内核重载相关函数   /////////////
    ////////////////////////////////////////////

    // 导入SSDT全局变量
    NTSYSAPI SSDTEntry KeServiceDescriptorTable;
    static char* g_pNewNtKernel; // 新内核
    static ULONG g_ntKernelSize; // 内核的映像大小
    static SSDTEntry* g_pNewSSDTEntry; // 新ssdt的入口地址
    static ULONG g_hookAddr; // 被hook位置的首地址
    static ULONG g_hookAddr_next_ins;// 被hook的指令的下一条指令的首地址.


    // 读取NT内核模块
    // 将读取到的缓冲区的内容保存到pBuff中.
    NTSTATUS loadNtKernelModule( UNICODE_STRING* ntkernelPath, char** pBuff );

    // 修复重定位.
    void fixRelocation( char* pDosHdr, ULONG curNtKernelBase );

    // 填充SSDT表
    // char* pDos - 新加载的内核堆空间首地址
    // char* pCurKernelBase - 当前正在使用的内核的加载基址
    void initSSDT( char* pDos, char* pCurKernelBase );

    // 安装HOOK
    void installHook( );

    // 卸载HOOK
    void uninstallHook( );

    // inline hook KiFastCallEntry的函数
    void myKiFastEntryHook( );


    ////////////////////////////////////////////
    /////////////// 驱动入口函数   //////////////
    ////////////////////////////////////////////
    NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath)
    {
    NTSTATUS status = STATUS_SUCCESS;
    DbgBreakPoint( );

    // 1. 找到内核文件路径
    // 1.1 通过遍历内核链表的方式来找到内核主模块
    LDR_DATA_TABLE_ENTRY* pLdr = ((LDR_DATA_TABLE_ENTRY*)pDriver->DriverSection);
    // 1.2 内核主模块在链表中的第2项.
    for(int i =0;i<2;++i)
    pLdr = (LDR_DATA_TABLE_ENTRY*)pLdr->InLoadOrderLinks.Flink;

    g_ntKernelSize = pLdr->SizeOfImage;

    // 1.3 保存当前加载基址
    char* pCurKernelBase = (char*)pLdr->DllBase;

    KdPrint( ("base=%p name=%wZ ", pCurKernelBase, &pLdr->FullDllName) );

    // 2. 读取nt模块的文件内容到堆空间.
    status = loadNtKernelModule( &pLdr->FullDllName, &g_pNewNtKernel );
    if(STATUS_SUCCESS != status)
    {
    return status;
    }

    // 3. 修复新nt模块的重定位.
    fixRelocation( g_pNewNtKernel, (ULONG)pCurKernelBase );

    // 4. 使用当前正在使用的内核的数据来填充
    //   新内核的SSDT表.
    initSSDT( g_pNewNtKernel, pCurKernelBase );

    // 5. HOOK KiFastCallEntry,使调用号走新内核的路线
    installHook( );

    pDriver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
    }


    VOID DriverUnload(PDRIVER_OBJECT pDriver)
    {
    UNREFERENCED_PARAMETER(pDriver);
    uninstallHook( );
    }



    // 关闭内存页写入保护
    void _declspec(naked) disablePageWriteProtect( )
    {
    _asm
    {
    push eax;
    mov eax, cr0;
    and eax, ~0x10000;
    mov cr0, eax;
    pop eax;
    ret;
    }
    }

    // 开启内存页写入保护
    void _declspec(naked) enablePageWriteProtect( )
    {
    _asm
    {
    push eax;
    mov eax, cr0;
    or eax, 0x10000;
    mov cr0, eax;
    pop eax;
    ret;
    }
    }


    // 加载NT内核模块
    // 将读取到的缓冲区的内容保存到pBuff中.
    NTSTATUS loadNtKernelModule( UNICODE_STRING* ntkernelPath, char** pOut )
    {
    NTSTATUS status = STATUS_SUCCESS;
    // 2. 获取文件中的内核模块
    // 2.1 将内核模块作为文件来打开.
    HANDLE hFile = NULL;
    char* pBuff = NULL;
    ULONG read = 0;
    char pKernelBuff[0x1000];

    status = createFile( ntkernelPath->Buffer,
    GENERIC_READ,
    FILE_SHARE_READ,
    FILE_OPEN_IF,
    FALSE,
    &hFile );
    if(status != STATUS_SUCCESS)
    {
    KdPrint( ("打开文件失败 ") );
    goto _DONE;
    }

    // 2.2 将PE文件头部读取到内存
    status = readFile( hFile, 0, 0, 0x1000, pKernelBuff, &read );
    if(STATUS_SUCCESS != status)
    {
    KdPrint( ("读取文件内容失败 ") );
    goto _DONE;
    }

    // 3. 加载PE文件到内存.
    // 3.1 得到扩展头,获取映像大小.
    IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)pKernelBuff;
    IMAGE_NT_HEADERS* pnt = (IMAGE_NT_HEADERS*)((ULONG)pDos + pDos->e_lfanew);
    ULONG imgSize = pnt->OptionalHeader.SizeOfImage;

    // 3.2 申请内存以保存各个区段的内容.
    pBuff = ExAllocatePool( NonPagedPool, imgSize );
    if(pBuff == NULL)
    {
    KdPrint( ("内存申请失败失败 ") );
    status = STATUS_BUFFER_ALL_ZEROS;//随便返回个错误码
    goto _DONE;
    }

    // 3.2.1 拷贝头部到堆空间
    RtlCopyMemory( pBuff,
       pKernelBuff,
       pnt->OptionalHeader.SizeOfHeaders );

    // 3.3 得到区段头, 并将按照区段头将区段读取到内存中.
    IMAGE_SECTION_HEADER* pScnHdr = IMAGE_FIRST_SECTION( pnt );
    ULONG scnCount = pnt->FileHeader.NumberOfSections;
    for(ULONG i = 0; i < scnCount; ++i)
    {
    //
    // 3.3.1 读取文件内容到堆空间指定位置.
    //
    status = readFile( hFile,
       pScnHdr[i].PointerToRawData,
       0,
       pScnHdr[i].SizeOfRawData,
       pScnHdr[i].VirtualAddress + pBuff,
       &read );
    if(status != STATUS_SUCCESS)
    goto _DONE;

    }


    _DONE:
    ZwClose( hFile );
    //
    // 保存新内核的加载的首地址
    //
    *pOut = pBuff;

    if(status != STATUS_SUCCESS )
    {
    if(pBuff != NULL)
    {
    ExFreePool( pBuff );
    *pOut = pBuff = NULL;
    }
    }
    return status;
    }


    // 修复重定位.
    void fixRelocation( char* pDosHdr , ULONG curNtKernelBase )
    {
    IMAGE_DOS_HEADER* pDos = (IMAGE_DOS_HEADER*)pDosHdr;
    IMAGE_NT_HEADERS* pNt = (IMAGE_NT_HEADERS*)((ULONG)pDos + pDos->e_lfanew);
    ULONG uRelRva = pNt->OptionalHeader.DataDirectory[5].VirtualAddress;
    IMAGE_BASE_RELOCATION* pRel =
    (IMAGE_BASE_RELOCATION*)(uRelRva + (ULONG)pDos);

    while(pRel->SizeOfBlock)
    {
    typedef struct
    {
    USHORT offset : 12;
    USHORT type : 4;
    }TypeOffset;

    ULONG count = (pRel->SizeOfBlock - 8) / 2;
    TypeOffset* pTypeOffset = (TypeOffset*)(pRel + 1);
    for(ULONG i = 0; i < count; ++i)
    {
    if(pTypeOffset[i].type != 3)
    {
    continue;
    }

    ULONG* pFixAddr = (ULONG*)(pTypeOffset[i].offset + pRel->VirtualAddress + (ULONG)pDos);
    //
    // 减去默认加载基址
    //
    *pFixAddr -= pNt->OptionalHeader.ImageBase;

    //
    // 加上新的加载基址(使用的是当前内核的加载基址,这样做
    // 是为了让新内核使用当前内核的数据(全局变量,未初始化变量等数据).)
    //
    *pFixAddr += (ULONG)curNtKernelBase;
    }

    pRel = (IMAGE_BASE_RELOCATION*)((ULONG)pRel + pRel->SizeOfBlock);
    }
    }

    // 填充SSDT表
    // char* pNewBase - 新加载的内核堆空间首地址
    // char* pCurKernelBase - 当前正在使用的内核的加载基址
    void initSSDT( char* pNewBase, char* pCurKernelBase )
    {
    // 1. 分别获取当前内核,新加载的内核的`KeServiceDescriptorTable`
    //   的地址
    SSDTEntry* pCurSSDTEnt = &KeServiceDescriptorTable;
    g_pNewSSDTEntry = (SSDTEntry*)
    ((ULONG)pCurSSDTEnt - (ULONG)pCurKernelBase + (ULONG)pNewBase);

    // 2. 获取新加载的内核以下三张表的地址:
    // 2.1 服务函数表基址
    g_pNewSSDTEntry->ServiceTableBase = (ULONG*)
    ((ULONG)pCurSSDTEnt->ServiceTableBase - (ULONG)pCurKernelBase + (ULONG)pNewBase);

    // 2.3 服务函数参数字节数表基址
    g_pNewSSDTEntry->ParamTableBase = (ULONG*)
    ((ULONG)pCurSSDTEnt->ParamTableBase - (ULONG)pCurKernelBase + (ULONG)pNewBase);

    // 2.2 服务函数调用次数表基址(有时候这个表并不存在.)
    if(pCurSSDTEnt->ServiceCounterTableBase)
    {
    g_pNewSSDTEntry->ServiceCounterTableBase = (ULONG*)
    ((ULONG)pCurSSDTEnt->ServiceCounterTableBase - (ULONG)pCurKernelBase + (ULONG)pNewBase);
    }

    // 2.3 设置新SSDT表的服务个数
    g_pNewSSDTEntry->NumberOfServices = pCurSSDTEnt->NumberOfServices;

    //3. 将服务函数的地址填充到新SSDT表(重定位时其实已经修复好了,
    //   但是,在修复重定位的时候,是使用当前内核的加载基址的, 修复重定位
    //   为之后, 新内核的SSDT表保存的服务函数的地址都是当前内核的地址,
    //   在这里要将这些服务函数的地址改回新内核中的函数地址.)
    disablePageWriteProtect( );
    for(ULONG i = 0; i < g_pNewSSDTEntry->NumberOfServices; ++i)
    {
    // 减去当前内核的加载基址
    g_pNewSSDTEntry->ServiceTableBase[i] -= (ULONG)pCurKernelBase;
    // 换上新内核的加载基址.
    g_pNewSSDTEntry->ServiceTableBase[i] += (ULONG)pNewBase;
    }
    enablePageWriteProtect( );
    }

    void installHook( )
    {
    g_hookAddr = 0;

    // 1. 找到KiFastCallEntry函数首地址
    ULONG uKiFastCallEntry = 0;
    _asm
    {
    ;// KiFastCallEntry函数地址保存
    ;// 在特殊模组寄存器的0x176号寄存器中
    push ecx;
    push eax;
    push edx;
    mov ecx, 0x176; // 设置编号
    rdmsr; ;// 读取到edx:eax
    mov uKiFastCallEntry, eax;// 保存到变量
    pop edx;
    pop eax;
    pop ecx;
    }

    // 2. 找到HOOK的位置, 并保存5字节的数据
    // 2.1 HOOK的位置选定为(此处正好5字节,):
    // 2be1           sub     esp, ecx ;
    // c1e902         shr     ecx, 2   ;
    UCHAR hookCode[5] = { 0x2b,0xe1,0xc1,0xe9,0x02 }; //保存inline hook覆盖的5字节
    ULONG i = 0;
    for(; i < 0x1FF; ++i)
    {
    if(RtlCompareMemory( (UCHAR*)uKiFastCallEntry + i,
    hookCode,
    5 ) == 5)
    {
    break;
    }
    }
    if(i >= 0x1FF)
    {
    KdPrint( ("在KiFastCallEntry函数中没有找到HOOK位置,可能KiFastCallEntry已经被HOOK过了 ") );
    uninstallHook( );
    return;
    }


    g_hookAddr = uKiFastCallEntry + i;
    g_hookAddr_next_ins = g_hookAddr + 5;

    // 3. 开始inline hook
    UCHAR jmpCode[5] = { 0xe9 };// jmp xxxx
    disablePageWriteProtect( );

    // 3.1 计算跳转偏移
    // 跳转偏移 = 目标地址 - 当前地址 - 5
    *(ULONG*)(jmpCode + 1) = (ULONG)myKiFastEntryHook - g_hookAddr - 5;

    // 3.2 将跳转指令写入
    RtlCopyMemory( uKiFastCallEntry + i,
       jmpCode,
       5 );

    enablePageWriteProtect( );
    }

    void uninstallHook( )
    {
    if(g_hookAddr)
    {

    // 将原始数据写回.
    UCHAR srcCode[5] = { 0x2b,0xe1,0xc1,0xe9,0x02 };
    disablePageWriteProtect( );

    // 3.1 计算跳转偏移
    // 跳转偏移 = 目标地址 - 当前地址 - 5

    _asm sti
    // 3.2 将跳转指令写入
    RtlCopyMemory( g_hookAddr,
       srcCode,
       5 );
    _asm cli
    g_hookAddr = 0;
    enablePageWriteProtect( );
    }

    if(g_pNewNtKernel)
    {
    ExFreePool( g_pNewNtKernel );
    g_pNewNtKernel = NULL;
    }
    }


    // SSDT过滤函数.
    ULONG SSDTFilter( ULONG index,/*索引号,也是调用号*/
      ULONG tableAddress,/*表的地址,可能是SSDT表的地址,也可能是Shadow SSDT表的地址*/
      ULONG funAddr/*从表中取出的函数地址*/ )
    {
    // 如果是SSDT表的话
    if(tableAddress == KeServiceDescriptorTable.ServiceTableBase)
    {
    // 判断调用号(190是ZwOpenProcess函数的调用号)
    if(index == 190)
    {
    // 返回新SSDT表的函数地址
    // 也就是新内核的函数地址.
    return g_pNewSSDTEntry->ServiceTableBase[190];
    }
    }
    // 返回旧的函数地址
    return funAddr;
    }

    // inline hook KiFastCallEntry的函数
    void _declspec(naked) myKiFastEntryHook( )
    {

    _asm
    {
    pushad; // 压栈寄存器: eax,ecx,edx,ebx, esp,ebp ,esi, edi
    pushfd; // 压栈标志寄存器

    push edx; // 从表中取出的函数地址
    push edi; // 表的地址
    push eax; // 调用号
    call SSDTFilter; // 调用过滤函数

    ;// 函数调用完毕之后栈控件布局,指令pushad将
    ;// 32位的通用寄存器保存在栈中,栈空间布局为:
    ;// [esp + 00] <=> eflag
    ;// [esp + 04] <=> edi
    ;// [esp + 08] <=> esi
    ;// [esp + 0C] <=> ebp
    ;// [esp + 10] <=> esp
    ;// [esp + 14] <=> ebx
    ;// [esp + 18] <=> edx <<-- 使用函数返回值来修改这个位置
    ;// [esp + 1C] <=> ecx
    ;// [esp + 20] <=> eax
    mov dword ptr ds : [esp + 0x18], eax;
    popfd; // popfd时,实际上edx的值就回被修改
    popad;

    ; //执行被hook覆盖的两条指令
    sub esp, ecx;
    shr ecx, 2;
    jmp g_hookAddr_next_ins;
    }
    }

    KernelFunction.h

    #pragma once
    #include <ntifs.h>
    #include <ntddk.h>

    //////////////////////////////////////////////////////////////////////////
    //////////////////////////// 内存操作 /////////////////////////////////////

    //************************************
    // Method:   alloc 申请内存
    // Returns:   void* 返回内存空间首地址, 申请失败返回NULL
    // Parameter: ULONG size 要申请的字节数
    //************************************
    void* alloc(ULONG size);
    //************************************
    // Method:   reAlloc 重新分配空间
    // Returns:   void* 返回新空间的内存地址
    // Parameter: void * src 原始内存空间(必须由alloc函数所返回)
    // Parameter: ULONG size 重新分配的字节数
    //************************************
    void* reAlloc(void* src, ULONG size);

    //************************************
    // Method:   free 释放内存空间
    // Returns:   void
    // Parameter: void *
    //************************************
    void free(void* data);



    //////////////////////////////////////////////////////////////////////////
    //////////////////////////// 文件操作 /////////////////////////////////////
    //************************************
    // Method:   createFile 创建文件
    // Returns:   NTSTATUS
    // Parameter: const wchar_t * filepath 文件路径,路径必须是设备名"\device\volumn\"或符号连接名"\??\C:\1.txt"
    // Parameter: ULONG access 访问权限, GENERIC_READ, GENERIC_XXX
    // Parameter: ULONG share 文件共享方式: FILE_SHARE_XXX
    // Parameter: ULONG openModel 打开方式: FILE_OPEN_IF,FILE_CREATE ...
    // Parameter: BOOLEAN isDir 是否为目录
    // Parameter: HANDLE * hFile
    //************************************
    NTSTATUS createFile(wchar_t* filepath,
    ULONG access,
    ULONG share,
    ULONG openModel,
    BOOLEAN isDir,
    HANDLE* hFile);

    //************************************
    // Method:   getFileSize 获取文件大小
    // Returns:   NTSTATUS 
    // Parameter: HANDLE hFile 文件句柄
    // Parameter: ULONG64 * size 文件大小
    //************************************
    NTSTATUS getFileSize(HANDLE hFile,
    ULONG64* size);

    //************************************
    // Method:   readFile 读取文件内容
    // Returns:   NTSTATUS
    // Parameter: HANDLE hFile 文件句柄
    // Parameter: ULONG offsetLow 文件偏移的低32位, 从此位置开始读取
    // Parameter: ULONG offsetHig 文件偏移的高32位, 从此位置开始读取
    // Parameter: ULONG sizeToRead 要读取的字节数
    // Parameter: PVOID pBuff 保存文件内容的缓冲区 , 需要自己申请内存空间.
    // Parameter: ULONG * read 实际读取到的字节数
    //************************************
    NTSTATUS readFile(HANDLE hFile,
      ULONG offsetLow,
      ULONG offsetHig,
      ULONG sizeToRead,
      PVOID pBuff,
      ULONG* read);

    NTSTATUS writeFile(HANDLE hFile,
       ULONG offsetLow,
       ULONG offsetHig,
       ULONG sizeToWrite,
       PVOID pBuff,
       ULONG* write);

    NTSTATUS copyFile(wchar_t* srcPath,
      wchar_t* destPath);

    NTSTATUS moveFile(wchar_t* srcPath,
      wchar_t* destPath);

    NTSTATUS removeFile(wchar_t* path);


    //************************************
    // Method:   listDirGet 列出一个目录下的文件和文件夹
    // Returns:   NTSTATUS
    // Parameter: wchar_t * dir 目录名, 目录名必须是设备名"\device\volumn\"或符号连接名"\??\C:\1.txt"
    // Parameter: FILE_BOTH_DIR_INFORMATION ** fileInfo 保存文件内容的缓冲区, 该缓冲区由函数内部申请空间, 必须通过函数`listDirFree`来释放.
    // Parameter: ULONG maxFileCount 要获取的最大文件个数.如果目录下有100个文件,此参数传了5,则只能获取到5个文件.
    //************************************
    NTSTATUS listDirGet(wchar_t* dir ,
    FILE_BOTH_DIR_INFORMATION** fileInfo,
    ULONG maxFileCount);



    //************************************
    // Method:   firstFile 获取一个目录下的第一个文件
    // Returns:   NTSTATUS
    // Parameter: wchar_t * dir 目录名, 目录名必须是设备名"\device\volumn\"或符号连接名"\??\C:\1.txt"
    // Parameter: HANDLE * hFind 函数输出值,是一个目录句柄
    // Parameter: FILE_BOTH_DIR_INFORMATION * fileInfo 保存文件内容的缓冲区,
    // 这个缓冲区的大小最好是: sizeof(FILE_BOTH_DIR_INFORMATION) + 267*2
    //************************************
    NTSTATUS firstFile(wchar_t* dir, HANDLE* hFind, FILE_BOTH_DIR_INFORMATION* fileInfo,int size);

    //************************************
    // Method:   nextFile 获取一个目录下的下一个文件.
    // Returns:   NTSTATUS
    // Parameter: HANDLE hFind 目录句柄, 该句柄是由firstFile函数所返回 .
    // Parameter: FILE_BOTH_DIR_INFORMATION * fileInfo 保存文件信息的缓冲区. 这个缓冲区的大小最好是: sizeof(FILE_BOTH_DIR_INFORMATION) + 267*2
    //************************************
    NTSTATUS nextFile(HANDLE hFind, FILE_BOTH_DIR_INFORMATION* fileInfo, int size);


    void listDirFree(FILE_BOTH_DIR_INFORMATION* fileInfo);

    #define ListDirNext(Type,fileinfo) ((Type*)((ULONG_PTR)fileinfo + fileinfo->NextEntryOffset))
    #define ListDirForEach(FileInfoType,fileInfo, iterator)
    for (FileInfoType* iterator = fileInfo;
    iterator->NextEntryOffset != 0;
    iterator = ListDirNext(FileInfoType,iterator))


    #pragma pack(1)
    typedef struct _ServiceDesriptorEntry
    {
    ULONG *ServiceTableBase;        // 服务表基址
    ULONG *ServiceCounterTableBase; // 计数表基址
    ULONG NumberOfServices;         // 表中项的个数
    UCHAR *ParamTableBase;          // 参数表基址
    }SSDTEntry, *PSSDTEntry;
    #pragma pack()

    typedef struct _LDR_DATA_TABLE_ENTRY
    {
    LIST_ENTRY InLoadOrderLinks;    //双向链表
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;
    // ...
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;


    void disablePageWriteProtect( );
    void enablePageWriteProtect( );
    VOID DriverUnload( PDRIVER_OBJECT pDriver );

    kernelFunction.c

    #include "KernelFunction.h"
    #include <ntifs.h>


    void* alloc(ULONG size)
    {
    return ExAllocatePool(PagedPool, size);
    }

    void* reAlloc(void* src, ULONG size)
    {
    if (!src)
    {
    return NULL;
    }

    void* data = alloc(size);
    RtlCopyMemory(data, src, size);
    ExFreePool(src);
    return data;
    }

    void free(void* data)
    {
    if (data)
    {
    ExFreePool(data);
    }
    }

    NTSTATUS createFile(wchar_t * filepath,
    ULONG access,
    ULONG share,
    ULONG openModel,
    BOOLEAN isDir,
    HANDLE * hFile)
    {

    NTSTATUS status = STATUS_SUCCESS;

    IO_STATUS_BLOCK StatusBlock = { 0 };
    ULONG           ulShareAccess = share;
    ULONG ulCreateOpt = FILE_SYNCHRONOUS_IO_NONALERT;

    UNICODE_STRING path;
    RtlInitUnicodeString(&path, filepath);

    // 1. 初始化OBJECT_ATTRIBUTES的内容
    OBJECT_ATTRIBUTES objAttrib = { 0 };
    ULONG ulAttributes = OBJ_CASE_INSENSITIVE/*不区分大小写*/ | OBJ_KERNEL_HANDLE/*内核句柄*/;
    InitializeObjectAttributes(&objAttrib,    // 返回初始化完毕的结构体
       &path,      // 文件对象名称
       ulAttributes,  // 对象属性
       NULL, NULL);   // 一般为NULL

    // 2. 创建文件对象
    ulCreateOpt |= isDir ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE;

    status = ZwCreateFile(hFile,                 // 返回文件句柄
      access, // 文件操作描述
      &objAttrib,            // OBJECT_ATTRIBUTES
      &StatusBlock,          // 接受函数的操作结果
      0,                     // 初始文件大小
      FILE_ATTRIBUTE_NORMAL, // 新建文件的属性
      ulShareAccess,         // 文件共享方式
      openModel, // 文件存在则打开不存在则创建
      ulCreateOpt,           // 打开操作的附加标志位
      NULL,                  // 扩展属性区
      0);                    // 扩展属性区长度
    return status;
    }


    NTSTATUS getFileSize(HANDLE hFile, ULONG64* size)
    {
    IO_STATUS_BLOCK isb = { 0 };
    FILE_STANDARD_INFORMATION fsi = { 0 };
    NTSTATUS status;
    status = ZwQueryInformationFile(hFile, /*文件句柄*/
    &isb, /*完成状态*/
    &fsi, /*保存文件信息的缓冲区*/
    sizeof(fsi), /*缓冲区的字节数*/
    FileStandardInformation/*要获取的信息类型*/);
    if (STATUS_SUCCESS == status)
    {
    // 保存文件字节数
    *size = fsi.EndOfFile.QuadPart;
    }
    return status;
    }

    NTSTATUS readFile(HANDLE hFile,
      ULONG offsetLow,
      ULONG offsetHig,
      ULONG sizeToRead,
      PVOID pBuff,
      ULONG* read)
    {
    NTSTATUS status;
    IO_STATUS_BLOCK isb = { 0 };
    LARGE_INTEGER offset;
    // 设置要读取的文件偏移
    offset.HighPart = offsetHig;
    offset.LowPart = offsetLow;

    status = ZwReadFile(hFile,/*文件句柄*/
    NULL,/*事件对象,用于异步IO*/
    NULL,/*APC的完成通知例程:用于异步IO*/
    NULL,/*完成通知例程序的附加参数*/
    &isb,/*IO状态*/
    pBuff,/*保存文件数据的缓冲区*/
    sizeToRead,/*要读取的字节数*/
    &offset,/*要读取的文件位置*/
    NULL);
    if (status == STATUS_SUCCESS)
    *read = isb.Information;
    return status;
    }

    NTSTATUS writeFile(HANDLE hFile,
       ULONG offsetLow,
       ULONG offsetHig,
       ULONG sizeToWrite,
       PVOID pBuff,
       ULONG* write)
    {

    NTSTATUS status;
    IO_STATUS_BLOCK isb = { 0 };
    LARGE_INTEGER offset;
    // 设置要写入的文件偏移
    offset.HighPart = offsetHig;
    offset.LowPart = offsetLow;

    status = ZwWriteFile(hFile,/*文件句柄*/
    NULL, /*事件对象,用户异步IO*/
    NULL,/*APC例程,用于异步IO*/
    NULL, /*APC环境*/
    &isb,/*IO状态*/
    pBuff,/*写入到文件中的缓冲区*/
    sizeToWrite,/*写入的字节数*/
    &offset,/*写入到的文件偏移*/
    NULL);
    if (status == STATUS_SUCCESS)
    *write = isb.Information;

    return status;
    }

    NTSTATUS copyFile(wchar_t* srcPath,
      wchar_t* destPath)
    {
    HANDLE hSrc = (HANDLE)-1;
    HANDLE hDest = (HANDLE)-1;
    NTSTATUS status = STATUS_SUCCESS;
    ULONG64 srcSize = 0;
    ULONG size = 0;
    char*   pBuff = NULL;
    __try
    {
    // 1. 先打开源文件
    status = createFile(srcPath,
    GENERIC_READ,
    FILE_SHARE_READ,
    FILE_OPEN_IF,
    FALSE,
    &hSrc);

    if (STATUS_SUCCESS != status)
    {
    __leave;
    }


    // 2. 获取源文件大小
    if (STATUS_SUCCESS != getFileSize(hSrc, &srcSize))
    {
    __leave;
    }

    // 3. 分配内存空间保存源文件的数据
    pBuff = ExAllocatePool(PagedPool, (ULONG)srcSize);
    if (pBuff == NULL)
    {
    __leave;
    }

    // 3. 读取源文件的数据到内存中.
    status = readFile(hSrc, 0, 0, (ULONG)srcSize, pBuff, &size);
    if (STATUS_SUCCESS != status || size != (ULONG)srcSize)
    {
    __leave;
    }

    // 4. 打开目标文件
    status = createFile(destPath,
    GENERIC_WRITE,
    FILE_SHARE_READ,
    FILE_CREATE,
    FALSE,
    &hDest);
    if (STATUS_SUCCESS != status)
    {
    __leave;
    }

    // 5. 将源文件的数据写入到目标文件
    status = writeFile(hDest, 0, 0, (ULONG)srcSize, pBuff, &size);
    if (STATUS_SUCCESS != status || size != srcSize)
    {
    __leave;
    }
    status = STATUS_SUCCESS;
    }
    __finally
    {
    // 6. 关闭源文件
    if (hSrc != (HANDLE)-1)
    {
    ZwClose(hSrc);
    }

    // 7. 关闭目标文件
    if (hDest != (HANDLE)-1)
    {
    ZwClose(hDest);
    }

    // 8. 释放缓冲区
    if (pBuff)
    {
    ExFreePool(pBuff);
    }
    }
    return status;
    }

    NTSTATUS moveFile(wchar_t* srcPath, wchar_t* destPath)
    {
    NTSTATUS status = STATUS_SUCCESS;
    // 1. 拷贝一份文件
    status = copyFile(srcPath, destPath);

    // 2. 如果拷贝成功了,删除源文件
    if (status == STATUS_SUCCESS)
    {
    status = removeFile(srcPath);
    }
    return status;
    }

    NTSTATUS removeFile(wchar_t* filepath)
    {

    UNICODE_STRING path;
    RtlInitUnicodeString(&path, filepath);

    // 1. 初始化OBJECT_ATTRIBUTES的内容
    OBJECT_ATTRIBUTES objAttrib = { 0 };
    ULONG             ulAttributes = OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
    InitializeObjectAttributes(&objAttrib,    // 返回初始化完毕的结构体
       &path,  // 文件对象名称
       ulAttributes,  // 对象属性
       NULL,          // 根目录(一般为NULL)
       NULL);         // 安全属性(一般为NULL)
    // 2. 删除指定文件/文件夹
    return ZwDeleteFile(&objAttrib);
    }


    NTSTATUS listDirGet(wchar_t* dir, FILE_BOTH_DIR_INFORMATION** fileInfo, ULONG maxFileCount)
    {
    NTSTATUS status = STATUS_SUCCESS;
    IO_STATUS_BLOCK isb = { 0 };
    HANDLE hDir = (HANDLE)-1;
    VOID* pBuff = NULL;
    __try{
    // 1. 打开目录
    status = createFile(dir,
    GENERIC_READ,
    FILE_SHARE_READ,
    FILE_OPEN_IF,
    TRUE,
    &hDir);
    if (STATUS_SUCCESS != status)
    __leave;

    // 计算出保存一个文件信息所需的最大字节数 = 结构体大小 + 文件名大小
    ULONG signalFileInfoSize = sizeof(FILE_BOTH_DIR_INFORMATION) + 267 * 2;
    // 计算出总空间字节数
    ULONG totalSize = signalFileInfoSize * maxFileCount;

    // 申请内存空间
    pBuff = ExAllocatePool(PagedPool, totalSize);
    if (pBuff == NULL)
    __leave;

    // 第一次调用,获取所需缓冲区字节数
    status = ZwQueryDirectoryFile(hDir, /*目录句柄*/
      NULL, /*事件对象*/
      NULL, /*完成通知例程*/
      NULL, /*完成通知例程附加参数*/
      &isb, /*IO状态*/
      pBuff, /*输出的文件信息*/
      totalSize,/*文件信息缓冲区的字节数*/
      FileBothDirectoryInformation,/*获取信息的类型*/
      FALSE,/*是否只获取第一个*/
      0,
      TRUE/*是否重新扫描目录*/);
    // 保存缓冲区的内容首地址.
    if (status == STATUS_SUCCESS)
    *fileInfo = (FILE_BOTH_DIR_INFORMATION*)pBuff;
    }
    __finally{

    if (hDir != (HANDLE)-1)
    {
    ZwClose(hDir);
    }
    if (status != STATUS_SUCCESS && pBuff != NULL)
    {
    ExFreePool(pBuff);
    }
    }
    return status;
    }

    NTSTATUS firstFile(wchar_t* dir, HANDLE* hFind, FILE_BOTH_DIR_INFORMATION* fileInfo, int size)
    {
    NTSTATUS status = STATUS_SUCCESS;
    IO_STATUS_BLOCK isb = { 0 };
    // 1. 打开目录
    status = createFile(dir,
    GENERIC_READ,
    FILE_SHARE_READ,
    FILE_OPEN_IF,
    TRUE,
    hFind);
    if (STATUS_SUCCESS != status)
    return status;

    // 第一次调用,获取所需缓冲区字节数
    status = ZwQueryDirectoryFile(*hFind, /*目录句柄*/
      NULL, /*事件对象*/
      NULL, /*完成通知例程*/
      NULL, /*完成通知例程附加参数*/
      &isb, /*IO状态*/
      fileInfo, /*输出的文件信息*/
      size,/*文件信息缓冲区的字节数*/
      FileBothDirectoryInformation,/*获取信息的类型*/
      TRUE,/*是否只获取第一个*/
      0,
      TRUE/*是否重新扫描目录*/);

    return status;
    }

    NTSTATUS nextFile(HANDLE hFind, FILE_BOTH_DIR_INFORMATION* fileInfo, int size)
    {
    IO_STATUS_BLOCK isb = { 0 };
    // 第一次调用,获取所需缓冲区字节数
    return ZwQueryDirectoryFile(hFind, /*目录句柄*/
    NULL, /*事件对象*/
    NULL, /*完成通知例程*/
    NULL, /*完成通知例程附加参数*/
    &isb, /*IO状态*/
    fileInfo, /*输出的文件信息*/
    size,/*文件信息缓冲区的字节数*/
    FileBothDirectoryInformation,/*获取信息的类型*/
    TRUE,/*是否只获取第一个*/
    0,
    FALSE/*是否重新扫描目录*/);
    }

    void listDirFree(FILE_BOTH_DIR_INFORMATION* fileInfo)
    {
    ExFreePool(fileInfo);
    }


  • 相关阅读:
    介绍自己
    第六周作业
    第五周作业
    第四周作业
    秋季学期学习总结
    币值转化
    justintime compiler
    PostgreSQL windows下安装出现问题的解决办法
    Java语言的异常处理,完全理解下面4点就可以了
    Ultraedit用途【来自网络】
  • 原文地址:https://www.cnblogs.com/ltyandy/p/11439466.html
Copyright © 2011-2022 走看看