2010년 6월 11일 금요일

WinDbg 커널모드 환경에서 유저모드 프로세스 디버깅

커널모드 디버깅 환경에서 특정 프로세스를 디버깅 하기위한 방법을 설명 합니다.

 

1. !process

타겟 프로세스의 정보를 얻습니다. 우리에게는 EPROCESS 정보가 필요합니다.

kd> !process 0 0 notepad.exe
PROCESS 8202b328  SessionId: 0  Cid: 042c    Peb: 7ffd5000  ParentCid: 0544
    DirBase: 103cf000  ObjectTable: e1cd7780  HandleCount:  43.
    Image: notepad.exe


2-1. .process /i

얻어온 EPROCESS 주소로 프로세스 컨텍스트를 변경합니다. /i 옵션을 사용하면 프로세스 컨텍스트가 타겟 프로세스로 맞춰지고 디버깅 권한을 가지게 됩니다.

kd> .process /i 8202b328
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

 

2-2. .thread /p /r

스레드 정보를 얻어서 컨텍스를 변경하는 또 다른 방법 입니다. '.f(+-)' 명령어를 사용하면 컨텍트를 자유롭게 넘나들 수 있습니다.

kd> !process 8202b328 2          <===== THREAD 정보획득
PROCESS 8202b328  SessionId: 0  Cid: 0774    Peb: 7ffda000  ParentCid: 0548
    DirBase: 0e2fb000  ObjectTable: e17d64e8  HandleCount:  41.
    Image: notepad.exe

        THREAD 823196b0  Cid 0774.0778  Teb: 7ffdf000 Win32Thread: e1b34390 WAIT: (WrUserRequest) UserMode Non-Alertable
            822c42e0  SynchronizationEvent

kd> .thread /p /r 823196b0
Implicit thread is now 823196b0
Implicit process is now 8228b118
.cache forcedecodeuser done
Loading User Symbols
..........................
kd> kn
  *** Stack trace for last set context - .thread/.cxr resets it
 # ChildEBP RetAddr 
00 f4f9ec38 804dc0f7 nt!KiSwapContext+0x2e
01 f4f9ec44 804dc143 nt!KiSwapThread+0x46
02 f4f9ec6c bf802f45 nt!KeWaitForSingleObject+0x1c2
03 f4f9eca8 bf801b1d win32k!xxxSleepThread+0x192
04 f4f9ecec bf819e81 win32k!xxxRealInternalGetMessage+0x418
05 f4f9ed4c 804de7ec win32k!NtUserGetMessage+0x27
06 f4f9ed4c 7c90e514 nt!KiFastCallEntry+0xf8
07 0007feb8 7e4191be ntdll!KiFastSystemCallRet
08 0007fed8 01002a1b USER32!NtUserGetMessage+0xc
09 0007ff1c 01007511 notepad!WinMain+0xe5
0a 0007ffc0 7c817077 notepad!WinMainCRTStartup+0x174

0b 0007fff0 00000000 kernel32!BaseProcessStart+0x23

kd> .frame 9
09 0007ff1c 01007511 notepad!WinMain+0xe5

kd> .f+
0a 0007ffc0 7c817077 notepad!WinMainCRTStartup+0x174
kd> .f-
09 0007ff1c 01007511 notepad!WinMain+0xe5
kd> .f-
08 0007fed8 01002a1b USER32!NtUserGetMessage+0xc

 

3. press 'g' <enter> => (2-1. 기준으로 설명 합니다)

프로세스 컨텍스트가 맞춰지면 'g'를 눌러 디버거를 실행 시킵니다. 잠시 후 int3 이 발생하면서 타겟 프로세스 디버깅이 가능한 상태가 됩니다.

kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
804e3592 cc              int     3

 

4. !process

타겟 프로세스로 디버깅 제어가 넘어왔는지 확인합니다.

kd> !process
PROCESS 8202b328 SessionId: 0  Cid: 042c    Peb: 7ffd5000  ParentCid: 0544
    DirBase: 103cf000  ObjectTable: e1cd7780  HandleCount:  43.
    Image: notepad.exe
    VadRoot 82119be8 Vads 62 Clone 0 Private 149. Modified 925. Locked 0.
    DeviceMap e15d9ab8
    Token                             e1c74b18
    ElapsedTime                       09:56:46.467
    UserTime                          00:00:00.110
    KernelTime                        00:00:00.931
    QuotaPoolUsage[PagedPool]         33764
    QuotaPoolUsage[NonPagedPool]      2480
    Working Set Sizes (now,min,max)  (129, 50, 345) (516KB, 200KB, 1380KB)
    PeakWorkingSetSize                835
    VirtualSize                       30 Mb
    PeakVirtualSize                   35 Mb
    PageFaultCount                    1009
    MemoryPriority                    BACKGROUND
    BasePriority                      8
    CommitCharge                      241

        THREAD 8227d4f0  Cid 042c.0440  Teb: 7ffdf000 Win32Thread: e1d82008 WAIT: (WrUserRequest) UserMode Non-Alertable
            8213e3e8  SynchronizationEvent

 

5. bp

브레이크 포인트를 설정하고 실제 디버깅이 가능한지 확인해 봅니다. 타겟 프로세스(메모장)에서 파일을 생성할 때 브레이크를 설정하겠습니다.

kd> bp kernel32!CreateFileA
kd> bl
 0 e 7c801a28     0001 (0001) kernel32!CreateFileA

 

6. g

브레이크가 설정된 후 실행(g) 합니다. 타겟 프로세스(메모장)에서 파일저장을 클릭하면 디버거에 브레이크 포인트가 걸리게 됩니다.

kd> g
Breakpoint 0 hit
kernel32!CreateFileA:
001b:7c801a28 8bff            mov     edi,edi

 

[참고 - WinDbg로 쉽게 배우는 Windows Debugging 413 page]

2010년 6월 10일 목요일

DKOM 프로세스 은닉#2

프로세스가 실행되거나 드라이버가 로드되면 커널 오브젝트가 생성되어 메모리에 저장 됩니다. 실제 EPROCESS 구조체의 실체를 따라가 보겠습니다. (Windows XP SP3 영문버전)

 

1. PsGetCurentProcess

PsGetCurrentProcess 함수는 현재 프로세스 컨텍스트 정보를 알려줍니다. 이 함수는 IoGetCurrentProcess 함수와 동일한 주소를 가리킵니다. fs 레지스터는 유저레벨이냐 커널레벨이냐에 따라 각기 다른 정보를 가집니다.

  • User Mode 에서 fs 레지스터는 ... TEB (Thread Environment Block)
  • Kernel Mode 에서 fs 레지스터는 ... KPCR (Kernel's Processor Control Region)
kd> u nt!PsGetCurrentProcess
nt!IoGetCurrentProcess:
804e464f 64a124010000    mov     eax,dword ptr fs:[00000124h]     =====> KPRCB.CurrentThread
804e4655 8b4044          mov     eax,dword ptr [eax+44h]             =====> KPROCESS
804e4658 c3              ret


2. KPCR

KPCR 구조체의 0x124 오프셋 번지는 KPRCB 구조체 범위에 들어갑니다. KPRCB 구조체의 주소(0x120)에서 0x04 오프셋을 더한 번지는 KTHREAD 포인터를 가리키고 있습니다.

kd> dt nt!_KPCR
nt!_KPCR
   +0x000 NtTib            : _NT_TIB
   +0x01c SelfPcr          : Ptr32 _KPCR
   +0x020 Prcb             : Ptr32 _KPRCB
   +0x024 Irql             : UChar
   +0x028 IRR              : Uint4B
   +0x02c IrrActive        : Uint4B
   +0x030 IDR              : Uint4B
   +0x034 KdVersionBlock   : Ptr32 Void
   +0x038 IDT              : Ptr32 _KIDTENTRY
   +0x03c GDT              : Ptr32 _KGDTENTRY
   +0x040 TSS              : Ptr32 _KTSS
   +0x044 MajorVersion     : Uint2B
   +0x046 MinorVersion     : Uint2B
   +0x048 SetMember        : Uint4B
   +0x04c StallScaleFactor : Uint4B
   +0x050 DebugActive      : UChar
   +0x051 Number           : UChar
   +0x052 Spare0           : UChar
   +0x053 SecondLevelCacheAssociativity : UChar
   +0x054 VdmAlert         : Uint4B
   +0x058 KernelReserved   : [14] Uint4B
   +0x090 SecondLevelCacheSize : Uint4B
   +0x094 HalReserved      : [16] Uint4B
   +0x0d4 InterruptMode    : Uint4B
   +0x0d8 Spare1           : UChar
   +0x0dc KernelReserved2  : [17] Uint4B
   +0x120 PrcbData         : _KPRCB
kd> dt nt!_KPRCB
   +0x000 MinorVersion     : Uint2B
   +0x002 MajorVersion     : Uint2B
   +0x004 CurrentThread    : Ptr32 _KTHREAD
   +0x008 NextThread       : Ptr32 _KTHREAD
   +0x00c IdleThread       : Ptr32 _KTHREAD
       <---------- 중략 ---------->


 

2. KPROCESS

KTHREAD 구조체의 0x44 오프셋 번지는 KAPC_STATE 구조체 범위에 들어갑니다. KAPC_STATE 구조체의 주소(0x34)에서 0x10 오프셋을 더한 번지는 KPROCESS 구조체를 가리키고 있습니다. 여기까지 종합해 보면 PsGetCurrentThread 함수는 KPROCESS 구조체 주소를 리턴하는 것 입니다.

  • PsGetCurrentThread() 함수는 KPROCESS 구조체 주소를 리턴한다.
kd> dt nt!_KTHREAD
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 MutantListHead   : _LIST_ENTRY
   +0x018 InitialStack     : Ptr32 Void
   +0x01c StackLimit       : Ptr32 Void
   +0x020 Teb              : Ptr32 Void
   +0x024 TlsArray         : Ptr32 Void
   +0x028 KernelStack      : Ptr32 Void
   +0x02c DebugActive      : UChar
   +0x02d State            : UChar
   +0x02e Alerted          : [2] UChar
   +0x030 Iopl             : UChar
   +0x031 NpxState         : UChar
   +0x032 Saturation       : Char
   +0x033 Priority         : Char
   +0x034 ApcState         : _KAPC_STATE
   +0x04c ContextSwitches  : Uint4B
       <---------- 중략 ---------->
kd> dt nt!_KAPC_STATE
   +0x000 ApcListHead      : [2] _LIST_ENTRY
   +0x010 Process          : Ptr32 _KPROCESS
   +0x014 KernelApcInProgress : UChar
   +0x015 KernelApcPending : UChar
   +0x016 UserApcPending   : UChar


3. EPROCESS

KPROCESS는 EPROCESS 구조체의 첫번째 멤버 입니다. 그토록 찾아 해메던 EPROCESS 구조체에 접근한 것 입니다.

kd> dt nt!_KPROCESS
   +0x000 Header           : _DISPATCHER_HEADER
   +0x010 ProfileListHead  : _LIST_ENTRY
   +0x018 DirectoryTableBase : [2] Uint4B
       <---------- 중략 ---------->
kd> dt nt!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
       <---------- 중략 ---------->
   +0x084 UniqueProcessId  : Ptr32 Void
   +0x088 ActiveProcessLinks : _LIST_ENTRY
   +0x090 QuotaUsage       : [3] Uint4B
       <---------- 중략 ---------->

 

프로세스 은닉을 재현해 보기위해 메모장을 하나 띄우고 PID를 확인합니다. (PID 1068)

 

메모장 프로세스의 핸들정보(0x8202b328)는 EPROCESS 구조체의 주소가 됩니다.

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 823c8b98  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000d68  HandleCount: 234.
    Image: System
       <---------- 중략 ---------->
ROCESS 822ed8d0  SessionId: 0  Cid: 02e8    Peb: 7ffd8000  ParentCid: 0218
    DirBase: 0f0d1000  ObjectTable: e1cdd3a0  HandleCount: 101.
    Image: alg.exe
PROCESS 8202b328  SessionId: 0  Cid: 042c    Peb: 7ffd5000  ParentCid: 0544
    DirBase: 103cf000  ObjectTable: e1cd7780  HandleCount:  43.
    Image: notepad.exe
PROCESS 821f8a48  SessionId: 0  Cid: 072c    Peb: 7ffdb000  ParentCid: 0544
    DirBase: 12ef7000  ObjectTable: e168cbc8  HandleCount:  65.
    Image: Kernel Detective.exe
       <---------- 중략 ---------->

 

Kernel Detective 도움으로 KPROCESS 정보를 얻을 수도 있습니다. KPROCESS 구조체는 EPROCES 구조체의 첫번째 멤버가 되므로 결국은 같은 주소를 가리키게 됩니다. (KPROCESS == EPROCES == 0x8202B328)

 

앞서 구한 메모장의 EPROCESS(0x8202B328) 에서 0x88 오프셋 만큼을 이동하면 EPROCESS 구조체의 ActiveProcessLinks 필드 입니다.

kd> dd 8202B328+0x88
8202b3b0  821f8ad0 822ed958 00000988 000081e4
8202b3c0  000000f1 00000b80 00009998 000000f1

***** notepad.exe ActiveProcessLinks *****
     - Flink     821f8ad0
     - Blink     822ed958


메모장과 서로 연결된 EPROCESS 구조체 정보를 확인해 봅니다. Flink, Blink 값은 앞뒤로 연결된 ActiveProcessLinks를 가리키고 있으므로 0x88 오프셋 만큼 빼주면 EPROCESS 구조체를 가리키게 됩니다. 메모장 앞단 EPROCESS 구조체의 Blink 값과 메모장 뒷단 EPROCESS 구조체의 Flink 값은 모두 메모장의 ACtirveProcessLinks(0x8202b3b0)를 가리키고 있습니다.

***** Flink *****
kd> dt nt!_EPROCESS 821f8ad0-88 ImageFileName
   +0x174 ImageFileName : [16]  "Kernel Detectiv"
kd> dt nt!_EPROCESS 821f8ad0-88 UniqueProcessId
   +0x084 UniqueProcessId : 0x0000072c
kd> dt nt!_EPROCESS 821f8ad0-88 ActiveProcessLinks
   +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8220de28 - 0x8202b3b0 ]
***** Blink *****
kd> dt nt!_EPROCESS 822ed958-88 ImageFileName
   +0x174 ImageFileName : [16]  "alg.exe"
kd> dt nt!_EPROCESS 822ed958-88 UniqueProcessId
   +0x084 UniqueProcessId : 0x000002e8
kd> dt nt!_EPROCESS 822ed958-88 ActiveProcessLinks
   +0x088 ActiveProcessLinks : _LIST_ENTRY [ 0x8202b3b0 - 0x821a4c98 ]

 

필요한 모든 정보를 얻었으니 메모장 프로세스의 연결고리를 그려 봅니다. 메모장은 alg.exe 프로세스의 Flink와 연결되어 있고 Kernel Detective 프로세스의 Blink와 연결되어 있습니다.

메모장 프로세스의 ActiveProcessLinks 정보를 지우고 양 끝단을 이어주면 메모장 프로세스는 은닉상태가 되는 것 입니다.

 Process Name

 alg

 notepad

 Kernel Detective

 EPROCESS

 822ed8d0

 8202b328

 821f8a48

 ActiveProcessLinks

 822ed958

 8202b3b0

 821f8ad0

 Flink

 8202b3b0

 821f8ad0

 8220de28

 Blink

 821a4c98

 822ed958

 8202b3b0

 은닉을 위한 변경정보

 Flink(821f8ad0)

 X

 Blink(822ed958)

 

실제로 메모장 프로세스를 숨겨 보겠습니다. 메모장의 ActiveProcessLinks는 그대로 두고 앞뒤로 연결된 프로세스의 ActiveProcessLinks를 변경해야 합니다.

* alg.exe
before => 822ed958 b0 b3 02 82 98 4c 1a 82
after    => 822ed958 d0 8a 1f 82 98 4c 1a 82

* Kernel Detective
before => 821f8ad0 28 de 20 82 b0 b3 02 82
after    => 821f8ad0 28 de 20 82 58 d9 2e 82

 

Process Explorer 와 작업관리자를 열어서 메모장 프로세스를 찾아 봅니다. 어디에도 찾을 수 없네요. 프로세스 은닉에 성공 하였습니다 ^^v

2010년 6월 7일 월요일

DKOM - 프로세스 은닉#1

윈도우즈는 프로세스를 관리하기 위해 EPROCESS(Executive Process)구조체를 사용 합니다. EPROCESS에서 현재 실행중인 프로세스와 관련있는 필드는 ActiveProcessLinks 라는 LIST_ENTRY 구조체 인데요. 이 필드는 현재 실행중인 프로세스들이 linked list 형태로 연결되어 있습니다. 현재의 엔트리를 기준으로 Flink는 앞단에 연결된 엔트리를 가리키고, Blink는 뒤쪽에 연결된 엔트리를 가리킵니다.

 

ntifs.h 파일에 정의된 EPROCESS 구조체를 살펴 보겠습니다. [출처 - http://www.acc.umu.se/~bosse/]

펼쳐두기..

 

EPROCESS 와 LIST_ENTRY 구조체를 WinDbg 에서 보겠습니다.

kd> dt !_EPROCESS
ntdll!_EPROCESS
   +0x000 Pcb              : _KPROCESS
   +0x06c ProcessLock      : _EX_PUSH_LOCK
   +0x070 CreateTime       : _LARGE_INTEGER
   +0x078 ExitTime         : _LARGE_INTEGER
   +0x080 RundownProtect   : _EX_RUNDOWN_REF
   +0x084 UniqueProcessId  : Ptr32 Void
  +0x088 ActiveProcessLinks : _LIST_ENTRY

       <---------- 중략 ---------->

 

kd> dt !_LIST_ENTRY
ntdll!_LIST_ENTRY
   +0x000 Flink            : Ptr32 _LIST_ENTRY
   +0x004 Blink            : Ptr32 _LIST_ENTRY

 

linked list 형태의 ActiveProcessLinks 를 그림으로 표현해 보았습니다. 리스트의 제일 첫 헤드를 가리키고 있는 PsActiveProcessHead가 있기 때문에 프로세스 링크가 순환될 수 있습니다. PsActiveProcessHead는 윈도우즈에서 익스포트되지 않은 심볼이기 때문에 PsInitialSystemProcess 심볼을 통해 우회 접근해야 합니다. (PsInitialSystemProcess 을 통해 System 프로세스 EPROCESS 구조체에 접근할 수 있습니다)

_declspec(dllimport) void *PsInitialSystemProcess;
PfirstProcess = (PEPROCESS)PsInitialSystemProcess;

 

또는 PsGetCurrentProcess 함수를 이용해서 우회 접근이 가능 합니다. PsGetCurrentProcess 함수는 IoGetCurrentProcess 함수와 동일한 주소를 가리키고 있습니다. 프로세스를 숨기기 위해서는 EPROCESS 구조체 정보를 얻어내고 은닉 대상의 양단(Flink, Blink)의 ActiveProcessLinks를 변경하면 가능할 것 입니다.

 

[참고 - http://www.security.org.sg/code/kproccheck.html]

2010년 3월 25일 목요일

SSDT Hooking - 프로세스 은닉

프로세스 은닉을 커널모드에서 구현하기 위해 SSDT(System Service Dispatch Table) Hook을 이용해 보겠습니다. 프로세스 정보와 관련된 시스템 함수는 ntdll.dll의 ZwQuerySystemInformation() 입니다. 먼저 유저레벨 ZwQuerySystemInformation() 부터 커널레벨 진입점 sysenter 를 지나서 SDT를 참조하는 KiFastCallEntry 까지 따라가 봅니다.

0:001> u ntdll!ZwQuerySystemInformation
ntdll!NtQuerySystemInformation:
7c93d910 b8ad000000      mov     eax,0ADh
7c93d915 ba0003fe7f      mov     edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c93d91a ff12            call    dword ptr [edx]
7c93d91c c21000          ret     10h
0:001> dd 7ffe0300
7ffe0300  7c93e4f0 7c93e4f4 00000000 00000000
7ffe0310  00000000 00000000 00000000 00000000
7ffe0320  00000000 00000000 00000000 00000000
7ffe0330  88be7aa1 00000000 00000000 00000000
7ffe0340  00000000 00000000 00000000 00000000
7ffe0350  00000000 00000000 00000000 00000000
7ffe0360  00000000 00000000 00000000 00000000
7ffe0370  00000000 00000000 00000000 00000000
0:001> uf 7c93e4f0
ntdll!KiFastSystemCall:
7c93e4f0 8bd4            mov     edx,esp
7c93e4f2 0f34            sysenter
7c93e4f4 c3              ret
kd> rdmsr 0x176
msr[176] = 00000000`804de6f0
kd> u 804de6f0
nt!KiFastCallEntry:
804de6f0 b923000000      mov     ecx,23h
804de6f5 6a30            push    30h
804de6f7 0fa1            pop     fs
804de6f9 8ed9            mov     ds,cx
804de6fb 8ec1            mov     es,cx
804de6fd 8b0d40f0dfff    mov     ecx,dword ptr ds:[0FFDFF040h]
804de703 8b6104          mov     esp,dword ptr [ecx+4]
804de706 6a23            push    23h
  • SDT(Service Descriptor Table) : SDE를 멤버변수로 가지는 구조체
  • SDE(Service Descriptor Entry) : Native API 정보를 담은 구조체
  • SSDT(System Service Dispatch Table) : 함수 실행을 위한 서비스 인덱스 번호 테이블, KiServiceTable

       typedef struct ServiceDescriptorTable {

              SDE ServiceDescriptor[4];

       } SDT;

 

       typedef struct ServiceDescriptorTable {   // 위의 SDT 구조체 멤버변수(SDE)를 풀어 놓은 것 입니다

              SDE ntoskrnl;             // native api 테이블

              SDE win32k;              // GUI 서브시스템 테이블

              SDE reserved[2];       // Reserved 영역

       } SDT;

 

       typedef struct ServiceDescriptorEntry {

              PDWORD KiServiceTable;             // 함수 테이블(SSDT), KiServiceTable

              PDWORD CounterTableBase;       // 함수 호출 횟수테이블, 디버그 버전에서만 사용됨

              DWORD ServiceLimit;                  // 함수 갯수

              PBYTE ArgumentTable;               // 함수 인자크기 테이블

       } SDE;                                             // KeServiceDescriptorTable

 

일반적인 스레드는 (SDT구조체의)ntoskrnl->KeServiceDescriptorTable 만을 참조하고, GUI 스레드는 (SDT 구조체의)win32k->KeServiceDescriptorTableShadow 를 함께 참조합니다.

 

ntoskrnl의 KeServiceDescriptorTable을 참조하여 SSDT를 찾고 ZwQuerySystemInformation 함수가 호출되는 주소를 따라가 봅니다.

kd> dd KeServiceDescriptorTable
8055a220  804e26a8 00000000 0000011c 80510088
8055a230  00000000 00000000 00000000 00000000
8055a240  00000000 00000000 00000000 00000000
8055a250  00000000 00000000 00000000 00000000
8055a260  00002730 bf80c349 00000000 00000000
8055a270  f8a11a80 f828db60 82259a90 82259a90
8055a280  00000000 00000000 00000000 00000000
8055a290  d4de7a40 01cacc3a 00000000 00000000
kd> dd KiServiceTable
804e26a8  8058fdf3 805756d8 80588d69 8059112e
804e26b8  8058ee53 806380ec 8063a27d 8063a2c6
804e26c8  80573bfe 806490bb 806378a7 8058e471
804e26d8  8062f9e8 8057a76f 80589cf8 8062694d
804e26e8  805dd3c1 80569153 805d975f 805a24ca
804e26f8  804e2cb4 806490cf 805c9b16 804ecfac
804e2708  805697ff 80567a6d 8058e8df 8064e9b0
804e2718  8058aae8 80590b3b 8064ec1d 80588dbb
kd> dd 804e26a8 + 0xAD * 4
804e295c  8057d062 805911b8 805885d6 805853d7
804e296c  8056a382 80570a2c 8056f843 80591089
804e297c  804e203a 8064814b 80576471 805da827
804e298c  8058a899 8057f0a0 8057c4c7 8056647b
804e299c  805892ce 80566f99 8065b316 8064e812
804e29ac  8064f16e 8057e103 8056b9be 8056b4d6
804e29bc  8062331e 8062c13b 805dd5ec 8056da20
804e29cc  8062bf34 8059eb88 8053bbf2 8064ed05
kd> u 8057d062
nt!NtQuerySystemInformation:
8057d062 6810020000      push    210h
8057d067 6840a44e80      push    offset nt!ExTraceAllTables+0x1eb (804ea440)
8057d06c e8ca53f6ff      call    nt!_SEH_prolog (804e243b)
8057d071 33c0            xor     eax,eax
8057d073 8945e4          mov     dword ptr [ebp-1Ch],eax
8057d076 8945dc          mov     dword ptr [ebp-24h],eax
8057d079 8945fc          mov     dword ptr [ebp-4],eax
8057d07c 64a124010000    mov     eax,dword ptr fs:[00000124h]


ZwQuerySystemInformation를 호출하면 커널모드로 진입하여 nt!NtQuerySystemInformation로 흘러들어오게 됩니다. SSDT의 nt!NtQuerySystemInformation 주소가 기록된 번지(804e26a8 + 0xAD * 4)에 공격자가 작성한 코드주소를 덮어쓰면 원하는 목적을 달성할 수 있습니다.

 

* Hook Code 선언부

#pragma pack(1)                                                            // 구조체의 멤버변수 저장크기를 결정(1,2,4,8,16)
typedef struct ServiceDescriptorEntry {                            // SDE(Service DescriptorTable Entry) 구조체 선언
     unsigned int *ServiceTableBase;                               // 함수 테이블(SSDT)
     unsigned int *ServiceCounterTableBase;                   // 함수 호출 횟수테이블, 디버그 버전에서만 사용됨
     unsigned int NumberOfServices;                                // 함수 갯수
     unsigned char *ParamTableBase;                             // 함수 인자크기 테이블
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()                                                             // 구조체의 멤버변수 저장크기를 디폴드로 복원
// SSDT(System Service Descriptor Table) IMPORT => ntoskrnl.exe 에서 EXPORT 한다
__declspec(dllimport) ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
// SYSTEM_THREADS 구조체 선언
struct _SYSTEM_THREADS
{
      LARGE_INTEGER KernelTime; // 커널모드에서 실행된 총시간 (100ns)
      LARGE_INTEGER UserTime;  // 유저모드에서 실행된 총시간 (100ns)
      LARGE_INTEGER CreateTime; // 생성시각
      ULONG WaitTime;    // 스레드 대기시각
      PVOID StartAddress;   // 스레드 시작주소
      CLIENT_ID ClientIs;   //
      KPRIORITY Priority;   //
      KPRIORITY BasePriority;  //
      ULONG ContextSwitchCount; //
      ULONG ThreadState;   //
      KWAIT_REASON WaitReason; //
};
// SYSTEM_PROCESSES 구조체 선언
struct _SYSTEM_PROCESSES
{
      ULONG NextEntryDelta;    // 다음 SYSTEMPROCESS 주소의 오프셋
      ULONG ThreadCount;     // 스레드 갯수
      ULONG Reserved[6];     // Reserved
      LARGE_INTEGER CreateTime;   // 생성시각
      LARGE_INTEGER UserTime;    // 유저모드에서 실행된 총시간 (100ns)
      LARGE_INTEGER KernelTime;   // 커널모드에서 실행된 총시간 (100ns)
      UNICODE_STRING ProcessName;   // 프로세스 이름
      KPRIORITY BasePriority;    // 권한정보
      ULONG ProcessId;     // 프로세스 ID
      ULONG InheritedFromProcessId;  // 부모 프로세스 ID
      ULONG HandleCount;     // 핸들 갯수
      ULONG Reserved2[2];     // Reserved
      VM_COUNTERS VmCounters;    // 프로세스가 사용하는 가상메모리 상태를 저장
      IO_COUNTERS IoCounters;    // 프로세스가 실행한 IO 카운터 (windows 2000 only)
      struct _SYSTEM_THREADS Threads[1]; // 프로세스 스레드리스트 (SYSTEM_THREAD 구조체로 관리)
};
NTSYSAPI // NTSYSAPI ntdef.h 에서DECLSPEC_IMPORT로 정의
NTSTATUS // 리턴타입
NTAPI   // NTAPI ntdef.h 에서 STDCALL로 정의
ZwQuerySystemInformation(
   IN ULONG SystemInformationClass,
   IN PVOID SystemInformation,
   IN ULONG SystemInformationLength,
   OUT PULONG ReturnLength);
typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
   ULONG SystemInformationCLass,
   PVOID SystemInformation,
   ULONG SystemInformationLength,
   PULONG ReturnLength);
// 기존 함수주소 저장을 위한 변수
ZWQUERYSYSTEMINFORMATION  OldZwQuerySystemInformation;
// CR0 Write Protection 해제 <코드참조>
VOID ClearCr_WP(VOID){}
// CR0 Write Protection 세팅 <코드참조>
VOID SetCr_WP(VOID){}

 

* 드라이버 로드 && 언로드 루틴

VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
       // WriteProtection 해제
       ClearCr_WP();
       // 후킹한 SSDT를 복원한다.
       InterlockedExchange((LONG *)Syscall_Ptr(ZwQueryDirectoryFile), (LONG)OldZwQueryDirectoryFile);
       InterlockedExchange((LONG *)Syscall_Ptr(ZwQuerySystemInformation), (LONG)OldZwQuerySystemInformation);
       // WriteProtection 설정
       SetCr_WP();
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath)
{
       theDriverObject->DriverUnload = OnUnload;
       OldZwQueryDirectoryFile = (ZWQUERYDIRECTORYFILE)Syscall_Ptr(ZwQueryDirectoryFile);
       OldZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)Syscall_Ptr(ZwQuerySystemInformation);
       // WriteProtection 해제
       ClearCr_WP();
       // SSDT 후킹
       OldZwQueryDirectoryFile = (ZWQUERYDIRECTORYFILE)InterlockedExchange((LONG *)Syscall_Ptr(ZwQueryDirectoryFile), (LONG)NewZwQueryDirectoryFile);
       OldZwQuerySystemInformation = (ZWQUERYSYSTEMINFORMATION)InterlockedExchange((LONG *)Syscall_Ptr(ZwQuerySystemInformation),(LONG)NewZwQuerySystemInformation);
       // WriteProtection 설정
       SetCr_WP();
       return STATUS_SUCCESS;
}


 

2010년 3월 12일 금요일

DriverEntry - 드라이버 특정 함수 부터 디버깅 하기

심볼이 없는 드라이버 파일에서 호출하는 함수에 브레이크 포인트를 걸고 디버깅 하는 방법 입니다. 드라이버 시작점 부터 디버깅 하기와 아주 조금만 다릅니다. [드라이버 시작점 부터 디버깅 하기]

 

nt!RtlpBreakWithStatusInstruction:
804e3592 cc              int     3
kd> sxe ld cr0.sys          // cr0.sys 드라이버 로드 시점에 BP 설정
kd> g
nt!DebugService2+0x10:
80506d3e cc              int     3          // BP가 걸렸습니다 (cr0.sys 드라이버 로딩)
kd> lm
start    end        module name
7c900000 7c9b2000   ntdll      (pdb symbols)
... <중략> ...
f8c2c000 f8c2c800   cr0        (deferred)          // cr0.sys pdb 없음
kd> !dh f8c2c000          // cr0.sys 베이스를 기준으로 image header dump#1
File Type: EXECUTABLE IMAGE
FILE HEADER VALUES
     14C machine (i386)
       5 number of sections
4B8F14E4 time date stamp Thu Mar 04 11:03:16 2010
       0 file pointer to symbol table
       0 number of symbols
      E0 size of optional header
... <중략> ...
       0 [       0] address [size] of Load Configuration Directory
       0 [       0] address [size] of Bound Import Directory
     580 [       C] address [size] of Import Address Table Directory
       0 [       0] address [size] of Delay Import Directory
       0 [       0] address [size] of COR20 Header Directory
       0 [       0] address [size] of Reserved Directory
... <중략> ...
kd> dps f8c2c000+580 LC/4          // IAT 출력#1
f8c2c580  80552000 nt!KeTickCount
f8c2c584  804ff079 nt!DbgPrint
f8c2c588  00000000
kd> dps f8c2c000+580 f8c2c000+580+C          // IAT 출력#2 (동일한 결과값의 다른 방법)
f8c2c580  80552000 nt!KeTickCount
f8c2c584  804ff079 nt!DbgPrint
f8c2c588  00000000
f8c2c58c  00000000
kd> ba r4 f8c2c584          // 4byte Read 될때(r4) 메모리브레이크(ba) 포인트 설정
kd> g
Single step exception - code 80000004 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
nt!DbgPrint:
804ff079 8bff            mov     edi,edi          // BP가 걸렸습니다 (nt!DbgPrint 호출)
kd> k
ChildEBP RetAddr 
f89a3c70 f8c2c4cf nt!DbgPrint          // 콜스택 정보를 참조
WARNING: Stack unwind information not available. Following frames may be wrong.
f89a3c7c 805a39ad cr0+0x4cf
f89a3d4c 805a3c83 nt!IopLoadDriver+0x66d
f89a3d74 804e426b nt!IopLoadUnloadDriver+0x45
f89a3dac 8057c9ca nt!ExpWorkerThread+0x100
f89a3ddc 804f88ea nt!PspSystemThreadStartup+0x34
00000000 00000000 nt!KiThreadStartup+0x16
kd> u f8c2c4cf          // nt!DbgPrint 호출 후 리턴주소 코드정보
cr0+0x4cf:
f8c2c4cf 83c404          add     esp,4
f8c2c4d2 8b4508          mov     eax,dword ptr [ebp+8]
f8c2c4d5 c7403490c4c2f8  mov     dword ptr [eax+34h],offset cr0+0x490 (f8c2c490)
f8c2c4dc 50              push    eax
f8c2c4dd 0f20c0          mov     eax,cr0
f8c2c4e0 3500000100      xor     eax,10000h
f8c2c4e5 0f22c0          mov     cr0,eax
f8c2c4e8 58              pop     eax
kd> u f8c2c4c0          // 조금 거슬러 올라갑니다
cr0+0x4c0:
f8c2c4c0 8bff            mov     edi,edi
f8c2c4c2 55              push    ebp
f8c2c4c3 8bec            mov     ebp,esp
f8c2c4c5 6810c5c2f8      push    offset cr0+0x510 (f8c2c510)       // nt!DbgPrint 인자값
f8c2c4ca e825000000      call    cr0+0x4f4 (f8c2c4f4)                  // nt!DbgPrint 호출
f8c2c4cf 83c404          add     esp,4                                       // 리턴주소
f8c2c4d2 8b4508          mov     eax,dword ptr [ebp+8]
f8c2c4d5 c7403490c4c2f8  mov     dword ptr [eax+34h],offset cr0+0x490 (f8c2c490)
kd> db f8c2c510         // DbgPrint 인자값 확인
f8c2c510  44 72 69 76 65 72 45 6e-74 72 79 28 29 20 53 74  DriverEntry() St
f8c2c520  61 72 74 0a 00 00 00 00-00 00 00 00 00 00 00 00  art.............
f8c2c530  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c2c540  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c2c550  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c2c560  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c2c570  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c2c580  00 20 55 80 79 f0 4f 80-00 00 00 00 00 00 00 00  . U.y.O.........

 

드라이버 파일의 부가정보 확인을 위한 명령어 (여기서도 base address 값의 확인이 가능 합니다)

kd> lmv m cr0
start    end        module name
f8c2c000 f8c2c800   cr0        (no symbols)        
 
    Loaded symbol image file: cr0.sys
    Image path: cr0.sys
    Image name: cr0.sys
    Timestamp:        Thu Mar 04 11:03:16 2010 (4B8F14E4)
    CheckSum:         00003A7F
    ImageSize:        00000800
    Translations:     0000.04b0 0000.04e4 0409.04b0 0409.04e4

2010년 3월 10일 수요일

DriverEntry - 드라이버 시작점 부터 디버깅 하기

드라이버(.sys)파일의 시작점(Entry-Point)부터 디버깅 하는 방법 입니다. 커널 디버깅 환경이 준비 되어야 하며 분석대상 드라이버 PDB 파일의 유무에 따라 분석 방법이 조금 달라 집니다. 먼저 디버기 시스템(Virtual PC 2007)에서 드라이버를 등록(Register Service)하고 나서 디버거(WinDBG)에 입력되는 명령어는 아래와 같습니다. [예제 드라이버 소스코드 경로]

 

1. 디버깅 대상 드라이버 파일의 PDB 있을 경우

nt!RtlpBreakWithStatusInstruction:
804e3592 cc              int     3
kd> sxe ld cr0.sys          // cr0.sys 드라이버 로드 시점에 BP 설정
kd> g
nt!DebugService2+0x10:
80506d3e cc              int     3          // BP가 걸렸습니다 (cr0.sys 드라이버 로딩)
kd> lm
start    end        module name
7c900000 7c9b2000   ntdll      (pdb symbols)
...
f8c8a000 f8c8a800   cr0        (private pdb symbols)     // cr0.sys pdb 확인
kd> bp cr0!DriverEntry          // cr0.sys 코드시작(DriverEntry)점에 BP 설정
kd> bl
 0 e f8c8a4c0     0001 (0001) cr0!DriverEntry
kd> g
Breakpoint 0 hit
cr0!DriverEntry:          // BP가 걸렸습니다 (DriverEntry)
f8c8a4c0 8bff            mov     edi,edi
kd> u
cr0!DriverEntry [c:\sdt_cr0\set_cr0.cpp @ 22]:
f8c8a4c0 8bff            mov     edi,edi
f8c8a4c2 55              push    ebp
f8c8a4c3 8bec            mov     ebp,esp
f8c8a4c5 6810a5c8f8      push    offset cr0! ?? ::FNODOBFM::`string' (f8c8a510)
f8c8a4ca e825000000      call    cr0!DbgPrint (f8c8a4f4)
f8c8a4cf 83c404          add     esp,4
f8c8a4d2 8b4508          mov     eax,dword ptr [ebp+8]
f8c8a4d5 c7403490a4c8f8  mov     dword ptr [eax+34h],offset cr0!OnUnload (f8c8a490)
kd> db f8c8a510          // DbgPrint 인자값 확인
f8c8a510  44 72 69 76 65 72 45 6e-74 72 79 28 29 20 53 74  DriverEntry() St
f8c8a520  61 72 74 0a 00 00 00 00-00 00 00 00 00 00 00 00  art.............
f8c8a530  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c8a540  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c8a550  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c8a560  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c8a570  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8c8a580  00 20 55 80 79 f0 4f 80-00 00 00 00 00 00 00 00  . U.y.O.........


2. 디버깅 대상 드라이버 파일의 PDB 없을 경우

nt!RtlpBreakWithStatusInstruction:
804e3592 cc              int     3
kd> sxe ld cr0.sys          // cr0.sys 드라이버 로드 시점에 BP 설정
kd> g
nt!DebugService2+0x10:
80506d3e cc              int     3          // BP가 걸렸습니다 (cr0.sys 드라이버 로딩)
kd> lm
start    end        module name
7c900000 7c9b2000   ntdll      (pdb symbols)
...
f8b84000 f8b84800   cr0        (deferred)          // cr0.sys pdb 없음
kd> bp cr0!DriverEntry
*** ERROR: Module load completed but symbols could not be loaded for cr0.sys
Couldn't resolve error at 'cr0!DriverEntry'          // DriverEntry 지점에 BP를 걸 수 없습니다
kd> u f8b846be          // cr0 start address(f8b84000) + cr0 Address Entry Point(6be)
cr0+0x6be:
f8b846be 8bff            mov     edi,edi
f8b846c0 55              push    ebp
f8b846c1 8bec            mov     ebp,esp
f8b846c3 e8bdffffff      call    cr0+0x685 (f8b84685)
f8b846c8 5d              pop     ebp
f8b846c9 e9f2fdffff      jmp     cr0+0x4c0 (f8b844c0)
f8b846ce cc              int     3
f8b846cf cc              int     3
kd> bp f8b844c0          // cr0.sys 코드시작(DriverEntry)점에 BP 설정
kd> bl
0 e f8b844c0     0001 (0001) cr0+0x4c0
kd> g
Breakpoint 0 hit
cr0+0x4c0:          // BP가 걸렸습니다 (DriverEntry)
f8b844c0 8bff            mov     edi,edi
kd> u
cr0+0x4c0:
f8b844c0 8bff            mov     edi,edi
f8b844c2 55              push    ebp
f8b844c3 8bec            mov     ebp,esp
f8b844c5 681045b8f8      push    offset cr0+0x510 (f8b84510)
f8b844ca e825000000      call    cr0+0x4f4 (f8b844f4)
f8b844cf 83c404          add     esp,4
f8b844d2 8b4508          mov     eax,dword ptr [ebp+8]
f8b844d5 c740349044b8f8  mov     dword ptr [eax+34h],offset cr0+0x490 (f8b84490)
kd> db f8b84510         // DbgPrint 인자값 확인
f8b84510  44 72 69 76 65 72 45 6e-74 72 79 28 29 20 53 74  DriverEntry() St
f8b84520  61 72 74 0a 00 00 00 00-00 00 00 00 00 00 00 00  art.............
f8b84530  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8b84540  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8b84550  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8b84560  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8b84570  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
f8b84580  00 20 55 80 79 f0 4f 80-00 00 00 00 00 00 00 00  . U.y.O........
.

2010년 3월 9일 화요일

Virtual PC 2007 - 시스템 복원하기 (undo)

Virtual PC 2007 환경에서 테스트를 마치고 초기화 상태로 복원하는 방법 입니다.

 

1. [Settings] - [UnDo Disks] - [Enable undo disks] Check

 

2. Virtual PC 2007 에서 테스트를 마치고 종료(Turn Off) 선택옵션

  • Commit changes to the virtual hard disk : 시스템의 변화된 환경 유지
  • Save undo disk changes : 차이점을 잘 모르겠음 ...
  • Delete undo disk changes : 시스템의 변화된 환경 무시. 처음 부팅 이미지로 복원