運転手は簡単です

多くの人は、Windows用のドライバーを作成することはフィクションの危機にonしていると考えています。 しかし、実際にはそうではありません。 もちろん、洗練されたデバイス用のドライバーを開発するのは簡単なことではありません。 しかし、複雑なプログラムやゲームの作成についても同じことが言えます。 単純なドライバーを開発するのに複雑なことは何もありません。例を挙げて説明します。



最初に、最初のドライバーを作成するものを決定する必要があります。 この資料は初心者を対象としているため、プログラミング言語は単純なものの1つとして選択されました。これはCやアセンブラーではなく、基本的なものです。 基本的な方言の1つであるPureBasicを使用します。 箱から出して、彼はドライバーを作成する訓練を受けていませんが、彼はコンパイルに使用されるファイルの良いセットを持っています、そして、少しのシャーマニズムはこの機能を追加することを可能にします。 コンパイルプロセスはいくつかの段階で構成されています。 要するに、次のようになります。最初に、トランスレーターは基本コードをアセンブラーに「蒸留」し、アセンブラーはオブジェクトファイルを作成するFASM(アセンブラーコンパイラー)に渡されます。 次に、実行可能ファイルを作成するpolinkリンカーが機能します。 アセンブラコンパイラとリンカの両方でドライバを作成できます。コンパイルオプションをわずかに変更すると、EXEやDLLなどの実行可能ファイルではなく、カーネルモードドライバ(SYS)が取得されます。



ファイルシールドミラーに、PureBasic 4.61 x86のわずかに変更された無料デモ版をダウンロードできます

x64システム用のドライバーを作成する必要がある場合は、 このバージョンミラーをダウンロードしてください



配布は小さく、それぞれ約3 MBです。 このバージョンでは、ドライバーのみを作成できます。

ファイル「PureBasic Portable」をクリックしてダウンロードし、解凍して起動します。 この場合、IDEが起動し、ウィンドウがポップアップして、これがデモバージョンであるというメッセージと制限のリストが表示されます。 その中で最も重要なのは、コードの行数が800に制限されていることであり、これは単純なドライバーを作成するのに十分かもしれません。 この場合の残りの制限は重要ではありません。



ロードされたドライバコードを含むIDEウィンドウがスクリーンショットに表示されます。



画像



ドライバーのコンパイルは、「Compiler」メニューから実行されます(誰かが理解できなかった場合)。

画像



次に、最初のドライバーが何をするかを決定します。 通常、プログラミングを勉強するときは、数学的な操作を実行して結果を出力するなど、簡単なことから始めます。 カーネルモードで生成される一般的な数学は非常に優れているため、ドライバーにも同じことをさせてください。



ドライバーコード:



Declare DriverEntry(*DriverObject, *RegistryPath) !public PureBasicStart !section '.code' code readable executable align 8 !PureBasicStart: *A=@DriverEntry() !jmp [p_A] ;    DriverEntry(). #IOCTL_MyPlus = $200 !extrn PB_PokeL CompilerSelect #PB_Compiler_Processor CompilerCase #PB_Processor_x86 !extrn _IoCompleteRequest@8 ;    . !extrn _RtlInitUnicodeString@8 !extrn _IoCreateDevice@28 !extrn _IoDeleteDevice@4 !extrn _IoCreateSymbolicLink@8 !extrn _IoDeleteSymbolicLink@4 !extrn _PB_PeekI@4 Import "ntoskrnl.lib" CompilerCase #PB_Processor_x64 !extrn IoCompleteRequest;    . !extrn RtlInitUnicodeString !extrn IoCreateDevice !extrn IoDeleteDevice !extrn IoCreateSymbolicLink !extrn IoDeleteSymbolicLink !extrn PB_PeekI ImportC "ntoskrnl.lib" CompilerEndSelect ;    . IoCompleteRequest(*IRP, PriorityBoost) RtlInitUnicodeString(*UString, *String) IoCreateDevice(*DriverObject, DeviceExtensionSize, *UDeviceName, DeviceType, DeviceCharacteristics, Exclusive, *DeviceObject) IoDeleteDevice(*DeviceObject) IoCreateSymbolicLink(*SymbolicLinkName, *DeviceName) IoDeleteSymbolicLink(*SymbolicLinkName) EndImport Structure MyData ; ,   . Plus_1.l Plus_2.l EndStructure ;     . Procedure DeviceIoControl(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) Protected *Stack.IO_STACK_LOCATION Protected *InpBuff, *OutBuff Protected InBuffSize, OutBuffSize Protected ntStatus, *MyData.MyData ntStatus = #STATUS_SUCCESS ;  . *Stack = *pIrp\Tail\Overlay\CurrentStackLocation ;   (. WinAPI  DeviceIoControl()) InBuffSize = *Stack\Parameters\DeviceIoControl\InputBufferLength OutBuffSize = *Stack\Parameters\DeviceIoControl\OutputBufferLength If InBuffSize >= SizeOf(Integer) And OutBuffSize >= 4 Select *Stack\Parameters\DeviceIoControl\IoControlCode Case #IOCTL_MyPlus *Point = *pIrp\SystemBuffer If *Point *MyData = PeekI(*Point) If *MyData Result.l = *MyData\Plus_1 + *MyData\Plus_2 PokeL(*pIrp\SystemBuffer, Result) *pIrp\IoStatus\Information = 4 Else ntStatus = #STATUS_BUFFER_TOO_SMALL *pIrp\IoStatus\Information = 0 EndIf EndIf Default ntStatus = #STATUS_UNSUCCESSFUL *pIrp\IoStatus\Information = 0 EndSelect Else ntStatus = #STATUS_BUFFER_TOO_SMALL ;    . *pIrp\IoStatus\Information = 0 EndIf *pIrp\IoStatus\Status = ntStatus IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn ntStatus EndProcedure ;  .     . Procedure UnloadDriver(*DriverObject.DRIVER_OBJECT) Protected uniDOSString.UNICODE_STRING ;  -. RtlInitUnicodeString(@uniDOSString, ?DosDevices) ;   . IoDeleteSymbolicLink (@uniDOSString) ;  . IoDeleteDevice(*DriverObject\DeviceObject) EndProcedure ;         CreateFile(). Procedure CreateDispatch(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) *pIrp\IoStatus\Information = 0 *pIrp\IoStatus\Status = #STATUS_SUCCESS IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn #STATUS_SUCCESS EndProcedure ;      CloseHandle(). Procedure CloseDispatch(*DeviceObject.DEVICE_OBJECT, *pIrp.IRP) *pIrp\IoStatus\Information = 0 *pIrp\IoStatus\Status = #STATUS_SUCCESS IoCompleteRequest(*pIrp, #IO_NO_INCREMENT) ProcedureReturn #STATUS_SUCCESS EndProcedure ;   .     . Procedure DriverEntry(*DriverObject.DRIVER_OBJECT, *RegistryPath.UNICODE_STRING) Protected deviceObject.DEVICE_OBJECT Protected uniNameString.UNICODE_STRING Protected uniDOSString.UNICODE_STRING ;  -. RtlInitUnicodeString(@uniNameString, ?Device) RtlInitUnicodeString(@uniDOSString, ?DosDevices) ;  . status = IoCreateDevice(*DriverObject, 0, @uniNameString, #FILE_DEVICE_UNKNOWN, 0, #False, @deviceObject) If status <> #STATUS_SUCCESS ProcedureReturn status EndIf ;         , ;      user-mode,  ,  ;       . status = IoCreateSymbolicLink(@uniDOSString, @uniNameString) If status <> #STATUS_SUCCESS IoDeleteDevice(@deviceObject) ProcedureReturn status EndIf ;     . *DriverObject\DriverUnload = @UnloadDriver() *DriverObject\MajorFunction[#IRP_MJ_CREATE] = @CreateDispatch() *DriverObject\MajorFunction[#IRP_MJ_CLOSE] = @CloseDispatch() ;       WinAPI DeviceIoControl(). *DriverObject\MajorFunction[#IRP_MJ_DEVICE_CONTROL] = @DeviceIoControl() ProcedureReturn #STATUS_SUCCESS EndProcedure ;   (). DataSection Device: !du '\Device\pbDrPlus', 0, 0 DosDevices: !du '\DosDevices\pbDrPlus', 0, 0 EndDataSection
      
      







これは無意味なコードの束のように見えるかもしれませんが、そうではありません。



各ドライバーにはエントリーポイントが必要です。通常はDriverEntry()という名前で、プロシージャまたは関数として実行されます。 ご覧のとおり、このドライバーにはこのような手順があります。 コードの先頭を見ると、最初の行に制御がどのように転送されるかがわかります。 この手順では、ドライバーが初期化されます。 ここでは、UnloadDriver()という名前のドライバーをシャットダウンする手順も割り当てられています。 プロシージャCreateDispatch()およびCloseDispatch()は、プログラムをユーザーモードに接続および切断するハンドラーによって割り当てられます。

DeviceIoControl()プロシージャは、このドライバーのユーザーモード接続であるDeviceIoControl()関数に対するWinAPI要求を処理します。 コードの最後には、いわゆるDataSectionがあり、Unicode形式で保存されたドライバー名が含まれています(このために、FASMアセンブラーチップの1つが使用されます)。



次に、ドライバーが外界とどのように相互作用するかを検討します。 これは、DeviceIoControl()プロシージャで発生します。 1つのメッセージ、つまり#IOCTL_MyPlusを監視します。このメッセージは、カーネルモードで2つの数値を追加する必要があるときにユーザーモードプログラムによって送信されます(かっこいいですね)。 そのようなメッセージを受信すると、システムバッファから読み取り、用語を含む構造体へのポインタのアドレスを追加し、結果をシステムバッファに配置します。 実際、これが最初のドライバーの主なタスクです。



最も簡単な数学的操作を実行するのに必要なコードの量がわかります-2つの数字の加算?



次に、このドライバーで動作するプログラムを検討します。 同じPureBasicで書かれています。



 #DriverName = "pbDrPlus" #IOCTL_MyPlus = $200 XIncludeFile "..\DrUserModeFramework.pbi" Structure MyData ; ,   . Plus_1.l Plus_2.l EndStructure ;    -. DrFile.s = GetPathPart(ProgramFilename())+#DriverName+".sys" ;     ,    . hDrv=OpenDriver(DrFile, #DriverName, #DriverName, #DriverName) If hDrv=0 ;    . Driver_UnInstall(#DriverName) MessageRequester("", "  ") End EndIf ;    . Procedure.q Plus(hDrv, x1, x2) Protected MyData.MyData, Result, *Point MyData\Plus_1=x1 MyData\Plus_2=x2 *Point = @MyData DeviceIoControl_(hDrv, #IOCTL_MyPlus, @*Point, SizeOf(MyData), @Result, 4, @BytesReturned, 0) ProcedureReturn Result EndProcedure OpenWindow(1,300,300,140,90,"Title",#PB_Window_SystemMenu|#PB_Window_ScreenCentered) StringGadget(1,10,10,50,20,"") StringGadget(2,10,40,50,20,"") TextGadget(3,70,30,70,20,"") Repeat ev=WaitWindowEvent() If ev=#PB_Event_Gadget op1=Val(GetGadgetText(1)) op2=Val(GetGadgetText(2)) Result = Plus(hDrv, op1, op2) SetGadgetText(3,Str(Result)) EndIf Until ev=#PB_Event_CloseWindow ;   ,     . If hDrv CloseHandle_(hDrv) hDrv=0 EndIf ;    . Driver_UnInstall(#DriverName)
      
      







プログラムが起動すると、OpenDriver()関数が呼び出され、ドライバーがロードされます。 簡単にするために、ドライバー名、サービス名、およびサービスの説明は同じ「pbDrPlus」に設定されています。 ダウンロードが失敗した場合、対応するメッセージが表示され、プログラムは作業を終了します。



Plus()プロシージャは、ドライバーと通信します。 ハンドル、ドライバへのアクセス、および構造体に配置された被加数、およびドライバへ渡されるポインタへのポインタを渡します。 数値を追加した結果は、変数「結果」になります。



以下は、 Wikipediaからコピーされた最も単純なGUI計算機のコードです



ウィンドウが閉じられると、プログラムが終了する前に、ドライバーとの接続が閉じられ、システムからアンインストールされます。



スクリーンショットに数字8と2を追加した結果。



画像



ドライバとプログラムのソースコードは、ファイルウォッシュのPureBasicの「Examples」フォルダにあります。このリンクは記事の冒頭に記載されています。 また、コンピューターのポアに直接アクセスするためのドライバーの例と、カーネルメモリを操作する例もあります。



PS。

カーネルでの作業には、BSOD(死のブルースクリーン)である小さな驚きがあふれているため、慎重に実験し、ドライバーを起動する前にすべてを必ず保存してください。



データ損失の可能性について、私は責任を負いません!



All Articles