微软技术分享
+ -

Windows内核中的链表LIST_ENTRY与结构体

2023-11-13 9 0
原文转自:https://lyshark.blog.csdn.net/article/details/127018142

csdn《微软技术分享》原文转载分享:https://lyshark.blog.csdn.net/

以下文章虽为转载,但也包含了本人的修改与测试验证。

Windows内核中是无法使用vector容器等数据结构的,当我们需要保存一个结构体数组时,就需要使用内核中提供的专用链表结构LIST_ENTRY通过一些列链表操作函数对结构体进行装入弹出等操作,LIST_ENTRY是Windows内核中定义的一个双向链表的结构体。它通常作为数据结构的一部分使用,用于将多个数据项连接在一起,形成链表结构。

该链表结构如下所示;

typedef struct _LIST_ENTRY {
    struct _LIST_ENTRY *Flink; // 前向指针
    struct _LIST_ENTRY *Blink; // 后向指针
} LIST_ENTRY, *PLIST_ENTRY;

在使用LIST_ENTRY时,通常会将其作为数据结构的一个成员,以建立链表的链接关系。每个数据项都包含一个LIST_ENTRY结构体成员,通过该成员的前向指针和后向指针可以实现链表的遍历和操作。

如下代码是本人总结的内核中使用链表存储多个结构体的通用案例。

首先实现一个枚举用户进程功能,将枚举到的进程存储到链表结构体内。

#include <ntifs.h>
#include <windef.h>

extern PVOID PsGetProcessPeb(_In_ PEPROCESS Process);
NTKERNELAPI NTSTATUS PsLookupProcessByProcessId(HANDLE ProcessId, PEPROCESS* Process);
extern "C" PVOID PsGetProcessWow64Process(_In_ PEPROCESS Process);
extern "C" HANDLE PsGetProcessInheritedFromUniqueProcessId(IN PEPROCESS Process);
extern "C" PCHAR PsGetProcessImageFileName(PEPROCESS pEProcess);

typedef struct
{
    DWROD forpid;
    DWORD Pid;
    UCHAR ProcessName[2048];
    DWORD Handle;
    LIST_ENTRY ListEntry;
}ProcessList;

// 根据进程ID返回进程EPROCESS结构体失败返回NULL
PEPROCESS LookupProcess(HANDLE Pid)
{
    PEPROCESS eprocess = NULL;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    Status = PsLookupProcessByProcessId(Pid, &eprocess);
    if (NT_SUCCESS(Status))
    {
        return eprocess;
    }
    return NULL;
}

// 内核链表操作
// By: LyShark
BOOLEAN GetAllProcess()
{
    PEPROCESS eproc = NULL;
    LIST_ENTRY linkListHead;

    // 初始化链表头部
    InitializeListHead(&linkListHead);
    ProcessList *pData = NULL;

    for (int temp = 0; temp < 100000; temp += 4)
    {
        eproc = LookupProcess((HANDLE)temp);
        if (eproc != NULL)
        {
            STRING nowProcessnameString = { 0 };
            RtlInitString(&nowProcessnameString, PsGetProcessImageFileName(eproc));

            // DbgPrint("进程名: %s --> 进程PID = %d --> 父进程PPID = %d\r\n", 
            // PsGetProcessImageFileName(eproc), PsGetProcessId(eproc), PsGetProcessInheritedFromUniqueProcessId(eproc));

            // 分配内核堆空间
            pData = (ProcessList *)ExAllocatePool(PagedPool, sizeof(ProcessList));
            RtlZeroMemory(pData, sizeof(ProcessList));

            // 设置变量
            pData->forpid = temp;
            pData->Pid = (DWORD)PsGetProcessId(eproc);
            RtlCopyMemory(pData->ProcessName, PsGetProcessImageFileName(eproc), strlen(PsGetProcessImageFileName(eproc)) * 2);
            pData->Handle = (DWORD)PsGetProcessInheritedFromUniqueProcessId(eproc);

            // 插入元素到
            InsertTailList(&linkListHead, &pData->ListEntry);
            ObDereferenceObject(eproc);
        }
    }

    // 输出链表内的数据
    while (!IsListEmpty(&linkListHead))
    {
        LIST_ENTRY *pEntry = RemoveHeadList(&linkListHead);
        pData = CONTAINING_RECORD(pEntry, ProcessList, ListEntry);

        DbgPrint("%d \n", pData->Pid);
        DbgPrint("%s \n", pData->ProcessName);
        DbgPrint("%d \n", pData->Handle);
        ExFreePool(pData);
    }
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark.com \n");

    GetAllProcess();

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

运行时某一个进程的内容如下:
162218943082

实际进程ID是全局对象句柄表中的索引,所以和句柄有相同的属性,所以也是4倍的关系。

运行后将可以在DbgView中看到输出的进程信息:
152433641020

如果需要返回一个结构体,则可以这样来写代码。

#include <ntifs.h>
#include <windef.h>

typedef struct
{
    int count;
    char username[256];
    char password[256];
}MyData;

// 模拟返回一个结构
BOOLEAN GetProcess(PVOID OutPut)
{
    RtlZeroMemory(OutPut, sizeof(MyData));
    MyData *data = OutPut;

    data->count = 100;
    RtlCopyMemory(data->username, "lyshark.com", sizeof("lyshark.com"));
    RtlCopyMemory(data->password, "https://www.cnblogs.com/lyshark", sizeof("https://www.cnblogs.com/lyshark"));
    return TRUE;
}

VOID UnDriver(PDRIVER_OBJECT driver)
{
    DbgPrint(("Uninstall Driver Is OK \n"));
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath)
{
    DbgPrint("hello lyshark.com \n");
    PVOID Ptr = (PVOID)ExAllocatePool(NonPagedPool, sizeof(MyData));


    GetProcess(Ptr);

    MyData *data = (MyData *)Ptr;

    DbgPrint("count = %d \n", data->count);
    DbgPrint("username = %s \n", data->username);
    DbgPrint("password = %s \n", data->password);

    Driver->DriverUnload = UnDriver;
    return STATUS_SUCCESS;
}

输出效果如下:
152503776001

0 篇笔记 写笔记

IRP与LIST_ENTRY的关联使用
IRP是Windows内核中的一个很重要的概念,代表着应用或内核对设备的请求。LIST_ENTRY又是Windows内核提供的一个链表。IRP挂入LIST_ENTRYVOID RecyleFrame(PFDO_DEVICE_EXTENSION deviceExtension, FrameHead......
链表LIST_ENTRY
Windows的内核开发者们自己开发了部分数据结构,比如说LIST_ENTRYLIST_ENTRY是一个双向链表结构。它总是在使用的时候,被插入到已有的数据结构中。下面举一个例子。我构筑一个链表,这个链表的每个节点,是一个文件名和一个文件大小两个数据成员组成的结构。此外有一个FILE_OBJEC......
IRP.Tail.ListEntry
IRP.Tail.ListEntry 用于用户自定义LIST_ENTRY使用。 如我们对IRP进行链表管理:VOID RecycleIrp(IN PFDO_DEVICE_EXTENSION DeviceExtension, PIRP Irp){ KIRQL OldLevel; ......
Windows内核中的链表LIST_ENTRY与结构体
以下文章虽为转载,但也包含了本人的修改与测试验证。Windows内核中是无法使用vector容器等数据结构的,当我们需要保存一个结构体数组时,就需要使用内核中提供的专用链表结构LIST_ENTRY通过一些列链表操作函数对结构体进行装入弹出等操作,LIST_ENTRY是Windows内核中定义的......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!