一、藍屏概述
當系統發生了非常嚴重的錯誤時,系統會以藍屏的形式停止工作。一般驅動程式中如果出現了null 指標等錯誤,都有可能引發藍屏。我們可以編寫驅動程式實現一個藍屏炸彈。
二、環境搭建
編譯驅動程式需要大量的標頭檔、庫檔案。這些檔案並沒有附帶在Visual Studio中。因此,如果需要編寫驅動程式,需要先下載安裝Windows Driver Development Kit(即DDK)。:Windows Driver Kit Version 7.1.0 下載後用虛擬光碟機載入安裝。
三、編寫驅動程式
我從《Windows驅動開發技術詳解》摘錄了一段代碼,貼在下面。
Driver.h
/*********************************************************</p><p>* 檔案名稱:Driver.h<br />* 作 者:張帆<br />* 完成日期:2007-11-1<br />*********************************************************/<br />#pragma once</p><p>#ifdef __cplusplus<br />extern "C"<br />{<br />#endif<br />#include <NTDDK.h><br />#ifdef __cplusplus<br />}<br />#endif </p><p>#define PAGEDCODE code_seg("PAGE")<br />#define LOCKEDCODE code_seg()<br />#define INITCODE code_seg("INIT")</p><p>#define PAGEDDATA data_seg("PAGE")<br />#define LOCKEDDATA data_seg()<br />#define INITDATA data_seg("INIT")</p><p>#define arraysize(p) (sizeof(p)/sizeof((p)[0]))</p><p>typedef struct _DEVICE_EXTENSION {<br />PDEVICE_OBJECT pDevice;<br />UNICODE_STRING ustrDeviceName;//裝置名稱<br />UNICODE_STRING ustrSymLinkName;//符號連結名<br />} DEVICE_EXTENSION, *PDEVICE_EXTENSION;</p><p>// 函式宣告</p><p>NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);<br />VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);<br />NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp);
Driver.cpp
/********************************************<br />* 檔案名稱:Driver.cpp<br />* 作 者:張帆<br />* 完成日期:2007-11-1<br />*********************************************/</p><p>#include "Driver.h"</p><p>/****************************************************<br />* 函數名稱:DriverEntry<br />* 功能描述:初始化驅動程式,定位和申請硬體資源,建立核心對象<br />* 參數列表:<br /> pDriverObject:從I/O管理器中傳進來的驅動對象<br /> pRegistryPath:驅動程式在註冊表的中的路徑<br />* 返回 值:返回初始化驅動狀態<br />*****************************************************/<br />#pragma INITCODE<br />extern "C" NTSTATUS DriverEntry (IN PDRIVER_OBJECT pDriverObject,<br />IN PUNICODE_STRING pRegistryPath)<br />{<br />NTSTATUS status;<br />KdPrint(("Enter DriverEntry/n"));<br />KeBugCheckEx(0x00000000, 0x12345678, 0x87654321, 0x11223344, 0x55667788);<br />//註冊其他驅動調用函數入口<br />pDriverObject->DriverUnload = HelloDDKUnload;<br />pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;<br />pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;<br />pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;<br />pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;</p><p>//建立驅動裝置對象<br />status = CreateDevice(pDriverObject);</p><p>KdPrint(("DriverEntry end/n"));<br />return status;<br />}</p><p>/************************************************<br />* 函數名稱:CreateDevice<br />* 功能描述:初始化裝置對象<br />* 參數列表:<br /> pDriverObject:從I/O管理器中傳進來的驅動對象<br />* 返回 值:返回初始化狀態<br />************************************************/<br />#pragma INITCODE<br />NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject)<br />{<br />NTSTATUS status;<br />PDEVICE_OBJECT pDevObj;<br />PDEVICE_EXTENSION pDevExt;</p><p>//建立裝置名稱<br />UNICODE_STRING devName;<br />RtlInitUnicodeString(&devName,L"//Device//MyDDKDevice");</p><p>//建立裝置<br />status = IoCreateDevice( pDriverObject,sizeof(DEVICE_EXTENSION),<br />&(UNICODE_STRING)devName,<br />FILE_DEVICE_UNKNOWN,<br />0, TRUE,<br />&pDevObj );<br />if (!NT_SUCCESS(status))<br />return status;</p><p>pDevObj->Flags |= DO_BUFFERED_IO;<br />pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;<br />pDevExt->pDevice = pDevObj;<br />pDevExt->ustrDeviceName = devName;<br />//建立符號連結<br />UNICODE_STRING symLinkName;<br />RtlInitUnicodeString(&symLinkName,L"//??//HelloDDK");<br />pDevExt->ustrSymLinkName = symLinkName;<br />status = IoCreateSymbolicLink( &symLinkName,&devName );<br />if (!NT_SUCCESS(status))<br />{<br />IoDeleteDevice( pDevObj );<br />return status;<br />}<br />return STATUS_SUCCESS;<br />}</p><p>/************************************************<br />* 函數名稱:HelloDDKUnload<br />* 功能描述:負責驅動程式的卸載操作<br />* 參數列表:<br /> pDriverObject:驅動對象<br />* 返回 值:返回狀態<br />*************************************************/<br />#pragma PAGEDCODE<br />VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)<br />{<br />PDEVICE_OBJECTpNextObj;<br />KdPrint(("Enter DriverUnload/n"));<br />pNextObj = pDriverObject->DeviceObject;<br />while (pNextObj != NULL)<br />{<br />PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)<br />pNextObj->DeviceExtension;</p><p>//刪除符號連結<br />UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;<br />IoDeleteSymbolicLink(&pLinkName);<br />pNextObj = pNextObj->NextDevice;<br />IoDeleteDevice( pDevExt->pDevice );<br />}<br />}</p><p>/***********************************************<br />* 函數名稱:HelloDDKDispatchRoutine<br />* 功能描述:對讀IRP進行處理<br />* 參數列表:<br /> pDevObj:功能裝置對象<br /> pIrp:從IO請求包<br />* 返回 值:返回狀態<br />************************************************/<br />#pragma PAGEDCODE<br />NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,IN PIRP pIrp)<br />{<br />KdPrint(("Enter HelloDDKDispatchRoutine/n"));<br />NTSTATUS status = STATUS_SUCCESS;<br />// 完成IRP<br />pIrp->IoStatus.Status = status;<br />pIrp->IoStatus.Information = 0;// bytes xfered<br />IoCompleteRequest( pIrp, IO_NO_INCREMENT );<br />KdPrint(("Leave HelloDDKDispatchRoutine/n"));<br />return status;<br />}
藍屏是由KeBugCheck或KeBugCheckEx這兩個函數引發的。這兩個函數的原型如下:
VOID KeBugCheck(IN ULONG BugCheckCode);<br />VOID KeBugCheckEx(IN ULONG BugCheckCode,<br /> IN ULONG_PTR BugCheckParameter1,<br /> IN ULONG_PTR BugCheckParameter2,<br /> IN ULONG_PTR BugCheckParameter3,<br /> IN ULONG_PTR BugCheckParameter4);</p><p>
其中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<br />TARGETTYPE=DRIVER<br />TARGETPATH=OBJ</p><p>INCLUDES=$(BASEDIR)/inc;/<br /> $(BASEDIR)/inc/ddk;/</p><p>SOURCES=Driver.cpp/
有了這些檔案,我們就可以開始編譯驅動程式了。
在編譯環境的命令列視窗中,進入你存放原始碼的目錄,並執行build命令。如果編譯成功的話你可以在目前的目錄下看到一個新的檔案夾,進入最裡層,你就能找到HelloDDK.sys這個驅動程式了。
五、測試
由於這個驅動程式的作用是讓電腦藍屏,儘管並不會對你的電腦造成什麼損害,但是在測試前請務必儲存好你正在工作的文檔,退出所有程式。電腦藍屏了以後,你就只能重啟了。
驅動程式不像應用程式那樣可以直接運行。因此我們還需要一個載入程式來載入驅動程式。限於篇幅,本文對此不展開詳細討論。一下僅給出程式碼,同樣來自《Windows驅動開發技術詳解》一書。
#include <windows.h><br />#include <winsvc.h><br />#include <conio.h><br />#include <stdio.h></p><p>#define DRIVER_NAME "HelloDDK"<br />#define DRIVER_PATH "HelloDDK.sys"</p><p>//裝載NT驅動程式<br />BOOL LoadNTDriver(char* lpszDriverName,char* lpszDriverPath)<br />{<br />char szDriverImagePath[256];<br />//得到完整的驅動路徑<br />GetFullPathName(lpszDriverPath, 256, szDriverImagePath, NULL);<br />BOOL bRet = FALSE;<br />SC_HANDLE hServiceMgr=NULL;//SCM管理器的控制代碼<br />SC_HANDLE hServiceDDK=NULL;//NT驅動程式的服務控制代碼<br />//開啟服務控制管理員<br />hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );<br />if( hServiceMgr == NULL )<br />{<br />//OpenSCManager失敗<br />printf( "OpenSCManager() Faild %d ! /n", GetLastError() );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />//OpenSCManager成功<br />printf( "OpenSCManager() ok ! /n" );<br />}<br />//建立驅動所對應的服務<br />hServiceDDK = CreateService( hServiceMgr,<br />lpszDriverName, //驅動程式的在註冊表中的名字<br />lpszDriverName, // 註冊表驅動程式的 DisplayName 值<br />SERVICE_ALL_ACCESS, // 載入驅動程式的存取權限<br />SERVICE_KERNEL_DRIVER,// 表示載入的服務是驅動程式<br />SERVICE_DEMAND_START, // 註冊表驅動程式的 Start 值<br />SERVICE_ERROR_IGNORE, // 註冊表驅動程式的 ErrorControl 值<br />szDriverImagePath, // 註冊表驅動程式的 ImagePath 值<br />NULL, NULL, NULL, NULL, NULL);<br />DWORD dwRtn;<br />//判斷服務是否失敗<br />if( hServiceDDK == NULL )<br />{<br />dwRtn = GetLastError();<br />if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_EXISTS )<br />{<br />//由於其他原因建立服務失敗<br />printf( "CrateService() Faild %d ! /n", dwRtn );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />//服務建立失敗,是由於服務已經創立過<br />printf( "CrateService() Faild Service is ERROR_IO_PENDING or ERROR_SERVICE_EXISTS! /n" );<br />}<br />// 驅動程式已經載入,只需要開啟<br />hServiceDDK = OpenService( hServiceMgr, lpszDriverName, SERVICE_ALL_ACCESS );<br />if( hServiceDDK == NULL )<br />{<br />//如果開啟服務也失敗,則意味錯誤<br />dwRtn = GetLastError();<br />printf( "OpenService() Faild %d ! /n", dwRtn );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />printf( "OpenService() ok ! /n" );<br />}<br />}<br />else<br />{<br />printf( "CrateService() ok ! /n" );<br />}<br />//開啟此項服務<br />bRet= StartService( hServiceDDK, NULL, NULL );<br />if( !bRet )<br />{<br />DWORD dwRtn = GetLastError();<br />if( dwRtn != ERROR_IO_PENDING && dwRtn != ERROR_SERVICE_ALREADY_RUNNING )<br />{<br />printf( "StartService() Faild %d ! /n", dwRtn );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />if( dwRtn == ERROR_IO_PENDING )<br />{<br />//裝置被掛住<br />printf( "StartService() Faild ERROR_IO_PENDING ! /n");<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />//服務已經開啟<br />printf( "StartService() Faild ERROR_SERVICE_ALREADY_RUNNING ! /n");<br />bRet = TRUE;<br />goto BeforeLeave;<br />}<br />}<br />}<br />bRet = TRUE;<br />//離開前關閉控制代碼<br />BeforeLeave:<br />if(hServiceDDK)<br />{<br />CloseServiceHandle(hServiceDDK);<br />}<br />if(hServiceMgr)<br />{<br />CloseServiceHandle(hServiceMgr);<br />}<br />return bRet;<br />}<br />//卸載驅動程式<br />BOOL UnloadNTDriver( char * szSvrName )<br />{<br />BOOL bRet = FALSE;<br />SC_HANDLE hServiceMgr=NULL;//SCM管理器的控制代碼<br />SC_HANDLE hServiceDDK=NULL;//NT驅動程式的服務控制代碼<br />SERVICE_STATUS SvrSta;<br />//開啟SCM管理器<br />hServiceMgr = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );<br />if( hServiceMgr == NULL )<br />{<br />//帶開SCM管理器失敗<br />printf( "OpenSCManager() Faild %d ! /n", GetLastError() );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />//帶開SCM管理器失敗成功<br />printf( "OpenSCManager() ok ! /n" );<br />}<br />//開啟驅動所對應的服務<br />hServiceDDK = OpenService( hServiceMgr, szSvrName, SERVICE_ALL_ACCESS ); </p><p>if( hServiceDDK == NULL )<br />{<br />//開啟驅動所對應的服務失敗<br />printf( "OpenService() Faild %d ! /n", GetLastError() );<br />bRet = FALSE;<br />goto BeforeLeave;<br />}<br />else<br />{<br />printf( "OpenService() ok ! /n" );<br />}<br />//停止驅動程式,如果停止失敗,只有重新啟動才能,再動態載入。<br />if( !ControlService( hServiceDDK, SERVICE_CONTROL_STOP , &SvrSta ) )<br />{<br />printf( "ControlService() Faild %d !/n", GetLastError() );<br />}<br />else<br />{<br />//開啟驅動所對應的失敗<br />printf( "ControlService() ok !/n" );<br />}<br />//動態卸載驅動程式。<br />if( !DeleteService( hServiceDDK ) )<br />{<br />//卸載失敗<br />printf( "DeleteSrevice() Faild %d !/n", GetLastError() );<br />}<br />else<br />{<br />//卸載成功<br />printf( "DelServer:eleteSrevice() ok !/n" );<br />}<br />bRet = TRUE;<br />BeforeLeave:<br />//離開前關閉開啟的控制代碼<br />if(hServiceDDK)<br />{<br />CloseServiceHandle(hServiceDDK);<br />}<br />if(hServiceMgr)<br />{<br />CloseServiceHandle(hServiceMgr);<br />}<br />return bRet;<br />}<br />void TestDriver()<br />{<br />//測試驅動程式<br />HANDLE hDevice = CreateFile("////.//HelloDDK",<br />GENERIC_WRITE | GENERIC_READ,<br />0,<br />NULL,<br />OPEN_EXISTING,<br />0,<br />NULL);<br />if( hDevice != INVALID_HANDLE_VALUE )<br />{<br />printf( "Create Device ok ! /n" );<br />}<br />else<br />{<br />printf( "Create Device faild %d ! /n", GetLastError() );<br />}<br />CloseHandle( hDevice );<br />}<br />int main(int argc, char* argv[])<br />{<br />//載入驅動<br />BOOL bRet = LoadNTDriver(DRIVER_NAME,DRIVER_PATH);<br />if (!bRet)<br />{<br />printf("LoadNTDriver error/n");<br />return 0;<br />}<br />//載入成功<br />printf( "press any to create device!/n" );<br />getch();<br />TestDriver();<br />//這時候你可以通過註冊表,或其他查看符號串連的軟體驗證。<br />printf( "press any to unload the driver!/n" );<br />getch();<br />//卸載驅動<br />UnloadNTDriver(DRIVER_NAME);<br />if (!bRet)<br />{<br />printf("UnloadNTDriver error/n");<br />return 0;<br />}<br />return 0;<br />}
將此程式編譯後和驅動程式放在同一個目錄。運行即可藍屏。
看到那湛藍的螢幕了沒?重新啟動吧~(事實上如果藍屏了,你是看不到這句話的……)