본문 바로가기
[ ★ ]Study/Windows Device Driver

윈도우 디바이스 드라이버 기초(1)

by nroses-taek 2020. 4. 11.

디바이스 인식 후 드라이버 생기는 순서

1) 버스 드라이버 2) 기능 드라이버 3) 필터 드라이버

3가지가 연속되어 디바이스 스택을 만들고 응용프로그램과 연결한다.

후에 명령을 필터 드라이버로 보내게 된다. 디바이스 스택에 가장 최상위에 있기 때문이다. 즉, 응용프로그램에서 명령을 받고 싶다면 디바이스 스택의 맨 위에 만들면 된다.

 

서비스 프로그램

NT 서비스 프로그램으로 불린다. 로그인, 로그아웃 무관하게 계속 동작할 수 있다. 일반적인 응용프로그램과 달리 GUI를 사용할 수 없다.
디바이스 드라이버는 서비스 프로그램과 같은 형태로 관리된다. 즉, 부팅 과정중에 윈도우 시스템에 자동으로 상주가 가능. 언제든지 메모리에서 제거될 수도 있다.

드라이버의 이중역할

응용 프로그램은 드라이버를 통해서만 디바이스를 인식한다.

1. 응용프로그램에게 서비스를 제공하는 방식
- 마우스 드라이버는 마우스 서비스, 프린터 드라이버는 프린터 서비스를 제공. 각각의 디바이스는 버스 인터페이느(PCIe, USB, l2C, Internal Bus ...) 를 통해 접근한다.

2. 디바이스를 동작시켜서 원하는 결과를 얻어내는 역할.

* 드라이버가 엉뚱한 디바이스를 사용하는 것을 응용 프로그램은 알 수 없다.

드라이버를 개발하려면 어떤 툴이 필요한가?

컴파일러, 링커, 헤더파일, 라이브러리 등

또한, 드라이버 동작을 실시간으로 확인하기 위해서 디버깅환경을 가져야 한다.
ex) 디버그 메시지 출력기 등

설치

Visual Studio(비쥬얼 스튜디오) 설치
- C, C++ 환경이 있어야 한다.

WDK10(Windows Driver Kit 10)
-드라이버를 빌드하는데 사용하는 헤더, 라이브러리, 커널 디버거를 제공한다.
-무조건 비쥬얼 스튜디오를 먼저 설치해야한다.

디버거 메시지 출력기
-DbgView 설치해서 쉽게 확인이 가능하다.

설치가 끝나면 Visual Studio에서 Empty WDM Driver를 찾아서 만들어준다.

Hello World 처럼 간단한 출력 메시지는 어떻게 만들까?
(커널레벨 동작하는 드라이버)

헤더파일 선언

#include <ntddk.h> // NT Legacy Style 드라이버를 개발하려는 경우
- 디바이스 스택을 사용하지 않는 드라이버
- 커널단에서 동작하는 애플리케이션으로 이해
#include <wdm.h> // WDM Style 디바이스 스택을 사용하는 드라이버
- ntddk 보다 세련됨

함수 구현

NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT	DriverObject,
    IN PUNICODE_STRING	RegistryPath
    )
{
	return STATUS_UNSUCCESSFUL;
}

함수 이름은 절대 변경이 불가능하다. 드라이버가 시작되는 곳이기 때문. main과 같은 역할을 한다.
코드 앞에 IN, OUT 등 컴파일에는 참여하지 않다.
IN은 읽어오는 역할, 가져오는 역할을 할 수 있다의 의미.
두 개의 변수는 운영체제가 드라이버에 대한 설명과 레지스트리 설치 위치를 알려준다.
리턴 값은 드라이버가 메모리에 상주하지 않도록 설정한다.

C++ STYLE

extern "C" NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT	DriverObject,
    IN PUNICODE_STRING	RegistryPath
)
{
	return STATUS_UNSUCCESSFUL;
}

extern "C" = C++의 맹글링을 피하기 위해서 반드시 사용해야한다.

NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT	DriverObject,
    IN PUNICODE_STRING	RegistryPath
    )
{
	DbgPrintEx(DPFLTR_IHVDRIVER_ID,0,"Hello World\n");
    //디버그 메시지를 프로그램에 출력
	
	return STATUS_UNSUCCESSFUL;
}

드라이버 빌드

윈도우는 하위 호환성을 지키기 때문에 Windows7으로 해준다. 만약 Windows10에서 만들고 Win7에서 실행시키면 블루스크린을 띄우게 된다. 즉, 상위 호환성은 지켜지지 않는다.

설치파일(.INF) 은 지워야 한다. NT Legacy Style은 간단해서 설치를 제공안한다.

소스파일 생성 후 간단하게 소스코드 적어준다.

#include <ntddk.h>

NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT	DriverObject,
	IN PUNICODE_STRING	RegistryPath
)
{
	//사용하지 않는 파라미터는 에러처리
	UNREFERENCED_PARAMETER(DriverObject); UNREFERENCED_PARAMETER(RegistryPath);

	//디버그 메시지를 프로그램에 출력
	DbgPrintEx(DPFLTR_IHVDRIVER_ID, 0, "Hello World\n");

	return STATUS_UNSUCCESSFUL;
}


드라이버 설치

sc.exe를 통하여 프로그램을 다룰 것.

사용방법(관리자 권한 필요)

sc [command] [servicename] <option>

sc의 대표적인 명령어 5가지

- create : 서비스를 생성하는 명령
- query : 서비스 상태를 확인하는 명령
- start : 서비스를 실행하는 명령 (드라이버가 메모리에 상주)
- stop : 서비스의 동작을 멈추는 명령 (드라이버가 메모리에서 제거)
- delete : 서비스를 제거하는 명령

서비스(드라이버)를 생성해서 사용하고 지울 때 까지의 명령어 사용순서

create -> start -> .... -> stop -> delete

추가로 알아야 할 것

단, 64bit 환경에서는 인증서명이 없으면 사용될 수가 없다.

서비스 이름과 드라이버 이름이 같을 필요는 없지만, 대부분 같은 이름을 사용한다.

보통 WDM Style의 드라이버 파일은 Windows\System32\Drivers 폴더에 존재해야 한다. 하지만 NT Legacy Style 드라이버 파일은 원하는 위치에 존재 가능하다.

sc create helloworld binpath= "c:\경로\helloworld.sys" displayname= "helloworld" start= demand type= kernel

* 공백주의
ex) binpath= "C~~~"   = 기호 다음에 공백이 하나 씩 있다.

여기서 binpath부터는 옵션. 즉 옵션이 binpath, displayname, start, type 총 4가지가 들어가 있음을 알 수 있다.

binpath = 바이너리 위치
displayname = 사용자 한테 보여질 이름
start = 구동 방법
type = 드라이버 서비스 유형

DebugView 에서 이렇게 체크해주자

 

설치파일 사이닝

마이크로 소프트는 드라이버를 제작하는 제조사가 사용자에게 배포할 때 까지 드라이버 코드의 외부적인(불법적인) 수정이 없다는 것을 보증하기 위한 크로스사이닝(Cross Signing)과정을 요구하고 있다.

설치파일을 사용하는 경우와 마찬가지로, 크로스사이닝되지 않은 드라이버는 윈도우 64비트 메모리에 상주할 수 없다.

Test Signing 부팅 환경

윈도우는 BCDEDIT.exe 프로그램을 제공. 이 프로그램은 BCD파일을 관리하는 유틸리티이다. BCD파일은 윈도우가 부팅되는 과정을 제어하는 여러가지 옵션들이 보관된다.

Test Signing 부팅환경을 사용하기 위해서는 BCDEDIT 프로그램을 사용해서 BCD파일을 수정해야 한다. 
TEST SIGNING 환경으로 부팅을 하기 위해서 사용하는 명령어. 각 명령어마다 재부팅이 필요하다.
Bcdedit.exe -set TESTSIGNING ON/OFF

 

'[ ★ ]Study > Windows Device Driver' 카테고리의 다른 글

윈도우 디바이스 드라이버(2)  (0) 2020.04.12

댓글