遍历驱动设备栈

参考:https://blog.csdn.net/hjxyshell/article/details/39106609

参考 《windows 驱动开发技术详解》,部分关于对象的结构,请看上节windbg命令 中介绍

公用头文件:

  1.  
    // IOCTLS.H -- IOCTL code definitions for fileio driver
  2.  
    // Copyright (C) 1999 by Walter Oney
  3.  
    // All rights reserved
  4.  
     
  5.  
    #ifndef IOCTLS_H
  6.  
    #define IOCTLS_H
  7.  
     
  8.  
    #ifndef CTL_CODE
  9.  
    #pragma message("CTL_CODE undefined. Include winioctl.h or wdm.h")
  10.  
    #endif
  11.  
     
  12.  
    #define IOCTL_DUMP_DEVICE_STACK CTL_CODE(
  13.  
    FILE_DEVICE_UNKNOWN,
  14.  
    0x800,
  15.  
    METHOD_BUFFERED,
  16.  
    FILE_ANY_ACCESS)
  17.  
     
  18.  
    #endif


ring3级代码

  1.  
    #include <stdio.h>
  2.  
    #include <windows.h>
  3.  
    #include <winioctl.h>
  4.  
    #include "..Ioctls.h"
  5.  
     
  6.  
    int main()
  7.  
    {
  8.  
    HANDLE hDevice = CreateFile("\\.\KeListDeviceSL",
  9.  
    GENERIC_READ | GENERIC_WRITE,
  10.  
    0,
  11.  
    NULL,
  12.  
    OPEN_EXISTING,
  13.  
    FILE_ATTRIBUTE_NORMAL,
  14.  
    NULL);
  15.  
    if(hDevice == INVALID_HANDLE_VALUE)
  16.  
    {
  17.  
    printf("Failed to obtain file handle to device: "
  18.  
    "%s with Win32 error code: %d ",
  19.  
    "MyWDMDevice", GetLastError() );
  20.  
    return 0;
  21.  
    }
  22.  
    WCHAR* InputBuffer = L"\Driver\ACPI";
  23.  
    DWORD dwOutput;
  24.  
    BOOL bRet;
  25.  
    bRet = DeviceIoControl(hDevice,
  26.  
    IOCTL_DUMP_DEVICE_STACK,
  27.  
    InputBuffer,
  28.  
    wcslen(InputBuffer)*2+2,
  29.  
    NULL,
  30.  
    0,
  31.  
    &dwOutput,
  32.  
    NULL);
  33.  
    CloseHandle(hDevice);
  34.  
    return 1;
  35.  
    }

ring0级代码

ListDevice.h

  1.  
    #pragma once
  2.  
    #ifdef __cplusplus
  3.  
    extern "C"
  4.  
    {
  5.  
    #endif
  6.  
    #include <ntddk.h>
  7.  
    NTKERNELAPI
  8.  
    NTSTATUS
  9.  
    ObReferenceObjectByName(
  10.  
    IN PUNICODE_STRING ObjectName,
  11.  
    IN ULONG Attributes,
  12.  
    IN PACCESS_STATE PassedAccessState OPTIONAL,
  13.  
    IN ACCESS_MASK DesiredAccess OPTIONAL,
  14.  
    IN POBJECT_TYPE ObjectType,
  15.  
    IN KPROCESSOR_MODE AccessMode,
  16.  
    IN OUT PVOID ParseContext OPTIONAL,
  17.  
    OUT PVOID *Object
  18.  
    );
  19.  
    NTKERNELAPI
  20.  
    PDEVICE_OBJECT
  21.  
    NTAPI
  22.  
    IoGetBaseFileSystemDeviceObject(
  23.  
    IN PFILE_OBJECT FileObject
  24.  
    );
  25.  
    extern POBJECT_TYPE IoDeviceObjectType;
  26.  
    extern POBJECT_TYPE *IoDriverObjectType;
  27.  
    #ifdef __cplusplus
  28.  
    }
  29.  
    #endif
  30.  
     
  31.  
    #include "..Ioctls.h"
  32.  
     
  33.  
    #define MAX_FILE_LENGTH 1024
  34.  
     
  35.  
    typedef struct _DEVICE_EXTENSION{
  36.  
    PDEVICE_OBJECT pDevice;
  37.  
    UNICODE_STRING ustrDeviceName;
  38.  
    UNICODE_STRING ustrSymLinkName;
  39.  
     
  40.  
    PUCHAR buffer; //缓冲区
  41.  
    ULONG file_length; //模拟文件长度,必须小于MAX_FILE_LENGTH
  42.  
    }DEVICE_EXTENSION, *PDEVICE_EXTENSION;
  43.  
     
  44.  
    //函数声明
  45.  
    //创建设备
  46.  
    NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject, UNICODE_STRING devname, UNICODE_STRING symLinkName);
  47.  
    //卸载驱动
  48.  
    VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
  49.  
    //IRP请求处理函数
  50.  
    NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device, PIRP pIrp);
  51.  
    //处理消息
  52.  
    NTSTATUS DeviceIoControlDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
  53.  
     
  54.  
    //枚举设备栈
  55.  
    PDRIVER_OBJECT EnumDeviceStack(PWSTR pwszDeviceName);
  56.  
     
  57.  
    typedef struct _OBJECT_CREATE_INFORMATION
  58.  
    {
  59.  
    ULONG Attributes;
  60.  
    HANDLE RootDirectory;
  61.  
    PVOID ParseContext;
  62.  
    KPROCESSOR_MODE ProbeMode;
  63.  
    ULONG PagedPoolCharge;
  64.  
    ULONG NonPagedPoolCharge;
  65.  
    ULONG SecurityDescriptorCharge;
  66.  
    PSECURITY_DESCRIPTOR SecurityDescriptor;
  67.  
    PSECURITY_QUALITY_OF_SERVICE SecurityQos;
  68.  
    SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  69.  
    }OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION;
  70.  
     
  71.  
    typedef struct _OBJECT_HEADER
  72.  
    {
  73.  
    LONG PointerCount;
  74.  
    union
  75.  
    {
  76.  
    LONG HandleCount;
  77.  
    PSINGLE_LIST_ENTRY SEntry;
  78.  
    };
  79.  
    POBJECT_TYPE Type;
  80.  
    UCHAR NameInfoOffset;
  81.  
    UCHAR HandleInfoOffset;
  82.  
    UCHAR QuotaInfoOffset;
  83.  
    UCHAR Flags;
  84.  
    union
  85.  
    {
  86.  
    POBJECT_CREATE_INFORMATION ObjectCreateInfo;
  87.  
    PVOID QuotaBlockCharged;
  88.  
    };
  89.  
    PSECURITY_DESCRIPTOR SecurityDescriptor;
  90.  
    QUAD Body;
  91.  
    }OBJECT_HEADER, * POBJECT_HEADER;
  92.  
     
  93.  
    #define NUMBER_HASH_BUCKETS 37
  94.  
     
  95.  
    typedef struct _OBJECT_DIRECTORY
  96.  
    {
  97.  
    struct _OBJECT_DIRECTORY_ENTRY* HashBuckets[NUMBER_HASH_BUCKETS];
  98.  
    struct _OBJECT_DIRECTORY_ENTRY** LookupBucket;
  99.  
    BOOLEAN LookupFound;
  100.  
    USHORT SymbolicLinkUsageCount;
  101.  
    struct _DEVICE_MAP* DeviceMap;
  102.  
    } OBJECT_DIRECTORY, * POBJECT_DIRECTORY;
  103.  
     
  104.  
    typedef struct _OBJECT_HEADER_NAME_INFO
  105.  
    {
  106.  
    POBJECT_DIRECTORY Directory;
  107.  
    UNICODE_STRING Name;
  108.  
    ULONG QueryReferences;
  109.  
    #if DBG
  110.  
    ULONG QueryReferences2 ;
  111.  
    LONG DbgDereferenceCount ;
  112.  
    #endif
  113.  
    } OBJECT_HEADER_NAME_INFO, * POBJECT_HEADER_NAME_INFO;
  114.  
    //////////////////////////////////////////////////////////////////////////
  115.  
    // ----OBJECT----
  116.  
    // ----------------------------
  117.  
    // | Object_Header |
  118.  
    // ----------------------------
  119.  
    // | Object_Body(QUAD Body) |
  120.  
    // ----------------------------
  121.  
    //example(device object):
  122.  
    //
  123.  
    //dt nt!_object_header
  124.  
    // +0x000 PointerCount : Int4B
  125.  
    // +0x004 HandleCount : Int4B
  126.  
    // +0x004 NextToFree : Ptr32 Void
  127.  
    // +0x008 Type : Ptr32 _OBJECT_TYPE
  128.  
    // +0x00c NameInfoOffset : UChar
  129.  
    // +0x00d HandleInfoOffset : UChar
  130.  
    // +0x00e QuotaInfoOffset : UChar
  131.  
    // +0x00f Flags : UChar
  132.  
    // +0x010 ObjectCreateInfo : Ptr32 _OBJECT_CREATE_INFORMATION
  133.  
    // +0x010 QuotaBlockCharged : Ptr32 Void
  134.  
    // +0x014 SecurityDescriptor : Ptr32 Void
  135.  
    // +0x018 Body : _QUAD //此处内容即下面的对象
  136.  
    //
  137.  
    //dt nt!_DEVICE_OBJECT
  138.  
    // +0x000 Type : Int2B
  139.  
    // +0x002 Size : Uint2B
  140.  
    // +0x004 ReferenceCount : Int4B
  141.  
    // +0x008 DriverObject : Ptr32 _DRIVER_OBJECT
  142.  
    // +0x00c NextDevice : Ptr32 _DEVICE_OBJECT
  143.  
    // +0x010 AttachedDevice : Ptr32 _DEVICE_OBJECT
  144.  
    // +0x014 CurrentIrp : Ptr32 _IRP
  145.  
    // +0x018 Timer : Ptr32 _IO_TIMER
  146.  
    // +0x01c Flags : Uint4B
  147.  
    // +0x020 Characteristics : Uint4B
  148.  
    // +0x024 Vpb : Ptr32 _VPB
  149.  
    // +0x028 DeviceExtension : Ptr32 Void
  150.  
    // +0x02c DeviceType : Uint4B
  151.  
    // +0x030 StackSize : Char
  152.  
    // +0x034 Queue : <unnamed-tag>
  153.  
    // +0x05c AlignmentRequirement : Uint4B
  154.  
    // +0x060 DeviceQueue : _KDEVICE_QUEUE
  155.  
    // +0x074 Dpc : _KDPC
  156.  
    // +0x094 ActiveThreadCount : Uint4B
  157.  
    // +0x098 SecurityDescriptor : Ptr32 Void
  158.  
    // +0x09c DeviceLock : _KEVENT
  159.  
    // +0x0ac SectorSize : Uint2B
  160.  
    // +0x0ae Spare1 : Uint2B
  161.  
    // +0x0b0 DeviceObjectExtension : Ptr32 _DEVOBJ_EXTENSION
  162.  
    // +0x0b4 Reserved : Ptr32 Void
  163.  
    //
  164.  
    //////////////////////////////////////////////////////////////////////////
  165.  
     
  166.  
    #define OBJECT_TO_OBJECT_HEADER( o )
  167.  
    CONTAINING_RECORD( (o), OBJECT_HEADER, Body )
  168.  
     
  169.  
    #define OBJECT_HEADER_TO_NAME_INFO( oh ) ((POBJECT_HEADER_NAME_INFO)
  170.  
    ((oh)->NameInfoOffset == 0 ? NULL : ((PCHAR)(oh) - (oh)->NameInfoOffset)))


ListDevice.cpp

  1.  
    // _ooOoo_
  2.  
    // o8888888o
  3.  
    // 88" . "88
  4.  
    // (| -_- |)
  5.  
    // O = /O
  6.  
    // ____/`---'\____
  7.  
    // . ' \| |// `.
  8.  
    // / \||| : |||//
  9.  
    // / _||||| -:- |||||-
  10.  
    // | | \ - /// | |
  11.  
    // | \_| ''---/'' | |
  12.  
    // .-\__ `-` ___/-. /
  13.  
    // ___`. .' /--.-- `. . __
  14.  
    // ."" '< `.___\_<|>_/___.' >'"".
  15.  
    // | | : `- \`.;` _ /`;.`/ - ` : | |
  16.  
    // `-. \_ __ /__ _/ .-` / /
  17.  
    // ======`-.____`-.___\_____/___.-`____.-'======
  18.  
    // `=---='
  19.  
    //
  20.  
    // .............................................
  21.  
    // 佛祖镇楼 BUG辟易
  22.  
    // 佛曰:
  23.  
    // 写字楼里写字间,写字间里程序员;
  24.  
    // 程序人员写程序,又拿程序换酒钱。
  25.  
    // 酒醒只在网上坐,酒醉还来网下眠;
  26.  
    // 酒醉酒醒日复日,网上网下年复年。
  27.  
    // 但愿老死电脑间,不愿鞠躬老板前;
  28.  
    // 奔驰宝马贵者趣,公交自行程序员。
  29.  
    // 别人笑我忒疯癫,我笑自己命太贱;
  30.  
    // 不见满街漂亮妹,哪个归得程序员?
  31.  
     
  32.  
    #include "ListDevice.h"
  33.  
     
  34.  
    NTSTATUS
  35.  
    DriverEntry(PDRIVER_OBJECT pDriverObject,PUNICODE_STRING pRegistryPath)
  36.  
    {
  37.  
    DbgPrint("Enter DriverEntry ");
  38.  
     
  39.  
    NTSTATUS status;
  40.  
    pDriverObject->DriverUnload = DriverUnload;
  41.  
    for (int i = 0; i< IRP_MJ_MAXIMUM_FUNCTION; i++)
  42.  
    {
  43.  
    pDriverObject->MajorFunction[i] = MyDispatchFunction;
  44.  
    }
  45.  
     
  46.  
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControlDispatch;
  47.  
    //准备创建设备
  48.  
    UNICODE_STRING ustrDevName, ustrSymbolicName;
  49.  
    RtlInitUnicodeString(&ustrDevName, L"\Device\KeListDevice");
  50.  
    RtlInitUnicodeString(&ustrSymbolicName, L"\DosDevices\KeListDeviceSL");
  51.  
    status = CreateDevice(pDriverObject, ustrDevName, ustrSymbolicName);
  52.  
    if(!NT_SUCCESS(status))
  53.  
    {
  54.  
    KdPrint(("Failed to Create Device ... "));
  55.  
    return STATUS_UNSUCCESSFUL;
  56.  
    }
  57.  
    KdPrint(("Exit DriverEntry "));
  58.  
    return STATUS_SUCCESS;
  59.  
    }
  60.  
     
  61.  
    NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject, UNICODE_STRING devname, UNICODE_STRING symLinkName)
  62.  
    {
  63.  
    NTSTATUS status;
  64.  
    PDEVICE_OBJECT pDevObj;
  65.  
    PDEVICE_EXTENSION pDevExt;
  66.  
    //创建设备
  67.  
    status = IoCreateDevice(pDriverObject,
  68.  
    sizeof(DEVICE_EXTENSION),
  69.  
    &devname,
  70.  
    FILE_DEVICE_UNKNOWN,
  71.  
    0,
  72.  
    TRUE,
  73.  
    &pDevObj);
  74.  
    if(!NT_SUCCESS(status))
  75.  
    return status;
  76.  
     
  77.  
    pDevObj->Flags |= DO_BUFFERED_IO;
  78.  
    pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
  79.  
    pDevExt->pDevice = pDevObj;
  80.  
    pDevExt->ustrDeviceName = devname;
  81.  
    pDevExt->ustrSymLinkName = symLinkName;
  82.  
     
  83.  
    //创建设备连接
  84.  
    status = IoCreateSymbolicLink(&symLinkName, &devname);
  85.  
    if(!NT_SUCCESS(status))
  86.  
    {
  87.  
    KdPrint(("Failed to IoCreateSymbolicLink and delete DeviceObject --- errorcode = %d ... ",status));
  88.  
    IoDeleteDevice(pDevObj);
  89.  
    return status;
  90.  
    }
  91.  
    return status;
  92.  
    }
  93.  
    //卸载驱动
  94.  
    VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
  95.  
    {
  96.  
    PDEVICE_OBJECT pNextObj;
  97.  
    KdPrint(("Enter DriverUnload... "));
  98.  
    pNextObj = pDriverObject->DeviceObject;
  99.  
    //循环遍历删除所有该驱动上的设备
  100.  
    while(pNextObj != NULL)
  101.  
    {
  102.  
    //设备的名称和链接名称均在扩展结构中存储
  103.  
    PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
  104.  
    //删除符号链接
  105.  
    UNICODE_STRING LinkName = pDevExt->ustrSymLinkName;
  106.  
    IoDeleteSymbolicLink(&LinkName);
  107.  
    pNextObj = pNextObj->NextDevice;
  108.  
    IoDeleteDevice(pDevExt->pDevice);
  109.  
    }
  110.  
    }
  111.  
    //IRP请求处理函数
  112.  
    NTSTATUS MyDispatchFunction(PDEVICE_OBJECT device, PIRP pIrp)
  113.  
    {
  114.  
    NTSTATUS status = STATUS_SUCCESS;
  115.  
    //完成IRP,此处不做任何特殊处理
  116.  
    pIrp->IoStatus.Status = status;
  117.  
    pIrp->IoStatus.Information = 0;
  118.  
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  119.  
    KdPrint(("Leave MyDispatchFunction "));
  120.  
    return status;
  121.  
    }
  122.  
    //处理消息
  123.  
    NTSTATUS DeviceIoControlDispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
  124.  
    {
  125.  
     
  126.  
    NTSTATUS status = STATUS_SUCCESS;
  127.  
     
  128.  
    KdPrint(("Enter DeviceIoControlDispatch... "));
  129.  
    PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
  130.  
    ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
  131.  
    ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
  132.  
    //得到IOCTL码
  133.  
    ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
  134.  
    ULONG info = 0;
  135.  
    switch(code)
  136.  
    {
  137.  
    case IOCTL_DUMP_DEVICE_STACK:
  138.  
    {
  139.  
    KdPrint(("IOCTL_DUMP_DEVICE_STACK "));
  140.  
    WCHAR* InputBuffer = (WCHAR*)pIrp->AssociatedIrp.SystemBuffer; //ring3级传入的是WCHAR类型的字符串
  141.  
    EnumDeviceStack(InputBuffer);
  142.  
    }
  143.  
    default:
  144.  
    status = STATUS_INVALID_VARIANT;
  145.  
    }
  146.  
    //完成IRP,此处不做任何特殊处理
  147.  
    pIrp->IoStatus.Status = status;
  148.  
    pIrp->IoStatus.Information = info;
  149.  
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
  150.  
    KdPrint(("Leave MyDispatchFunction "));
  151.  
    return status;
  152.  
    }
  153.  
     
  154.  
    //获取设备对象的信息
  155.  
    VOID GetDeviceObjectInfo(PDEVICE_OBJECT DevObj)
  156.  
    {
  157.  
    POBJECT_HEADER ObjectHeader;
  158.  
    POBJECT_HEADER_NAME_INFO ObjectNameInfo;
  159.  
     
  160.  
    if(DevObj == NULL)
  161.  
    {
  162.  
    DbgPrint("DevObj is NULL! ");
  163.  
    return;
  164.  
    }
  165.  
    ObjectHeader = OBJECT_TO_OBJECT_HEADER(DevObj); //由对象得到对象头
  166.  
    if(ObjectHeader)
  167.  
    {
  168.  
    //查询设备对象名称并打印
  169.  
    ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO(ObjectHeader);
  170.  
    if(ObjectNameInfo && ObjectNameInfo->Name.Buffer)
  171.  
    {
  172.  
    //打印设备相关信息
  173.  
    DbgPrint(" Driver Name : %wZ - Device Name :%wZ -Driver Address :0x%x - Device Address: 0x%x ",
  174.  
    &(DevObj->DriverObject->DriverName),
  175.  
    &(ObjectNameInfo->Name),
  176.  
    DevObj->DriverObject,
  177.  
    DevObj);
  178.  
    }
  179.  
    //无名子设备处理
  180.  
    else if (DevObj->DriverObject)
  181.  
    {
  182.  
    //打印设备相关信息
  183.  
    DbgPrint(" Driver Name : %wZ - Device Name :%wZ -Driver Address :0x%x - Device Address: 0x%x ",
  184.  
    &(DevObj->DriverObject->DriverName),
  185.  
    L"NULL",
  186.  
    DevObj->DriverObject,
  187.  
    DevObj);
  188.  
    }
  189.  
    }
  190.  
    }
  191.  
     
  192.  
    //遍历一个设备上所有的附加设备
  193.  
    VOID GetAttachedDeviceInfo(PDEVICE_OBJECT DevObj)
  194.  
    {
  195.  
    PDEVICE_OBJECT AtDeviceObject; //附加的设备
  196.  
    if(DevObj == NULL)
  197.  
    {
  198.  
    DbgPrint("DevObj is NULL! ");
  199.  
    return;
  200.  
    }
  201.  
     
  202.  
    AtDeviceObject = DevObj->AttachedDevice;
  203.  
     
  204.  
    while(AtDeviceObject)
  205.  
    {
  206.  
    DbgPrint( "Attached Driver Name:%wZ,Attached Driver Address:0x%x,Attached DeviceAddress:0x%x ",
  207.  
    &(AtDeviceObject->DriverObject->DriverName),
  208.  
    AtDeviceObject->DriverObject,
  209.  
    AtDeviceObject );
  210.  
    AtDeviceObject = AtDeviceObject->AttachedDevice;
  211.  
    }
  212.  
    }
  213.  
    //枚举设备栈
  214.  
    PDRIVER_OBJECT EnumDeviceStack(PWSTR pwszDeviceName)
  215.  
    {
  216.  
    UNICODE_STRING DriverName;
  217.  
    PDRIVER_OBJECT DriverObject = NULL;
  218.  
    PDEVICE_OBJECT DeviceObject = NULL;
  219.  
     
  220.  
    RtlInitUnicodeString(&DriverName, pwszDeviceName); //本例是获得 高级电源管理驱动的设备栈
  221.  
     
  222.  
    ObReferenceObjectByName(&DriverName,
  223.  
    OBJ_CASE_INSENSITIVE,
  224.  
    NULL,
  225.  
    0,
  226.  
    (POBJECT_TYPE)IoDriverObjectType,
  227.  
    KernelMode,
  228.  
    NULL,
  229.  
    (PVOID*)&DriverObject);
  230.  
     
  231.  
    if(DriverObject == NULL)
  232.  
    return NULL;
  233.  
     
  234.  
    DeviceObject = DriverObject->DeviceObject;
  235.  
    while(DeviceObject)
  236.  
    {
  237.  
    GetDeviceObjectInfo(DeviceObject);
  238.  
    //判断是否有过滤驱动(附加驱动)
  239.  
    if(DeviceObject->AttachedDevice)
  240.  
    GetAttachedDeviceInfo(DeviceObject);
  241.  
     
  242.  
    //进一步判断设备上的VPB 中设备
  243.  
    if(DeviceObject->Vpb && DeviceObject->Vpb->DeviceObject)
  244.  
    {
  245.  
    GetDeviceObjectInfo(DeviceObject->Vpb->DeviceObject);
  246.  
    if(DeviceObject->Vpb->DeviceObject->AttachedDevice)
  247.  
    GetAttachedDeviceInfo(DeviceObject->Vpb->DeviceObject);
  248.  
    }
  249.  
    //得到驱动上的下一个设备DEVICE_OBJECT
  250.  
    DeviceObject = DeviceObject->NextDevice;
  251.  
    }
  252.  
    return DriverObject;
  253.  
    }
原文地址:https://www.cnblogs.com/kuangke/p/12390513.html