IRP cleanup和close的调用时机

在用户态打开一个驱动设备链接时可以获取到一个句柄,这个句柄是基于当前进程的,句柄的值在当前进程的句柄表中作为索引。

调用CreateFile打开一个已存在的内核设备时,会触发设备所在驱动的IRP_MJ_CREATE请求派遣函数。

句柄对应的对象头在内核中用_OBJECT_HEADER结构体来表示,使用!handle 句柄值 即可查看句柄信息。

HANDLE hControl = CreateFile(L"\\\\.\\MyFilterSerialPort", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM, 0);
if (INVALID_HANDLE_VALUE != hControl)
{
  printf("open success\n");
  getchar();
  HANDLE target = INVALID_HANDLE_VALUE;
  BOOL bDuplicate = DuplicateHandle(GetCurrentProcess(), hControl, GetCurrentProcess(), &target, DUPLICATE_SAME_ACCESS, FALSE, DUPLICATE_SAME_ACCESS);
  __asm int 3
  CloseHandle(hControl);
  CloseHandle(target);

}

 

自己在驱动中创建了设备,并创建了设备链接名

 

并填写了MajorFunction回调函数。

在用户态打开设备时,会触发IRP_MJ_CREATE,下图中注释有问题 IRP_MJ_CLOSE中应该是pointercount为0时会触发IRP_MJ_CLOSE, IRP_MJ_CLEANUP中应该是handlecount为0时会触发此IRP。

 

 

 

在用户态程序中下断点,查看返回的句柄值

 

 

 

hControl句柄值为0x1c,对应的_OBJECT_HEADER的地址为85dafb88,具体的对象地址为85dafba0  ,距离header偏移0x18,查看对象头中PointerCount 和HandleCount 都为1

kd> dt _OBJECT_HEADER 85dafb88
nt!_OBJECT_HEADER
  +0x000 PointerCount : 0n1
  +0x004 HandleCount : 0n1

接下来使用DuplicateHandle在当前进程中复制一份这个句柄,获取到新的句柄,如下

 

target的值为0x20,查看句柄对应的对象地址也是85dafba0,对象头为85dafb88,跟hControl对应的对象是一样的。

每当在用户态中打开一个对象时,PointerCount 会加1,HandleCount也会加1。因为使用DuplicateHandle复制了句柄,所以此时PointerCount 为2,HandleCount也为2。

调用DuplicateHandle没有触发IRP_MJ_CREATE。

接下来调用CloseHandle

 

 

 先使用硬件断点指令对HandleCount内存处下写入内存断点ba w4 85dafb88+4 ,如上图,调用CloseHandle时会调用nt!ObpDecrementHandleCount对HandleCount进行减法操作,当断点断下来后,已经写入了新的数据,

使用ub命令查看nt!ObpDecrementHandleCount+0x68地址前的指令

 

 

并没有触发cleanup和close。closeHandle还会调用nt!ObfDereferenceObjectWithTag将PointerCount减1。调用栈为

第二次调用CloseHandle时,会再次将HandleCount减1,如果HandleCount为0,会先触发IRP_MJ_CLEANUP

 

此时对象中PointerCount还是为2,因为在ObfReferenceObjectWithTag中对PointerCount加1了,在IopCloseFile中调用ObfReferenceObject对pointer+1,所以此时pointerCount为2,再在IopCloseFile中调用IofCallDriver触发IRP_MJ_CLEANUP。

 

 

 

 

 使用ba w4 85dafb88命令对PointerCount地址下写入断点,可以看到

 

 

 nt!ObfDereferenceObjectWithTag会修改PointerCount,现在为1,继续运行,再次触发写入断点

当PointerCount为0时,会触发IRP_MJ_CLOSE,调用栈如下:

 

 

总结:调用CloseHandle关闭句柄时,会对HandleCount计数减1,对PointerCount减1,只有当HandleCount减到0时,才会触发IRP_MJ_CLEANUP,只有当PointerCount减到0时,才会触发IRP_MJ_CLOSE。

先触发IRP_MJ_CLEANUP,才会触发IRP_MJ_CLOSE。

 

 

     

 

posted @ 2022-10-19 15:55  psj00  阅读(502)  评论(0)    收藏  举报