以下内容全部来自《Windows驱动开发技术详解》,作者张帆、史彩成等,属摘抄型笔记。
///
1.驱动程序的局部变量存放在栈(Stack)空间中,但是栈空间不大,对于内核程序来说,内存是非常宝贵的资源,不适合递归调用或者使用大型结构体作为局部变量。(类似EPROCESS结构是PEPROCESS指针动态分配内存使用的)
2.堆内存申请及释放
堆内存分配函数原型如下:
PVOID ExAllocatePool(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes);
PVOID ExAllocatePoolWithTag(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes,
IN ULONG Tag);
PVOID ExAllocatePoolWithQuota(IN POOL_TYPE PoolType,
IN SIZE_T NumberOfBytes);
PoolType:枚举变量,如果此值为NonPagedPool,则分配非分页内存;如果此值为PagedPool,则分配内存为分页内存。
NumberOfBytes:为分配内存的大小,最好为4的倍数。
返回值为分配的内存地址,如果返回NULL,则代表分配失败。
堆内存回收函数原型如下:
VOID ExFreePool(IN PVOID P);
VOID ExFreePoolWithTag(IN PVOID P,
IN ULONG Tag);
DDK提供标准双向链表,BLINK指针指向前一个元素,FLINK指针指向下一个元素。
typedef struct _LIST_ENTRY
{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
链表这个数据结构非常熟悉了,就不用太多文字进行介绍。
a.链表初始化
将BLINK和FLINK指向自己。可以使用InitializeListHead()函数实现。
VOID InitializeListHead(IN PLIST_ENTRY ListHead)
{
ListHead->Flink = ListHead->Blink = ListHead;
}
b.判断链表是否为空
判断FLINK和BLONK是否指向自己即可。
#define IsListEmpty(ListHead) \
((ListHead)->Flink == (ListHead))
c.头插法
VOID InsertHeadList(IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry)
{
PLIST_ENTRY Flink;
Flink = ListHead->Flink;
Entry->Flink = Flink;
Entry->Blink = ListHead;
Flink->Blink = Entry;
ListHead->Flink = Entry;
}
d.尾插法
VOID InsertTailList(IN PLIST_ENTRY ListHead,
IN PLIST_ENTRY Entry)
{
PLIST_ENTRY Blink;
Blink = ListHead->Blink;
Entry->Flink = ListHead;
Entry->Blink = Blink;
Blink->Flink = Entry;
ListHead->Blink = Entry;
}
e.头删法
PLIST_ENTRY RemoveHeadList(IN PLIST_ENTRY ListHead)
{
PLIST_ENTRY Flink;
PLIST_ENTRY Entry;
Entry = ListHead->Flink;
Flink = Entry->Flink;
ListHead->Flink = Flink;
Flink->Blink = ListHead;
return Entry;
}
f.尾删法
PLIST_ENTRY RemoveTailList(IN PLIST_ENTRY ListHead)
{
PLIST_ENTRY Blink;
PLIST_ENTRY Entry;
Entry = ListHead->Blink;
Blink = Entry->Blink;
ListHead->Blink = Blink;
Blink->Flink = ListHead;
return Entry;
}
以上都是LIST_ENTRY的基本用法,更多的内容在NTDDK.H中都有,这里就不粘贴代码了。
将自己的数据和链表结合,如下:
typedef struct _POINT
{
LIST_ENTRY ListEntry;
ULONG x;
ULONG y;
}POINT *PPOINT;
4.使用链表进行实验,这次不抄书上的例子了,自己尝试一下。
PS:当时想就像遍历进程,然后写入链表,之后再从链表中读取信息。没想到PEDIY上已经有前辈写过示例代码,更没想到的是蓝屏的位置和引起蓝屏的原因都是一模一样的,中午用了点点时间调通了程序,下面整合了.h一并发上来,内容都已经注释过,如果有什么问题,可以留言,一起学习。
#include <ntddk.h>
#define SystemProcessesAndThreadsInformation 5
//更多信息可以参考SYSTEM_PROCESSES结构
typedef struct _PROCESSINFO
{
LIST_ENTRY ListEntry; //链表结构
ULONG ProcessId; //存储进程ID
LPWSTR ProcessName; //存储进程名
} PROCESSINFO, *PPROCESSINFO;
typedef struct _SYSTEM_PROCESSES
{
ULONG NextEntryDelta;
ULONG ThreadCount;
ULONG Reserved[6];
LARGE_INTEGER CreateTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER KernelTime;
UNICODE_STRING ProcessName;
KPRIORITY BasePriority;
ULONG ProcessId;
ULONG InheritedFromProcessId;
ULONG HandleCount;
ULONG Reserved2[2];
VM_COUNTERS VmCounters;
IO_COUNTERS IoCounters;
} SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;
NTKERNELAPI NTSTATUS ZwQuerySystemInformation(IN ULONG SystemInformationClass,
IN OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL);
BOOLEAN EnumProcess()
{
LIST_ENTRY ListHead;
PPROCESSINFO pDataList;
ULONG cbBuffer = 0x8000; //开辟缓冲区的长度,预设为0x8000字节
PVOID pBuffer = NULL; //用来执行缓冲区
NTSTATUS rc; //返回值,等下获取信息的返回值放这里面
PSYSTEM_PROCESSES pInfo; //指向SYSTEM_PROCESSES的指针
InitializeListHead(&ListHead); //初始化链表头
KdPrint(("--------Enum Process Begin--------\n"));
do
{
pBuffer = ExAllocatePool(NonPagedPool, cbBuffer); //开辟内存,这里需要非分页内存
if (pBuffer == NULL)
{
return FALSE; //申请不成功,直接返回失败,不继续了
}
rc = ZwQuerySystemInformation(SystemProcessesAndThreadsInformation, pBuffer, cbBuffer, NULL);
//获取
if (rc == STATUS_INFO_LENGTH_MISMATCH) //缓冲区不足
{
ExFreePool(pBuffer);
//缓冲区不足,则先把申请好的删掉,再把申请内存的大小扩大一倍,等下再申请
cbBuffer *= 2;
}
else if (!NT_SUCCESS(rc))
{
ExFreePool(pBuffer);
//其它错误,直接返回失败,不继续
return FALSE;
}
} while(rc == STATUS_INFO_LENGTH_MISMATCH);//直到有充足的缓冲区
//如果没有足够的缓冲区,就会接着循环,直到足够,然后获得了信息
//开始遍历进程,并且将需要的信息放入链表当中
pInfo = (PSYSTEM_PROCESSES)pBuffer;
while(1)
{
//此处的一个Alloc引起可一个蓝屏,主要是sizeof申请成了指针大小,引起的内存问题
pDataList = (PPROCESSINFO)ExAllocatePool(NonPagedPool, sizeof(PROCESSINFO));
pDataList->ProcessId = pInfo->ProcessId;
pDataList->ProcessName = pInfo->ProcessName.Buffer;
InsertHeadList(&ListHead, &pDataList->ListEntry);
if (pInfo->NextEntryDelta == 0)
{
break;
//如果没有下一个就结束
}
pInfo = (PSYSTEM_PROCESSES)(((PUCHAR)pInfo) + pInfo->NextEntryDelta);
}
ExFreePool(pBuffer);
pBuffer = NULL;
while (!IsListEmpty(&ListHead))
{
//遍历链表结构。使用尾删法,并将删除的节点内存Free
PLIST_ENTRY pEntry = RemoveTailList(&ListHead);
pDataList = CONTAINING_RECORD(pEntry, PROCESSINFO, ListEntry);
KdPrint(("ProcessId:%4d ProcessName:%ws\n", pDataList->ProcessId, pDataList->ProcessName));
ExFreePool(pDataList);
}
pDataList = NULL;
KdPrint(("--------Enum Process End--------\n"));
return STATUS_SUCCESS;
}
VOID DriverUnload(PDRIVER_OBJECT p)
{
KdPrint(("DriverUnload"));
}
NTSTATUS DriverEntry(PDRIVER_OBJECT d, PUNICODE_STRING s)
{
KdPrint(("DriverEntey\n"));
d->DriverUnload = DriverUnload;
if (EnumProcess() != STATUS_SUCCESS)
{
KdPrint(("Enum Error\n"));
}
return STATUS_SUCCESS;
}