2008년 09월 30일
CsrWalker - using csrss as rkdetector
Seek'n'Hide game was always interesting. Especially when this seek'n'hide stuff is related to Windows Processes objects.
숨바꼭질 게임은 항상 흥미롭다. 특히 그 숨바꼭질이 Windows Process들의 object들과 관련이 있을 경우 더욱 그렇다.
As you probably know exists a lot of "ultimate" hidden processes proof-of-concepts, which main idea is to hide the user land process from a different detection methods.
아마 당신은 많은 종류의 hidden process들에 대한 굉장한 개념들을 알고 있을 것이며 그들의 주된 아이디어는 다른 종류의 detection 방법으로부터 userland process들을 숨기는 것이다.
This article is about building conceptual pure user mode hidden processes detection software and also it contains source code of the PoC.
이 기사는 순수한 user mode process detection software의 개념을 설립하는 것에 관한 것이며 PoC source code가 포함되어 있다.
There is just a little list of available conceptual processes hiders (most of them out-of-dated, some really new):
여기에 약간의 이용가능한 개념적인 processes hiders들이 있다.(그들 중 몇몇은 시간이 좀 된 것이고 몇몇은 정말로 새로운 것이다.)
Hacker Defender
FU/FUTO
rkdemo (1.0-1.2)
phide_ex
Z0mBiE
Several others not listed here.
Their main idea - is subverting Windows internals by doing a manupalations with a internals objects to hide them from Windows users and programs like TaskManager or Process Explorer.
그들의 주된 아이디어는 그들 자신을 윈도우 사용자나 TaskManager 또는 Process Explorer과 같은 프로그램으로부터 숨기기 위해 내부 오브젝트들을 수정하는 방법으로 윈도우즈 내부를 휘젓는 것이다.
From the other side exists a lot of public (or not public ) detectors, which main idea - reveal such hidden stuff by doing numerous checks of OS internals.
반면에 많은 공식적인(또는 비공식적인) 디텍터들의 주된 아이디어는 그러한 숨기는 기술들을 운영체제 내부의 여러 곳을 검사하는 방법을 사용하여 드러내는 것이다.
Each new hider bypasses all of them (or at least 90% of them) because it is always known how these detectors will check for hidden stuff. Most of detection methods based on locating EPROCESS or ETHREAD objects with several different ways such as PspCidTable (handle table) parsing, Scheduler lists analysis (as for example in GMER v1.14), checking of the specified EPROCESS fields such as all LIST_ENTRY, hooking KiSwapContext function, looking for threads, brute-forcing etc. Actually this all is enough for any kind of existing malware which is hiding processes while work. If exists some "unpublic malware" which is doing better hiding job, then it isn't meaningful, because it is "unpublic malware".
각 새로운 hider들은 디텍터들 모두를(또는 적어도 90% 이상을)우회한다. 왜냐하면 이 디텍터들이 hidden stuff을 검사하는 방법을 항상 알고 있기 때문이다. 대부분의 detection 방법들은 PspCidTable(handle table) paring이나 스케쥴러 리스트 분석(GMER v1.14에서 사용)하거나, LIST_ENTRY 같은 정의된 EPROCESS 필드를 검사하거나, KiSwapContext 함수를 후킹하거나, thread들을 찾거나, 기타 brute-forcing 같은 몇몇 다른 방법같이 EPROCESS 또는 ETHREAD 오브젝트를 찾아내는데 기반을 두고 있다.
This was a preamble.
여기까지는 서론이다.
The mysterious CSRSS was always interesting to me, simple because it is playing a supervisor role for user land stuff. All win32 processes should notify it at process creation stage to continue work. Exactly this stuff doing all job of initializing processes subsystems (from csrsrv.dll which is a heart of CSRSS).
미스터리한 CSRSS는 항상 나에게 흥미롭다 간단히 말해 그것은 user land를 통제자로써 활동하기 때문이다. 모든 win32 프로세스들은 생성되는 시점에 일을 계속하기 위해 CSRSS에 알려야만 한다. 정확하게 이 stuff는 프로세스들의 subsystem들을(CSRSS의 핵심인 csrsrv.dll로부터) 초기화 하는 모든 작업들에 대해서 그렇게 한다.
And exactly it - a "new" processes detector (actually build into NT since beginning). All this is completely undocumented. All what I got when tried to find any reference to this was ReactOs code, which is not complete and simple different.
그리고 정확하게 그것은 "새로운" 프로세스들의 detector이다(actually build into NT since beginning). 이 모든 것은 완전히 비문서화 되어있다. 내가 이 원리에 대한 어떤 참고 자료라도 발견하기 위해 노력할 때 내가 얻은 모든 것들은 완전하지 않고 또 조금은 다른 ReactOS code였다.
takes a huge amount of time to reverse and rebuild the whole subsystem manager code but it was really fun and not so hard with a little piece of source code . The csrss is not a very complex thing to reverse and all most interesting stuff located in csrsrv.dll
전체 subsystem manager code 매우 많은 시간을 리버스와 리빌드에 소비하였지만 그 작업은 정말로 재미있었고 source code의 작은 부분들도 힘들지 않았다. csrss는 리버스 하기에 매우 목잡하지 않고 csrsrv.dll에 모든 재미있는 것들이 위치하고 있다.
Inside it exists not exported symbol called CsrRootProcess. It is playing a huge role in all CSRSS, because of this: CsrRootProcess is the pointer to CSR_PROCESS structure which looks like this.
csrsrv.dll 내에는 non-export symbol인 CsrRootProcess가 있다. 이것은 모든 CSRSS 내에 큰 role로써 작용하는데 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;
Here the field ListLink which is LIST_ENTRY.
In csrsrv exist another unexported internal function called CsrInsertProcess. It is inserting a other CSR_PROCESS structure to this list entry of the CsrRootProcess.
여기 LIST_ENTRY 구조체인 ListLink 필드가 있다.
csrsrv에는 다른 unexport 내부 함수인 CsrInsertProcess가 있다. 그것은 다른 CSR_PROCESS 구조체를 이 CsrRootProcess의 list entry에 집어넣는다.
CsrInsertProcess was a key to me, to find a CsrRootProcess.
It is called from CsrCreateProcess exported but not documented function, nearly to the end.
CsrInsertProcess는 CsrRootProcess를 발견하기 위해 나에게 매우 중요했다.
그것은 CsrCreateProcess로부터 호출되는 export된 함수지만 문서화 되지는 않은 함수이며 거의 끝에서 가까스로 발견할 수 있었다.
.text:75B15E94 call CsrSetBackgroundPriority
.text:75B15E99 mov dword ptr [esi+64h], 280h
.text:75B15EA0 mov eax, large fs:18h
.text:75B15EA6 mov eax, [eax+3Ch]
.text:75B15EA9 push esi ; a3
.text:75B15EAA push dword ptr [eax+20h] ; a2
.text:75B15EAD xor esi, esi
.text:75B15EAF push esi ; Session
.text:75B15EB0 call CsrInsert Process
CsrInsertProcess looks very interesting.
CsrInsertProcess 매우 재미있어 보인다.
.text:75B14CC9 ; int __stdcall CsrInsertProcess(int Session, int a2, int a3)
.text:75B14CC9 CsrInsertProcess proc near ; CODE XREF: sub_75B13BF8+12Ap
.text:75B14CC9 ; CsrCreateProcess+214p
.text:75B14CC9
.text:75B14CC9 Session = dword ptr 8
.text:75B14CC9 a2 = dword ptr 0Ch
.text:75B14CC9 a3 = dword ptr 10h
.text:75B14CC9
.text:75B14CC9 mov edi, edi
.text:75B14CCB push ebp
.text:75B14CCC mov ebp, esp
.text:75B14CCE mov eax, [ebp+Session]
.text:75B14CD1 push esi
.text:75B14CD2 mov esi, [ebp+a3]
.text:75B14CD5 mov [esi+18h], eax
.text:75B14CD8 mov ecx, CsrRootProcess
.text:75B14CDE mov edx, [ecx+0Ch]
.text:75B14CE1 add ecx, 8
.text:75B14CE4 lea eax, [esi+8]
.text:75B14CE7 mov [eax], ecx
.text:75B14CE9 mov [eax+4], edx
.text:75B14CEC push edi
.text:75B14CED mov [edx], eax
.text:75B14CEF mov [ecx+4], eax
.text:75B14CF2 xor edi, edi
.text:75B14CF4
.text:75B14CF4 loc_75B14CF4: ; CODE XREF: CsrInsertProcess+48j
.text:75B14CF4 mov eax, CsrLoadedServerDll[edi]
.text:75B14CFA test eax, eax
.text:75B14CFC jz short loc_75B14D0B
.text:75B14CFE mov eax, [eax+44h]
.text:75B14D01 test eax, eax
.text:75B14D03 jz short loc_75B14D0B
.text:75B14D05 push esi
.text:75B14D06 push [ebp+a2]
.text:75B14D09 call eax
.text:75B14D0B
.text:75B14D0B loc_75B14D0B: ; CODE XREF: CsrInsertProcess+33j
.text:75B14D0B ; CsrInsertProcess+3Aj
.text:75B14D0B add edi, 4
.text:75B14D0E cmp edi, 10h
.text:75B14D11 jb short loc_75B14CF4
.text:75B14D13 pop edi
.text:75B14D14 pop esi
.text:75B14D15 pop ebp
.text:75B14D16 retn 0Ch
.text:75B14D16 CsrInsertProcess endp
Regarding on this I've build a pseudo code for it.
이 부분을 제외한 pseudo code를 만들어보았다.
CsrInsertProcess( .... )
{
PCSR_SERVER_DLL ServerDll;
ULONG i;
InsertToList(&CsrRootProcess->ListLink, &CsrProcess->ListLink);
for (i = 0; i < CSR_SERVER_DLL_MAX; i++)
{
ServerDll = CsrLoadedServerDll;
if (ServerDll && ServerDll->NewProcessCallback)
{
(*ServerDll->NewProcessCallback)(CurrentProcess, CsrProcess);
}
}
}
As you see CsrRootProcess contains a linked list of processes.
But this is not very good way to locate this unexported symbol.
There is another exported function from which is easy extract this symbol.
이미 보았듯이 CsrRootProcess는 프로세스들의 linked list를 포함하고 있다.
그러나 이것은 이 unexport된 symbol을 찾아내는 좋은 방법이 아니다.
쉬운 추출물이 이 symbol인 다른 export된 함수가 있다.
It is CsrLockProcessByClientId.
.text:75B152D3 CsrLockProcessByClientId proc near
.text:75B152D3
.text:75B152D3 arg_0 = dword ptr 8
.text:75B152D3 arg_4 = dword ptr 0Ch
.text:75B152D3
.text:75B152D3 mov edi, edi
.text:75B152D5 push ebp
.text:75B152D6 mov ebp, esp
.text:75B152D8 push ebx
.text:75B152D9 push esi
.text:75B152DA push edi
.text:75B152DB mov edi, offset unk_75B189A0
.text:75B152E0 push edi
.text:75B152E1 call ds:RtlEnterCriticalSection
.text:75B152E7 mov edx, [ebp+arg_4]
.text:75B152EA and dword ptr [edx], 0
.text:75B152ED mov esi, CsrRootProcess
.text:75B152F3 add esi, 8
Once you get CsrRootProcess it is possible to list most of existing processes via simple list entry parsing, where each list entry belongs to CSR_PROCESS structure. However I found this detection method is quite unstable. I don't know why, maybe because of not enough List locking, but to check this it will be required inject detectors code inside csrss.exe address space and try to get list of processes. Since this is more than should be for PoC I'm leaving this idea and problem for everybody else, who wants to try this.
당신이 한번 CsrRootProcess를 얻었다면 간단한 list entry parsing을 통해 존재하는 프로세스들의 대부분을 list하는 것이 가능하다. 각 리스트 엔트리는 CSR_PROCESS 구조체를 포함하고 있다. 하지만 왜 그런지는 알 수 없지만 나는 이 검출 방법을 꽤 불안함을 발견했다. 아마도 List locking가 충분하지 않아서이기 때문일 것이다. 그러나 이것은 csrss.exe 주소 공간 내에 검출 코드를 삽입하고 프로세스들의 목록을 얻으려고 시도하는 것을 필요로 한다. 이것은 PoC의 성격이 강하기때문에 이 아이디어만 남겨두고 문제 해결은 이것을 해결하기 원하는 다른 모든 사람들에게 남긴다.
Besides this csrss also keeps a list of threads in another unexported symbol called CsrHashThread. It is a array of 256 items, where every item represents LIST_ENTRY. Each list entry item related to _CSR_THREAD structure.
게다가 이 csrss는 역시 다른 unexport된 symbol인 CsrHashThread 내의 스레드들의 리스트를 유지한다. 이것은 256 아이템의 배열이며 모든 아이템은 LIST_ENTRY 구조체로 나타내어진다. 각 list entry item은 _CSR_THREAD 구조체와 연관이 있다.
typedef struct _CSR_THREAD { // <size 0x38>
union _LARGE_INTEGER CreateTime;
struct _LIST_ENTRY Link;
struct _LIST_ENTRY HashLinks;
struct _CLIENT_ID ClientId;
struct _CSR_PROCESS* Process;
struct _CSR_WAIT_BLOCK* WaitBlock;
void* ThreadHandle;
unsigned long Flags;
unsigned long ReferenceCount;
unsigned long ImpersonateCount;
} CSR_THREAD, *PCSR_THREAD;
This CsrHashThread symbol can be easily obtained from exported function CsrLockThreadByClientId.
이 CsrHashThread symbol은 export된 함수인 CsrLockThreadByClientId 로부터 쉽게 얻을 수 있다.
.text:75B15353 CsrLockThreadByClientId proc near
.text:75B15353
.text:75B15353 a1 = dword ptr 8
.text:75B15353 a2 = dword ptr 0Ch
.text:75B15353
.text:75B15353 mov edi, edi
.text:75B15355 push ebp
.text:75B15356 mov ebp, esp
.text:75B15358 push ebx
.text:75B15359 push esi
.text:75B1535A push edi
.text:75B1535B mov esi, offset unk_75B189A0
.text:75B15360 push esi
.text:75B15361 call ds:RtlEnterCriticalSection
.text:75B15367 mov ebx, [ebp+a1]
.text:75B1536A mov edi, [ebp+a2]
.text:75B1536D and dword ptr [edi], 0
.text:75B15370 mov eax, ebx
.text:75B15372 and eax, 0FFh
.text:75B15377 lea edx, CsrHashThread.anonymous_0[eax*8]
.text:75B1537E mov ecx, [edx]
With it help I was able to build another list of processes.
이것의 도움으로 나는 프로세스들의 또 다른 목록을 작성할 수 있었다.
So the most valuable here CsrRootProcess parsing. But only CLIENT_ID and some little information is available.
I've spend some time on building a little proof-of-concept detector which will take advantage of CsrRootProcess and CsrHashThread array parsing.
그래서 대부분의 유용한 값들은 CsrRootProcess를 parsing해서 얻을 수 있다. 그러나 오직 CLIENT_ID와 몇 개의 작은 정보만이 이용가능하다. 나는 CsrRootProcess와 CsrHashThread 배열을 파싱의 유용성을 이용하는 작은 개념증명 디텍터를 설계하는데 어느 정도의 시간을 소비했다.
Results with rootktis was really amazing. Even if CSRSS internal structures doesn't gives a lot of information about processes (there is no useful pointers to ETHREAD nor EPROCESS, or maybe I must to clean my glasses?) even the little piece of information was enough to detect EVERYTHING from PoC I've.
rootki들과의 결과는 정말 놀랍다. CSRSS 내부 구조체가 프로세스들에 관한 많은 양의 정보를 주지 않음에도 불구하고(ETHREAD나 EPROCESS를 가리키는 어떤 유용한 포인터도 없고, or maybe I must to clean my glasses?) 작은 정보의 조각은 내가 설계한 PoC로부터 모든것을 탐지하기에 충분했다.
However I do believe, that such kind of detection is useless, because of restricted information it provides. So because of this you are reading this now
하지만 나는 그런 종류의 탐지는 유용성이 적다고 믿고 있다. 그것이 제공하는 제한된 정보 때문이다. 그래서 이런 이유 때문에 당신은 이것을 지금 읽고 있다.
See screenshots (they are not fake). Please do not bother me with asking a samples to test. If you don't have them now, then it must the reasons why you haven't them.
스크린샷을 봐라(구라가 아니다). 제발 테스트 하기 위해 샘플에 대한 질문으로 나를 괴롭히지 말아달라. 만약 당신이 지금 그들을 가지고 있지 않다면 왜 당신이 그들을 가지고 있지 않은지의 이유가 있다.
CsrWalker against RKDEMO v1.2.
http://img129.imageshack.us/my.php?image=cwalker2rkdemo12rq1.gif
CsrWalker against PHIDE_EX
http://img360.imageshack.us/my.php?image=cwalker3phideexpocbh1.gif
CsrWalker against Z0mBiE v1.1
http://img367.imageshack.us/my.php?image=cwalker2z0mbiepocra1.gif
CsrWalker against n0name PoC
http://img212.imageshack.us/my.php?image=cwalker1n0namepocdb0.gif
The most cool part of this -> all this done from User Mode, and all this easy to port on every NT version since 2000 till 2008.
The most cool part of this -> 이것은 모두 유저모드에서 실행되었다. 그리고 2000부터 2008에 이르기까지 모든 NT 계열에 쉽게 포팅할 수 있다.
Important Vista addition.
Since Vista there are few csrss.exe presents in memory. This was done for more isolation of the SYSTEM account processes from all others (I suppose each new user account now have its own csrss.exe). So under Vista these method of processes detection was slightly updated. Before parsing csrss.exe it is required to build a list of csrss.exe processes and then parse each of them.
비스타에 대한 중요한 추가가 있다.
비스타는 메모리 내에서 csrss.exe를 나타낸다. 이것은 SYSTEM 계정의 프로세스들을 다른 프로세스들로부터 더욱 분리시키기 위하여 이렇게 실행된다(나는 각 새로운 사용자 계정이 자신의 csrss.exe를 가지고 있을 것이라 생각한다). 그래서 비스타 하에서 이 방법의 프로세스 탐지방법은 약간 업데이트 되었다. csrss.exe 를 파싱하기 전에 csrss.exe 프로세스들의 리스트를 만들 필ㄹ요가 있고 그 후에 그들 각각을 파싱한다.
It was simple for XP - get a csrss.exe process by using CsrGetProcessId function. All what it is doing -> reading variable that holds process id of csrss.exe
XP에서는 간단하다. CsrGetProcessId 함수를 사용해 csrss.exe 프로세스를 얻을 수 있기 때문이다. 이것이 실제로 하는 것은 -> csrss.exe의 프로세스 id를 유지하는 변수를 읽는 것이다.
.text:77F5BAE2 public CsrGetProcessId
.text:77F5BAE2 CsrGetProcessId proc near
.text:77F5BAE2 mov eax, CsrProcessId
.text:77F5BAE7 retn
.text:77F5BAE7 CsrGetProcessId endp
Since Vista we can't anymore relay only on this value. The best way to get list of available csrss.exe processes will be allocate global table of the handles, and try to retrieve their names, because csrss exclusively keeps two handles of type Port/ALPC named ApiPort and SbApiPort. However ALPC object under Vista have some open/query restrictions (for example try to look on it properties with Process Explorer you will be surprised), and this is not a way. There are few possible solutions one of them already implemented in CsrWalker. First - search for all csrss.exe named processes and try to parse them. Not bad solution, but not true hackers way. Second will be try to do experiment and inject your code in one of the SYSTEM account processes - for example one of the svchost.exe, then try from it call the same CsrGetProcessId. I'm left all this to somebody who wants to try and improve/bypass CsrWalker.
비스타에서 우리는 더 이상 이 값을 이용해 릴레이를 할 수 없다. 이용 가능한 csrss.exe 프로세스들의 목록을 얻는 가장 좋은 방법은 handle들의 전역 테이블을 할당하고 그들의 이름을 추출하는 것일 것이다. 왜냐하면 csrss 는 ApiPort와 SbApiPort라고 불리는 Port/ALPC의 두 개 타입의 핸들을 독점적으로 유지하기 때문이다. 하지만 비스타 하의 ALPC 오브젝트는 몇몇 open/query에 제한이 걸려있고(예를 들어 process Explorer로 그것의 등록정보를 볼려고 시도할 때 깜짝 놀랄 것이다) 이것은 올바른 길이 아니다. CsrWalker에는 몇몇 가능한 해결책들 중의 하나가 이미 구현되어 있다. 첫번째로 모든 csrss.exe 이름을 가진 프로세스들을 찾아 그들을 파싱한다. 좋지않은 해결책은 아니지만 진정한 hacker들의 길은 아니다. 두번째는 실험하고 당신의 코드를 SYSTEM 계정 프로세스들의 하나에 삽입한다 - 예를 들어 svchost.exe의 하나에, 그리고 바로 그 프로세스에서 CsrGetProcessID를 호출한다. 나는 CsrWalker를 개선/우회를 시도할 누군가에게 이 모든 것을 남긴다.
Keep in mind - this is a PoC. So it can work, and it can not work. No BSOD's I promise.
명심하라. 이것은 PoC이다. 그래서 그것은 동작할 수도, 동작하지 않을 수도 있다. 내 생각에는 어떤 BSOD도 없을 것이다.
# by | 2008/09/30 16:38 | Windows | 트랙백(1) | 핑백(1) | 덧글(2)





☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
제목 : csrsrv.dll 의 CsrRootProcess조..
lucid7님의 블로그를 통하여 csrss 프로세스의 unexport 구조체를 통하여 숨겨진 프로세스를 탐지하는 법을 알게 되었다. 물론 루트킷사이트에서 제일먼저 보긴했지만, lucid7님이 번역하셔서 쉽게 이해할 수 있었다. "csrss는 유저영역의 통제자로써 활동하기때문에 모든 Win32 프로세스들이 생성시 csrss에 알려야만 한다. csrss.exe는 csrsrv.dll의 노출되지않은 심벌(Unexported symbol)인 CsrRo......more
... 출처 : http://lucid7.egloos.com/2079525Seek'n'Hide game was always interesting. Especially when this seek'n'hide stuff is r ... more