본문 바로가기

My Study/Reversing

NtSetInformationThread 를 이용한 Anti-Debugging

밥 먹으면서 뭘 보다가 그냥 잼난거 같아서 정리해봅니다.

햇반 질려요 .. ㅠ_ㅠ ..



이번엔 NtSetInformationThread  함수를 사용한 안티디버깅 방법인데요.

함수 정의는 아래와 같이 생겼습니다.


NTSTATUS ZwSetInformationThread(
  __in  HANDLE ThreadHandle,
  __in  THREADINFOCLASS ThreadInformationClass,
  __in  PVOID ThreadInformation,
  __in  ULONG ThreadInformationLength
);


MSDN의 설명을 보니 스레드의 우선순위를 지정한다고 나와있습니다. 스레드 우선순위와 안티디버깅?

MSDN을 더 뒤져봐도 스레드 우선순위 관련 설명밖에 안나와있군요. 그러면 전 문서에 나온 설명을 적겠습니다.


Windows 2000은 오직 anti-debugging 목적으로 함수를 확장했다는데요. 

바로 ThreadInformationClass 에 ThreadHideFromDebugger(0x11) 멤버입니다.

ntdll의 NtSetInformationThread함수를 사용해 각각의 스레드 마다 지정될 수 있습니다.


바로 코드를 봐보겠습니다.




50줄도 안되는 짧막한 코드네요.

보시면 39번째 라인에서 NtSetInfortmationThread 함수를 호출하고 있습니다. 인자를 보면 현재 쓰레드를 대상으로 말이죠.


실행결과 화면입니다.



NtSetInformationThread 함수의 리턴 값이 0x00000000로 제대로 호출은 되고 있군요.

이제 프로그램을 Debugger에 Attach를 시켜보겠습니다... 이 부분은 동영상으로 봐보죠 그게 더 보기 쉽겠네요.



(화면이 잘 안보실태니 브라우져 크기를 조절하셔서 보세요~)

동영상에서 보이시는 바와 같이 작동하게 됩니다.


그러면 문서에 있는 설명을 조금 더 읽어보죠.


이 함수가 호출되게 되면 해당 thread는 계속 실행되지만 debugger 같은 경우는 더이상 thread에 관련된 어떠한 이벤트를 받지 못한다고 되어 있습니다. 즉, BP를 걸었으면 debugger에 breakpoints 가 걸렸다는 이벤트가 전달이 되야되는데 전달이 되지 못하므로 CC를 만난 타겟 프로그램은 그냥 예외가 나버려 죽게되는 것입니다.


Olly Advanced 플러그인에 있는 ZwSetInformationThread 우회 설정을 해놓으면 될까 싶어서 해보았는데 타겟 프로그램을 바로 디버거에 붙혀서 실행하면 플러그인에 의하여0xFFFFFFFE 를 뱉으며 우회가 되지만 실행 중인 프로그램을 Attach를 할 경우는 이미 

ZwSetInformationThread 함수가 호출된 상황이기 때문에 이 경우는 어떻게 우회를...


근데 이 방법을 몰랐을 경우에는 Software BP를 탐지하는 안티 디버깅이라고 착각을 할수도 있겠네요.


============================ 커널 분석 과정 ======================== 굳이 볼 필요는 없습니다.

그러면 NtSetInformationThread는 user 상에선 어떻게 작동하는지 확인하지 못합니다.

소화할겸 오랜만에 커널 디버깅이나 해봐야겠습니다.


일단 cpu제어를 제 프로세스의 스레드 context로 옮겨놓고 멈춰놓고 커널 내부적으로 NtSetInformationThread 에 BP를 걸어 멈추고 디버깅을 시작해보았습니다. 맞게 표현한건지 모르겠군요.

NtSetInformationThread 시작 루틴

8057afa7 68d0000000 push 0D0h 8057afac 68a05c4f80 push offset nt!ObWatchHandles+0x32c (804f5ca0) 8057afb1 e8bd9ef6ff call nt!_SEH_prolog (804e4e73) 8057afb6 64a124010000 mov eax,dword ptr fs:[00000124h] 8057afbc 8945dc mov dword ptr [ebp-24h],eax 8057afbf 8a8040010000 mov al,byte ptr [eax+140h] 8057afc5 8845e4 mov byte ptr [ebp-1Ch],al 8057afc8 84c0 test al,al 8057afca 0f847f8b0600 je nt!NtSetInformationThread+0x9d (805e3b4f) 8057afd0 8365fc00 and dword ptr [ebp-4],0 8057afd4 8b450c mov eax,dword ptr [ebp+0Ch] ; 두번째 인자였던 0x11 가져옴 8057afd7 48 dec eax 8057afd8 48 dec eax 8057afd9 740f je nt!NtSetInformationThread+0x3b (8057afea) 8057afdb 48 dec eax 8057afdc 48 dec eax 8057afdd 740b je nt!NtSetInformationThread+0x3b (8057afea) 8057afdf 83e803 sub eax,3 8057afe2 0f84c2a40800 je nt!NtSetInformationThread+0x74 (806054aa) 8057afe8 48 dec eax 8057afe9 48 dec eax 8057afea 6a04 push 4 8057afec 5b pop ebx 8057afed 895dd8 mov dword ptr [ebp-28h],ebx 8057aff0 8b7d14 mov edi,dword ptr [ebp+14h] 8057aff3 8b7510 mov esi,dword ptr [ebp+10h] 8057aff6 85ff test edi,edi 8057aff8 7423 je nt!NtSetInformationThread+0x6c (8057b01d) ; 점프 8057b01d 83caff or edx,0FFFFFFFFh 8057b020 8955fc mov dword ptr [ebp-4],edx 8057b023 6a0a push 0Ah 8057b025 58 pop eax 8057b026 8b4d0c mov ecx,dword ptr [ebp+0Ch] ; 0x11 다시 가져옴 8057b029 3bc8 cmp ecx,eax 8057b02b 0f8fb82c0300 jg nt!NtSetInformationThread+0x5c4 (805adce9) ; 0x0A보다 크므로 점프 805adce9 8bc1 mov eax,ecx 805adceb 6a0d push 0Dh 805adced 59 pop ecx 805adcee 2bc1 sub eax,ecx 805adcf0 0f85337a0500 jne nt!NtSetInformationThread+0x5d1 (80605729) ; 0x0D와 다르므로 점프 80605729 48 dec eax 8060572a 0f8455010000 je nt!NtSetInformationThread+0x719 (80605885) 80605730 48 dec eax 80605731 0f84e6000000 je nt!NtSetInformationThread+0x6bb (8060581d) 80605737 48 dec eax 80605738 48 dec eax 80605739 0f84a3000000 je nt!NtSetInformationThread+0x680 (806057e2) ; 여기서 eax가 0 되면서 점프 806057e2 85ff test edi,edi 806057e4 0f854bfeffff jne nt!NtSetInformationThread+0x3f2 (80605635) 806057ea 57 push edi 806057eb 8d45ac lea eax,[ebp-54h] 806057ee 50 push eax 806057ef ff75e4 push dword ptr [ebp-1Ch] 806057f2 ff355cb75680 push dword ptr [nt!PsThreadType (8056b75c)] 806057f8 6a20 push 20h 806057fa ff7508 push dword ptr [ebp+8] 806057fd e8578df6ff call nt!ObReferenceObjectByHandle (8056e559) ; 프로그램의 ETHREAD 구조체 주소 얻음 80605802 8bf0 mov esi,eax 80605804 85f6 test esi,esi 80605806 0f8c7f59f7ff jl nt!NtSetInformationThread+0x3a7 (8057b18b) 8060580c 8b4dac mov ecx,dword ptr [ebp-54h] ; 얻은 ETHREAD 구조체 주소 8060580f 8d8148020000 lea eax,[ecx+248h] ; ETHREAD.각종 비트 단위 정보 80605815 f00918 lock or dword ptr [eax],ebx ; edx = 00000100b, ETHREAD.HideFromDebugger 값 1로 셋팅 80605818 e96959f7ff jmp nt!NtSetInformationThread+0x3a2 (8057b186) 8057b186 e87587f6ff call nt!ObfDereferenceObject (804e3900) 8057b18b 8bc6 mov eax,esi 8057b18d eba0 jmp nt!NtSetInformationThread+0x879 (8057b12f) 8057b12f e87a9df6ff call nt!_SEH_epilog (804e4eae) 8057b134 c21000 ret 10h


코드는 방대하지만 그냥 추적하면서 필요한 부분만 추출해봤습니다. 그 중에서도 중요시 봐볼 부분은 아랫 쪽인데 아랫 쪽에서 보면 ETHREAD 구조체 주소를 얻어와 HideFromDebugger 멤버 값을 1로 셋팅하는 부분이 있습니다.

빨간색으로 된 부분만 보시면 됩니다.


이로서 NtSetInforamtionThread 루틴은 끝입니다. -_-;; 고로 최종 작동 코드는 아래와 같은 한줄...


ETHREAD.HideFromDebugger = 0x01;


이때 간단히 저 부분을 0x00으로 다시 패치하면 anti-debugging 우회! .. 뭐 이렇게 하는건 의미가 없네요.

그러면 저 메모리 값(HideFromDebugger)을 어디서 참고해서 쓰고 있는지 봐보겠습니다.


b1d20934 8051a017 nt!DbgkForwardException+0x38 b1d20cf4 804e03f3 nt!KiDispatchException+0x1f4 b1d20d5c 804e0b4c nt!CommonDispatchException+0x4d b1d20d5c 00401068 nt!KiTrap03+0xae WARNING: Frame IP not in any known module. Following frames may be wrong. 0012ffc0 7c817067 0x401068 80582109 f6804802000004 test byte ptr [eax+248h],4

80582110 0f8560b40900 jne nt!DbgkForwardException+0x3a (8061d576) [br=1]


윗 부분은 해당 멤버변수를 참고하는 위치에서 멈췄을 때의 콜스택이고 

아래 부분은 해당 멤버변수를 참고하는 코드입니다.

( 현재 스레드 부분에 BP를 건 후 멈춘 루틴입니다. 단순 Attach를 했을 땐 여기를 오지 않습니다. )


일단 4와 비교한 후 점프를 할지 말지 결정되는 걸로봐서 저 부분 루틴을 따라가면 차이점이 있겠군요.

HideFromDebugger 셋 되어 있으면 점프하고 클리어 되어 있으면 점프를 하지 않습니다.


조금만 더 분석해보니...

점프를 하지 않으면 DbgkpSendApiMessage 함수를 수행하는 반면 점프를 하면 DbgkpSendApiMessage 함수를 실행 못하는 차이점이 있는것 같군요.


이로서 이 방법을 만났을 경우 우회할 수 있는 방법으로는 user 상에서 NtSetInformationThread 함수 호출 시 두번째 인자 값을 그냥 0으로 줘버리는 방법이 있겠군요.


아함.. C언어 수업가야되요.. ㅠ_ㅠ .. 그냥 급하게 정리를 마칩니다. 잉여잉여 -0-;;

'My Study > Reversing' 카테고리의 다른 글

Prefetch queue Anti Debugging  (4) 2012.05.25
백신 Avast의 Anti-attach 우회하기  (11) 2012.04.19
Themida 2.1.2.0 Virtual Machine  (9) 2012.02.01
ARM CPU 공부시작.. ㅠ_ㅠ  (6) 2012.01.25
Code Virtualized  (9) 2011.06.27