实现效果
本例最终实现了,启动驱动后会捕获所有打开的应用程序信息,并且在debugview里面打印出应程序的执行名称,和ID号,
在驱动卸载后会打印出所有应用程序的信息,应用程序是保存在链表里面的。
内存分配
查找内存泄露的工具 PoolMonX.exe
//-----------------内存分配---------------
PVOID tempbuffer = ExAllocatePoolWithTag(NonPagedPool,0x1000,'xxaa');
链表
知识点
链表初始化
先定义一个结构 ,和链表头
/// <summary>
/// 自定义的结构体
/// </summary>
typedef struct _Mystruct
{
LIST_ENTRY list;//链表的指针,有前后两个指针
HANDLE pid;
PEPROCESS peprocessobj;
BYTE processname[16];
}Mystruct,*PMyStruct;
LIST_ENTRY listhead = {
0 };//链表头
初始化函数
InitializeListHead(&listhead);//初始化链表头
DbgPrint("--%p--%p--%p--\n",&listhead,listhead.Flink,listhead.Blink);
运行效果,链表,链表前指针,和后指针都一样
定义一个回调函数,获取电脑打开软件的信息,就是进程ID
/// <summary>
/// 进程信息的回调函数
/// </summary>
/// <param name="pid">操作系统传入父进程ID</param>
/// <param name="pid2">操作系统传入本进程ID</param>
/// <param name="bcareaf">操作系统传入TRUE表示创建进程 faste 表示结束进程</param>
VOID ProcessNotifyFun(HANDLE pid, HANDLE pid2, BOOLEAN bcareaf)
{
//链表测试函数
if (bcareaf)
{
DbgPrint("process carete,PID id %d", pid2);//打印进程创建信息,和本进程ID
PEPROCESS tempep =NULL;
PsLookupProcessByProcessId(pid2, &tempep);
if (!tempep)
{
return;
}
/* 下面的语句需要放到引用文件之后,
NTKERNELAPI UCHAR * PsGetProcessImageFileName(__in PEPROCESS Process); */
PCHAR processname = PsGetProcessImageFileName(tempep);
DbgPrint("Process name is %s", processname);// 打印进程的名字
}
return;
}
启用这个回调函数,可以放在入口函数执行
//创建一个进程通知,当进程发生变化创建或结束的时候,操作系统就自动调用我们自己写的函数ProcessNotifyFun
//并且把一些相关的信息通过参数的形式传给我们,
//传回的参数1是父进程ID
//传回的参数2是自己进程ID
//传回的参数3如果是真就是Create ,如果是假就是Close,也就是说如果是真就是进程创建,如果是假就是进程结束
PsSetCreateProcessNotifyRoutine(ProcessNotifyFun,FALSE);
驱动卸载的同时也要卸载这个回调函数的调用
// 加在 VOID DrvUnload(PDRIVER_OBJECT pdriver) 函数里面
PsSetCreateProcessNotifyRoutine(ProcessNotifyFun,TRUE);//移除回调,参数2 TRUE就是移除的意思
执行效果:
打开程序后会显示程序的名字和ID
把打开的进程信息写入链表
把每次打开的应用程序信息写入 链表结构,本例中 ptempptr 就是用来存放链表结构的
/// <summary>
/// 进程信息的回调函数
/// </summary>
/// <param name="pid">操作系统传入父进程ID</param>
/// <param name="pid2">操作系统传入本进程ID</param>
/// <param name="bcareaf">操作系统传入TRUE表示创建进程 faste 表示结束进程</param>
VOID ProcessNotifyFun(HANDLE pid, HANDLE pid2, BOOLEAN bcareaf)
{
//链表测试函数
if (bcareaf)
{
DbgPrint("process carete,PID id %d", pid2);//打印进程创建信息,和本进程ID
PEPROCESS tempep = NULL;
PsLookupProcessByProcessId(pid2, &tempep);
if (!tempep)
{
return;
}
ObDereferenceObject(tempep);//节引用
PCHAR processname = PsGetProcessImageFileName(tempep);
DbgPrint("Process name is %s", processname);// 打印进程的名字
PMyStruct ptempptr = ExAllocatePool(NonPagedPool, sizeof(Mystruct));//申请一个链表结构的内存空间
if (ptempptr)
{
KIRQL oldirql = 0;
// 避免多核CPU同时创建进程时,都把打开软件的进程信息同时写到这个链表,这里用自旋锁让他们排队写入
KeAcquireSpinLock(&spinlock, &oldirql); //上锁,只让一个程序来访问
PLIST_ENTRY templist = NULL;
RtlZeroMemory(ptempptr, sizeof(Mystruct));
//成员赋值
ptempptr->peprocessobj = tempep;
ptempptr->pid = pid2;
RtlCopyMemory(ptempptr->processname, processname, strlen(processname));
// ptempptr 结构的指针
//Mystruct 结构的类型
//list 在参数2结构里面的链表指针的别名
//templist = CONTAINING_RECORD(ptempptr, Mystruct, list);
//&listhead 链表头
InsertTailList(&listhead,&(ptempptr->list));
KeReleaseSpinLock(&spinlock, oldirql); // 解锁
}
}
return;
}
打印出链表信息,循环链表打印出来,这个打印信息我是写在卸载函数里面的
这里是用到了移除以前的链表,避免了内存泄露
//遍历链表,打印出链表,本例中就是打印出驱动启动后捕获到所有打开的应用程序的信息
PLIST_ENTRY temlist = NULL;
PMyStruct tempptr = NULL;
while (listhead.Blink !=&listhead)
{
temlist = RemoveTailList(&listhead);//移出链表头,保存到临时指针
tempptr = CONTAINING_RECORD(temlist,Mystruct,list);
DbgPrint("--%d--%p--%s", tempptr->pid,tempptr->peprocessobj,tempptr->processname);
ExFreePool(tempptr);//释放掉这个临时保存链表信息的指针
}
下图的方法是没有用移除链表 ,只用FOR循环遍历链表,打印出链表信息