Anti-Reversing Code [3/4]

S/W 역공학 분석(Reversing) 2008. 12. 22. 11:00 Posted by TEAMCR@K
▷ 작성자 : Hong10 (hong10@a3sc.co.kr)
▷ 편집자 : 니키 (ngnicky@a3sc.co.kr)

문서 작성일 : 2008년 12월 19일
최종 수정일 : 2008년 12월 19일

Hong10님께서 작성하신 "Anti Reversing Code" 큰 주제를 바탕으로 4개로 나눠 작성하였습니다.


2.1 PEB.NtGlobalFlag
PEB은 BeingDebugged 플래그 외에도, PEB는 NtGlobalFlag라는 필드를 갖고 있는데 NtGlobalFlag는 PEB으로부터 0x68 위치에 존재합니다. LiveKD를 이용하여 PEB의 구조체 값을 확인 해보았습니다.
 


이 플러그인의 값은 디버깅 중이 아니라면 0x0 값이 담기지만 디버깅 중이라면 0x70 값이 담겨 집니다. 그와 같은 것을 이용하여 디버깅 탐지를 합니다.

다음은 NtGlobalFlag Code 부분입니다.

 .386
.model flat, stdcall
option casemap :none   ; case sensitive

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

.data
DbgNotFoundTitle db "Debugger status:",0h
DbgFoundTitle db "Debugger status:",0h
DbgNotFoundText db "Debugger not found!",0h
DbgFoundText db "Debugger found!",0h
.code
start:

ASSUME FS:NOTHING
MOV EAX,DWORD PTR FS:[30h]
ADD EAX,68h
MOV EAX,DWORD PTR DS:[EAX]
CMP EAX,70h
JE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
@DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

@exit:

PUSH 0
CALL ExitProcess

end start




여기서 ASSUME FS:NOTHING 이란 구문은 세그먼트 레지스트의 주소값을 재할당 하는 것이 아니라 이 디렉티브를 맞나면 실행 시에 어셈블러가 주소를 계산하는 방법을 변경한다. 즉 FS 세그먼트에 NOTHING이라는 속성을 붙이는 의미이다.
해당 내용을 더 깊에 알아 보았는데 다음과 같은 사이트에서 친철히 설명 해주고 있었다.
http://www.winasm.net/forum/index.php?showtopic=2082

간단히 축약해서 애기하자면(맞는 애기인지도 모르겠다,필자의 영어실력은 가히 밑바닥 수준이라서…) FS:[0] 은 Exception handler를 Default를 가지고 있다. MASM 컴파일러는 기본적으로 이 레지스터를 사용할 때 ERORR를 뱉기 때문에 위와 같이 ASSUM FS:NOTHING 을 써주면 그와 같은 ERORR Check를 Remove 해준다고 설명이 되어 있다.

다음으로FS:[30] 는 PEB의 위치에서 ADD EAX,68h 에는 NtGlobalFlag 가 존재한다. 해당 코드를 올리로 열어 보았다.
 

Olly 로 확인한 NtGlobalFlag 루틴

2.2 NtGlobalFlag 우회
해당 탐지를 우회는 간단하다. 직접 코드 패치를 하여 바꾸 거나 혹은 플래그 값을 수정하거나 이다. 하지만 이것도 역시 올리에서 플러그인 형태로 지원을 하고 있다. 다음 사이트에서 다운로드 하여 쓰시면 됩니다.
http://www.openrce.org/downloads/details/241/Olly_Advanced

해당 플러그 인을 실행하고 NtGlobalFlag 를 체크 뒤 확인 해주면 자동적으로 리턴시 플래그 값을 수정하여 디버거 탐지를 우회 하게 됩니다. 그 외 코드 패치 등 다양한 방법 이 존재 합니다. 툴로써 좀 더 편하게 하는게 좋겠죠 ??;
 
다음은 Olly Advanced NtGlobalFlag 입니다.



3.1 Heap.HeapFlag , Heap.ForceFlags
ProcessHeap 은 PEB 에서 0x18 만큼 떨어져 있고 이 플래그는 프로세서가 디버깅 될 때 heap 이 만들어 지고 이때 설정 되는 플래그들은 HeapFlag와 ForceFlas 입니다. 처음 프로그램이 힙 영역을 만들면 ForceFlags 에는 0x0 값이 HeapFlag 에는 0x2 값이 설정 됩니다. 하지만 디버깅 중이라면 NtGlobalFlag 에 따라 두개의 플래그 값이 변경 됩니다.
만약 디버깅이 탐지 되어 변경 되었다면 FoceFlags에는 0x40000060값이 할당 되며 HeapFlag 에는 0x50000062 값이 할당 됩니다.이때 변경된 값을 체크 하므로써 디버깅의 유무를 판별합니다.

다음은 PEB 의 ProcessHeap 구조체 offset 입니다.


여기서 ProcessHeap의 구조체 를 살펴보면 언급한 두개의 값들이 존재합니다.



다음은 Process_Heap Code 부분입니다.

 .386
.model flat, stdcall
option casemap :none   ; case sensitive

include c:\masm32\include\windows.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

.data
DbgNotFoundTitle db "Debugger status:",0h
DbgFoundTitle db "Debugger status:",0h
DbgNotFoundText db "Debugger not found!",0h
DbgFoundText db "Debugger found!",0h
.code

start:

ASSUME FS:NOTHING
MOV EAX,DWORD PTR FS:[18h] ;TEB
MOV EAX,DWORD PTR [EAX+30h] ;PEB
MOV EAX,DWORD PTR[EAX+18h] ;Process_Heap
CMP DWORD PTR DS:[EAX+10h],0 ;Force_Flags
JNE @DebuggerDetected

MOV EAX,DWORD PTR FS:[18h] ;TEB
MOV EAX,DWORD PTR [EAX+30h] ;PEB
MOV EAX,DWORD PTR[EAX+18h] ;Process_Heap
CMP DWORD PTR DS:[EAX+0ch],2 ;Force_Flags
JNE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
@DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

@exit:

PUSH 0
CALL ExitProcess

end start


3.2 Heap.HeapFlag , Heap.ForceFlags 우회
역시 앞선 방법과 동일 하게 코드 패치 및 플래그 변조로 우회를 할수 있다. 또는 올리 플러그인 OllyAdvanced 를 이용하면 쉽게 플래그 값을 자동으로 수정 해준다.
 
다음은 Olly Advanced HeapFlag,ForceFlags 우회 부분입니다.




4.1 NtQueryInformationProcess
CheckRemoteDebuggerPresent API는 디버거가 프로세서를 attach 하는 것을 감지합니다. 이 API 는 내부적으로 NtQueryInformationProcess를 호출합니다.이 함수는 또한 내부적으로 커널 구조체인 EPROCESS의 DebugPort 플래그를 검사합니다. 가령 유저 모드 디버거가 프로세서를 attach 한 상태이면 DebugPort 플래그 값은 0이 아닌값으로 설정이 됩니다.
앞서 저희는 PEB에 대해 간략하게 알아 봤습니다. PEB은 유저모드 레벨 프로세스에 대한 추가 정보 이며 여기서 설명하는 EPROCESS는 커널에서 프로세스를 관리하기 위해 사용하는 구조체 정도가 되겠습니다. 또한 NtQueryInformationProcess 을 내부적으로 불러 낼때에는 인자 값중 ProcessInformation 값은 7이 됩니다.
또한 DebugPort의 오프셋은 0x120 위치에 존재하며 유저모드 에서 디버깅 중이라면 NtQueryInformationProcess 함수의 인자 값중 hProcess는 0xfffffff 값이 되며 그렇지 않을 경우에는 0이라는 값이 담기게 됩니다

다음은 NtQueryInformationProcess Code 부분입니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc

      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib

    .data
       DbgNotFoundTitle db "Debugger status:",0h
       DbgFoundTitle db "Debugger status:",0h
       DbgNotFoundText db "Debugger not found!",0h
       DbgFoundText db "Debugger found!",0h
       ntdll db "ntdll.dll",0h
       zwqip db "NtQueryInformationProcess",0h
    .data?
       NtAddr dd ?
       MinusOne dd ?
    .code

start:

MOV [MinusOne],0FFFFFFFFh

PUSH offset ntdll ;ntdll.dll
CALL LoadLibrary

PUSH offset zwqip ;NtQueryInformationProcess
PUSH EAX
CALL GetProcAddress

MOV [NtAddr],EAX

MOV EAX,offset MinusOne
PUSH EAX
MOV EBX,ESP

PUSH 0
PUSH 4
PUSH EBX
PUSH 7
PUSH DWORD PTR[EAX]
CALL [NtAddr]

POP EAX

TEST EAX,EAX
JNE @DebuggerDetected

PUSH 40h
PUSH offset DbgNotFoundTitle
PUSH offset DbgNotFoundText
PUSH 0
CALL MessageBox

JMP @exit
  @DebuggerDetected:

PUSH 30h
PUSH offset DbgFoundTitle
PUSH offset DbgFoundText
PUSH 0
CALL MessageBox

  @exit:

PUSH 0
CALL ExitProcess

end start



위의 코드에서 ? 는 아마 변수를 선언할 때 값을 할당하지 않은 상태일꺼라 생각이 듭니다. 어째든 위의 코드에서 커널 API를 수행하기 위해서 LoadLibrary와 GetProcAddress를 이용하여 해당 함수 주소를 얻은뒤 앞서 설명한 인자값을 토대로 호출 하고 디버깅 중이라면 리턴되는 값을 체크하여 디버거 존재 유무를 판별 하고 있습니다.

다음 그림은 위의 코드를 Olly에서 확인한 장면(NtQueryInformationProcess in Olly)입니다.
 


그림에서 알수 있듯이 인자 값이 저런식으로 들어가 호출하게 되어야지 디버깅 체크를 할수 있습니다.


4.2 NtQueryInformationProces 우회
이 방법을 우회하는 방법 역시 여러가지가 존재 할수 있습니다. 다만 올리 플러그 인중 Olly Advanced 는 NtQueryInformationProcess 의 인자 값중 하나인 hProcess를 0으로 만듬으로써 해당 루틴을 우회하게 될꺼라고 생각했으나 잘 안되는군요. 일단은 그냥 주 루틴이 나오면 리턴되는 부분에 EAX값을 0을 설정하면 우회가 됩니다.
 



5.1 Debugger Interrupts

이 방법은 디버거가 하나의 소프트 브레이크 포인트를 수행하려고 할 때 INT3(0xcc)를 삽입 함으로 써 수행이된다. 인터럽터 가 수행이 된다는 말은 즉 예외처리가 일어 난다는 의미이다. 하지만 디버거가 디버깅 수행중 일 때 같은 OP 코드인 INT3을 만난 다면 예외 처리없이 수행을 하게된다.
이 Debugger Interrupts는 바로 이러한 것을 착안하여 안티 디버깅을 수행한다. 즉 Interrupt 수행중에 같은 인터럽터 코드를 만났을 때 exception을 하지 않는 경우를 디버깅 중이라고 판별하게 된다. 좀더 유연한 이해를 위해 아래 코드를 참고 한다.

다음은 Debugger Interrupts 부분입니다.

 .386
      .model flat, stdcall
      option casemap :none   ; case sensitive

      include c:\masm32\include\windows.inc
      include c:\masm32\include\user32.inc
      include c:\masm32\include\kernel32.inc

      includelib c:\masm32\lib\user32.lib
      includelib c:\masm32\lib\kernel32.lib

    .data
msgTitle db "Execution status:",0h
msgText1 db "No debugger detected!",0h
msgText2 db "Debugger detected!",0h
    .code

start:

ASSUME FS:NOTHING
PUSH offset @Check
PUSH FS:[0]
MOV FS:[0],ESP

; Exception
INT 3h

PUSH 30h
PUSH offset msgTitle
PUSH offset msgText2
PUSH 0
CALL MessageBox

PUSH 0
CALL ExitProcess

; SEH handleing
@Check:
POP FS:[0]
ADD ESP,4

PUSH 40h
PUSH offset msgTitle
PUSH offset msgText1
PUSH 0
CALL MessageBox

PUSH 0
CALL ExitProcess

end start


위에 코드에서 @Check는 exception이 일어 날 때 등록되는 SEH 이다. INT 3이 수행될 때 익셉션이 일어 나지 않는다면 Debuger dectect 메시지가 출력 될것이다.다음은 올리(Debugger Interrupt in Olly)에서 살펴본 모습이다.

 


저기에서 INT3을 유심히 살펴 보면 저기가 분기문이라고 이해하시는 편이 더 수월 할수도 있습니다 INT3이 exception을 일으킨다면 0x0040103e로 향하게 될것이며 그렇지 않을경우 0x00401024코드로 향하게 될것입니다.


5.2 Debug Interrupts 우회
우회 방법은 간단합니다. 올리에서 디버깅 옵션에 보면 INT 3 을 만났을 때 무시하지 않고 SEH를 following 하라는 옵션을 선택하여 줍니다.




다음과 같이 우회를 하게 된다면 Exception Handle로 빠지는 루틴(Exception Handle 을 호출하는 루틴)을 보실수 있습니다.



[계속...]

참고사이트 및 문서
EDIT PLUS 를 이용한 MASM 환경 구축
http://mysilpir.net/entry/EditPlus-Assembly-%EC%84%A4%EC%A0%95-MASM
ANTI REVERSE
http://zesrever.xstone.org/
http://slaxcore.tistory.com
http://beist.org/research/public/artofunpacking/artofunpacking.pdf
http://openrce.org
정덕영님의 윈도우 구조와 원리
THX to zersrever,slaxcore,ashine,ap0x,정덕영FROM Hong10


Copyright(c) 1998-2008 A3 Security ,LTD


Disclaimer
※ 현재 ㈜에이쓰리시큐리티에서 테스트 및 분석 중에 있으며, 이 문서는 계속 업데이트될 것입니다. 본 문서는 보안취약점으로 인한 피해를 최소화하는 데 도움이 되고자 작성되었으나, 본 문서에 포함된 대응방안의 유효성이나 기타 예상치 못한 시스템의 오작동 발생에 대하여서는 ㈜에이쓰리시큐리티에서는 일체의 책임을 지지 아니합니다.