Static anti debugging
Static 안티 디버깅
Static 안티 디버깅의 목적
- 디버깅 당하는지 여부 파악하는 기법으로, 만약 디버깅 중이라고 판단되면 일반 실행과는 다른 코드(주로 종료 코드)를 실행하는 것이 핵심.
PEB
- 디버깅 여부 판단하기 위해 PEB(Process Enviroment Block)구조체 정보를 이용할 수 있다.
- 가장 널리 사용되는 안티 디버깅 기법이다.
- window xp sp3의 peb 구조체에서 안티디버깅 기법에 사용되는 멤버
- +0x002 BeingDebugged : UChar : 디버깅 여부를 표시하는 Flag로 사용
- +0x00c Ldr : Ptr32 PEB LDR DATA : Heap 메모리 특성과 관련
- +0x018 ProcessHeap : Ptr32 Void : Heap 메모리 특성과 관련
- +0x068 NtglobalFlah : Uint4B : Heap 메모리 특성과 관련
- 참고
- PEB 주소 구하는 방법 : MOV EAX, DWORD PTR FS:[0x30] ;FS[0x30] = address of PEB
- TEB(Thread Environment Block)을 구하고 ProcessEnvironmentBlock 멤버(+0x30 옵셋)를 이용하는 방법)
- MOV EAX, DWORD PTR FS:[0x18] ;FS[0x18] = addressof TEB
- MOV EAX, DWORD PTR DS:[EAX+0x30] ; DS[EAX+0x30]=address of PEB
BeingDebugged(+0x2)
- PEB.BeingDebugged 멤버(+0x2)의 값은 디버깅 중일 때 1(True)로 세팅되고, 일반 실행인 경우 0(False)으로 세팅
IsDebuggerPresent()
- 회피 방법 : OllyDbg의 Edit 명령을 이용하여 PEB.BeingDebugged 값을 0(False)으로 변경하면 된다.
Ldr (+0xC)
- 디버깅 프로세스는 힙(heap)메모리 영역에 자신이 디버깅 당하는 프로세스라는 표시를 한다.
- 메모리에 사용되지 않는 영역에 0xfeeefeee 값으로 채워 버린다. (프로세스가 디버깅을 당할 때 나타아는 흔적)
- PEB.Ldr 멤버는 PEB LDR DATA 구조체를 가리키는 포인터이다. PEB LDR DATA 구조체는 힙 메모리 영역에 생성되기 때문에 이 영역을 scan해보면 쉽게 알 수 있다.
- 회피방법 : 0xfeeefeee로 채워진 영역을 NULL로 덮어쓰면 된다. 0xfeeefeee영역에 메뉴 ‘Binary - Fill with 00’s’를 누르면 0으로 채워진다.
Process Heap(+0x18)
- HEAP 구조체를 가리키는 포인터
GetProcessHeap()
- IsDebuggerPresent()와 비슷하다. TEB -> PEB -> PEB.Processheap 순서
- 회피방법 : Heap.Flags = 2, Heap.ForceFlags = 0으로 세팅하면 된다.
NtGlobalFlag(+0x68)
- 디버깅 중일 때 PEB.NtGlobalFlag 멤버(+0x68)의 값은 0x70으로 세팅된다.
- 회피방법 : PEB.NtglobalFlag = 0으로 세팅하면 된다.
_
NtQueryInformationProcess()
- ntdll!NtQueryInfomationProcess() API를 이용하면 프로세스의 디버깅 관련 정보를 비롯하여 다양한 정보를 얻을 수 있다.
- 구조체 변수 중 디버거 탐지에 사용되는 것 : PROCESSINFOCLASS ProcessInformationClass 변수에서 ProcessDebugPort(0x7),ProcessDebugObjecthandle(0x1E), ProcessDebugFlags(0x1F)
- 회피방법 : 특정입력(ProcessInfomationClass)값들에 대하여 출력(리턴 - Process Infomation) 값을 조작하는 것.
- 특정입력 : ProcessDebugPort(0x7), ProcessDebugObjectHandle(0x1E), ProcessDebugFlags(0x1F)
- API가 한두 번 정도 호출되는 거라면 디버거에서 수동으로 조작을 하고, 반복적으로 호출된다면 API 후킹을 시켜버리면 된다.
- 보통 plugin(Advanced)를 이용하여 자동으로 API를 후킹한다.
- jmp를 통한 후킹은 보통 함수 시작부분에 넣는다.
- PE프로텍트의 API 후킹 탐지 기법이 있으면, 뒤에 하는게 좋다.
- NtQueryInfomationProcess() API시작 주소의 첫 바이트를 읽어서 후킹 여부를 확인한다.
ProcessDebugPort(0x7)
- 디버깅 중이 아니면, dwDebugPort 변수에 0이 세팅된다.
- 디버깅 중이라면 0xffffffff 이 세팅 된다.
CheckRemoteDebuggerPresent()
- IsDebuggerPresent() API와 비슷하게 디버깅 여부를 판별해 주는 함수
- 차이 : CheckRemoteDebuggerPresent()는 다를 프로세스의 디버깅 여부까지 판단할 수 있다.
- 내부 코드를 보면 NtQueryInfomationProcess(ProcessDebugPort) API를 이용한다.
ProcessDebugObjectHandle(0x1E)
- 프로세스가 디버깅 중이면 Debug Object Handle은 값이 존재하며, 디버깅 중이 아니라면 Debug Object Handle은 NULL이 된다.
ProcessDebugFlags(0x1F)
- ProcessDebugFlags(0x1F)를 입력하여 Debug flag를 구하며, 값이 0이면 디버깅 상태이고, 1이면 디버깅이 아니다.
NtQuerySystemInfomation()
- 현재 OS가 Debug mode로 부팅되었는지를 판단하는 안티 디버깅 기법이다.
- Debug mode는 windbg를 이용한 커널 디버깅하는 경우를 의미한다.
SystemKernelDebuggerInfomation(0x23)
회피방법 : windows xp 의 경우 boot.ini를 편집하고 ‘/debugport=com1 /baudrate=115200 /Debug’ 값을 제거한다. windows 7 인 경우 cmd에서 ‘bcdedit /debug off’ 명령을 내리면 된다. 그리고 재부팅하면, 일반모드(normal mode)로 부팅된다.
NtQueryObject()
- 시스템에서 어떤 디버거가 다른 프로세스를 디버깅 중이라면 그 때 DebugObject 타입의 커널 객체가 생성되는데, 그 DebugObject의 존재를 확인하는 것이다.
- NtQueryObject() API는 시스템의 다양한 종류의 커널 객체 정보를 구해오는 함수이다.
ZwSetInfomationThread()
- 디버깅 당하는 쪽(디버기)에서 강제로 디버거를 떼어내는(Detech) 기법에 대한 설명이다. ZwSetInformationThread() API를 사용하면 자신을 디버깅하고 있는 디버거를 떼어낼 수 있다.
- 스레드에게 정보를 세팅하는 System Native API입니다.
- 회피방법 : ZwSetInfomationThread() API가 호출되기 직전에 스택에 저장된 두 번째 파라미터 ThreadInfomationClass 값을 살펴보고 ThreadHideFromDebugger(0x11) 값이 입력된 경우라면 0으로 변경해주면 된다.
ZWSetInformationThread() API를 후킹하여 같은 방식으로 파라미터를 조작하면 된다.
TLS 콜백 함수
- Entry Point 코드보다 먼저 실행되는 특징 때문에 안티 디버깅에 많이 활용된다.TLS 콜백 함수 내에서 IsDebuggingPresent() 등의 함수로 간단히 디버깅 여부를 판별하여 프로그램을 계속 실행할지 말지 결정할 수 있다.
댓글 없음:
댓글 쓰기