zoukankan      html  css  js  c++  java
  • 【旧文章搬运】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;

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

  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/achillis/p/10183379.html
Copyright © 2011-2022 走看看