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 구조체들을 가리키며, 아래와 같이 생겼다.
- // Vista/2008 csrsrv.dll (valid also for XP, 2003)
- typedef struct _CSR_PROCESS {
- struct _CLIENT_ID ClientId;
- struct _LIST_ENTRY ListLink;
- struct _LIST_ENTRY ThreadList;
- struct _CSR_NT_SESSION* NtSession;
- ULONG ExpectedVersion;
- void* ClientPort;
- char* ClientViewBase;
- char* ClientViewBounds;
- void* ProcessHandle;
- ULONG SequenceNumber;
- ULONG Flags;
- ULONG DebugFlags;
- ULONG ReferenceCount;
- ULONG ProcessGroupId;
- ULONG ProcessGroupSequence;
- ULONG fVDM;
- ULONG ThreadCount;
- ULONG LastMessageSequence;
- ULONG NumOutstandingMessages;
- ULONG ShutdownLevel;
- ULONG ShutdownFlags;
- struct _LUID Luid;
- void* ServerDllPerProcessData[1];
- } CSR_PROCESS, *PCSR_PROCESS;
// Vista/2008 csrsrv.dll (valid also for XP, 2003) typedef struct _CSR_PROCESS { struct _CLIENT_ID ClientId; struct _LIST_ENTRY ListLink; struct _LIST_ENTRY ThreadList; struct _CSR_NT_SESSION* NtSession; ULONG ExpectedVersion; void* ClientPort; char* ClientViewBase; char* ClientViewBounds; void* ProcessHandle; ULONG SequenceNumber; ULONG Flags; ULONG DebugFlags; ULONG ReferenceCount; ULONG ProcessGroupId; ULONG ProcessGroupSequence; ULONG fVDM; ULONG ThreadCount; ULONG LastMessageSequence; ULONG NumOutstandingMessages; ULONG ShutdownLevel; ULONG ShutdownFlags; struct _LUID Luid; void* ServerDllPerProcessData[1]; } 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
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에 접근하기 위한