章五.Windows内存管理(中)

以下内容全部来自《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);


3.驱动的链表结构

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



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值