編寫完核心模式驅動程式(Kernel Mode Driver, KMD)後,如何將其載入系統並執行?根據 Four-F 的經驗分享,主要有三種途徑:服務控制管理器(SCM)、服務控制程式(SCP)以及服務程式本身。本文將深入實作「服務控制程式(SCP)」這一方式,實現驅動程式的動態加載與卸載,並透過一個經典的蜂鳴器驅動範例,讓您親耳聽到 Ring 0 代碼直接操作硬體發出的聲音。
SCP 是一個運行在用戶模式(Ring 3)的應用程式,它的任務是透過 Windows Service Control Manager (SCM) API,將驅動程式(.sys 檔案)註冊為系統服務,隨後啟動它。驅動程式載入後執行 DriverEntry,完成任務(如播放音樂)後主動返回錯誤狀態以觸發卸載,SCP 隨後清理服務註冊資訊。
OpenSCManager:取得 SCM 資料庫控制代碼,要求 SC_MANAGER_CREATE_SERVICE 權限。GetFullPathName:解析驅動檔案(如 beeper.sys)的完整路徑,因 CreateService 需絕對路徑。CreateService:在 SCM 中建立服務記錄,類型為 SERVICE_KERNEL_DRIVER,啟動類型為 SERVICE_DEMAND_START(手動啟動)。StartService:觸發驅動載入,系統呼叫 DriverEntry。DeleteService / CloseServiceHandle:驅動執行完畢後移除服務定義並關閉代碼。;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
; scp.asm - Service Control Program for beeper.sys driver
; Written by Four-F (four-f@mail.ru)
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\include\advapi32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
includelib \masm32\lib\advapi32.lib
include \masm32\Macros\Strings.mac
.code
start proc
local hSCManager:HANDLE
local hService:HANDLE
local acDriverPath[MAX_PATH]:CHAR
; 1. 連接 SCM
invoke OpenSCManager, NULL, NULL, SC_MANAGER_CREATE_SERVICE
.if eax != NULL
mov hSCManager, eax
; 2. 取得驅動完整路徑
push eax
invoke GetFullPathName, $CTA0("beeper.sys"), sizeof acDriverPath, addr acDriverPath, esp
pop eax
; 3. 註冊驅動服務
invoke CreateService, hSCManager, $CTA0("beeper"), $CTA0("Nice Melody Beeper"), \
SERVICE_START + DELETE, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, \
SERVICE_ERROR_IGNORE, addr acDriverPath, NULL, NULL, NULL, NULL, NULL
.if eax != NULL
mov hService, eax
; 4. 啟動驅動 -> 此時 DriverEntry 執行,喇叭響起
invoke StartService, hService, 0, NULL
; 5. 清理:移除服務註冊
invoke DeleteService, hService
invoke CloseServiceHandle, hService
.else
invoke MessageBox, NULL, $CTA0("Can't register driver."), NULL, MB_ICONSTOP
.endif
invoke CloseServiceHandle, hSCManager
.else
invoke MessageBox, NULL, $CTA0("Can't connect to Service Control Manager."), NULL, MB_ICONSTOP
.endif
invoke ExitProcess, 0
start endp
end start
此驅動展示三種硬體存取方式:直接 I/O 埠操作、HAL 封裝函式、以及 HalMakeBeep 高層 API。DriverEntry 執行完畢後返回 STATUS_DEVICE_CONFIGURATION_ERROR,讓系統自動卸載驅動,實現「閃現式」執行。
;::: 核心片段 :::
TIMER_FREQUENCY equ 1193167
OCTAVE equ 2
PITCH_C equ 523
PITCH_E equ 659
PITCH_G equ 784
TONE_1 equ TIMER_FREQUENCY/(PITCH_C*OCTAVE)
TONE_2 equ TIMER_FREQUENCY/(PITCH_E*OCTAVE)
TONE_3 equ (PITCH_G*OCTAVE)
DELAY equ 1800000h
MakeBeep1 proc dwPitch:DWORD ; 直接操作 8254 定時器與 61H 埠
cli
mov al, 10110110b
out 43h, al
mov eax, dwPitch
out 42h, al
mov al, ah
out 42h, al
in al, 61h
or al, 3
out 61h, al
sti
DO_DELAY
cli
in al, 61h
and al, 0FCh
out 61h, al
sti
ret
MakeBeep1 endp
DriverEntry proc pDriverObject:PDRIVER_OBJECT, pusRegistryPath:PUNICODE_STRING
invoke MakeBeep1, TONE_1 ; Do
invoke MakeBeep2, TONE_2 ; Mi (via HAL READ/WRITE_PORT_UCHAR)
invoke HalMakeBeep, TONE_3 ; Sol (via HAL.DLL)
DO_DELAY
invoke HalMakeBeep, 0 ; 關閉
mov eax, STATUS_DEVICE_CONFIGURATION_ERROR ; 強制卸載
ret
DriverEntry endp
set drv=beeper
\masm32\bin\ml /nologo /c /coff %drv%.asm
\masm32\bin\link /nologo /driver /base:0x10000 /align:32 /out:%drv%.sys /subsystem:native %drv%.obj
del %drv%.obj
KmdKit\examples\simple\Beeper\ 目錄。beeper.bat 編譯產生 beeper.sys。scp.asm 產生 scp.exe。scp.exe。結果: 您將聽到主板蜂鳴器播放一段簡單的旋律(Do-Mi-Sol)。這證明了您的代碼已成功在 Ring 0 執行,直接控制了硬體 I/O 埠(0x42, 0x43, 0x61),突破了用戶模式的限制。
HKLM\System\CurrentControlSet\Services 即可即時測試驅動。READ/WRITE_PORT_UCHAR 與 HalMakeBeep 提供了跨平台(如多處理器、不同晶片組)的硬體存取抽象層。