Driver Entry
윈도우 드라이버를 리버스 엔지니어링할 때 우선적으로 살펴보아야 할 부분 중 하나는
DriverEntry가 되겠다. DriverEntry는 드라이버가 로드될 때 처음 실행되는 함수이며
항상 프로그램의 시작점이 되는 main 함수부터 살펴보듯이 커널 드라이버도 DriverEntry부터 살펴보면서 드라이버 초기화 과정과 주요 핸들러 등록 (dispatch table) , 드라이버가 어떤 기능을 하는지, 어떤 IOCTL 코드나 함수 포인터를 설정하는지 확인할 수 있다.
악성 드라이버의 경우 DriverEntry에서 보안 소프트웨어를 우회하거나 SSDT(System Service Descriptor Table) 후킹을 수행 할 수도 있기 때문에
초기화 과정을 살펴볼 필요가 있다.
마침 동적으로 DriverEntry를 수동으로 찾는 방법에 대해 기술한 블로그 포스트가 있어
소개하면서 어떻게 커널에서 DriverEntry 가 실행되는지 작성하려 한다.
https://shhoya.github.io/driverentry.html
NtLoadDriver
테스트 환경은 Windows 10, 19045 기준으로 작성되었다.
우선적으로 유저모드에서 드라이버 로딩하기 위해선 Service Control Manager (SCM)에 해당 드라이버의 경로와 설정을 등록하는 과정이 필요한데 CreateService 또는 RegCreateKeyEx를 통해서 드라이버의 레지스트리를 등록한다.
드라이버가 등록되면 유저모드에서 드라이버를 실제로 로드하기 위해 NtLoadDriver를 호출한다.
NtLoadDriver은 내부적으로 IopLoadDriver 함수를 호출하여 드라이버를 메모리로 로드하는 작업을 수행한다.
이후 드라이버가 커널에 정상적으로 로드되면 커널은 드라이버의 DriverEntry 함수를 호출하여 초기화 작업을 수행한다.
드라이버를 로드하는 데 사용되는 루틴을 봤을 때 NtLoadDriver를 우선적으로 살펴봐야 할 필요가 있다.
NtLoadDriver는 내부적으로 IopLoadDriver를 호출하여 드라이버를 로딩하기 때문에
IopLoadDriver 함수에 bp를 걸고 Trace 하며 호출 루틴들을 확인할 수 있는데
IopLoadDriver -> DriverEntry까지 호출 루틴을 정리하면 다음과 같다.
IopLoadDriver
nt!NtQueryKey
nt!IopVerifierExAllocatePool
nt!NtQueryKey
nt!IopVerifierExAllocatePool
nt!memcpy
nt!RtlAppendUnicodeToString
nt!HeadlessKernelAddLogEntry
nt!PnpDiagnosticTraceObject
nt!IopBuildFullDriverPath
nt!IopGetDriverNameFromKeyNode
nt!ExAcquireResourceExclusiveLite
nt!MmLoadSystemImage
nt!RtlImageNtHeader
nt!PnpPrepareDriverLoading
nt!ObCreateObject
nt!memset
nt!RtlImageNtHeader
nt!ObInsertObject
nt!ExReleaseResourceLite
nt!ObReferenceObjectByHandle
nt!ZwClose
nt!IopVerifierExAllocatePool
nt!memcpy
nt!IopVerifierExAllocatePool
nt!NtQueryObject
nt!IopVerifierExAllocatePool
nt!memcpy
nt!PnpDiagnosticTraceObject
nt!VfDifCaptureDriverEntry
nt!PnpCallDriverEntry
ㄴ guard_dispatch_icall
guard_dispatch_icall + 0x71
lfence
jmp rax ; rax = DriverEntry
guard_dispatch_icall + 0x71에서 jmp rax 명령을 통해 DriverEntry가 실행된다.
guard_dispatch_icall + 0x71에서 jmp rax 명령을 통해
제작한 HelloWorld.sys의 DriverEntry 실행까지 도달한 모습을 확인할 수 있다.
크게 NtLoadDriver -> IopLoadDriver -> PnpCallDriverEntry -> _guard_dispatch_icall 루틴으로 로딩되어 DriverEntry가 실행된다.
'C , C++ > Kernel' 카테고리의 다른 글
Hyper-V windbg 원격 디버깅 설정 (0) | 2025.03.09 |
---|---|
Windows Driver 개발환경 셋팅과 간단한 디버그 프린트 출력 (0) | 2024.07.02 |