390 likes | 581 Views
Ch 7. Driver Dispatch Routines. Goals. 드라이버와 드라이버 디스패치 루틴 점진적인 개발 방법론. Kernel Mode Object Type 결정 Driver 에 필요한 Context 결정 및 Store Place 결정 DriverEntry, Unload Routine 을 작성 (6 장 ) IRP_MJ_CREATE, IRP_MJ_CLOSE dispatch Routine(7 장 ) CreateFile , CloseHandle
E N D
Goals • 드라이버와 드라이버 디스패치 루틴 • 점진적인 개발 방법론 • Kernel Mode Object Type 결정 • Driver에 필요한 Context결정 및 Store Place 결정 • DriverEntry, Unload Routine을 작성(6장) • IRP_MJ_CREATE, IRP_MJ_CLOSE dispatch Routine(7장) • CreateFile , CloseHandle • Hardware 검색및 자원을 할당하는 Code 추가 • Unload시 할당된 자원을 해제하는 Code 추가 • IRP_MJ_XXX 함수를 처리하는 dispatch Routine 추가 • Win32 App에서 ReadFile, WriteFile 등으로 Test 가능 • 실질적인 Routine 작성 • Start I/O Routine • Interrupt Service Routine • DPC Routine • DeviceIOControl Code 추가 • Win32 App가 직접 Hardware Register를 제어할수 있음 Ch 7. Driver Dispatch Routines
Contents • Driver Dispatch Routines • Writing Driver Dispatch Routines • Processing Read and Write Requests • Code Example : Loop-back Device • Extending the Dispatch interface • Testing Driver Dispatch Routine • Summary Ch 7. Driver Dispatch Routines
디스패치 루틴에서의 I/O 요청 메커니즘(1) <IRP의 구조> <DriverObject 구조> <IRP 외부로 보여지는 IRP 스택 로케이션 필드> Ch 7. Driver Dispatch Routines
디스패치 루틴에서의 I/O 요청 메커니즘(2) MajorFunction [IRP_MJ_CREATE] MajorFunction[] . . . [IRP_MJ_READ] [IRP_MJ_WRITE] . . . 쓰기 디스패치 루틴 ※ _IopInvalidDeviceRequest는 올바르지 않은 I/O 요청일 경우에 불리는 함수로써 에러를 I/O요청자에게 리턴 Ch 7. Driver Dispatch Routines
특정 함수 코드의 활성화 NTSTATUS DriverEntry( IN PDRIVER_OBJECT pDO, IN PUNICODE_STRING pRegPath) { : pDO->MajorFunction[ IRP_MJ_CREATE ] = DispCreate; pDO->MajorFunction[ IRP_MJ_CLOSE ] = DispClose; pDO->MajorFunction[ IRP_MJ_CLEANUP ] = DispCleanup; pDO->MajorFunction[ IRP_MJ_READ ] = DispRead; pDO->MajorFunction[ IRP_MJ_WRITE ] = DispWrite; : return STATUS_SUCCESS; } • IRP_MJ_XXX(NTDDK.h, WDM.h) • DriverEntry가 호출되기 전에 _IopInvalidDeviceRequest 포인터로 다채운다. Ch 7. Driver Dispatch Routines
어떤 함수 코드를 지원할 지 결정하기 Ch 7. Driver Dispatch Routines
실행 컨텍스트 • 디스패치 루틴은 비슷한 형태를 가진다. • PASSIVE_LEVEL IRQL <디스패치 루틴의 함수 원형> • Buffered I/O 와 Direct I/O 문제 • IRP와 IRP이외의 구조체의 사용에 대한 문제 • 공유 데이터 구조 IRP에 따른 문제 Ch 7. Driver Dispatch Routines
디스패치 루틴에서 수행하는 동작 • IoGetCurrentIrpStackLocation • IRP에 대한 유효성 체크 • 하부 드라이버로 전달 • 에러발생시 _IopInvalidDeviceRequest를 리턴하고 수행중지 Ch 7. Driver Dispatch Routines
디스패치 루틴에서 빠져나오기(1) • 에러 통보 • 요청 완료 • 디바이스 동작을 스케줄링 Ch 7. Driver Dispatch Routines
디스패치 루틴에서 빠져나오기(2) • 에러의 통보 NTSTATUS DispatchWrite(IN PDEVICE_OBJECT pDO, IN PIRP pIrp) { : // 만약 해당 요청이 이 디바이스에서 지원되지 않는 것이라면 // 결과를 보고하고 요청을 거절한다. pIrp->IoStatus.Status = STATUS_NOT_INCREMENT_SUPPORTED; // 전송된 데이터가 없음을 보고한다. pIrp->IoStatus.Information = 0; //우선순위의 증가 없이 IRP를 완료시킨다. IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_NOT_SUPPORTED; } Ch 7. Driver Dispatch Routines
디스패치 루틴에서 빠져나오기(3) • 요청의 완료 NTSTATUS DispatchClose(IN PDEVICE_OBJECT pDO, IN PIRP pIrp) { : pIrp->IoStatus.Status = STATUS_SUCCESS; // 전송된 데이터가 0바이트라고 지정한다. pIrp->IoStatus.Information = 0; // IRP를 완료로 지정한다. – 더 이상의 처리를 수행하지 않는다. IoCompleteRequest(pIrp, IO_NO_INCREMENT); return STATUS_SUCCESS; } Ch 7. Driver Dispatch Routines
디스패치 루틴에서 빠져나오기(4) • 디바이스 동작을 스케줄링 NTSTATUS DispatchWrite(IN PDEVICE_OBJECT pDO, IN PIRP pIrp) { : // IRP를 진행중으로 설정한다. IoMarkIrpPending(pIrp); // 드라이버의 Start I/O 루틴을 통해 이벤트 처리의 // IRP를 큐잉(스케줄링)한다. // 세 번째 매개변수는 해당 I/O 요청을 큐의 끝에 삽입되도록 한다. // 네 번째 매개변수는 Cancel 루틴에 대한 지점이다. IoStartPacket(pDO, pIrp, 0, NULL); return STATUS_PENDING; } Ch 7. Driver Dispatch Routines
사용자 버퍼로의 접근 • 디바이스 객체의 Flags 필드(DO_BUFFERED_IO, DO_DIRECT_IO) • BUFFERED I/O - Non-paged 풀(pool) 버퍼를 할당 - IRP의 AssociatedIrp.SystemBuffer 필드에 위치 • DIRECT I/O - 사용자 버퍼에 해당하는 물리 메모리 페이지를 잠근다(lock) - MDL(Memory Descriptor List) - IRP의 MdlAddress 필드에 저장 • NEITHER Method - Flags필드 세팅이 되지 않았을 경우 - I/O 관리자는 어떤 버퍼 관리도 처리하지 않는다. Ch 7. Driver Dispatch Routines
Code Example loop-back routine
Code Example : 루프백 디바이스(1) Write 요청에 대한 디스패치루틴(1) NTSTATUS DispatchWrite ( IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp ) { NTSTATUS status = STATUS_SUCCESS; PVOID userBuffer; ULONG xferSize; // 스택 로케이션은 사용자 버퍼의 정보를 갖고 있다. PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); // 현재 버퍼의 포인터는 디바이스 객체 내에 포함된 DEVICE_EXTENSION 안에 저장된다. PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; // 버퍼가 이미 할당되어 있다면 해제한다. if (pDevExt->deviceBuffer != NULL) { ExFreePool(pDevExt->deviceBuffer); pDevExt->deviceBuffer = NULL; pDevExt->deviceBufferSize = 0; } xferSize = pIrpStack->Parameters.Write.Length; // 이 예제에서는 디바이스가 Buffered I/O를 사용하고 있다고 가정 userBuffer = pIrp->AssociatedIrp.SystemBuffer; Ch 7. Driver Dispatch Routines
Code Example : 루프백 디바이스(2) Write 요청에 대한 디스패치루틴(2) pDevExt->deviceBuffer = ExAllocatePool( PagedPool, xferSize ); if (pDevExt->deviceBuffer == NULL) { // 버퍼 할당에 실패 status = STATUS_INSUFFICIENT_RESOURCES; xferSize = 0; } else { // 버퍼를 복사 pDevExt->deviceBufferSize = xferSize; RtlCopyMemory( pDevExt->deviceBuffer, userBuffer, xferSize ); } // 디바이스 동작을 수행하지 않고 IRP를 완료한다. pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = xferSize; // bytes xfered IoCompleteRequest( pIrp, IO_NO_INCREMENT ); return status; } Ch 7. Driver Dispatch Routines
Code Example : 루프백 디바이스(3) Read 요청에 대한 디스패치루틴(1) NTSTATUS DispatchRead (IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; PVOID userBuffer; ULONG xferSize; // 스택 로케이션은 사용자 버퍼의 정보를 가지고 있다. PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation( pIrp ); // 현재 버퍼의 포인터는 디바이스 객체 내에 포함된 DEVICE_EXTENSION 안에 저장 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension; xferSize = pIrpStack->Parameters.Read.Length; userBuffer = pIrp->AssociatedIrp.SystemBuffer; // 사용자의 요청에 대해 더 이상 전송을 수행하지 않는다. xferSize = (xferSize < pDevExt->deviceBufferSize) ? xferSize : pDevExt->deviceBufferSize; Ch 7. Driver Dispatch Routines
Code Example : 루프백 디바이스(4) Read 요청에 대한 디스패치루틴(1) // 현재 버퍼를 사용자 공간으로 복사한다. RtlCopyMemory( userBuffer, pDevExt->deviceBuffer, xferSize ); // 현재 paged 풀 버퍼를 해제한다. ExFreePool( pDevExt->deviceBuffer ); pDevExt->deviceBuffer = NULL; pDevExt->deviceBufferSize = 0; // 그리고 I/O 요청을 완료한다. pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = xferSize; IoCompleteRequest( pIrp, IO_NO_INCREMENT ); return status; } Ch 7. Driver Dispatch Routines
Code Example : 루프백 디바이스(5) Ch 7. Driver Dispatch Routines
디스패치 인터페이스의 확장 • read/write 동작 이외에 다른 동작 (예 : 디스크 포맷, 파티션) • IRP_MJ_DEVICE_CONTROL - IoControl(IOCTL 디바이스 컨트롤 값) • IRP_MJ_INTERNAL_DEVICE_CONTROL - 커널 모드에서의 확장 - IRP_MJ_DEVICE_CONTROL와 유사 Ch 7. Driver Dispatch Routines
개별적인 IOCTL 값에 대한 정의(1) 0 31 디바이스 타입 요청된 접근 컨트롤 코드 전송 타입 <IOCTL 코드 구조체의 레이아웃> Ch 7. Driver Dispatch Routines
개별적인 IOCTL 값에 대한 정의(2) <CTL_CODE 매크로 인자> Ch 7. Driver Dispatch Routines
IOCTL 인자 전달 방법 • CTL_CODE의 TransferType(2bit) • METHOD_BUFFERED • METHOD_IN_DIRECT • METHOD_OUT_DIRECT • METHOD_NEITHER Ch 7. Driver Dispatch Routines
IOCTL 헤더 파일 작성하기 #define IOCTL_MISSLEDEVICE_AIM CTL_CODE( FILE_DEVICE_UNKNOWN, 0x801, METHOD_BUFFERED, FILE_ACCESS_ANY) // IOCTL_MISSLEDEVICE_AIM 에서 사용된 구조체 Typedef struct _AIM_IN_BUFFER { ULONG Longitude; ULONG Latitude; } AIM_IN_BUFFER, *PAIM_IN_BUFFER; Typedef struct _AIM_OUT_BUFFER { ULONG ExtendedStatus; } AIM_OUT_BUFFER, *PAIM_OUT_BUFFER; #define IOCTL_MISSLEDEVICE_LAUNCH CTL_CODE( \ FILE_DEVICE_UNKNOWN, \ 0x802, \ METHOD_NEITHER, \ FILE_ACCESS_ANY) Ch 7. Driver Dispatch Routines
IOCTL 요청의 처리(1) • I/O 관리자가 아닌 드라이버의 책임 NTSTATUS DispatchIoControl(IN PDEVICE_OBJECT pDO, IN PIRP pIrp) { NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION pDE; PVOID userBuffer; ULONG inSize; ULONG outSize; ULONG controlCode; // IOCTL 요청 변수 // 스택 로케이션은 사용자 버퍼의 정보를 담고 있다. PIO_STACK_LOCATION pIrpStack; pIrpStack = IoGetCurrentIrpStackLocation(pIrp); // IOCTL 요청을 뽑아낸다. controlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; // 그리고 요청된 전송 크기도 알아낸다. inSize = pIrpStack->Parameters.DeviceIoControl.InputBufferLength; outSize = pIrpStack->Parameters.DeviceIoControl.OutputBufferLength; Ch 7. Driver Dispatch Routines
// // 두 번째 Switch 문을 구현한다. switch (controlCode) { case IOCTL_MISSLEDEVICEAIM : // 각 case에서 항상 매개변수의 유효함을 확인하다. if(inSize < sizeof(AIM_IN_BUFFER) || outSize < sizeof(AIM_OUT_BUFFER)) { status = STATUS_INVALID_BUFFER_SIZE; break; } // 유효한 IRP이므로 디바이스를 구동한다. IoMarkIrpPending(pIrp); IoStartPacket(pDO, pIrp, 0, NULL); return STATUS_PENDING; case IOCTL_DEVICE_LAUNCH : if(inSize > 0 || outSize >0) // 진짜 에러는 아니다. 그러나 호출자에게 해당 호출의 //목적에 대해 다시 생각하도록 주의를 준다. { status = STATUS_INVALID_PARAMETER; break; } // 디바이스를 구동하게 하는 동일한 코드를 넣는다. // : return STATUS_PENDING; default : // 드라이버에서 인식할 수 없는 요청을 받았다. status = STATUS_INVALID_DEVICE_REQUEST; break; } IOCTL 요청의 처리(2) Ch 7. Driver Dispatch Routines
IOCTL 요청의 처리(3) // 유효한 컨트롤 코드의 경우는 상위 계층으로 리턴된다. // 이 부분에서의 실행은 에러가 발생했을 경우이다. // IRP 요청을 실패 처리한다. pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = 0; //아무런 데이터가 없음을 지정한다. IoCompleteRequest(pIrp, IO_NO_INCREMENT); return status; } Ch 7. Driver Dispatch Routines
IOCTL 버퍼 관리 • 독립적인 버퍼 전송 메커니즘 • 입력과 출력의 2개의 버퍼 • METHOD_BUFFERED • METHOD_IN_DIRECT • METHOD_OUT_DIRECT • METHOD_NEITHER Ch 7. Driver Dispatch Routines
테스트 과정 • 디바이스의 핸들을 제대로 열고 닫을 수 있는지 • 비록 데이터의 전송은 수행하지 않는다 하더라도, Win32 I/O 함수의 호출이 성공적인지 • 다수의 I/O요청에 대해 정상동작을 하는지 Ch 7. Driver Dispatch Routines
예제 테스트 프로그램 #include <windows.h> #include <stdio.h> Void main() { HANDLE hDevice; BOOL status; hDevice = CreateFile(\\\\.\\LBK1 ...); : status = ReadFile(hDevice, ...); : status = WriteFile(hDevice, ...); : status = DeviceIoControl(hDevice, ...); : status = CloseHandle(hDevice, ...); } <Win32 콘솔 테스트 프로그램> Ch 7. Driver Dispatch Routines
예제 테스트 프로그램 결과 Ch 7. Driver Dispatch Routines
Summary • 드라이버 디스패치 루틴은 드라이버와 I/O 요청자 간의 인터페이스를 제공한다. • 드라이버는 읽기, 쓰기 그리고 디바이스 I/O 컨트롤을 지원한다. Ch 7. Driver Dispatch Routines