본문 바로가기
디버그

csrsrv.dll 의 CsrRootProcess조사하여 숨겨진 프로세스 탐지 기법

by WeZZ 2009. 4. 11.


lucid7님의 블로그를 통하여 csrss 프로세스의 unexport 구조체를 통하여  숨겨진 프로세스를 탐지하는 법을
알게 되었다.
물론 루트킷사이트에서 제일먼저 보긴했지만, lucid7님이 번역하셔서 쉽게 이해할 수 있었다.

"csrss는 유저영역의 통제자로써 활동하기때문에 모든 Win32 프로세스들이 생성시 csrss에 알려야만 한다.
csrss.exe는 csrsrv.dll의 노출되지않은 심벌(Unexported symbol)인 CsrRootProcess을 통해서 생성된
프로세스정보들을 관리한다."

이 점에 착안하여 루트킷 디텍터를 만들 수 있다는 것이 요지이다.
이기법은 Win2k부터 win2008까지 모두 사용가능하다고 한다. 또한 유저모드에서 동작하므로 드라이버가 필요없다.
다음 내용은 위 내용을 개인적으로 답습한것에 불과하다.

** csrss.exe 디버깅 **

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 823b9830  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000cc0  HandleCount: 269.
    Image: System

PROCESS 820e5020  SessionId: 0  Cid: 025c    Peb: 7ffdc000  ParentCid: 022c
    DirBase: 0c9af000  ObjectTable: e15ea458  HandleCount: 353.
    Image: csrss.exe
... 생략 ...

kd> .process /i 820e5020 ; g; .reload /user
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.
Break instruction exception - code 80000003 (first chance)
Loading User Symbols
..............


** csrsrv에 CsrRootProcess가 존재하는지 확인 **

kd> x csrsrv!CsrRootProcess
75a8891c CSRSRV!CsrRootProcess = <no type information>

CsrRootProcess는 _CSR_PROCESS 구조체들을 가리키며, 아래와 같이 생겼다.
  1. // Vista/2008 csrsrv.dll (valid also for XP, 2003)   
  2.   
  3. typedef struct _CSR_PROCESS {   
  4.     struct _CLIENT_ID ClientId;   
  5.     struct _LIST_ENTRY ListLink;   
  6.     struct _LIST_ENTRY ThreadList;   
  7.     struct _CSR_NT_SESSION* NtSession;   
  8.     ULONG ExpectedVersion;   
  9.     void* ClientPort;   
  10.     char* ClientViewBase;   
  11.     char* ClientViewBounds;   
  12.     void* ProcessHandle;   
  13.     ULONG SequenceNumber;   
  14.     ULONG Flags;   
  15.     ULONG DebugFlags;   
  16.     ULONG ReferenceCount;   
  17.     ULONG ProcessGroupId;   
  18.     ULONG ProcessGroupSequence;   
  19.     ULONG fVDM;   
  20.     ULONG ThreadCount;   
  21.     ULONG LastMessageSequence;   
  22.     ULONG NumOutstandingMessages;   
  23.     ULONG ShutdownLevel;   
  24.     ULONG ShutdownFlags;   
  25.     struct _LUID Luid;   
  26.     void* ServerDllPerProcessData[1];   
  27. } CSR_PROCESS, *PCSR_PROCESS;  



** CsrRootProcess가 가리키는 ListEntry를 따라서 프로세스 정보 출력 **

아래의 내용을 파일로 생성하여 windbg에서 실행시킨다

$$ CsrRootProcess에서 ProcessList의 주소를 구함
r $t0 = poi(csrsrv!CsrRootProcess)+8

.echo
.echo "----------------------------------------"
.echo "     CsrWalker script for windbg        "
.echo "                                        "
.echo "           http://0range.net            "
.echo "----------------------------------------"
.echo 

$$ Linked List을 순회하면서 PID출력
.for (r $t1 = poi(@$t0);
      (@$t1 != 0) & (@$t1 != @$t0);
      r $t1 = poi(@$t1))
{

    r? $t2 = @$t1 - 8
    dt _CLIENT_ID @$t2
}

kd> $$><"C:\windbg scripts\WalkCsrss.txt"

----------------------------------------
     CsrWalker script for windbg

           http://0range.net
----------------------------------------

ntdll!_CLIENT_ID
   +0x000 UniqueProcess    : 0x00000274 
   +0x004 UniqueThread     : 0x00000278 
ntdll!_CLIENT_ID
   +0x000 UniqueProcess    : 0x000002a0 
   +0x004 UniqueThread     : 0x000002a4 
ntdll!_CLIENT_ID
   +0x000 UniqueProcess    : 0x000002ac 
   +0x004 UniqueThread     : 0x000002b0 
ntdll!_CLIENT_ID
   +0x000 UniqueProcess    : 0x00000348 
   +0x004 UniqueThread     : 0x0000034c 
... 생략 ...

위 방법으로 프로세스정보를 출력한 것과 '!process 0 0' 으로 출력한 결과와 비교해 보니, CsrRootProcess
을 Enumeration한 결과는 다음과 같은 프로세스의 정보는 존재 하지 않았다.

PROCESS 823b9830  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00039000  ObjectTable: e1000cc0  HandleCount: 269.
    Image: System

PROCESS 8223cd38  SessionId: none  Cid: 022c    Peb: 7ffdf000  ParentCid: 0004
    DirBase: 0a03d000  ObjectTable: e14593f8  HandleCount:  19.
    Image: smss.exe

PROCESS 820e5020  SessionId: 0  Cid: 025c    Peb: 7ffdc000  ParentCid: 022c
    DirBase: 0c9af000  ObjectTable: e15ea458  HandleCount: 353.
    Image: csrss.exe


CsrRootProcess는 unexport된 심벌이므로 찾으려면, CsrLockProcessByClientId() 을 통해서 알아낼 수 있다.

kd> uf csrsrv!CsrLockProcessByClientId
CSRSRV!CsrLockProcessByClientId:
75a852d3 8bff            mov     edi,edi
75a852d5 55              push    ebp
75a852d6 8bec            mov     ebp,esp
75a852d8 53              push    ebx
75a852d9 56              push    esi
75a852da 57              push    edi
75a852db bfa089a875      mov     edi,offset CSRSRV!CsrProcessStructureLock (75a889a0)
75a852e0 57              push    edi
75a852e1 ff151811a875    call    dword ptr [CSRSRV!_imp__RtlEnterCriticalSection (75a81118)]
75a852e7 8b550c          mov     edx,dword ptr [ebp+0Ch]
75a852ea 832200          and     dword ptr [edx],0
75a852ed 8b351c89a875    mov     esi,dword ptr [CSRSRV!CsrRootProcess (75a8891c)]
75a852f3 83c608          add     esi,8
75a852f6 c7450c010000c0  mov     dword ptr [ebp+0Ch],0C0000001h
75a852fd 8bce            mov     ecx,esi

저 함수를 조사하면 CsrRootProcess의 주소를 구할 수 있으며, 또한 CsrRootProcess에 접근하기 위한
동기화락인 CsrProcessStructureLock 의 주소도 얻을 수 있겠다.


출처: http://0range.net/267