WINDOWSアプリケーションとカーネル通信



Windows Application



Windowsアプリケーションとカーネルの相互作用

画像

Windowsカーネルには、入出力に関連する重要なデータ構造であるデータ構造IRP(I / O要求パッケージ)入力および出力要求パッケージがあります。上位レベルのアプリケーションが下位レベルのドライバーと通信すると、アプリケーションはI / O要求を発行して動作します。システムは、I / O要求を対応するIRPデータに変換し、さまざまなタイプのIRPがさまざまなディスパッチに渡されます。タイプに応じて機能します。 IRPの2つの基本タイプであるMajorFunctionとMinorFunctionは、それぞれIRPのメインタイプとサブタイプを記録します。オペレーティングシステムは、MajorFunctionに従ってIRPをさまざまなディスパッチ機能に「ディスパッチ」します。ディスパッチ機能は、このIRPが属するMinorFunctionを引き続き判別できます。



1つは、機器の読み取りと書き込み

ドライバによって作成されたデバイスには、通常、次の3つの読み取りおよび書き込みメソッドがあります。



1.バッファモード(ユーザーモードアドレスとカーネルモードアドレスのデータコピー)

バッファモードでデバイスに書き込む場合、オペレーティングシステムは、writefileによって提供されるユーザーモードバッファをカーネルモードアドレスにコピーします。バッファモードでデバイスを読み取る場合、オペレーティングシステムはカーネルモードメモリのセクションを割り当てます。そのサイズはreadfileまたはwritefileで指定されたバイト数に等しく、IRP要求が終了すると、このメモリアドレスはにコピーされます。 readfileによって提供されるバッファコピープロセスはオペレーティングシステムを担当し、ユーザーモードアドレスはreadfileまたはwritefileによって提供され、カーネルモードアドレスはオペレーティングシステムによって提供されます。割り当てとリサイクル。

2.直接的な方法



デバイスを直接読み書きすると、オペレーティングシステムはユーザーモードでバッファーをロックし、オペレーティングシステムはこのバッファーをカーネルモードアドレスに再度マップし、ユーザーモードバッファーとカーネルモードバッファーは同じ領域を指します。メモリ。オペレーティングシステムが最初にユーザーモードのアドレスをロックした後、オペレーティングシステムはこのメモリをメモリ記述子(MDLデータ構造)で記録します。ユーザーモードのこのバッファは、仮想メモリでは連続していますが、物理メモリでは離散的である場合があります。

3.その他の方法

他の方法でデバイスを読み書きする場合、ディスパッチ機能はアプリケーションによって提供されたバッファアドレスを直接読み書きします。このメソッドは、ドライバーとアプリケーションが同じスレッドコンテキストで実行されている場合にのみ使用されます。ドライバがユーザーモードアドレスを使用する前に、このメモリが読み取り可能か書き込み可能かを検出します。

2. I / Oデバイス制御操作

ReadFileとWriteFileに加えて、Win32 APIDeviceIoControlを使用してデバイスを操作することもできます。 DeviceIoControlは、オペレーティングシステムにタイプIRP_MJ_DEVICE_CONTROLのIRPを内部的に作成させ、オペレーティングシステムはこのIRPをディスパッチ機能に転送します。 DeviceIoControlを使用して読み取りと書き込みを定義することで、プログラムとドライバーが通信できるようにすることもできます。

例として、I / Oデバイス制御操作を取り上げます。

現在のI / Oスタックの取得、入力バッファーサイズの取得、出力バッファーサイズの取得、IOCTLコードの取得、バッファーモード、出力バッファーの操作、実際の操作出力バッファーの長さの設定、IRP完了ステータスの設定、IRPの設定リクエスト操作バイト数、IRPリクエストの終了

アプリケーション->カーネル

CreateFile関数
HANDLE CreateFileA( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) #define MYDEVICE L'\\.\MyWDF_LINK' //Start with '\\.' and add the symbolic link name we provided for it HANDLE hDevice = CreateFile(MYDEVICE, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)

*備考:*
デバイスハンドルを取得するには、デバイス名またはデバイスに関連付けられているドライバーの名前を使用してCreateFile関数を呼び出す必要があります。デバイス名を指定するには、次の形式を使用します。
* \\。装置名*

1。アプリケーション層はCreateFile関数を呼び出します
2。この関数は実際にはkernel32.dllにカプセル化されています。この関数では、NtCreateFileが呼び出されます。これは、ntdll.dllのネイティブAPIを呼び出すためのものです。 ntdll.dllには通常2つあります。グループ関数-Ntで始まりZwで始まる場合、これら2つの関数グループの間に大きな違いはありません。
3。ネイティブAPIでは、interrupt int 2eh(windows 2000以下)またはsysenter命令(windows xp以降)を介してカーネルに入るため、このメソッドはソフト割り込みと呼ばれます。割り込みが発生すると、サービス番号が取得され、サービス番号に従ってssdtテーブルでサービス番号が検索されます(8086の割り込みメカニズムと同様)。
4。 SSDTテーブルに記録されているサービス機能アドレスに従って、関連するサービス機能を呼び出します。
5。次に、実行コンポーネントを入力します。 CreateFile操作の場合、この時点でIOマネージャーが呼び出されます。 IOマネージャーは、IO操作要求を開始し、これらの要求を管理する責任があります。主に生成されたIRP構造、システムにはさまざまなマネージャーがあり、主にそのような少数があります-仮想メモリマネージャー、IOマネージャー、オブジェクトマネージャー、プロセスマネージャー、スレッドマネージャー、構成マネージャー。
6。マネージャーはIRP要求を生成し、カーネル内のドライバーを呼び出してこの操作に応答します。 CreateFileの場合、NtCreateFile関数が呼び出されます。
7。最後に、ハードウェア抽象化レイヤーであるカーネル実装部分を呼び出します。最後に、ハードウェアアブストラクションレイヤーはハードウェアを操作して、ファイルを開くまたは作成する操作を完了します。

DeviceIoControl関数:
BOOL WINAPI DeviceIoControl( _In_ HANDLE hDevice, //Already opened device _In_ DWORD dwIoControlCode, //I/O control code IOCTL _In_opt_ LPVOID lpInBuffer, //Input buffer _In_ DWORD nInBufferSize, //Input buffer size _Out_opt_ LPVOID lpOutBuffer, //Output buffer _In_ DWORD nOutBufferSize, //Output buffer size _Out_opt_ LPDWORD lpBytesReturned, //The actual number of bytes returned _Inout_opt_ LPOVERLAPPED lpOverlapped //Whether OVERLAP operation ) CTL_CODE(DeviceType, Function, Method, Access) /* DeviceType: The type of the device object, this type should match the type when creating the device (IoCreateDevice), Function: This is the IOCTL code defined by the driver 0X0000 to 0X7FFF reserved by Microsoft 0X8000 to 0XFFFF defined by programmers Method: Operation mode (Buffer METHOD_BUFFERED, Write METHOD_IN_DIRECT directly, Read METHOD_OUT_DIRECT directly, Other methods METHOD_NEITHER) Access: access authority, no special requirements, generally FILE_ANY_ACCESS */

*戻り値:*
操作が正常に完了すると、DeviceIoControlはゼロ以外の値を返します。

操作が失敗するか待機している場合、DeviceIoControlはゼロを返します。拡張エラー情報を取得するには、GetLastErrorを呼び出します。

応用:
state = DeviceIoControl(DeviceHandle, PCIe_DMA_BUFFER_SIZE_READ, NULL, NULL, &outBuf, 4, NULL, NULL)
カーネル読み取り操作
status = WdfRequestRetrieveOutputBuffer( Request, sizeof(ULONG64), &outBuffer, NULL ) if (!NT_SUCCESS(status)) { goto Exit } value_u64 = READ_REGISTER_ULONG64((PULONG64)&pDevContext->DMABufferSize) WRITE_REGISTER_ULONG64((ULONG64*)outBuffer, value_u64*pDevContext->DMABufferNum) WdfRequestCompleteWithInformation(Request, status, sizeof(ULONG)) KdPrint(('PCIe_DMA_BUFFER_SIZE_READ %llu ', value_u64)) KdPrint(('PCIe_DMA_BUFFER_SIZE_READ OK '))

カーネル->アプリケーション

カーネル書き込み操作
status = WdfRequestRetrieveInputBuffer( // Retrieve the input buffer of the I/O request Request, sizeof(ULONG), &inBuffer, NULL ) value = READ_REGISTER_ULONG((ULONG*)inBuffer) //Counting unit is double word KdPrint(('DMA_WRITE_SIZE %d ', value)) WRITE_REGISTER_ULONG((PULONG)&pDevContext->Regs->RD_MEM_SIZE_L, value) WRITE_REGISTER_ULONG((PULONG)&pDevContext->Regs->WR_MEM_SIZE_L, value) pDevContext->Length = value * 4 WdfRequestCompleteWithInformation(Request, status, sizeof(ULONG)) if (!NT_SUCCESS(status)) { goto Exit } KdPrint(('PCIe_DMA_WRITE_SIZE OK '))
応用:
state = DeviceIoControl(DeviceHandle, PCIe_WRITE_SIZE, &inData, 4, NULL, NULL, NULL, NULL)
関数:
NTSTATUS WdfRequestRetrieveOutputBuffer( WDFREQUEST Request, size_t MinimumRequiredSize, PVOID *Buffer, size_t *Length ) NTSTATUS WdfRequestRetrieveInputBuffer( WDFREQUEST Request, size_t MinimumRequiredLength, PVOID *Buffer, size_t *Length )

書き込みによって要求されたデータをデバイスオブジェクトコンテキストに書き込み、読み取り要求が来ると、デバイスコンテキストのデータが読み取られ、ユーザーモードプログラムに返されます。wdfには2つの関数があり、これら2つの関数は要求された入力を取得できます。および出力バッファー、読み取り要求にはoutptbufferのみ、書き込み要求にはinputbufferのみ、IOcontrolは同僚に入力バッファーと出力バッファーを要求します

status = WdfRequestRetrieveOutputBuffer( Request, sizeof(ULONG64), &outBuffer, NULL )

このコードはリクエストの出力リクエストを取得します。outbufferはポインタです。関数呼び出しが成功すると、outbufferは出力バッファをポイントし、カーネルモードのアドレスをポイントし、データを直接読み取りまたは書き込みできます。読み取りリクエスト関数は成功します。 、書き込み要求は失敗します。