一、蓝屏概述
当系统发生了非常严重的错误时,系统会以蓝屏的形式停止工作。一般驱动程序中如果出现了空指针等错误,都有可能引发蓝屏。我们可以编写驱动程序实现一个蓝屏炸弹。
二、环境搭建
编译驱动程序需要大量的头文件、库文件。这些文件并没有附带在Visual Studio中。因此,如果需要编写驱动程序,需要先下载安装Windows Driver Development Kit(即DDK)。下载地址:Windows Driver Kit Version 7.1.0 下载后用虚拟光驱加载安装。
三、编写驱动程序
我从《Windows驱动开发技术详解》摘录了一段代码,贴在下面。
Driver.h
/********************************************************* * 文件名称:Driver.h * 作 者:张帆 * 完成日期:2007-11-1 *********************************************************/ #pragma once #ifdef __cplusplus extern "C" { #endif #include #ifdef __cplusplus } #endif #define PAGEDCODE code_seg("PAGE") #define LOCKEDCODE code_seg() #define INITCODE code_seg("INIT") #define PAGEDDATA data_seg("PAGE") #define LOCKEDDATA data_seg() #define INITDATA data_seg("INIT") #define arraysize(p) (sizeof(p)/sizeof((p)[0])) typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT pDevice; UNICODE_STRING ustrDeviceName; //设备名称 UNICODE_STRING ustrSymLinkName; //符号链接名 } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // 函数声明 NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject); VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject); NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp);
Driver.cpp
/******************************************** * 文件名称:Driver.cpp * 作 者:张帆 * 完成日期:2007-11-1 *********************************************/ #include "Driver.h" /**************************************************** * 函数名称:DriverEntry * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 pRegistryPath:驱动程序在注册表的中的路径 * 返回 值:返回初始化驱动状态 *****************************************************/ #pragma INITCODE extern "C" NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) { NTSTATUS status; KdPrint(("Enter DriverEntry/n")); KeBugCheckEx(0x00000000, 0x12345678, 0x87654321, 0x11223344, 0x55667788); //注册其他驱动调用函数入口 pDriverObject->DriverUnload = HelloDDKUnload; pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine; pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine; //创建驱动设备对象 status = CreateDevice(pDriverObject); KdPrint(("DriverEntry end/n")); return status; } /************************************************ * 函数名称:CreateDevice * 功能描述:初始化设备对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 * 返回 值:返回初始化状态 ************************************************/ #pragma INITCODE NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject) { NTSTATUS status; PDEVICE_OBJECT pDevObj; PDEVICE_EXTENSION pDevExt; //创建设备名称 UNICODE_STRING devName; RtlInitUnicodeString(&devName,L"//Device//MyDDKDevice"); //创建设备 status = IoCreateDevice( pDriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj ); if (!NT_SUCCESS(status)) return status; pDevObj->Flags |= DO_BUFFERED_IO; pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; pDevExt->pDevice = pDevObj; pDevExt->ustrDeviceName = devName; //创建符号链接 UNICODE_STRING symLinkName; RtlInitUnicodeString(&symLinkName,L"//??//HelloDDK"); pDevExt->ustrSymLinkName = symLinkName; status = IoCreateSymbolicLink( &symLinkName,&devName ); if (!NT_SUCCESS(status)) { IoDeleteDevice( pDevObj ); return status; } return STATUS_SUCCESS; } /************************************************ * 函数名称:HelloDDKUnload * 功能描述:负责驱动程序的卸载操作 * 参数列表: pDriverObject:驱动对象 * 返回 值:返回状态 *************************************************/ #pragma PAGEDCODE VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject) { PDEVICE_OBJECT pNextObj; KdPrint(("Enter DriverUnload/n")); pNextObj = pDriverObject->DeviceObject; while (pNextObj != NULL) { PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION) pNextObj->DeviceExtension; //删除符号链接 UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName; IoDeleteSymbolicLink(&pLinkName); pNextObj = pNextObj->NextDevice; IoDeleteDevice( pDevExt->pDevice ); } } /*********************************************** * 函数名称:HelloDDKDispatchRoutine * 功能描述:对读IRP进行处理 * 参数列表: pDevObj:功能设备对象 pIrp:从IO请求包 * 返回 值:返回状态 ************************************************/ #pragma PAGEDCODE NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp) { KdPrint(("Enter HelloDDKDispatchRoutine/n")); NTSTATUS status = STATUS_SUCCESS; // 完成IRP pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); KdPrint(("Leave HelloDDKDispatchRoutine/n")); return status; }
蓝屏是由KeBugCheck或KeBugCheckEx这两个函数引发的。这两个函数的原型如下:
VOID KeBugCheck(IN ULONG BugCheckCode); VOID KeBugCheckEx(IN ULONG BugCheckCode, IN ULONG_PTR BugCheckParameter1, IN ULONG_PTR BugCheckParameter2, IN ULONG_PTR BugCheckParameter3, IN ULONG_PTR BugCheckParameter4);
其中KeBugCheckEx函数的第一个参数和KeBugCheck中的参数是一样的。关于这些参数,从下面一张蓝屏的图片中就能看出其作用了。
四、编译
打开开始菜单,找到DDK的目录,展开Build Environment,选择与你电脑符合的编译环境。比如我的电脑是 Windows Vista x86。注意,有两个版本Checked和Free,类似与VC++中的Debug和Release版本。这里我们选择Free,可以去掉所有的调试符号,减小代码的文件尺寸。
在编译之前,我们需要准备两个文件,makefile和Sources
makefile只需一行:
!INCLUDE $(NTMAKEENV)/makefile.def
继承DDK默认配置
而Sources文件需要指明文件类型以及相关包含文件的路径
TARGETNAME=HelloDDK TARGETTYPE=DRIVER TARGETPATH=OBJ INCLUDES=$(BASEDIR)/inc;/ $(BASEDIR)/inc/ddk;/ SOURCES=Driver.cpp/
有了这些文件,我们就可以开始编译驱动程序了。
在编译环境的命令行窗口中,进入你存放源代码的目录,并执行build命令。如果编译成功的话你可以在当前目录下看到一个新的文件夹,进入最里层,你就能找到HelloDDK.sys这个驱动程序了。
五、测试
由于这个驱动程序的作用是让电脑蓝屏,尽管并不会对你的电脑造成什么损害,但是在测试前请务必保存好你正在工作的文档,退出所有程序。电脑蓝屏了以后,你就只能重启了。
驱动程序不像应用程序那样可以直接运行。因此我们还需要一个加载程序来加载驱动程序。限于篇幅,本文对此不展开详细讨论。一下仅给出程序代码,同样来自《Windows驱动开发技术详解》一书。
#include #include #include #include #define DRIVER_NAME "HelloDDK" #define DRIVER_PATH "HelloDDK.sys" //装载NT驱动程序 BOOL LoadNTDriver(char* lpszDriverName,char* lpszDriverPath) { char szDriverImagePath[256]; //得到完整的驱动路径 GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL); BOOL bRet = FALSE; SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄 SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄 //打开服务控制管理器 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( hServiceMgr == NULL ) { //OpenSCManager失败 printf( "OpenSCManager() Faild %d ! /n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { //OpenSCManager成功 printf( "OpenSCManager() ok ! /n" ); } //创建驱动所对应的服务 hServiceDDK = CreateService( hServiceMgr, lpszDriverName, //驱动程序的在注册表中的名字 lpszDriverName, // 注册表驱动程序的 DisplayName 值 SERVICE_ALL_ACCESS, // 加载驱动程序的访问权限 SERVICE_KERNEL_DRIVER,// 表示加载的服务是驱动程序 SERVICE_DEMAND_START, // 注册表驱动程序的 Start 值 SERVICE_ERROR_IGNORE, // 注册表驱动程序的 ErrorControl 值 szDriverImagePath, // 注册表驱动程序的 ImagePath 值 NULL, NULL, NULL, NULL, NULL); DWORD dwRtn; //判断服务是否失败 if( hServiceDDK == NULL ) { dwRtn = GetLastError(); if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS ) { //由于其他原因创建服务失败 printf( "CrateService() Faild %d ! /n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { //服务创建失败,是由于服务已经创立过 printf( "CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! /n" ); } // 驱动程序已经加载,只需要打开 hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS ); if( hServiceDDK == NULL ) { //如果打开服务也失败,则意味错误 dwRtn = GetLastError(); printf( "OpenService() Faild %d ! /n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { printf( "OpenService() ok ! /n" ); } } else { printf( "CrateService() ok ! /n" ); } //开启此项服务 bRet= StartService( hServiceDDK, NULL, NULL ); if( !bRet ) { DWORD dwRtn = GetLastError(); if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING ) { printf( "StartService() Faild %d ! /n", dwRtn ); bRet = FALSE; goto BeforeLeave; } else { if( dwRtn == ERROR_IO_PENDING ) { //设备被挂住 printf( "StartService() Faild ERROR_IO_PENDING ! /n"); bRet = FALSE; goto BeforeLeave; } else { //服务已经开启 printf( "StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! /n"); bRet = TRUE; goto BeforeLeave; } } } bRet = TRUE; //离开前关闭句柄 BeforeLeave: if(hServiceDDK) { CloseServiceHandle(hServiceDDK); } if(hServiceMgr) { CloseServiceHandle(hServiceMgr); } return bRet; } //卸载驱动程序 BOOL UnloadNTDriver( char * szSvrName ) { BOOL bRet = FALSE; SC_HANDLE hServiceMgr=NULL;//SCM管理器的句柄 SC_HANDLE hServiceDDK=NULL;//NT驱动程序的服务句柄 SERVICE_STATUS SvrSta; //打开SCM管理器 hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS ); if( hServiceMgr == NULL ) { //带开SCM管理器失败 printf( "OpenSCManager() Faild %d ! /n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { //带开SCM管理器失败成功 printf( "OpenSCManager() ok ! /n" ); } //打开驱动所对应的服务 hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS ); if( hServiceDDK == NULL ) { //打开驱动所对应的服务失败 printf( "OpenService() Faild %d ! /n", GetLastError() ); bRet = FALSE; goto BeforeLeave; } else { printf( "OpenService() ok ! /n" ); } //停止驱动程序,如果停止失败,只有重新启动才能,再动态加载。 if( !ControlService( hServiceDDK, SERVICE_CONTROL_STOP , &SvrSta ) ) { printf( "ControlService() Faild %d !/n", GetLastError() ); } else { //打开驱动所对应的失败 printf( "ControlService() ok !/n" ); } //动态卸载驱动程序。 if( !DeleteService( hServiceDDK ) ) { //卸载失败 printf( "DeleteSrevice() Faild %d !/n", GetLastError() ); } else { //卸载成功 printf( "DelServer:eleteSrevice() ok !/n" ); } bRet = TRUE; BeforeLeave: //离开前关闭打开的句柄 if(hServiceDDK) { CloseServiceHandle(hServiceDDK); } if(hServiceMgr) { CloseServiceHandle(hServiceMgr); } return bRet; } void TestDriver() { //测试驱动程序 HANDLE hDevice = CreateFile("////.//HelloDDK", GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL); if( hDevice != INVALID_HANDLE_VALUE ) { printf( "Create Device ok ! /n" ); } else { printf( "Create Device faild %d ! /n", GetLastError() ); } CloseHandle( hDevice ); } int main(int argc, char* argv[]) { //加载驱动 BOOL bRet = LoadNTDriver(DRIVER_NAME,DRIVER_PATH); if (!bRet) { printf("LoadNTDriver error/n"); return 0; } //加载成功 printf( "press any to create device!/n" ); getch(); TestDriver(); //这时候你可以通过注册表,或其他查看符号连接的软件验证。 printf( "press any to unload the driver!/n" ); getch(); //卸载驱动 UnloadNTDriver(DRIVER_NAME); if (!bRet) { printf("UnloadNTDriver error/n"); return 0; } return 0; }
将此程序编译后和驱动程序放在同一个目录。运行即可蓝屏。
看到那湛蓝的屏幕了没?重新启动吧~(事实上如果蓝屏了,你是看不到这句话的……)