06文件操作

文件操作

文件操作函数在内容中使用的ZwXXXX系列,下面是函数一览表

函数名
功能

ZwCreateFile
打开文件

ZwReadFile
读取文件内容

ZwWriteFile
将数据写入文件

ZwQueryInformationFile
查询文件信息

ZwDeleteFile
删除文件

ZwClose
关闭文件

打开/创建文件

在内核中打开文件比较繁琐, 该函数使用了比较多的API:

要打开的文件路径必须设置在OBJECT_ATTRIBUTES对象中, 此外,OBJECT_ATTRIBUTES结构体的对象属性一般可以设置为OBJ_CASE_INSENSITIVE(不区分大小写) , OBJ_KERNEL_HANDLE(内核句柄)

NTSTATUS createFile(wchar_t * filepath,/*文件路径*/
                    ULONG access, /*访问权限,: GENERIC_READ, GENERIC_XXX*/
                    ULONG share,/*文件共享方式: FILE_SHARE_XXX*/
                    ULONG openModel,/* 打开方式: FILE_OPEN_IF,FILE_CREATE ...*/
                    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 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;
}

遍历文件

在用户层中,遍历文件有两个API. 在内核层, 遍历文件只有一个API

NTSTATUS ZwQueryDirectoryFile(
  _In_      HANDLE FileHandle,   // 目录句柄
  _In_opt_  HANDLE Event, // 事件对象(异步IO)
  _In_opt_  PIO_APC_ROUTINE ApcRoutine, // 完成通知例程
  _In_opt_  PVOID ApcContext,  // 例程附加参数
  _Out_     PIO_STATUS_BLOCK IoStatusBlock, // IO状态
  _Out_     PVOID FileInformation, // 获取到的文件信息
  _In_      ULONG Length,  // 文件信息缓冲区长度
  _In_      FILE_INFORMATION_CLASS FileInformationClass, // 获取的信息类型
  _In_      BOOLEAN ReturnSingleEntry, // 是否只获取一个
  _In_opt_  PUNICODE_STRING FileName, // 要获取的文件的名字.
  _In_      BOOLEAN RestartScan  // 是否重新扫描.
);

参数解析:

  • 参数1 - 目录句柄 , 由ZwCreateFile打开.

  • 参数2~参数4 - 填NULL即可

  • 参数5 - 函数执行后的状态

  • 参数6 - 保存函数输出信息的缓冲区, 该缓冲区使用哪种结构体由参数7决定.

  • 参数7 - 获取的文件信息类型

    • FileBothDirectoryInformation - 使用FILE_BOTH_DIR_INFORMATION 结构保存信息

    • FileDirectoryInformation - 使用FILE_DIRECTORY_INFORMATION 结构保存信息

    • FileFullDirectoryInformation - 使用FILE_FULL_DIR_INFORMATION 结构保存信息

    • FileIdBothDirectoryInformation - 使用FILE_ID_BOTH_DIR_INFORMATION 结构保存信息

    • FileIdFullDirectoryInformation - 使用FILE_ID_FULL_DIR_INFORMATION 结构保存信息

    • FileNamesInformation - 使用FILE_NAMES_INFORMATION 结构保存信息

    • FileObjectIdInformation - 使用FILE_OBJECTID_INFORMATION 结构保存信息, 这个信息只在Windows 2000版本系统以上的NTFS文件系统下有效.

    • FileReparsePointInformation - 使用FILE_REPARSE_POINT_INFORMATION 结构保存信息

  • 参数8 - 是否只读取第一个

    • 如果ReturnSingleEntry== TRUEFileName==NULL ,则只返回一个.

    • 如果ReturnSingleEntry==FALSEFileName==NULL , 则返回本目录下的所有文件

    • 如果ReturnSingleEntry==FALSEFileName==NULL , 则返回本目录下所有和FileName相匹配的文件.

  • 参数9 -

备注

该例程调用成功之后, 会将信息输出到缓冲区, 但如果缓冲区不足以容纳输出的信息, 则例程不会输出, 它也不会将所需缓冲区的字节数保存在IoStatusBlockinformation字段中.

因此, 要获取一个目录下的文件, 需要自己分配足够大的缓冲区, 然后再调用此函数. 否则是得不到全部信息的.

用法如下:

遍历文件: 版本一

这个函数能够一次性获取完整个目录下的文件和文件夹, 缺点是保存数据的缓冲区必须预先分配好,如果缓冲区不够,那么就得不到更多的数据.

//************************************
// 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)
{
    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,
                                      FALSE/*是否重新扫描目录*/);
        // 保存缓冲区的内容首地址.
        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;
}

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))

此函数的用法是:

FILE_BOTH_DIR_INFORMATION* fileInfo=NULL;
status = listDirGet(L"\??\C:\windows", /*目录路径*/
                    &fileInfo, /*保存缓冲区地址的指针*/
                    255 /*最大文件个数*/);
if(status != STATUS_SUCCESS)
    break;

// 遍历输出文件信息
// 输出调试信息
ListDirForEach(FILE_BOTH_DIR_INFORMATION, fileInfo, i)
{
    KdPrint(("%ws ", i->FileName));
}
// 释放缓冲区
listDirFree(fileInfo);

遍历文件: 版本二:

//************************************
// 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);

使用方式:

HANDLE hFind = NULL;
// 先定义一个缓冲区
char tempBuff[sizeof(FILE_BOTH_DIR_INFORMATION) + 267 * 2];
FILE_BOTH_DIR_INFORMATION* pInfo = (FILE_BOTH_DIR_INFORMATION*)tempBuff;
// 获取第一个文件
status = firstFile(L"\??\C:\windows", &hFind, pInfo, sizeof(tempBuff));

if (status == STATUS_SUCCESS)
{
    do
   {
        KdPrint(("%ws ", pInfo->FileName));
        // 遍历下一个文件
   } while (STATUS_SUCCESS==nextFile(hFind, pInfo,sizeof(tempBuff)));
}

删除文件

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 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;
}

查询文件属性

查询文件信息的API是:ZwQueryInformationFile

这个函数

获取文件属性中的文件大小

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;
}

进程线程操作

在用户层, 一个进程/线程, 都使用一个HANDLE句柄来表示, 它们都属于内核对象, 在用户层无法直接操作它们的内在结构, 但在内核层里面, 是可以直接操作它们的,

在内核层中,一个进程的内核对象使用EPROCESS结构体来描述, 线程内核对象则使用ETHREAD结构体来表示, 这两个结构体都非常的大, 下面是结构体的字段的简单介绍(内容来自WRK项目源码 )

进程内核对象

在这个结构体中, 第一个字段KPROCESS Pcb; // 内核的进程结构体也非常的重要, 它保存了进程内核对象的大量信息

KPROCESS
typedef struct _KPROCESS {

    //
    // The dispatch header and profile listhead are fairly infrequently
    // referenced.
    //

    DISPATCHER_HEADER Header;    // 分发器对象.
    LIST_ENTRY ProfileListHead;  // 全局的性能分析进程列表中的节点.

    //
    // The following fields are referenced during context switches.
    //

    ULONG_PTR DirectoryTableBase[2];// [0]保存进程的页目录表地址,[1]保存进程超空间也目录表地址.

#if defined(_X86_)

    KGDTENTRY LdtDescriptor;    // LDT(局部描述表)描述符
    KIDTENTRY Int21Descriptor;  // 兼容DOS程序的int 21h中断.
    USHORT IopmOffset;            // IOPM(I/O权限表)的位置
    UCHAR Iopl;                    // 进程的I/O优先级
    BOOLEAN Unused;             

#endif

#if defined(_AMD64_)

    USHORT IopmOffset;            // IOPM(I/O权限表)的位置

#endif

    volatile KAFFINITY ActiveProcessors; // 记录当前进程在哪些处理器上运行

    //
    // The following fields are referenced during clock interrupts.
    //
    // 这两个时间仅当进程中的一个线程结束时才更新.
    ULONG KernelTime;  // 进程对象在内核模式下的总时间.
    ULONG UserTime;       // 进程对象在用户模式下的总时间.

    //
    // The following fields are referenced infrequently.
    //

    LIST_ENTRY ReadyListHead;            // 记录已经就绪,但尚未被加入到全局就绪链表的线程的双向链表.
    SINGLE_LIST_ENTRY SwapListEntry;    // 单链表, 当进程被换出或换入内存时,都会使用到这个节点.

#if defined(_X86_)

    PVOID VdmTrapcHandler;

#else

    PVOID Reserved1;

#endif

    LIST_ENTRY ThreadListHead;        // 指向一个本进程中所有线程的链表头
    KSPIN_LOCK ProcessLock;            // 自旋锁对象.
    KAFFINITY Affinity;                // 记录该进程的线程可在何种处理器上运行的标志

    //
    // N.B. The following bit number definitions must match the following
    //      bit field.
    //
    // N.B. These bits can only be written with interlocked operations.
    //

#define KPROCESS_AUTO_ALIGNMENT_BIT 0
#define KPROCESS_DISABLE_BOOST_BIT 1
#define KPROCESS_DISABLE_QUANTUM_BIT 2

    union {
        struct {
            LONG AutoAlignment : 1;        // 内存访问对齐设置
            LONG DisableBoost : 1;        // 和线程调度的优先级提升有关.
            LONG DisableQuantum : 1;    // 和线程调度的时限有关.
            LONG ReservedFlags : 29;
        };

        LONG ProcessFlags;        //
    };

    SCHAR BasePriority;            // 进程的基本优先级
    SCHAR QuantumReset;            // 记录进程中所有线程的基本时限重置值,一般被设置为6
    UCHAR State;                // 说明进程是否在内存中,共有6中状态:
    // ProcessInMemory(进程在内存中),ProcessOutOfMemory(进程不在内存中)
    // ProcessInTransition(进程正在转移过程中),ProcessOutOfTransition(进程不在转移过程中),
    // ProcessInSwap(进程已被换出到内存交换空间中)
    // ProcessOutOfSwap(进程不在内存交换空间中)


    UCHAR ThreadSeed;    // 用于记录进程中新线程的理想处理器
    UCHAR PowerState;    // 记录电源状态
    UCHAR IdealNode;    // 进程在被创建时,优先选择的处理器节点.
    BOOLEAN Visited;
    union {
        KEXECUTE_OPTIONS Flags;
        UCHAR ExecuteOptions;    // 进程的内存执行选项
    };

#if !defined(_X86_) && !defined(_AMD64_)

    PALIGNMENT_EXCEPTION_TABLE AlignmentExceptionTable;

#endif

    ULONG_PTR StackCount; // 记录当前进程中有多个线程的栈在内存中.
    LIST_ENTRY ProcessListEntry; // 活动进程链表中的节点(KiProcessListHead),记录系统中所有具有活动线程的进程.
} KPROCESS, *PKPROCESS, *PRKPROCESS;
EPROCESS
// Process structure.
//
// If you remove a field from this structure, please also
// remove the reference to it from within the kernel debugger
// (ntprivatesdktools
tsd
tkext.c)
//

typedef struct _EPROCESS {
    KPROCESS Pcb;   // 内核的进程结构体

    //
    // Lock used to protect:
    // The list of threads in the process.
    // Process token.
    // Win32 process field.
    // Process and thread affinity setting.
    //

    EX_PUSH_LOCK ProcessLock; // 自旋锁,推锁对象,用于保护EPROCESS中数据成员.

    LARGE_INTEGER CreateTime; // 进程创建时间
    LARGE_INTEGER ExitTime;   // 进程退出时间

    //
    // Structure to allow lock free cross process access to the process
    // handle table, process section and address space. Acquire rundown
    // protection with this if you do cross process handle table, process
    // section or address space references.
    //

    EX_RUNDOWN_REF RundownProtect;// 停止保护锁,当进程被销毁时,使用此锁来等待.

    HANDLE UniqueProcessId; // 进程唯一标识,pid,在进程创建时被赋值.

    //
    // Global list of all processes in the system. Processes are removed
    // from this list in the object deletion routine.  References to
    // processes in this list must be done with ObReferenceObjectSafe
    // because of this.
    //

    LIST_ENTRY ActiveProcessLinks; // 活动进程链表(双向链表),表头由PsActiveProcessHead记录.

    //
    // Quota Fields.
    //

    SIZE_T QuotaUsage[PsQuotaTypes]; // 进程内存使用率
    SIZE_T QuotaPeak[PsQuotaTypes]; // 进程尖峰使用量.
    SIZE_T CommitCharge; // 进程的虚拟内存已提交的页面数量

    //
    // VmCounters.
    //

    SIZE_T PeakVirtualSize;
    SIZE_T VirtualSize;

    LIST_ENTRY SessionProcessLinks;

    PVOID DebugPort;// 调试端口 , 当进程被调试,此句柄有值.
    PVOID ExceptionPort; // 异常端口
    PHANDLE_TABLE ObjectTable; // 句柄表

    //
    // Security. 安全相关:
    //

    EX_FAST_REF Token; // 访问令牌,用于进程的安全访问检查

    PFN_NUMBER WorkingSetPage; //
    KGUARDED_MUTEX AddressCreationLock;
    KSPIN_LOCK HyperSpaceLock;

    struct _ETHREAD *ForkInProgress;
    ULONG_PTR HardwareTrigger;

    PMM_AVL_TABLE PhysicalVadRoot;
    PVOID CloneRoot;
    PFN_NUMBER NumberOfPrivatePages;
    PFN_NUMBER NumberOfLockedPages;
    PVOID Win32Process;
    struct _EJOB *Job;
    PVOID SectionObject;

    PVOID SectionBaseAddress;

    PEPROCESS_QUOTA_BLOCK QuotaBlock;

    PPAGEFAULT_HISTORY WorkingSetWatch;
    HANDLE Win32WindowStation;
    HANDLE InheritedFromUniqueProcessId;

    PVOID LdtInformation;
    PVOID VadFreeHint;
    PVOID VdmObjects;
    PVOID DeviceMap;

    PVOID Spare0[3];
    union {
        HARDWARE_PTE PageDirectoryPte;
        ULONGLONG Filler;
    };
    PVOID Session;
    UCHAR ImageFileName[ 16 ];

    LIST_ENTRY JobLinks;
    PVOID LockedPagesList;

    LIST_ENTRY ThreadListHead;  // 一个进程的全部线程的双向链表.

    //
    // Used by rdr/security for authentication.
    //

    PVOID SecurityPort; // 安全端口,指向该进程与lsass进程之间的跨进程通信端口.

#ifdef _WIN64
    PWOW64_PROCESS Wow64Process;
#else
    PVOID PaeTop;
#endif

    ULONG ActiveThreads; // 进程中活动线程的总数(如果为0,进程则退出.)

    ACCESS_MASK GrantedAccess;// 进程的访问权限.位域,具体值参见:publicsdkinc
tpsapi.h中的系列宏: PROCESS_XXXX

    ULONG DefaultHardErrorProcessing;

    NTSTATUS LastThreadExitStatus; // 最后一个线程的退出状态.

    //
    // Peb
    //

    PPEB Peb;        // 位于进程地址空间的内存块,包含有关进程地址空间中的对和系统模块等信息

    //
    // Pointer to the prefetches trace block.
    //
    EX_FAST_REF PrefetchTrace;

    LARGE_INTEGER ReadOperationCount; // 记录NtReadFfile的执行次数
    LARGE_INTEGER WriteOperationCount;// 记录NtWriteFfile的执行次数
    LARGE_INTEGER OtherOperationCount;// 除读和写之外其它IO操作的执行次数
    LARGE_INTEGER ReadTransferCount;  // 记录IO读操作的完成次数
    LARGE_INTEGER WriteTransferCount; // 记录IO写操作的完成次数
    LARGE_INTEGER OtherTransferCount; // 记录其它IO操作的完成次数

    SIZE_T CommitChargeLimit;
    SIZE_T CommitChargePeak;

    PVOID AweInfo;    // 用于支持AWE(地址窗口扩展).

    //
    // This is used for SeAuditProcessCreation.
    // It contains the full path to the image file.
    //

    SE_AUDIT_PROCESS_CREATION_INFO SeAuditProcessCreationInfo;// 创建进程时指定的进程映像全路径.

    MMSUPPORT Vm; // 管理进程虚拟内存的重要结构

#if !defined(_WIN64)
    LIST_ENTRY MmProcessLinks;
#else
    ULONG Spares[2];
#endif

    ULONG ModifiedPageCount;

    #define PS_JOB_STATUS_NOT_REALLY_ACTIVE      0x00000001UL
    #define PS_JOB_STATUS_ACCOUNTING_FOLDED      0x00000002UL
    #define PS_JOB_STATUS_NEW_PROCESS_REPORTED   0x00000004UL
    #define PS_JOB_STATUS_EXIT_PROCESS_REPORTED  0x00000008UL
    #define PS_JOB_STATUS_REPORT_COMMIT_CHANGES  0x00000010UL
    #define PS_JOB_STATUS_LAST_REPORT_MEMORY     0x00000020UL
    #define PS_JOB_STATUS_REPORT_PHYSICAL_PAGE_CHANGES  0x00000040UL

    ULONG JobStatus;


    //
    // Process flags. Use interlocked operations with PS_SET_BITS, etc
    // to modify these.
    //

    #define PS_PROCESS_FLAGS_CREATE_REPORTED        0x00000001UL // Create process debug call has occurred
    #define PS_PROCESS_FLAGS_NO_DEBUG_INHERIT       0x00000002UL // Don't inherit debug port
    #define PS_PROCESS_FLAGS_PROCESS_EXITING        0x00000004UL // PspExitProcess entered
    #define PS_PROCESS_FLAGS_PROCESS_DELETE         0x00000008UL // Delete process has been issued
    #define PS_PROCESS_FLAGS_WOW64_SPLIT_PAGES      0x00000010UL // Wow64 split pages
    #define PS_PROCESS_FLAGS_VM_DELETED             0x00000020UL // VM is deleted
    #define PS_PROCESS_FLAGS_OUTSWAP_ENABLED        0x00000040UL // Outswap enabled
    #define PS_PROCESS_FLAGS_OUTSWAPPED             0x00000080UL // Outswapped
    #define PS_PROCESS_FLAGS_FORK_FAILED            0x00000100UL // Fork status
    #define PS_PROCESS_FLAGS_WOW64_4GB_VA_SPACE     0x00000200UL // Wow64 process with 4gb virtual address space
    #define PS_PROCESS_FLAGS_ADDRESS_SPACE1         0x00000400UL // Addr space state1
    #define PS_PROCESS_FLAGS_ADDRESS_SPACE2         0x00000800UL // Addr space state2
    #define PS_PROCESS_FLAGS_SET_TIMER_RESOLUTION   0x00001000UL // SetTimerResolution has been called
    #define PS_PROCESS_FLAGS_BREAK_ON_TERMINATION   0x00002000UL // Break on process termination
    #define PS_PROCESS_FLAGS_CREATING_SESSION       0x00004000UL // Process is creating a session
    #define PS_PROCESS_FLAGS_USING_WRITE_WATCH      0x00008000UL // Process is using the write watch APIs
    #define PS_PROCESS_FLAGS_IN_SESSION             0x00010000UL // Process is in a session
    #define PS_PROCESS_FLAGS_OVERRIDE_ADDRESS_SPACE 0x00020000UL // Process must use native address space (Win64 only)
    #define PS_PROCESS_FLAGS_HAS_ADDRESS_SPACE      0x00040000UL // This process has an address space
    #define PS_PROCESS_FLAGS_LAUNCH_PREFETCHED      0x00080000UL // Process launch was prefetched
    #define PS_PROCESS_INJECT_INPAGE_ERRORS         0x00100000UL // Process should be given inpage errors - hardcoded in trap.asm too
    #define PS_PROCESS_FLAGS_VM_TOP_DOWN            0x00200000UL // Process memory allocations default to top-down
    #define PS_PROCESS_FLAGS_IMAGE_NOTIFY_DONE      0x00400000UL // We have sent a message for this image
    #define PS_PROCESS_FLAGS_PDE_UPDATE_NEEDED      0x00800000UL // The system PDEs need updating for this process (NT32 only)
    #define PS_PROCESS_FLAGS_VDM_ALLOWED            0x01000000UL // Process allowed to invoke NTVDM support
    #define PS_PROCESS_FLAGS_SMAP_ALLOWED           0x02000000UL // Process allowed to invoke SMAP support
    #define PS_PROCESS_FLAGS_CREATE_FAILED          0x04000000UL // Process create failed

    #define PS_PROCESS_FLAGS_DEFAULT_IO_PRIORITY    0x38000000UL // The default I/O priority for created threads. (3 bits)

    #define PS_PROCESS_FLAGS_PRIORITY_SHIFT         27

    #define PS_PROCESS_FLAGS_EXECUTE_SPARE1         0x40000000UL //
    #define PS_PROCESS_FLAGS_EXECUTE_SPARE2         0x80000000UL //


    union {

        ULONG Flags;

        //
        // Fields can only be set by the PS_SET_BITS and other interlocked
        // macros.  Reading fields is best done via the bit definitions so
        // references are easy to locate.
        //

        struct {
            ULONG CreateReported            : 1;
            ULONG NoDebugInherit            : 1;
            ULONG ProcessExiting            : 1;
            ULONG ProcessDelete             : 1;
            ULONG Wow64SplitPages           : 1;
            ULONG VmDeleted                 : 1;
            ULONG OutswapEnabled            : 1;
            ULONG Outswapped                : 1;
            ULONG ForkFailed                : 1;
            ULONG Wow64VaSpace4Gb           : 1;
            ULONG AddressSpaceInitialized   : 2;
            ULONG SetTimerResolution        : 1;
            ULONG BreakOnTermination        : 1;
            ULONG SessionCreationUnderway   : 1;
            ULONG WriteWatch                : 1;
            ULONG ProcessInSession          : 1;
            ULONG OverrideAddressSpace      : 1;
            ULONG HasAddressSpace           : 1;
            ULONG LaunchPrefetched          : 1;
            ULONG InjectInpageErrors        : 1;
            ULONG VmTopDown                 : 1;
            ULONG ImageNotifyDone           : 1;
            ULONG PdeUpdateNeeded           : 1;    // NT32 only
            ULONG VdmAllowed                : 1;
            ULONG SmapAllowed               : 1;
            ULONG CreateFailed              : 1;
            ULONG DefaultIoPriority         : 3;
            ULONG Spare1                    : 1;
            ULONG Spare2                    : 1;
        };
    };

    NTSTATUS ExitStatus;

    USHORT NextPageColor;
    union {
        struct {
            UCHAR SubSystemMinorVersion;
            UCHAR SubSystemMajorVersion;
        };
        USHORT SubSystemVersion;
    };
    UCHAR PriorityClass;

    MM_AVL_TABLE VadRoot; // 指向平衡二叉树的根节点,用于管理进程的虚拟地址空间.

    ULONG Cookie; // 存储代表该进程的随机值.

} EPROCESS, *PEPROCESS;

线程内核对象

在这个结构体中, 第一个字段KTHREAD Tcb; // 内核的线程结构体也非常的重要, 它保存了线程内核对象的大量信息

KTHREAD
typedef struct _KTHREAD {

    //
    // The dispatcher header and mutant listhead are fairly infrequently
    // referenced.
    //

    DISPATCHER_HEADER Header;  // 线程分发器对象.
    LIST_ENTRY MutantListHead; // 记录本线程突变体对象(互斥体)的链表.

    //
    // The following fields are referenced during context switches and wait
    // operatings. They have been carefully laid out to get the best cache
    // hit ratios.
    //

    PVOID InitialStack; // 记录线程栈的原始位置(高地址)
    PVOID StackLimit;   // 记录线程栈的结束位置(低地址)
    PVOID KernelStack;    // 记录了真正内核调用栈的开始位置.


    KSPIN_LOCK ThreadLock;  // 自旋锁.用于保护本结构体中的成员.
    union {
        KAPC_STATE ApcState; // APC信息
        struct {
            UCHAR ApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
            BOOLEAN ApcQueueable; // 是否可以插入APC
            volatile UCHAR NextProcessor; // 关于处理器调度的选择
            volatile UCHAR DeferredProcessor;
            UCHAR AdjustReason;// 优先级调整原因
            SCHAR AdjustIncrement;// 优先级调整量.
        };
    };

    KSPIN_LOCK ApcQueueLock; // 自旋锁,用户保护APC队列的操作.

#if !defined(_AMD64_)

    ULONG ContextSwitches; // 记录了该线程的切换次数.
    volatile UCHAR State;  // 线程的状态,KTHREAD_STATE枚举类型
    UCHAR NpxState; // 线程浮点处理器的状态
    KIRQL WaitIrql;
    KPROCESSOR_MODE WaitMode;  // 当前线程等待时的处理器模式

#endif

    LONG_PTR WaitStatus;  // 线程等待的结果.
    union {
        PKWAIT_BLOCK WaitBlockList; // 记录了一个链表, 记录了该线程在等待哪些分发器对象.
        PKGATE GateObject; // 记录了正在等待的门对象.
    };

    BOOLEAN Alertable; // 说明一个线程是否可以被唤醒
    BOOLEAN WaitNext; // 是否马上要调用一个内核等待函数
    UCHAR WaitReason; // 线程等待的原因,KWAIT_REASON枚举类型
    SCHAR Priority;      // 动态优先级值
    UCHAR EnableStackSwap;// 本线程的内核栈是否允许被换出到内存外.
    volatile UCHAR SwapBusy;// 当前线程是否正在进行上下文环境切换.
    BOOLEAN Alerted[MaximumMode];// 该线程在内核模式或用户模式下是否可唤醒.
    union {
        LIST_ENTRY WaitListEntry;  //
        SINGLE_LIST_ENTRY SwapListEntry;
    };

    PRKQUEUE Queue;

#if !defined(_AMD64_)

    ULONG WaitTime;
    union {
        struct {
            SHORT KernelApcDisable;
            SHORT SpecialApcDisable;
        };

        ULONG CombinedApcDisable;
    };

#endif

    PVOID Teb;
    union {
        KTIMER Timer;
        struct {
            UCHAR TimerFill[KTIMER_ACTUAL_LENGTH];

            //
            // N.B. The following bit number definitions must match the
            //      following bit field.
            //
            // N.B. These bits can only be written with interlocked
            //      operations.
            //

#define KTHREAD_AUTO_ALIGNMENT_BIT 0
#define KTHREAD_DISABLE_BOOST_BIT 1

            union {
                struct {
                    LONG AutoAlignment : 1;
                    LONG DisableBoost : 1;
                    LONG ReservedFlags : 30;
                };

                LONG ThreadFlags;
            };
        };
    };

    union {
        KWAIT_BLOCK WaitBlock[THREAD_WAIT_OBJECTS + 1];
        struct {
            UCHAR WaitBlockFill0[KWAIT_BLOCK_OFFSET_TO_BYTE0];
            BOOLEAN SystemAffinityActive;
        };

        struct {
            UCHAR WaitBlockFill1[KWAIT_BLOCK_OFFSET_TO_BYTE1];
            CCHAR PreviousMode;
        };

        struct {
            UCHAR WaitBlockFill2[KWAIT_BLOCK_OFFSET_TO_BYTE2];
            UCHAR ResourceIndex;
        };

        struct {
            UCHAR WaitBlockFill3[KWAIT_BLOCK_OFFSET_TO_BYTE3];
            UCHAR LargeStack;
        };

#if defined(_AMD64_)

        struct {
            UCHAR WaitBlockFill4[KWAIT_BLOCK_OFFSET_TO_LONG0];
            ULONG ContextSwitches;
        };

        struct {
            UCHAR WaitBlockFill5[KWAIT_BLOCK_OFFSET_TO_LONG1];
            volatile UCHAR State;
            UCHAR NpxState;
            KIRQL WaitIrql;
            KPROCESSOR_MODE WaitMode;
        };

        struct {
            UCHAR WaitBlockFill6[KWAIT_BLOCK_OFFSET_TO_LONG2];
            ULONG WaitTime;
        };

        struct {
            UCHAR WaitBlockFill7[KWAIT_BLOCK_OFFSET_TO_LONG3];
             union {
                 struct {
                     SHORT KernelApcDisable;
                     SHORT SpecialApcDisable;
                 };

                 ULONG CombinedApcDisable;
             };
        };

#endif

    };

    LIST_ENTRY QueueListEntry;

    //
    // The following fields are accessed during system service dispatch.
    //

    PKTRAP_FRAME TrapFrame;  // 线程环境(寄存器)
    PVOID CallbackStack;     // 线程的回调栈地址,当线程从内核模式调用到用户模式时使用.
    PVOID ServiceTable;         // 系统服务表,也在全局变量KeServiceDescriptorTable中记录着,当这是一个GUI线程是,指向的是影子系统服务表(KeServiceDescriptorTableShadow)

#if defined(_AMD64_)

    ULONG KernelLimit;

#endif

    //
    // The following fields are referenced during ready thread and wait
    // completion.
    //

    UCHAR ApcStateIndex;    // 一个索引,当前APC状态在ApcStatePointer中的索引.
    UCHAR IdealProcessor;
    BOOLEAN Preempted;    // 记录是否被高优先级的线程抢占了.
    BOOLEAN ProcessReadyQueue;// 线程是否在所属进程的ReadyListHead链表中.

#if defined(_AMD64_)

    PVOID Win32kTable;
    ULONG Win32kLimit;

#endif

    BOOLEAN KernelStackResident; // 该线程的内核栈是否驻留在内存中.
    SCHAR BasePriority;  // 静态优先级,初始值是进程结构体中的BasePriority
    SCHAR PriorityDecrement;// 线程在优先级动态调整过程中的递减值.
    CHAR Saturation;
    KAFFINITY UserAffinity;
    PKPROCESS Process;
    KAFFINITY Affinity;

    //
    // The below fields are infrequently referenced.
    //

    PKAPC_STATE ApcStatePointer[2];
    union {
        KAPC_STATE SavedApcState;
        struct {
            UCHAR SavedApcStateFill[KAPC_STATE_ACTUAL_LENGTH];
            CCHAR FreezeCount;
            CCHAR SuspendCount;
            UCHAR UserIdealProcessor;
            UCHAR CalloutActive;

#if defined(_AMD64_)

            BOOLEAN CodePatchInProgress;

#elif defined(_X86_)

            UCHAR Iopl;

#else

            UCHAR OtherPlatformFill;

#endif

        };
    };

    PVOID Win32Thread;  //
    PVOID StackBase;    // 记录当前栈的基地址(高地址,栈顶).
    union {
        KAPC SuspendApc;
        struct {
            UCHAR SuspendApcFill0[KAPC_OFFSET_TO_SPARE_BYTE0];
            SCHAR Quantum;
        };


        struct {
            UCHAR SuspendApcFill1[KAPC_OFFSET_TO_SPARE_BYTE1];
            UCHAR QuantumReset;
        };

        struct {
            UCHAR SuspendApcFill2[KAPC_OFFSET_TO_SPARE_LONG];
            ULONG KernelTime;
        };

        struct {
            UCHAR SuspendApcFill3[KAPC_OFFSET_TO_SYSTEMARGUMENT1];
            PVOID TlsArray;
        };

        struct {
            UCHAR SuspendApcFill4[KAPC_OFFSET_TO_SYSTEMARGUMENT2];
            PVOID BBTData;
        };

        struct {
            UCHAR SuspendApcFill5[KAPC_ACTUAL_LENGTH];
            UCHAR PowerState;
            ULONG UserTime;
        };
    };

    union {
        KSEMAPHORE SuspendSemaphore;
        struct {
            UCHAR SuspendSemaphorefill[KSEMAPHORE_ACTUAL_LENGTH];
            ULONG SListFaultCount;
        };
    };

    LIST_ENTRY ThreadListEntry;
    PVOID SListFaultAddress;

#if defined(_WIN64)

    LONG64 ReadOperationCount;
    LONG64 WriteOperationCount;
    LONG64 OtherOperationCount;
    LONG64 ReadTransferCount;
    LONG64 WriteTransferCount;
    LONG64 OtherTransferCount;

#endif

} KTHREAD, *PKTHREAD, *PRKTHREAD;
ETHREAD
typedef struct _ETHREAD {
    KTHREAD Tcb;   // 内核层的线程结构体

    LARGE_INTEGER CreateTime; // 线程创建时间

    union {
        // 下面三个字段由于时间不重叠,因此组成了一个union
        LARGE_INTEGER ExitTime;  // 线程结束时间
        LIST_ENTRY LpcReplyChain;// 用于跨进程通信(LPC)的双向链表的节点
        LIST_ENTRY KeyedWaitChain; // 用于带键事件的等待链表
    };
    union {
        NTSTATUS ExitStatus;  // 线程退出状态
        PVOID OfsChain;
    };

    //
    // Registry
    //

    LIST_ENTRY PostBlockList; // 用于一个线程向配置管理器等级注册表键的变化通知.

    //
    // Single linked list of termination blocks
    //

    union {
        //
        // List of termination ports
        //

        PTERMINATION_PORT TerminationPort;//线程退出事件的接收端口链表.

        //
        // List of threads to be reaped. Only used at thread exit
        //

        struct _ETHREAD *ReaperLink;

        //
        // Keyvalue being waited for
        //
        PVOID KeyedWaitValue;

    };

    KSPIN_LOCK ActiveTimerListLock;
    LIST_ENTRY ActiveTimerListHead;

    CLIENT_ID Cid;

    //
    // Lpc
    //

    union {
        KSEMAPHORE LpcReplySemaphore;
        KSEMAPHORE KeyedWaitSemaphore;
    };

    union {
        PVOID LpcReplyMessage;          // -> Message that contains the reply
        PVOID LpcWaitingOnPort;
    };

    //
    // Security
    //
    //
    //    Client - If non null, indicates the thread is impersonating
    //        a client.
    //

    PPS_IMPERSONATION_INFORMATION ImpersonationInfo;// 线程模仿信息

    //
    // Io
    //

    LIST_ENTRY IrpList;//正在处理尚未完成的IO请求(IRO对象)

    //
    //  File Systems
    //

    ULONG_PTR TopLevelIrp;  // either NULL, an Irp or a flag defined in FsRtl.h
    struct _DEVICE_OBJECT *DeviceToVerify;

    PEPROCESS ThreadsProcess; // 线程所属进程,THREAD_TO_PROCESS这个宏正式使用这个字段来工作
    PVOID StartAddress;
    union {
        PVOID Win32StartAddress; // 线程回调函数地址.
        ULONG LpcReceivedMessageId;
    };
    //
    // Ps
    //

    LIST_ENTRY ThreadListEntry; //

    //
    // Rundown protection structure. Acquire this to do cross thread
    // TEB, TEB32 or stack references.
    //

    EX_RUNDOWN_REF RundownProtect;

    //
    // Lock to protect thread impersonation information
    //
    EX_PUSH_LOCK ThreadLock;

    ULONG LpcReplyMessageId;    // MessageId this thread is waiting for reply to

    ULONG ReadClusterSize;

    //
    // Client/server
    //

    ACCESS_MASK GrantedAccess;

    //
    // Flags for cross thread access. Use interlocked operations
    // via PS_SET_BITS etc.
    //

    //
    // Used to signify that the delete APC has been queued or the
    // thread has called PspExitThread itself.
    //

    #define PS_CROSS_THREAD_FLAGS_TERMINATED           0x00000001UL

    //
    // Thread create failed
    //

    #define PS_CROSS_THREAD_FLAGS_DEADTHREAD           0x00000002UL

    //
    // Debugger isn't shown this thread
    //

    #define PS_CROSS_THREAD_FLAGS_HIDEFROMDBG          0x00000004UL

    //
    // Thread is impersonating
    //

    #define PS_CROSS_THREAD_FLAGS_IMPERSONATING        0x00000008UL

    //
    // This is a system thread
    //

    #define PS_CROSS_THREAD_FLAGS_SYSTEM               0x00000010UL

    //
    // Hard errors are disabled for this thread
    //

    #define PS_CROSS_THREAD_FLAGS_HARD_ERRORS_DISABLED 0x00000020UL

    //
    // We should break in when this thread is terminated
    //

    #define PS_CROSS_THREAD_FLAGS_BREAK_ON_TERMINATION 0x00000040UL

    //
    // This thread should skip sending its create thread message
    //
    #define PS_CROSS_THREAD_FLAGS_SKIP_CREATION_MSG    0x00000080UL

    //
    // This thread should skip sending its final thread termination message
    //
    #define PS_CROSS_THREAD_FLAGS_SKIP_TERMINATION_MSG 0x00000100UL

    union {

        ULONG CrossThreadFlags;

        //
        // The following fields are for the debugger only. Do not use.
        // Use the bit definitions instead.
        //

        struct {
            ULONG Terminated              : 1;
            ULONG DeadThread              : 1;
            ULONG HideFromDebugger        : 1;
            ULONG ActiveImpersonationInfo : 1;
            ULONG SystemThread            : 1;
            ULONG HardErrorsAreDisabled   : 1;
            ULONG BreakOnTermination      : 1;
            ULONG SkipCreationMsg         : 1;
            ULONG SkipTerminationMsg      : 1;
        };
    };

    //
    // Flags to be accessed in this thread's context only at PASSIVE
    // level -- no need to use interlocked operations.
    //

    union {
        ULONG SameThreadPassiveFlags;

        struct {

            //
            // This thread is an active Ex worker thread; it should
            // not terminate.
            //


            ULONG ActiveExWorker : 1;
            ULONG ExWorkerCanWaitUser : 1;
            ULONG MemoryMaker : 1;

            //
            // Thread is active in the keyed event code. LPC should not run above this in an APC.
            //
            ULONG KeyedEventInUse : 1;
        };
    };

    //
    // Flags to be accessed in this thread's context only at APC_LEVEL.
    // No need to use interlocked operations.
    //

    union {
        ULONG SameThreadApcFlags;
        struct {

            //
            // The stored thread's MSGID is valid. This is only accessed
            // while the LPC mutex is held so it's an APC_LEVEL flag.
            //

            BOOLEAN LpcReceivedMsgIdValid : 1;
            BOOLEAN LpcExitThreadCalled   : 1;
            BOOLEAN AddressSpaceOwner     : 1;
            BOOLEAN OwnsProcessWorkingSetExclusive  : 1;
            BOOLEAN OwnsProcessWorkingSetShared     : 1;
            BOOLEAN OwnsSystemWorkingSetExclusive   : 1;
            BOOLEAN OwnsSystemWorkingSetShared      : 1;
            BOOLEAN OwnsSessionWorkingSetExclusive  : 1;
            BOOLEAN OwnsSessionWorkingSetShared     : 1;

            #define PS_SAME_THREAD_FLAGS_OWNS_A_WORKING_SET    0x000001F8UL

            BOOLEAN ApcNeeded                       : 1;
        };
    };

    BOOLEAN ForwardClusterOnly;
    BOOLEAN DisablePageFaultClustering;
    UCHAR ActiveFaultCount;

#if defined (PERF_DATA)
    ULONG PerformanceCountLow;
    LONG PerformanceCountHigh;
#endif

} ETHREAD, *PETHREAD;

进程操作函数一览

函数名
功能

PsGetCurrentProcess()
获得当前进程的EPROCESS结构

PsLookupProcessByProcessId
根据进程ID获取进程EPROCESS结构 ,此函数会返回EPROCESS指针, 该指针需要调用ObDereferenceObject来递减引用计数

PsSuspendProcess
暂停一个进程

PsResumeProcess
恢复一个进程

ZwOpenProcess
打开一个进程

ZwTerminateProcess
结束一个进程

ZwClose
关闭句柄

线程操作函数一览

函数名
功能

PsGetCurrentThread
获得当前线程的ETHREAD结构

PsLookupThreadByThreadId
根据线程ID获取线程ETHREAD结构,此函数会返回EPROCESS指针, 该指针需要调用ObDereferenceObject来递减引用计数

ZwOpenThread
打开一个线程

PsCreateSystemThread
创建一个内核线程

ZwClose
关闭句柄

IoThreadToProcess
得到线程所属的进程EPROCESS

遍历进程以及遍历进程模块

方法一:

在进程内核对象结构体中(EPROCESS)中,有一个字段是:LIST_ENTRY ActiveProcessLinks; , 这个字段记录的就是当前系统上的活动进程链表, 通过遍历这条链表就能得到当前系统上的所有进程.

但是,由于Windows内核对于大多数内核结构体没有进行文档化, 导致无法直接在驱动项目中使用这些结构体字段. 因此,使用时可以配合windbg的dt命令来查看想要得到的字段的偏移.

1567043160377

 // 声明函数原型
CHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
void enumProcess()
{
    DbgBreakPoint();

    // 1. 获取当前进程对象
    ULONG_PTR proc = (ULONG_PTR)PsGetCurrentProcess();
    // 2. 获取进程对象内的当前活动进程链表
    LIST_ENTRY* pProcList = (LIST_ENTRY*)(proc + 0xB8);
    LIST_ENTRY* listBegin = pProcList;
    // 开始遍历

    do{
        proc = (ULONG_PTR)pProcList - 0xB8;
        // 获取进程ID,进程路径,EPROCESS.

        ULONG pid = (ULONG)PsGetProcessId((PEPROCESS)proc); //*(ULONG*)(proc + 0xB4);
        // 获取进程路径
        CHAR* path = PsGetProcessImageFileName((PEPROCESS)proc);//(CHAR*)proc + 0x16c;

        KdPrint(("PID=%d,PATH=%s
", pid, path));

        // 得到进程链表的下一个.
        pProcList = pProcList->Flink;
    }while (pProcList != listBegin);

}

方法二:

方法一有一个缺点: 如果进程链表中的一个节点被有意地断开, 那么通过这种方式就无法遍历(这就是进程隐藏的原理, 只要将目标进程的EPROCESS链表节点从链表删除, 用户层程序就无法遍历到这个进程).

第二种方法是通过API:PsLookupProcessByProcessId ,这个API的原理是给其一个进程ID,它就能够返回PID所对应的进程内核对象, 但如果PID是无效时,函数不会返回进程内核对象.

因此可以这样做:

CHAR* PsGetProcessImageFileName(IN PEPROCESS Process);
void enumProcess()
{
    PEPROCESS proc = NULL;
    // 0xFFFF是最大的进程ID,这个值并非绝对的,可以设置大一点
    // 如果设置得太小有些进程就无法遍历到.
    for (ULONG i = 4; i < 0xFFFF; i+=sizeof(HANDLE))
    {
        // 如果函数返回STATUS_SUCCESS则说明该ID是有对应的进程的.
        if (STATUS_SUCCESS == PsLookupProcessByProcessId((HANDLE)i, &proc))
        {
            ULONG pid = (ULONG)PsGetProcessId((PEPROCESS)proc); //*(ULONG*)(proc + 0xB4);
            // 获取进程路径
            CHAR* path = PsGetProcessImageFileName((PEPROCESS)proc);//(CHAR*)proc + 0x16c;
            KdPrint(("PID=%d,PATH=%s
", pid, path));

            // 减少引用计数
            ObDereferenceObject(proc);
        }
    }
}

创建线程

NTSTATUS
PsCreateSystemThread(
    _Out_     PHANDLE ThreadHandle,   //得到句柄
    _In_      ULONG DesiredAccess,    //创建的权限
    _In_opt_  POBJECT_ATTRIBUTES ObjectAttributes,//为NULL
    _In_opt_  HANDLE ProcessHandle,   //线程从属于谁
    _Out_opt_ PCLIENT_ID ClientId,    //用于获得ID
    _In_      PKSTART_ROUTINE StartRoutine,//运行的地址
    _In_opt   PVOID StartContext      //传递给函数的参数
    );

进程挂靠

  • KeStackAttachProcess

  • KeUnstackDetachProcess

void enumProcess()
{
    DbgBreakPoint();
    PEPROCESS proc = NULL;
    BOOLEAN isAttach = FALSE;
    // 0xFFFF是最大的进程ID,这个值并非绝对的,可以设置大一点
    // 如果设置得太小有些进程就无法遍历到.
    for (ULONG i = 4; i < 0xFFFF; i += sizeof(HANDLE))
    {
        // 获取进程ID有无对应的进程
        if (STATUS_SUCCESS == PsLookupProcessByProcessId((HANDLE)i, &proc))
        {
            // 获取进程ID,实际就是i的值
            ULONG pid = (ULONG)PsGetProcessId((PEPROCESS)proc); //*(ULONG*)(proc + 0xB4);
            // 获取进程路径
            CHAR* path = PsGetProcessImageFileName((PEPROCESS)proc);//(CHAR*)proc + 0x16c;
            KdPrint(("PID=%d,PATH=%s
", pid, path));

            // 遍历进程模块
            KAPC_STATE state = { 0 };

            // 进程模块保存在进程的虚拟地址空间,需要挂靠到这个进程,才能将该进程的虚拟地址
            // 转换为真正的物理地址,读取到内存数据

            // 挂靠遍历出来的进程
            KeStackAttachProcess(proc, &state);
            // 获取进程PEB
            ULONG_PTR peb = *(ULONG_PTR*)((ULONG_PTR)proc + 0x1a8);

            if (peb)
            {
                // pebLdr  = PEB.Ldr
                ULONG_PTR pebLdr = *(ULONG_PTR*)(peb + 0x0C);
                // 获取模块链表
                // moduelList = Ldr.InLoadOrderModuleList
                LIST_ENTRY* pModList = (LIST_ENTRY*)(pebLdr + 0x0C);
                LIST_ENTRY* moduleListBegin = pModList;

                do
                {
                    // 获取模块链表入口: _LDR_DATA_TABLE_ENTRY
                    ULONG_PTR pebDataTableEntry = (ULONG_PTR)(pModList->Flink);
                    // DataTableEntry.DllBase (win7 32位下的偏移)
                    ULONG_PTR dllBase = *(ULONG_PTR*)(pebDataTableEntry + 0x18);
                    // LDR_DATA_TABLE_ENTRY.DllName
                    UNICODE_STRING* dllName = (UNICODE_STRING*)(pebDataTableEntry + 0x24);

                    // 输出字段的值
                    KdPrint(("	BASE:%08X NAME:%S
", dllBase, dllName->Buffer));

                    pModList = pModList->Flink;
                } while (pModList != moduleListBegin);
            }            
            // 解除进程挂靠
            KeUnstackDetachProcess(&state);

            // 减少引用计数
            ObDereferenceObject(proc);
        }
    }
}

注册表

创建键

NTSTATUS RegCreateKey(LPWSTR KeyName/*路径命名规则:Registry键的路径*/)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName;
    NTSTATUS ntStatus;
    HANDLE hRegister;
    RtlInitUnicodeString( &usKeyName, KeyName);
    // 初始化对象信息(注册表的路径)
    InitializeObjectAttributes(&objectAttributes,
                               &usKeyName,
                               OBJ_CASE_INSENSITIVE,//对大小写敏感
                               NULL,
                               NULL );
    ntStatus=ZwCreateKey(&hRegister,/*输出的注册表句柄*/
                         KEY_ALL_ACCESS,
                         &objectAttributes,
                         0,
                         NULL, 
                         REG_OPTION_NON_VOLATILE,
                         NULL);
    if(NT_SUCCESS(ntStatus)){
        ZwClose(hRegister);
    }
    return ntStatus;
}

获取键值

NTSTATUS RegQueryValueKey(LPWSTR KeyName, LPWSTR ValueName,  
                          PKEY_VALUE_PARTIAL_INFORMATION *pkvpi)
{
    ULONG ulSize;
    NTSTATUS ntStatus;
    PKEY_VALUE_PARTIAL_INFORMATION pvpi;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE hRegister;
    UNICODE_STRING usKeyName;
           UNICODE_STRING usValueName;
    RtlInitUnicodeString(&usKeyName, KeyName);
    RtlInitUnicodeString(&usValueName, ValueName);
    InitializeObjectAttributes(&objectAttributes,
                          &usKeyName,
                          OBJ_CASE_INSENSITIVE,//对大小写敏感
                          NULL,
                          NULL );
    // 打开注册表键的句柄
    ntStatus = ZwOpenKey( &hRegister, KEY_ALL_ACCESS, &objectAttributes);
    // 查询键
    ntStatus = ZwQueryValueKey(hRegister,
                               &usValueName,
                               KeyValuePartialInformation ,
                               NULL,
                               0,
                               &ulSize);
    if (ntStatus==STATUS_OBJECT_NAME_NOT_FOUND || ulSize==0)
    {
        DbgPrint("ZwQueryValueKey 1 failed!
");
        return STATUS_UNSUCCESSFUL;
    }
    pvpi = (PKEY_VALUE_PARTIAL_INFORMATION)ExAllocatePool(PagedPool,ulSize);
    // 查询键的值
    ntStatus = ZwQueryValueKey(hRegister,
                               &usValueName,
                               KeyValuePartialInformation ,
                               pvpi,
                               ulSize,
                               &ulSize);
    if (!NT_SUCCESS(ntStatus))
    {
        DbgPrint("ZwQueryValueKey 2 failed!
");
        return STATUS_UNSUCCESSFUL;
    }
    //这里的pvpi是没有释放的用完要释放。ExFreePool(pvpi);
    *pkvpi=pvpi;
    DbgPrint("ZwQueryValueKey success!
");
    return STATUS_SUCCESS;
}

设置键值

void RegSetValueKey(LPWSTR KeyName, LPWSTR ValueName, DWORD DataType, PVOID DataBuffer, DWORD DataLength){
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName,usValueName;
    NTSTATUS ntStatus;
    HANDLE hRegister;
    ULONG Type;
    RtlInitUnicodeString(&usKeyName, KeyName);
    RtlInitUnicodeString(&usValueName, ValueName);
    InitializeObjectAttributes(&objectAttributes,
                               &usKeyName,
                               OBJ_CASE_INSENSITIVE,//对大小写敏感
                               NULL,
                               NULL );
    // 打开注册表键,获取键的句柄
    ntStatus = ZwOpenKey(&hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
       ntStatus = ZwSetValueKey(hRegister, &usValueName, 0, DataType, DataBuffer, DataLength);
       ZwFlushKey(hRegister);
       ZwClose(hRegister);
       DbgPrint("ZwSetValueKey success!
");
    }
    else
       DbgPrint("ZwSetValueKey failed!
");
}

删除键值

NTSTATUS RegDeleteKey(LPWSTR KeyName/*路径命名规则:Registry键的路径*/)
{
    OBJECT_ATTRIBUTES objectAttributes;
    UNICODE_STRING usKeyName;
    NTSTATUS ntStatus;
    HANDLE hRegister;
    RtlInitUnicodeString( &usKeyName, KeyName);
    // 初始化注册表的路径
    InitializeObjectAttributes(&objectAttributes,
                               &usKeyName,
                               OBJ_CASE_INSENSITIVE,//对大小写敏感
                               NULL,
                               NULL );
    // 打开注册表,获取注册表键的路径
    ntStatus = ZwOpenKey( &hRegister, KEY_ALL_ACCESS, &objectAttributes);
    if (NT_SUCCESS(ntStatus)){
        // 删除注册表键
        ntStatus = ZwDeleteKey(hRegister);
        ZwClose(hRegister);
        DbgPrint("ZwDeleteKey success!
");
    }
    return ntStatus;
}

枚举子键

VOID EnumSubValueTest(WCHAR *MY_KEY_NAME)
{
    //WCHAR MY_KEY_NAME[] = L"\Registry\Machine\Software\Microsoft\.NETFramework";
    UNICODE_STRING RegUnicodeString;
    HANDLE hRegister;
    OBJECT_ATTRIBUTES objectAttributes;
    ULONG ulSize,i;
    UNICODE_STRING uniKeyName;
    PKEY_FULL_INFORMATION pfi;
    NTSTATUS ntStatus;
    //初始化UNICODE_STRING字符串
    RtlInitUnicodeString( &RegUnicodeString, MY_KEY_NAME);
    //初始化objectAttributes
    InitializeObjectAttributes(&objectAttributes,
                     &RegUnicodeString,

                                          OBJ_CASE_INSENSITIVE,//对大小写敏感
                     NULL, 
                     NULL );
    //打开注册表
    ntStatus = ZwOpenKey( &hRegister,KEY_ALL_ACCESS,&objectAttributes);
    if (NT_SUCCESS(ntStatus))
    {
        DbgPrint("Open register successfully
");
    }
    //查询VALUE的大小
    ZwQueryKey(hRegister,KeyFullInformation,NULL,0,&ulSize);
    pfi = (PKEY_FULL_INFORMATION)    ExAllocatePool(PagedPool,ulSize);
    ZwQueryKey(hRegister,KeyFullInformation,pfi,ulSize,&ulSize);

    for (i=0;i<pfi->Values;i++)
    {
        PKEY_VALUE_BASIC_INFORMATION pvbi;
        //查询单个VALUE的大小
        ZwEnumerateValueKey(hRegister,i,KeyValueBasicInformation,NULL,0,&ulSize);
        pvbi = (PKEY_VALUE_BASIC_INFORMATION)ExAllocatePool(PagedPool,ulSize);
        //查询单个VALUE的详情
        ZwEnumerateValueKey(hRegister,i,KeyValueBasicInformation,pvbi,ulSize,&ulSize);
        uniKeyName.Length = (USHORT)pvbi->NameLength;
        uniKeyName.MaximumLength = (USHORT)pvbi->NameLength;
        uniKeyName.Buffer = pvbi->Name;
        DbgPrint("The %d sub value name:%wZ
",i,&uniKeyName);
        if (pvbi->Type==REG_SZ)
            DbgPrint("The sub value type:REG_SZ
");
        else if (pvbi->Type==REG_MULTI_SZ)
            DbgPrint("The sub value type:REG_MULTI_SZ
"); 
        else if (pvbi->Type==REG_DWORD)
            DbgPrint("The sub value type:REG_DWORD
"); 
        else if (pvbi->Type==REG_BINARY)
            DbgPrint("The sub value type:REG_BINARY
"); 
        ExFreePool(pvbi);
    }
    ExFreePool(pfi);
    ZwClose(hRegister);
}

原文地址:https://www.cnblogs.com/ltyandy/p/11432804.html