cgy12306
Windows Driver 본문
디바이스 드라이버
-
운영체제가 키보드, 마우스, 디스크, LAN카드 같은 디바이스 등과 상호작용할 수 있게 한 S/W입니다.
-
디바이스 드라이버는 드라이버 공급업체에 의해 작성되어 제공되고 있습니다.(서드파티)
-
storahci라는 드라이버는 메모리에 디바이스 객체(Device Object)라는 자료구조를 만듭니다.
-
디바이스 객체는 디바이스 드라이버가 직접 생성하며, 디바이스에 관여하고 있다는 의미입니다.
-
필터 드라이버, 기능 드라이버, 버스 드라이버 세개를 생성하는데 이 세개를 묶어 디바이스 스택이라고 부릅니다.
-
가장 먼저 버스 드라이버가 디바이스를 발견하면 디바이스 객체를 생성하고, 이후 기능 드라이버, 필터 드라이버 순으로 생성하게 됩니다.

-
애플리케이션은 디바이스 드라이버에 접근을 시도하는데 이때 필터 드라이버를 가장 먼저 접근하게 됩니다.
-
드라이버 객체(Driver Object)는 드라이버가 로드될 시 I/O manager가 드라이버 객체를 생성하게 됩니다.
typedef struct _DRIVER_OBJECT {
CSHORT Type;
CSHORT Size;
PDEVICE_OBJECT DeviceObject;
ULONG Flags;
PVOID DriverStart;
ULONG DriverSize;
PVOID DriverSection;
PDRIVER_EXTENSION DriverExtension;
UNICODE_STRING DriverName;
PUNICODE_STRING HardwareDatabase;
PFAST_IO_DISPATCH FastIoDispatch;
PDRIVER_INITIALIZE DriverInit;
PDRIVER_STARTIO DriverStartIo;
PDRIVER_UNLOAD DriverUnload;
PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;
드라이버 오브젝트 중 디스패치 루틴 MajorFunction이 존재합니다.

14번째인 IRP_MJ_DEVICE_CONTROL을 통해 user는 커널과 통신을 할 수 있게 됩니다.
이 DispatchDeviceControl 루틴은 I/O control codes(IOCTL)로 어떤 루틴을 수행할지 구분합니다.
User는 커널과 통신하기 위해 IOCTL, Inputbufferlength, Outputbufferlength, systembuffer 등을 포함한 IRP 패킷을 보내게 됩니다.
IRP_MJ_DEVICE_CONTROL은 입력을 드라이버에게 전달되는 방식이 여러가지가 있습니다.
-
METHOD_BUFFERD
METHOD_BUFFERED 방식은 Inputbuffer와 Outputbuffer를 모두 SystemBuffer를 통해 전달하거나 전달 받습니다.#define IOCTL (ULONG) CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS) ... inputbuffer = pIrp->AssociatedIrp.SystemBuffer; outputbuffer = pIrp->AssociatedIrp.SystemBuffer; ... RtlZeroMemory(outputbuffer, inputbufferlength); RtlCopyMemory(outputbuffer, H, strlen(H)); pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = strlen(H);
Inputbuffer는 애플리케이션 측에서 드라이버에게 전달할 내용이 들어있는 버퍼이고, Outputbuffer는 드라이버 측에서 애플리케이션 측으로 전달할 내용을 담을 버퍼입니다. 이 두개의 버퍼를 Systembuffer 하나만 이용해서 통신을 하는게 불가능 해보이지만 가능합니다.
DeviceIoControl() 함수가 호출될 때 InputBuffer에 담겨진 내용을 우선 Systembuffer로 복사한 후 드라이버를 호출합니다. 그러면 드라이버는 Systembuffer에 담겨진 내용이 Inputbuffer와 동일하기 때문에 Systembuffer를 읽음으로써 Inpubuffer의 목적을 달성하게 됩니다. 그런 다음, 드라이버는 반대로 사용자에게 전달할 내용을 Systembuffer에 기록합니다. 그러면 I/O manager는 애플리케이션 측으로 돌아가는 과정 중에 Systembuffer에 들어있는 내용을 Outputbuffer로 복사를 합니다. 즉, 순서를 정해서 먼저 Inputbuffer를 처리하고 Outputbuffer를 처리하는 순서로 Systembuffer를 사용하게 됩니다. -
METHOD_NEITHER
#define IOCTL (ULONG) CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_NEITHER, FILE_ANY_ACCESS) ... inputbuffer = irpstack->Parameters.DeviceIoControl.Type3InputBuffer; outputbuffer = pIrp->UserBuffer; ... RtlZeroMemory(outputbuffer, inputbufferlength); RtlCopyMemory(outputbuffer, H, strlen(H)); pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = strlen(H);
METHOD_NEITHER 방식은 METHOD_BUFFERED 방식과는 달리 Type3InputBuffer로 Inputbuffer를 받아오고 UserBuffer를 통해 Outputbuffer를 반환합니다.
드라이버 코딩을 할 때 가장 애먹었던 부분이 Outputbuffer가 출력이 안되었던 문제였습니다.
Outputbuffer를 출력하기 위해서는 pIrp->IoStatus.Information의 값을 Outputbuffer의 길이만큼을 주어야 반환이 됩니다.
다시 돌아와서 user는 IRP 통신을 하기 위해서는 DispatchDeviceControl 루틴을 타야하는데 위에서 잠깐 나왔던 DeviceIoControl을 통해 루틴을 탈 수 있고 이로 인해 커널과 통신이 가능하게 됩니다.
BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);