DriverEntry와 드라이버 오브젝트 — DRIVER_OBJECT 완전 해부
DRIVER_OBJECT 구조체
OS가 드라이버를 로드하면 DRIVER_OBJECT 구조체를 하나 만들어서 DriverEntry에 전달합니다. 이 구조체는 드라이버가 살아있는 동안 계속 존재하면서 드라이버 전체를 대표해요.
typedef struct _DRIVER_OBJECT { CSHORT Type; // 오브젝트 타입 (항상 4) CSHORT Size; PDEVICE_OBJECT DeviceObject; // 이 드라이버가 만든 디바이스 오브젝트 체인 ULONG Flags; PVOID DriverStart; // 드라이버 이미지 시작 주소 ULONG DriverSize; // 드라이버 이미지 크기 PVOID DriverSection; // LDR_DATA_TABLE_ENTRY (모듈 정보) PDRIVER_EXTENSION DriverExtension; UNICODE_STRING DriverName; // "\Driver\MyDriver" 형태 PUNICODE_STRING HardwareDatabase; PFAST_IO_DISPATCH FastIoDispatch; // 빠른 I/O 처리 (파일시스템 드라이버용) PDRIVER_INITIALIZE DriverInit; // DriverEntry 함수 포인터 PDRIVER_STARTIO DriverStartIo; // StartIO 루틴 PDRIVER_UNLOAD DriverUnload; // ← 언로드 루틴 (우리가 등록) PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; // ← 핵심! } DRIVER_OBJECT;여기서 가장 중요한 필드가 바로 MajorFunction 배열이에요.
MajorFunction 배열 — I/O 요청의 라우팅 테이블
MajorFunction은 함수 포인터 배열이에요. 각 인덱스는 I/O 요청의 종류(Major Code)를 나타내고, 해당 인덱스에 등록된 함수가 그 요청을 처리합니다.
| 인덱스 (Major Code) | 의미 | 트리거 시점 |
|---|---|---|
| IRP_MJ_CREATE (0x00) | 파일/디바이스 열기 | CreateFile() 호출 |
| IRP_MJ_CLOSE (0x02) | 핸들 닫기 | CloseHandle() 호출 |
| IRP_MJ_READ (0x03) | 읽기 | ReadFile() 호출 |
| IRP_MJ_WRITE (0x04) | 쓰기 | WriteFile() 호출 |
| IRP_MJ_DEVICE_CONTROL (0x0E) | 디바이스 제어 명령 | DeviceIoControl() 호출 |
| IRP_MJ_PNP (0x1B) | 플러그앤플레이 이벤트 | 장치 연결/제거 등 |
| IRP_MJ_POWER (0x16) | 전원 관리 | 절전/복귀 등 |
DriverEntry에서 이 배열에 함수를 등록해두면, 앱이 CreateFile로 디바이스를 열거나 DeviceIoControl로 명령을 보낼 때 해당 함수가 호출됩니다. 마치 웹 서버의 URL 라우팅 테이블과 비슷한 개념이에요.
실용적인 DriverEntry 패턴
실제로는 이렇게 씁니다:
#include <ntddk.h> #define DEVICE_NAME L"\\Device\\MyDevice" #define SYMLINK_NAME L"\\DosDevices\\MyDevice" VOID DriverUnload(_In_ PDRIVER_OBJECT DriverObject) { UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYMLINK_NAME); IoDeleteSymbolicLink(&symLink); IoDeleteDevice(DriverObject->DeviceObject); DbgPrint("[MyDriver] Unloaded.\n"); } // IRP_MJ_CREATE / IRP_MJ_CLOSE 처리 NTSTATUS DispatchCreateClose( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) { UNREFERENCED_PARAMETER(DeviceObject); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry( _In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); NTSTATUS status; PDEVICE_OBJECT deviceObject = NULL; UNICODE_STRING devName = RTL_CONSTANT_STRING(DEVICE_NAME); UNICODE_STRING symLink = RTL_CONSTANT_STRING(SYMLINK_NAME); // 1. 디바이스 오브젝트 생성 status = IoCreateDevice( DriverObject, 0, // DeviceExtension 크기 (나중에 배워요) &devName, FILE_DEVICE_UNKNOWN, // 디바이스 타입 FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { DbgPrint("[MyDriver] IoCreateDevice failed: 0x%08X\n", status); return status; } // 2. 심볼릭 링크 생성 (유저모드에서 \\.\MyDevice로 열 수 있게) status = IoCreateSymbolicLink(&symLink, &devName); if (!NT_SUCCESS(status)) { IoDeleteDevice(deviceObject); return status; } // 3. MajorFunction 등록 DriverObject->MajorFunction[IRP_MJ_CREATE] = DispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = DispatchCreateClose; DriverObject->DriverUnload = DriverUnload; DbgPrint("[MyDriver] Loaded successfully.\n"); return STATUS_SUCCESS; }심볼릭 링크가 왜 필요한가요?
\Device\MyDevice는 커널 네임스페이스에만 있어서, 유저 모드 앱이 직접 열 수 없어요. \DosDevices\MyDevice(유저 모드에서는 \\.\MyDevice)로 심볼릭 링크를 만들어야 앱에서 CreateFile("\\\\.\\MyDevice", ...)로 열 수 있습니다.
유저 모드 앱에서 드라이버 열기
// 유저 모드 앱 (user32 기반 일반 C 코드) HANDLE hDevice = CreateFile( L"\\\\.\\MyDevice", // 심볼릭 링크 이름 GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hDevice == INVALID_HANDLE_VALUE) { printf("Failed to open device: %d\n", GetLastError()); return 1; } // 이 순간 드라이버의 IRP_MJ_CREATE 핸들러가 호출됩니다! CloseHandle(hDevice); // IRP_MJ_CLOSE 핸들러 호출DeviceExtension — 드라이버만의 개인 공간
IoCreateDevice의 두 번째 파라미터가 DeviceExtensionSize인데, 이게 뭔지 궁금하셨을 거예요. DeviceExtension은 드라이버가 디바이스 오브젝트에 붙여놓는 사설 데이터 공간이에요. 드라이버가 유지해야 하는 상태, 설정 등을 여기에 저장합니다.
// DeviceExtension 구조체 정의 typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT DeviceObject; UNICODE_STRING DeviceName; BOOLEAN IsRunning; LONG RequestCount; // ... 드라이버에 필요한 데이터 } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // IoCreateDevice 호출 시 status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), // 이 크기만큼 NonPaged Pool에 할당됨 &devName, ... ); // 이후 접근 PDEVICE_EXTENSION ext = (PDEVICE_EXTENSION)deviceObject->DeviceExtension; ext->IsRunning = TRUE;- DRIVER_OBJECT는 드라이버의 신분증으로, OS가 만들어서 DriverEntry에 전달합니다.
- MajorFunction 배열에 I/O 종류별 처리 함수를 등록하는 것이 DriverEntry의 핵심 작업이에요.
- IoCreateDevice로 디바이스 오브젝트를, IoCreateSymbolicLink로 유저 모드 접근 경로를 만듭니다.
- DeviceExtension은 드라이버가 디바이스별로 유지할 사설 데이터 공간이에요.
#DRIVER_OBJECT #MajorFunction #IoCreateDevice #DeviceExtension #DriverEntry
'Programming > 7. Device Driver' 카테고리의 다른 글
| 드라이버 개발 핵심 - 13화 IRP 디스패치 루틴 구현 (0) | 2026.06.01 |
|---|---|
| 드라이버 개발 핵심 - 11화 디바이스 오브젝트와 디바이스 스택 (0) | 2026.05.29 |
| 드라이버 개발 핵심 - 9화 첫 번째 커널 드라이버 (0) | 2026.05.27 |
| Windows 아키텍처 기초 - 8화 개발 환경 구축 (0) | 2026.05.26 |
| Windows 아키텍처 기초 - 7화 Windows I/O 시스템 개관 (0) | 2026.05.25 |