• 【旧文章搬运】PsVoid中IrpCreateFile函数在Win7下蓝屏BUG分析及解决


    原文发表于百度空间,2010-04-05
    ==========================================================================

    这也许是我这个假期唯一做的跟Code有关的事了,已经一个多月没写代码了~~
    问题是这样的,某同学A,在自己的工程中使用了PsVoid的部分驱动源码(感谢炉子开源~~),提到里面的IrpCreateFile函数在Win7下始终会蓝屏,希望我能帮忙解决一下。于是,拿出调试器开始debug之旅~~
    先简单介绍下,IrpCreateFile中PsVoid中Irp文件操作库中的一个函数,主要功能就是直接发IRP实现文件打开操作。该函数在WinXP/2003下均正常工作,但是在Win7下总是会蓝屏。写了个测试驱动扔到Vmware里,挂上调试器跟踪了一下,初步发现问题出在填充完IRP准备向下IoCallDriver时。现场如下:

    kd> u IofCallDriver
    nt!IofCallDriver:
    83a7b458 8bff             mov      edi,edi
    83a7b45a 55               push     ebp
    83a7b45b 8bec             mov      ebp,esp
    83a7b45d 51               push     ecx
    83a7b45e a15c7aba83       mov      eax,dword ptr [nt!pIofCallDriver (83ba7a5c)]
    83a7b463 56               push     esi
    83a7b464 8bf1             mov      esi,ecx  //DeviceObject参数保存至esi
    83a7b466 33c9             xor      ecx,ecx
    ....
    83a88492 8a08             mov      cl,byte ptr [eax]  //eax指向IoStackLocation,這里是判断MajorFunction
    83a88494 897014           mov      dword ptr [eax+14h],esi //取刚才保存的DeviceObject参数,赋给IrpStack->DeviceObject
    83a88497 80f916           cmp      cl,16h
    83a8849a 7514             jne      nt!IofCallDriver+0x57 (83a884b0)
    kd> p
    nt!IofCallDriver+0x57:
    83a884b0 8b4608           mov      eax,dword ptr [esi+8]  //取DeviceObject->DriverObject,很不幸这里DeviceObject是NULL,于是杯具发生了。。。

    大概就是这样的,DeviceObject是IrpCreateFile函数中的一个局部变量,当IoCallDriver时DeviceObject参数竟然是NULL,那取DeviceObject->DriverObject必然会蓝屏了~~

    刚开始,我怀疑前面取到的DeviceObject就是NULL,代码如下:

    DbgPrint("The Volume FileObject=0x%08X
    ",pFile);
    DeviceObject = pFile->Vpb->DeviceObject;
    DbgPrint("pFile->Vpb->DeviceObject = 0x%08X
    ",DeviceObject);
    RealDevice = pFile->Vpb->RealDevice;
    DbgPrint("pFile->Vpb->RealDevice = 0x%08X
    ",RealDevice);

    但是再次调试,发现这里是正常的,DeviceObject的值有效,不知怎的在准备IoCallDriver时变成了0,于是,再次调试~~

    这次调试时,密切监视DeviceObject值的变化。终于发现,在走过以下代码之后,DeviceObject变成了0

    RtlZeroMemory(&AuxData, sizeof(AUX_ACCESS_DATA));
    ntStatus = SeCreateAccessState( &AccessState,
       &AuxData,
       DesiredAccess,
       IoGetFileObjectGenericMapping());

    在调用完SeCreateAccessState之后,DeviceObject变成了0,很莫名的问题啊。不过,目标已经锁定在SeCreateAccessState,再重启,再次来过。。。

    过程如下:
    在IrpCreateFile中:

    //DeviceObject = pFile->Vpb->DeviceObject;
    93f14759 8b4df4           mov      ecx,dword ptr [ebp-0Ch]
    93f1475c 8b5108           mov      edx,dword ptr [ecx+8]
    93f1475f 8b4208           mov      eax,dword ptr [edx+8]
    93f14762 898550ffffff     mov      dword ptr [ebp-0B0h],eax  //DeviceObject保存在了ebp-b0处,如下:
    kd> dd ebp-b0
    807e1994   85bcb020 00000006 807e19b4 83e16b29  //可以看到,此时DeviceObject=85bcb020,是有效值
    807e19a4   83b2ac02 80000574 3c8fb9d2 83b2ac02
    807e19b4   807e19c0 83e16cca 534fa145 807e19d0

    继续往下,一直到调用SeCreateAccessState之前,这个值都没有变化。

    到了SeCreateAccessState,跟进去,发现这个函数只是直接调用了SeCreateAccessStateEx函数,WRK中源码如下,Win7中也没什么变化。

    NTSTATUS
    SeCreateAccessState(
        __out PACCESS_STATE AccessState,
        __out PAUX_ACCESS_DATA AuxData,
        __in ACCESS_MASK DesiredAccess,
        __in_opt PGENERIC_MAPPING GenericMapping
        )
    {
        return SeCreateAccessStateEx (PsGetCurrentThread (),
                                       PsGetCurrentProcess (),
                                       AccessState,
                                       AuxData,
                                       DesiredAccess,
                                       GenericMapping);
    }

    继续跟进至SeCreateAccessStateEx。。。

    发现了两个memset调用~~

    kd> 
    nt!SeCreateAccessStateEx+0x23:
    83c6f633 e8e0440400       call     nt!RtlMapGenericMask (83cb3b18)
    83c6f638 8b7d10           mov      edi,dword ptr [ebp+10h]  //取第三个参数至edi
    83c6f63b 6a74             push     74h
    83c6f63d 6a00             push     0
    83c6f63f 57               push     edi
    83c6f640 e8fbe7e0ff       call     nt!memset (83a7de40) //将第三个参数AccessState指向的内存清零
    83c6f645 8b7514           mov      esi,dword ptr [ebp+14h] ////取第三个参数至esi
    83c6f648 83c40c           add      esp,0Ch
    83c6f64b 68c0000000       push     0C0h
    83c6f650 6a00             push     0
    83c6f652 56               push     esi
    83c6f653 e8e8e7e0ff       call     nt!memset (83a7de40)  //将第四个参数AuxData指向的内存清零

    这两句对应于以下代码:

    RtlZeroMemory(AccessState, sizeof(ACCESS_STATE));
    RtlZeroMemory(AuxData, sizeof(AUX_ACCESS_DATA));
    //在SeCreateAccessStateEx这个函数中:
    kd> p
    nt!SeCreateAccessStateEx+0x3b:
    83c3664b 68c0000000       push     0C0h
    83c36650 6a00             push     0
    83c36652 56               push     esi
    83c36653 e8e8e7e0ff       call     nt!memset (83a44e40)  //准备清零
    kd> dd esp l4
    8cc678cc   8cc6793c 00000000 000000c0 8cc67958   //memset的参数
    kd> dd 8cc67994  //此时局部变量DeviceObject的值,仍然是正确值
    8cc67994   85bd4020 00040001 00000000 8cc679a0
    8cc679a4   8cc679a0 80000648 00000000 00000000
    kd> ? 8cc6793c+c0
    Evaluate expression: -1933149700 = 8cc679fc

    也就是说,要memset清零的区域是从8cc6793c到8cc679fc, 我们的局部变量的地址8cc67994就在这之间,于是被清零了。。。

    为什么会出现这样一个结果呢?AuxData是一个结构体,该参数是从IrpCreateFile中传过来的,和DeviceObject都是局部变量。而现在,对AuxData的操作竟然影响到了DeviceObject,能想到的,就是从他们在栈中的布局入手了。


    可以看到,AuxData位于ebp-108h处,与它相邻的局部变量是SourceString,位于ebp-ECh处。
    也就是说,AuxData的实际大小只有0x108-0xEC=0x1C
    而在SeCreateAccessStateEx中,我们明显可以看到系统认为这个结构的大小是0xC0,那么进行memset时,实际影响的范围是
    从ebp-108h到ebp-108h+0xC0=ebp-48h,而我们的局部变量DeviceObject位于ebp-B0h处,正位于这个范围之间,于是就被清零了,同样受影响的还有位于该范围内的SourceString,NameBuffer,ntStatus,ObjectAttributes,DeviceObject,Event,Handle,AccessState等局部变量,如果要再使用这些变量的话,同样会出现问题,比如你要使用Handle的时候,发现竟然成无效句柄了。。。
    所以,根本原因就是使用了不正确的结构定义,导致SeCreateAccessStateEx函数对局部变量AuxData清零初始化时同时修改了其它局部变量的值。
    原因既然找到了,解决起来就容易了。经询问,A同学说这个结构是他根据PsVoid源码中的定义自己修改的,是一个未文档化的结构。恩,未文档化的东西,用起来难免会有不可靠的时候啊~~~
    解决方案:修改AUX_ACCESS_DATA的定义就行了。该结构原来的定义为:

    typedef struct _AUX_ACCESS_DATA {
        PPRIVILEGE_SET PrivilegesUsed;
        GENERIC_MAPPING GenericMapping;
        ACCESS_MASK AccessesToAudit;
        ACCESS_MASK MaximumAuditMask;
    } AUX_ACCESS_DATA, *PAUX_ACCESS_DATA;

    该结构现在的大小为0x1C,而Win7中该结构实际大小为0xC0。(0xC0-0x1C)/sizeof(ULONG)=0x29=41,那么修改定义如下:

    typedef struct _AUX_ACCESS_DATA {
        PPRIVILEGE_SET PrivilegesUsed;
        GENERIC_MAPPING GenericMapping;
        ACCESS_MASK AccessesToAudit;
        ACCESS_MASK MaximumAuditMask;
        ULONG Unknown[41];
    } AUX_ACCESS_DATA, *PAUX_ACCESS_DATA;

    修改之后,经测试,蓝屏问题解决~~

  • 相关阅读:
    springboot(七):springboot+mybatis多数据源最简解决方案
    springboot(六):如何优雅的使用mybatis
    springboot(五):spring data jpa的使用
    springboot(四):thymeleaf使用详解
    springboot(三):Spring boot中Redis的使用
    springboot(二):web综合开发
    springboot(一):入门篇
    常用十六进制颜色对照表代码查询
    burpsuit之Repeater、Sequencer、Decoder、Comparer模块
    burpsuit之Spider、Scanner、Intruder模块
  • 原文地址:https://www.cnblogs.com/achillis/p/10183379.html
Copyright © 2020-2023  润新知