D:\Dev> sc create reversedrv binPath= D:\Dev\redrv.sys type= kernel start= demand
[SC] CreateService SUCCESS
D:\Dev> sc start reversedrv
SERVICE_NAME: reversedrv
TYPE : 1 KERNEL_DRIVER
STATE : 4 RUNNING
(STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
PID : 0
FLAGS :
D:\Dev> sc stop reversedrv
SERVICE_NAME: reversedrv
TYPE : 1 KERNEL_DRIVER
STATE : 1 STOPPED
(NOT_STOPPABLE,NOT_PAUSABLE,IGNORES_SHUTDOWN)
WIN32_EXIT_CODE : 0 (0x0)
SERVICE_EXIT_CODE : 0 (0x0)
CHECKPOINT : 0x0
WAIT_HINT : 0x0
D:\Dev> sc delete reversedrv
[SC] DeleteService SUCCESS
D:\Dev>
에러코드(오류 조회 툴로 살펴볼 수 있음)
129 - %1 응용 프로그램은 Win32 모드에서 실행될 수 없습니다.
1056 - 서비스의 인스턴스가 이미 실행 중입니다.
1060 - 지정된 서비스가 설치된 서비스로는 없습니다.
kd> !memusage
loading PFN database
loading (100% complete)
Compiling memory usage data (99% Complete).
Zeroed: 36645 (146580 kb)
Free: 0 ( 0 kb)
Standby: 36638 (146552 kb)
Modified: 532 ( 2128 kb)
ModifiedNoWrite: 0 ( 0 kb)
Active/Valid: 57142 (228568 kb)
Transition: 0 ( 0 kb)
Bad: 0 ( 0 kb)
Unknown: 0 ( 0 kb)
TOTAL: 130957 (523828 kb)
Building kernel map
Finished building kernel map
Invalid PTE framebase - (41% complete)
PFN 0000D581 at address 811E1A1C
flink 00000000 blink / share count 00000001 pteaddress 0006AC09
reference count 0001 Cached color 0
restore pte 00000080 containing page FFEDCB Active RW
ReadInProgress WriteInProgress
PFN 00FFEDCB at address 9D04C234
flink 00000000 blink / share count 00000000 pteaddress 00000000
reference count 0000 NonCached color 0
restore pte 00000000 containing page 000000 Zeroed
... 생략 ...
subsection address: 00000000
Invalid PTE framebase - (42% complete)
PFN 0000D8C0 at address 811E7500
flink 00000000 blink / share count 00000001 pteaddress 0006C601
reference count 0001 Cached color 0
restore pte 00000080 containing page FFEDCB Active RW
ReadInProgress WriteInProgress
PFN 00FFEDCB at address 9D04C234
flink 00000000 blink / share count 00000000 pteaddress 00000000
reference count 0000 NonCached color 0
restore pte 00000000 containing page 000000 Zeroed
subsection address: 00000000
Scanning PFN database - (99% complete)
Usage Summary (in Kb):
Control Valid Standby Dirty Shared Locked PageTables name
82356568 0 120 0 0 0 0 mapped_file( mxdwdui.dll )
825828e8 0 824 0 0 0 0 No Name for File
82463290 0 64 0 0 0 0 mapped_file( fltmgr.sys )
824d32f0 356 128 0 312 0 0 mapped_file( wininet.dll )
8257a870 696 6576 0 0 0 0 mapped_file( $LogFile )
82174cf8 0 28 0 0 0 0 mapped_file( msvidc32.dll )
...생략...
820ba3b0 0 8 0 0 0 0 mapped_file( msdirectx.sys )
8245c8e8 0 4 0 0 0 0 No Name for File
8245e680 0 4 0 0 0 0 No Name for File
82163b88 0 4 0 0 0 0 mapped_file( CollectedData_57.xml )
82373520 0 16 0 0 0 0 mapped_file( CONIME.EXE-13EEEA1A.pf )
...생략...
-------- 1724 0 0 ----- 0 ----- driver ( ntoskrnl.exe )
-------- 112 0 0 ----- 0 ----- driver ( hal.dll )
...생략...
-------- 1560 432 468 ----- 0 ----- driver ( Kernel Stacks )
-------- 29836 12 12 ----- 0 ----- driver ( NonPaged Pool )
빨간색 표시된 드라이버가 숨겨진 드라이버이다
최근 이상한 버그를 하나 잡았다.
작업한 라이브러리가 적용된 프로그램에서 랜덤한 시간에 프로그램이 잠깐(3~10초) 멈추는 현상이
간헐적으로 발생한 것이다. 전혀 발생을 안하는 PC도 있었지만 몇몇 PC에서 발생 해서 원인을 파악에
감을 잡을수 없었다.
개발한 라이브러리의 기능을 하나씩 제거하면서 테스트해 보았지만, 프로그램 멈춤 현상은
간헐적으로 계속 발생을 했다.
보안 솔루션과의 충돌일거라 생각해서 보안 솔루션을 하나씩 제거해서 테스트해 보았지만 원인을 찾지 못했다.
다시 시스템을 원래 상태로 복원시키고 테스트 도중 프로그램만 멈추는 것이 아니라 시스템 전체가 멈춘다는 것을 알게 되었다.
프로세스 익스플로러의 CPU 그래프를 실행하고, 멈춤현상시 CPU 사용을 확인하니 커널 CPU타임이 거의 80%를
육박하는 것을 볼 수 있었다. 그런데 이 CPU타임이 특정 프로세스에서 지속적으로 발생하는 것이 아니라
매 회 테스트마다 각기 다른 프로세스들이 커널에서 무언가 시간을 많이 잡아 먹는것이었다.
[Process Explorer의 CPU 시간 그래프]
빨간색 그래프가 커널 시간이고, 녹색 그래프가 유저 영역 코드가 실행시 소비되는 CPU시간을 나타낸다.
그래서 커널에서 어떤 녀석이 CPU시간을 많이 소모한다고 추측하였다.
첫번째 대상은 백신 프로그램이었다. 멈춤현상이 I/O가 많아질때 발생을 했기 때문이었다.
멈춤현상이 발생할때마다 insidewindows.kr의 운영자님이 상콤하게 덤프를 떠 주셨다.
멈춤현상시 스레드를 살펴보면 다음과 같았다.
1: kd> !running -t -i System Processors 3 (affinity mask) Idle Processors 0 Prcbs Current Next 0 ffdff120 88a2c4e8 ................ ChildEBP RetAddr WARNING: Stack unwind information not available. Following frames may be wrong. b4845478 b34a05a9 bug_driver+0x1214d b4845494 b34a0731 bug_driver+0x15a9 b48454c8 b34a3aab bug_driver+0x1731 b48454fc b34a3bcc bug_driver+0x4aab b4845590 b466df70 bug_driver+0x4bcc b48455ac 804e33eb UDSecDrvXP+0x1f70 b48455bc b468d22e nt!IopfCallDriver+0x31 b4845608 b468f6f1 mfehidk+0x522e b484569c b469d367 mfehidk+0x76f1 b48456ac b469d3b7 mfehidk+0x15367 b48456d4 804e33eb mfehidk!DEVICEDISPATCH::DispatchPassThrough+0x48 b48456e4 8057dbed nt!IopfCallDriver+0x31 b48457c4 8056f03b nt!IopParseDevice+0xa12 b484583c 80572358 nt!ObpLookupObjectName+0x53c b4845890 8057e246 nt!ObOpenObjectByName+0xea b484590c 80586cc8 nt!IopCreateFile+0x407 b4845954 b4692e3a nt!IoCreateFileSpecifyDeviceObjectHint+0x52 b48459f0 b4692a72 mfehidk+0xae3a b4845a48 b4637046 mfehidk+0xaa72 b4845a98 b463785e mfeavfk+0x4046 1 f771f120 88aebda8 ................ ChildEBP RetAddr b4941334 ba7127fb nt!KeBugCheckEx+0x1b b4941350 ba712033 i8042prt!I8xProcessCrashDump+0x237 b4941398 804dd90f i8042prt!I8042KeyboardInterruptService+0x21c b4941398 b34b1154 nt!KiInterruptDispatch+0x45 WARNING: Stack unwind information not available. Following frames may be wrong. b4941478 b34a05a9 bug_driver+0x12154 b4941494 b34a0731 bug_driver+0x15a9 b49414c8 b34a3aab bug_driver+0x1731 b49414fc b34a3bcc bug_driver+0x4aab b4941590 b466df70 bug_driver+0x4bcc b49415ac 804e33eb UDSecDrvXP+0x1f70 b49415bc b468d22e nt!IopfCallDriver+0x31 b4941608 b468f6f1 mfehidk+0x522e b494169c b469d367 mfehidk+0x76f1 b49416ac b469d3b7 mfehidk+0x15367 b49416d4 804e33eb mfehidk!DEVICEDISPATCH::DispatchPassThrough+0x48 b49416e4 8057dbed nt!IopfCallDriver+0x31 b49417c4 8056f03b nt!IopParseDevice+0xa12 b494183c 80572358 nt!ObpLookupObjectName+0x53c b4941890 8057e246 nt!ObOpenObjectByName+0xea b494190c 80586cc8 nt!IopCreateFile+0x407
역시 예상한대로 백신프로그램의 드라이버가 동작중(검은색으로 짙은 글씨, mfehidk)이었다.
IoCallDriver를 처리중에 발생을 하는 것이었다.
그래서 백신을 지우고 테스트를 해 보았다. 그런데 백신을 지워도 현상이 재현되는 것이었다.
콜 스택을 조금더 살펴보니 스택의 끝에 다른 보안 모듈(빨간색 글씨, 편의상 bug_driver)이 있는 것을 있는것을 발견했다.
이 녀석도 IoCallDriver가 호출될때 호출되는 녀석인 것이었다.
후킹하고 있는 IoCallDriver를 분석해보니 다음과 같이 IoCallDriver의 pIopfCallDriver 함수포인터의 값을
패치하여 후킹하고 있는 것이었다.
1: kd> u nt!IoCallDriver nt!IoCallDriver: 80534842 8bff mov edi,edi 80534844 55 push ebp 80534845 8bec mov ebp,esp 80534847 8b550c mov edx,dword ptr [ebp+0Ch] 8053484a 8b4d08 mov ecx,dword ptr [ebp+8] 8053484d ff1580d75580 call dword ptr [nt!pIofCallDriver (8055d780)] 80534853 5d pop ebp 80534854 c20800 ret 8 1: kd> dps 8055d780 L4 8055d780 b34a3bc0 dump_wmimmc+0x4bc0 8055d784 804e37da nt!IopfCompleteRequest 8055d788 804ecd58 nt!IopAllocateIrpPrivate 8055d78c 804eceb1 nt!IopFreeIrp
이 보안모듈은 임의로 제거하기가 쉽지가 않기 때문에 bypass를 해보기로 했다. :-(
IoCallDriver에서 내부적으로 함수포인터 pIopfCallDriver을 호출하는 인스트럭션을 다음과 같이 바꾸어 주었다.
8053484d ff1580d75580 call dword ptr [nt!pIofCallDriver (8055d780)]
after
8053484d e872ebfaff call nt!IopfCallDriver (804e33c4)
80534852 90 nop
그리고 다시 테스트해 보았으나, 역시 멈춤현상이 발생되었다.
좌절을 하다가 insidewindows.kr의 운영자님이 IoCallDriver외에 IofCallDriver도 export되는 함수라고 하셨다.
그래서 이번엔 IofCallDriver도 손을 봐주었다.
804e33b9 ff2580d75580 jmp dword ptr [nt!pIofCallDriver (8055d780)]
after
804e33b9 e906000000 jmp nt!IopfCallDriver (804e33c4)
804e33be 90 nop
최종적으로 테스트 해보니 멈춤현상은 발생하지 않고 잘 동작하였다. ^^
라이브러리를 개발한 것이 우리쪽이었고, 이 라이브러리가 적용만 되면 프로그램이 멈춤현상이 발생했기 때문에
우리 라이브러리 때문이라고 의심을 받았다.
단지 라이브러리에 내에서 I/O를 발생시키는 부분이 많기 때문에 적용만 되면, 문제가 되는 보안솔루션에서 CPU를
시간을 많이 소요하는 현상의 빈도가 높았기 때문이었다.
덤프분석을 통한 문제해결이 아니었으면, 빠른 시간내에 해결하기 힘든 케이스였던 것 같다.
앞으로 windbg를 통한 덤프분석 스킬도 필요할 것 같다.
각종 안티루트킷들을 운영자가 테스트하여 결과를 내놓는 사이트인것 같다.
실제 안티루트킷들의 성능을 한눈에 볼 수 있다.
테스트로 사용한 루트킷도 소개하고 있다.
참고:
http://www.ntinternals.org/process_detection_test.php
http://www.ntinternals.org/driver_detection_test.php
http://www.ntinternals.org/dll_detection_test.php

