WinKMDLoader - 윈도우 커널 드라이버 로드 & 언로드를 편하게 해 보자!

rxndev·2023년 12월 8일

WinKMDLoader

Index

  1. 개요
  2. 개발 및 테스트 환경
    2-1. 개발 환경
    2-1. 테스트 환경
  3. 기능
  4. 주의 사항
  5. 사용법
    5-1. 테스트 모드 설정
    5-2. 커맨드 라인에서 사용
    5-3. Visual Studio에서 사용
  6. 구현 상세
    6-1. 진입점
    6-2. Loader 프로세스
    6-3. Unloader 프로세스
  7. 개발자 코멘트
전체 소스 코드는 GitHub Repository에 공개되어 있습니다.

개요

WinKMDLoader(Windows Kernel Mode Driver Loader)는 Windows의 번거로운 커널 드라이버 로드, 언로드 및 서비스 생성, 제거 작업에 대한 자동화를 제공하는 간단한 도구입니다.

커맨드 라인으로 실행하거나, 대상 Visual Studio 커널 드라이버 프로젝트의 디버깅 설정을 통해 개발 중 F5를 눌러 대상 커널 드라이버를 즉시 로드할 수 있습니다.

프로그램을 사용자가 종료하면, 대상 커널 드라이버가 자동으로 언로드되고 서비스가 제거됩니다.


개발 및 테스트 환경

개발 환경

C# 10.0 .NET Framework 4.8.1 Visual Studio 2022

테스트 환경

Windows 10 22H2 19045.3693 x64 Visual Studio 2022


기능

  • 커널 드라이버 서비스 생성

  • 커널 드라이버 로드

  • 프로그램 종료 시 커널 드라이버 자동 언로드

  • 프로그램 종료 시 커널 드라이버 서비스 자동 제거


주의 사항

  • 커널 드라이버 서비스를 제어하기 위해 관리자 권한이 필요합니다.

  • 안전하지 않은 코드가 작성된 커널 드라이버를 무심코 로드하여 BSOD를 발생시키는 불상사가 없도록 합시다.

커널 드라이버 개발 시 코드의 모든 동작에 대한 호환성과 안정성을 검증하는 습관은 필수입니다.

사용법

테스트 모드 설정

대상 드라이버가 서명되어 있다면 이 과정은 건너뜁니다.

서명되지 않은 드라이버를 Windows에 로드하려면, 테스트 모드가 활성화되어 있어야 합니다.

다음 커맨드 라인을 실행한 후 PC를 재부팅 하여 테스트 모드를 활성화하거나 비활성화할 수 있습니다.

테스트 모드 활성화

bcdedit /set testsigning on

테스트 모드 비활성화

bcdedit /set testsigning off

커맨드 라인에서 사용

WinKMDLoader.exe <DRIVER_FILE_PATH>
  • DRIVER_FILE_PATH : 대상 드라이버 파일(.sys)의 경로입니다. 상대 경로가 지정된 경우 자동으로 내부에서 절대 경로로 변환하여 사용합니다.

커맨드 라인 예시

WinKMDLoader.exe TestDriver.sys
WinKMDLoader.exe "C:\TestDriver.sys"

Visual Studio에서 사용

Visual Studio 커널 드라이버 프로젝트 속성 > 구성 속성 > 디버깅으로 이동하여 아래 값을 설정합니다.

  • 시작할 디버거 : 로컬 Windows 디버거를 선택합니다.

  • 명령 : WinKMDLoader.exe절대 경로를 입력합니다.

별도의 전역 환경변수 설정 등의 작업을 거친다면 WinKMDLoader.exe만 입력해도 작동합니다.
  • 명령 인수 : "$(TargetPath)"를 입력합니다.
대상 커널 드라이버 파일의 경로에 공백 문자가 존재하는 경우를 위해 큰따옴표를 포함하여 입력합니다.

설정 예시

이제 코드 편집기에서 F5(디버깅 시작)를 누르면 해당 커널 드라이버의 서비스가 생성된 후 커널 드라이버가 로드되고, 디버깅을 중지하면 해당 커널 드라이버가 언로드된 후 커널 드라이버 서비스가 제거됩니다.

WinKMDLoader는 커널 드라이버 디버깅 기능이 없습니다. 위 설정으로 Visual Studio 디버거로 사용한다고 하여도 디버깅이 수행되는 것은 아닙니다.

구현 상세

전체 소스 코드는 GitHub Repository에 공개되어 있습니다.

진입점

private static void Main(string[] args)
{
	// ...
    
    if (args.Length == 1)
    {
        LoaderEntry(args[0]);
    }
    else if (args.Length == 3 && args[0].Equals("-unloader", StringComparison.CurrentCultureIgnoreCase) && int.TryParse(args[1], out int loaderProcessId))
    {
        UnloaderEntry(loaderProcessId, args[2]);
    }
    
    // ...
}

프로그램이 실행되면 Loader 프로세스로 실행할지, Unloader 프로세스로 실행할지 파라미터로 판단합니다.

5-2. 커맨드 라인에서 사용, 5-3. Visual Studio에서 사용과 같은 방법으로 커널 드라이버 파일의 경로를 파라미터로 전달하면 Loader 프로세스로 실행됩니다.

Loader 프로세스

private static void LoaderEntry(string driverFilePath)
{
	// ...
    
    CreateUnloaderProcess(driverName);
    
    // ... 
    
    if (ExecuteBackgroundProcess("sc", $"create \"{driverName}\" type= kernel binpath= \"{driverFileAbsolutePath}\"") != 0)
    {
        ExitWithError("Failed to create service.");
    }

    Console.WriteLine("Service created successfully.");

    if (ExecuteBackgroundProcess("sc", $"start \"{driverName}\"") != 0)
    {
        ExitWithError("Failed to start service.");
    }

    Console.WriteLine("Service started successfully.");

    while (true)
    {
        Console.ReadKey(true);
    }
}

private static void CreateUnloaderProcess(string driverName)
{
    using Process currentProcess = Process.GetCurrentProcess();
    ExecuteBackgroundProcess(Assembly.GetExecutingAssembly().Location, $"-unloader {currentProcess.Id} {driverName}", false);
}

Loader 프로세스로 실행되면, 현재 Loader 프로세스가 종료되었을 때 자동으로 대상 커널 드라이버를 언로드하고 커널 드라이버 서비스를 제거해 줄 Unloader 프로세스를 새로 생성하여 실행합니다. (CreateUnloaderProcess)

파라미터로 전달받은 드라이버 파일 경로의 검증 및 변환이 성공했다면, Windows 시스템 구성 요소인 sc.exe 파일을 통해 커널 드라이버 서비스를 생성한 후 커널 드라이버를 로드합니다.

모든 과정이 성공했다면, 사용자가 직접 프로그램을 닫을 때까지 대기합니다.

Unloader 프로세스

private static void UnloaderEntry(int loaderProcessId, string driverName)
{
    using Process loaderProcess = Process.GetProcessById(loaderProcessId);
    loaderProcess.WaitForExit();
    ExecuteBackgroundProcess("sc", $"stop \"{driverName}\"");
    ExecuteBackgroundProcess("sc", $"delete \"{driverName}\"");
}

Unloader 프로세스로 실행되면, 파라미터로 전달받은 부모 Loader 프로세스가 종료될 때까지 대기합니다.

해당 Loader 프로세스가 종료되면, 대상 커널 드라이버를 언로드한 후 커널 드라이버 서비스를 제거합니다.

profile
Studying C, C++, C#, Windows Kernel and Reversing

0개의 댓글