1、第五章 驱动程序加载实验指导书1. 实验1.1 实验目的验证驱动程序的动态加载过程。1.2 实验原理验证用户态应用程序在动态加载驱动程序时对 NtLoadDriver ( )函数的调用过程。1.3 实验环境调试工具:Windbg编程工具:Visual Studio 2005操作系统:VMware 5.0+Windows Server 20031.4 实验内容利用 Visual Studio 编写一个 loadsys 函数实现驱动程序的动态加载,然后编写一个程序 test001,对驱动程序的功能表现进行测试。1.5 实验过程步骤 1:修改源代码(1)根据关键代码分析部分的结果,对 wrk-v1.
2、2basentosioiomgrloadunld.c 中的NtLoadDriver 函数做如下修改,标记出 NtLoadDriver 执行的六个主要步骤:NTSTATUSNtLoadDriver (_in PUNICODE_STRING DriverServiceName)requestorMode = KeGetPreviousModeByThread(DbgPrint (“(1) get the previous mode!“);DbgPrint (“(2) Then check: KernelMode or UserMode!“);if (requestorMode != KernelM
3、ode) DbgPrint (“(3-1) In UserMode!“); else DbgPrint (“(3-2) IN kernel model“);driverServiceName = *DriverServiceName;DbgPrint (“(4) do the exact load driver job!“);KeInitializeEvent (loadPacket.DriverObject = (PDRIVER_OBJECT) NULL;loadPacket.DriverServiceName = if (PsGetCurrentProcessByThread(Curren
4、tThread) = PsInitialSystemProcess)IopLoadUnloadDriver (DbgPrint (“(4-1) NtLoadDriver calls IopLoadUnloadDriver!n”); else ExInitializeWorkItem (ExQueueWorkItem (KeWaitForSingleObject (DbgPrint (“(4-2) Not in a system process! n”);DbgPrint (“(5) free allocated buffer!“);if (nameBuffer) ExFreePool (nam
5、eBuffer);DbgPrint (“(6) return operation status code!“);return loadPacket.FinalStatus;说明:用 DbgPrint( )函数打印的这些调试信息可以在 Windbg 中看到,也就验证了对NtLoadDriver 函数的调用过程。(2)要验证 NtLoadDriver( )是否真的调用了 IopLoadUnloadDriver 函数。为了验证,在Internal.c 中修改 IopLoadDriver()的代码,添加一个 Dbg 打印语句(见图中红字部分)VOID IopLoadUnloadDriver(IN PV
6、OID Parameter )PLOAD_PACKET loadPacket;NTSTATUS status, driverEntryStatus;HANDLE keyHandle;PAGED_CODE();DbgPrint(“(inside 4) if this was printed ,IopLoadUnloadDriver was called by tLoadDriver()!n“);/ Begin by getting a pointer to the load packet./loadPacket = (PLOAD_PACKET) Parameter;if (loadPacket-
7、DriverObject) loadPacket-DriverObject-DriverUnload( loadPacket-DriverObject );status = STATUS_SUCCESS; else status = IopOpenRegistryKey( if (NT_SUCCESS( status ) status = IopLoadDriver( keyHandle, TRUE, FALSE, if (status = STATUS_FAILED_DRIVER_ENTRY) status = driverEntryStatus; else if (status = STA
8、TUS_DRIVER_FAILED_PRIOR_UNLOAD) status = STATUS_OBJECT_NAME_NOT_FOUND;IopCallDriverReinitializationRoutines();loadPacket-FinalStatus = status;(VOID) KeSetEvent( 步骤 2:重新编译 WRK 内核内核代码修改之后要进行重新编译。编译过程如图 5-7 所示:图 5-7 编译内核将重新编译生成的内核镜像 wrkx86.exe 拷贝到 C:WINDOWSSystem32目录下,覆盖以前编译所生成的内核。然后重新启动系统。步骤 3:加载驱动程序(
9、1)重新启动虚拟机,会看到系统在启动的时候也会调用 NtLoadDriver()函数来动态的加载驱动程序。系统启动时 Windbg 打印的消息如图 5-8 所示:图 5-8 加载时的打印信息图 5-8 中方框部分的信息就是我们在步骤一中添加的打印信息。图中标出三个框,说明系统在启动的时候调用了三次 NtLoadDriver。(2)接下来在 NtLoadDriver 函数上设置断点。要通过函数名设定断点,首先要保证Windbg 已经加载了对应的 PDB 文件,加载 PDB 符号文件的工作在 “WRK 实验环境设置”部分已经介绍过。设定好之后,接下来用 x 命令找到 NtLoadDriver 函数
10、的二进制入口点。找到地址之后,就可以用 bp 命令在这个地址上下断点了。如图 5-9 所示。x 命令查询函数的二进制入口点函数的二进制入口点图 5-9 查找 NtLoadDriver 函数入口点我们用 X 命令查看函数的二进制入口点,为了查询的全面性,我们使用了通配符“*”,查询结果如上图中的第二个方框。由图可知,NtLoadDriver 和 ZwLoadDriver 函数都是由 ntdll.dll 导出,而且他们的内存地址都是 7c9515d8。这说明 NtLoadDriver 和ZwLoadDriver 是同一个函数。通过实验可知,对 ZwLoadDriver 函数设置断点和对NtLoad
11、Driver 函数设置断点,其实验结果都是一样的。设置断点:kd bp 7c9515d8断点设置好之后,用 g 命令恢复虚拟机的执行。下面我们就来执行我们的 loadsys 函数,动态加载 HelloWorld 驱动程序。(3)加载驱动程序的源代码参考 loadsys 中的 loadsys.c编译执行 loadsys.exe,当程序运行到 CreateService()的时候遇到我们设置的NtLoadDriver 断点,如图 5-10。图 5-10 执行到 NtLoadDriver 时中断对应的在 Windbg 中看到的情况如图 5-11 所示:图 5-11 中断时 Windbg 上的信息图
12、5-11 说明 CreateService 在启动服务时调用了 NtLoadDriver 函数(Windows Server 2003 中的驱动也是被作为服务对待),所以当系统运行到这里的时候停了下来。使用 g 命令,让程序继续运行。1432图 5-12 加载驱动执行图如图 5-12 所示:表示 OpenSCManager()操作成功。该函数主要作用是:获取服务控制管理器的句柄,CreateService()和 OpenService()都需要这个句柄。表示 CreateService()操作成功,该函数主要用来向系统中加载一个服务或驱动。表示 StartService()操作成功,该函数主要
13、作用是启动一个已经加载的服务或驱动。表示测试驱动程序成功,加载驱动程序执行的这四个步骤,我们只观察第二步 CreateService 和第四步测试驱动程序。在 Windbg 中对应的如图 5-13 所示:24图 5-13 调用 NtLoadDriver 时的打印信息图 5-13 第二部分显示的是步骤一中添加的调试信息:(1) 获得调用者的模式。(2) 根据第一步的操作结果,判断要求动态加载驱动程序的请求者来自用户态还是内核态。(3) 判断结果是:用户态请求。(4) 执行具体的加载操作。(4-0)do the exact load driver job!(inside 4)这句信息是我们添加在
14、IopLoadUnloadDriver()函数中的,说明NtloadDriver 要调用 IopLoadUnloadDriver()函数执行具体的加载操作。紧跟其后显示的”hi,starting DeviceEntry()”信息,表示执行了 HelloWorld 驱动程序的入口点函数 DeviceEntry() 。(4-2)Not in a system process 表示动态加载驱动程序的请求来自用户。(5) 释放前边分配的缓冲区。(6) 返回操作状态字,然后退出。前面就是验证动态加载驱动程序的过程。图 5-13 第四部分显示的是测试驱动程序阶段的结果,下面是驱动加载源码中测试驱动程序的部
15、分。/测试驱动程序hwdm=CreateFile(TEXT(“.HelloWorld“),GENERIC_WRITE|GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL);驱动程序测试部分,使用 CreateFile 打开已经存在的驱动程序,这个 CREATE 请求被驱动程序截获,然后调用驱动程序的 HelloWorldDispatch 函数,执行 IRP_MJ_CREATE I/O请求对应的操作,打印出“hi ,starting HelloWorldDispatch()”“hi,IRP_MJ_CREATE ”。步骤 4:卸载驱动程序上图最后一个步骤为卸载驱动程序
16、,卸载时的主要工作是调用 DeleteService()函数动态卸载指定的驱动程序。在虚拟机中,继续执行 loadsys.exe输入任意字符执行动态卸载驱动程序的操作,如图 5-14。图 5-14 卸载驱动程序对应的在 Windbg 中可以看到如下信息:卸载驱动程序图 5-15 Windbg 中的卸载信息由图 5-15 中的“(inside 4)if this was printed ,IopLoadUnloadDriver was called by NtLoadDriver()”可知,动态卸载驱动程序的时候,也要调用 IopLoadUnloadDriver()函数,同时调用 HelloWorld 驱动程序的 HelloWorldUnload()函数实现驱动程的动态卸载。1.6 测试驱动程序在控制台执行 5.2.3 节编译生成的 exe 文件,执行结果如图 5-16 所示。同时启动或停止驱动程序,也会看到 hellotest 有不一样的输出,执行如图 5-16 所示:图 5-16 测试驱动程序结果图当使用“-start ”参数启动驱动程序时,显示:start success! 说明驱动程序已经成功加载,而且是可用的。当使用“-stop”参数启动驱动程序时,显示:stop success! ,说明驱动程序可以成功卸载。