일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- hxd
- 분석 보고서
- 디버깅
- x32dbg
- dotPeek
- UTF-32
- .net
- Visual Studio
- CodeEngn
- CP949
- Static안티디버깅
- Self-Creation
- OllyDbg
- Python
- JIT
- DebugBlocker
- 리버싱핵심원리
- 서비스디버깅
- Exe2Aut
- 서비스프로세스
- Advanced안티디버깅
- Dynamic안티디버깅
- PEImage
- RedLine Stealer
- IL코드
- 안티디버깅
- ImageSwitching
- 리버싱
- PEImageSwitching
- UTF-16
- Today
- Total
-榮-
Static 안티 디버깅 본문
디버거 탐지, 디버깅 환경 탐지, 디버거 강제 분리
많은 static 안티 디버깅은 OS 의존성을 가지고 있다.
1. PEB
현재 프로세스의 디버깅 여부 판단에 사용.
PEB 구조체 정보 이용
- +0x002 BeingDebugged
- +0x00C Ldr
- +0x018 ProcessHeap
- +0x068 NtGlobalFlag
BeingDebugged - 디버깅 여부를 표시하는 Flag
Ldr, ProcessHeap, NtGlobalFlag - 디버깅 프로세스의 독특한 힙(Heap) 메모리 특성과 관련
TEB.ProsessEnvironmentBlock (+ 0x30) == PEB
PEB 구조체구하는법
바로 PEB 구하기
MOV EAX, DWORD PTR FS:[0x30]; FS:[0x30]=address of PEB
TEB 주소를 구하고 ProcessEnvironmentBlock(+0x30) 이용하기
MOV EAX, DWORD PTR FS:[0x18]; FS:[0x18]= address of TEB
MOU EAX, DWORD PTR DS: [EAX+0x30]; DS:[eax+0x30]=address of PEB
-1. BeingDebugged (+0x2) - IsDebuggerPresent()
디버깅 중일 때 1(TRUE)로 세팅 / 일반실행의 경우 0(false)으로 세팅
회피 방법 - OllyDbg의 edit 명령어를 사용하여 PEB.BeingDebugged 값을 0(FALSE)으로 변경
-2. Ldr (+0xC)
디버깅 프로세스는 힙(Heap) 메모리에 디버기가 디버기라는 것을 표기한다.
이때, 눈에 띄는 특징은 힙 메모리의 사용되지 않는 영역을 0xFEEEFEEE 값으로 채운다.
PEB.Ldr은 _PEB_LDR_DATA 구조체 포인터
_PEB_LDR_DATA 구조체는 힙 메모리 영역에 생성 따라서 이 영역을 스캔(Scan)하면 쉽게 알 수 있다.
회피 방법 - 0xFEEEFEEE로 채워진 영역을 NULL로 덮어 씌운다.
but, XP이하에서만 동작, Attach의 경우 힙 메모리 특성이 나타나지 않는다.
-3. ProcessHeap (+0x18) - GetProcessHeap()
Heap 구조체 포인터
Heap 구조체 중 Flags(+0xC)와 Force Flags(+0x10)은 디버깅 중 특정한 값으로 세팅
회피 방법 - Heap.Flags = 2, Heap.ForceFlags = 0으로 세팅 (defulat 값으로 세팅)
but, XP에서만 효과, 7에선 없어진 특성이며 Attach의 경우 이 특성이 나타나지 않는다.
-4. NtGlobalFlag(+0x68)
디버깅 중이면 0x70으로 세팅
회피 방법 - PEB.NtGrobalFlag = 0으로 세팅 (defualt 값으로 세팅)
but, Attach의 경우 변하지 않는다.
2. NtQueryInformationProcess()
ntdll!NtQueryInformationProcess() API로 프로세스의 다양한 정보를 얻을 수 있으며, 그중 디버깅 관련 정보가 포함되어 있다.
NtQueryInformationProcess() API - 지정된 프로세스에 대한 정보를 검색 |
__kernel_entry NTSTATUS NtQueryInformationProcess( [in] HANDLE ProcessHandle, [in] PROCESSINFOCLASS ProcessInformationClass, [out] PVOID ProcessInformation, [in] ULONG ProcessInformationLength, [out, optional] PULONG ReturnLength ); |
출처 : MSDN
NtQueryInformationProcess()의 두 번째 파라미터 PROCESSINFOCLASS ProcessInformaionClass에 해당 정보가 세팅된다.
PROCESSINFOCLASS는 열거형(enum)으로 여러 가지 정보를 찾을 수 있고, 그중 디버거 탐지에 사용되는 것은 ProcessDebugPort(0x7), ProcessDebugObjectHandle(0x1E), ProcessDebugFlags(0x1F)
-1. ProcessDebugPort(0x7)
디버깅 중일 때는 Debug Port가 할당된다.
ProcessInformationClass 파라미터에 ProcessDebugProt(0x7)을 입력하면 Debug Port를 얻을 수 있다.
dwDebugPort에 디버깅 중이면 0xFFFFFFFF, 아니면 0이 세팅된다.
CheckRemoteDebugPresent() API는 IsDebuggerPresent() API와 비슷하게 디버깅 여부를 판별
차이점은 CheckRemoteDebugPresent()의 경우 다른 프로세스의 디버깅 여부까지 판단
CheckRemoteDebugPresent()의 내부에서 NtQueryInformationProcess(ProcessDebugPort) API를 사용 중이다.중이다.
-2. ProcessDebugObjectHandle(0x1E)
프로세스가 디버깅될 때 Debug Object가 생성
ProcessDebugObjectHandle(0x1E)을 입력하면, Debug Object Handle을 얻을 수 있다.
디버깅 중이면 Debug Object Handle의 값이 존재할 것이고, 아니면 NULL
-3. ProcessDebugFlags(0x1F)
디버깅 중이면 Debug Flags 값이 0, 아니면 1
3. NtQuerySystemInformation()
디버깅 환경을 체크 - 현재 OS가 Debug Mode로 부팅되었는지 판단
(디버깅을 당하는지 체크하는 것은 직접적인 디버거 탐지 방법 / 디버깅 환경을 체크하는 것은 간접적인 디버거 탐지 방법)
ntdll!NtQyerySystemInformation() API는 현재 동작 중이 OS 시스템에 대한 다양한 정보를 구할 수 있는 시스템 함수
NtQyerySystemInformation() API - 지정된 시스템 정보를 검색 |
__kernel_entry NTSTATUS NtQuerySystemInformation( [in] SYSTEM_INFORMATION_CLASS SystemInformationClass, [in, out] PVOID SystemInformation, [in] ULONG SystemInformationLength, [out, optional] PULONG ReturnLength ); |
출처 : MSDN
첫 번째 파라미터인 SYSTEM_INFORMATION_CLASS SystemInformationClass 파라미터에 원하는 시스템 정보를 입력, PVOID SystemInformation 파라미터에 관련 구조체 주소를 넘기면 API가 리턴하며 그 구조체의 관련 정보를 채워준다.
SYSTEMP_INGOTMATION_CLASS는 열거형(enum)으로 SystemInformationClass 파라미터에 SystemKernelDebuggerInformation(0x23)을 입력하면 현재 OS 시스템이 디버그 모드로 부팅되었는지 알 수 있다.
회피방법 - wimdows XP의 경우, boot.ini를 편집(’/debugport=com1 /baudarate=115200 /Debug’ 값 삭제)
windows 7의 경우, cmd에 ‘bcdedit /debug off’ 명령어 입력 후 재부팅
4. NtQueryObject()
디버깅 중 생성되는 DebugObject 타입의 커널 객체의 존재를 확인
ntdll!NtQueryObject() API는 시스템의 다양한 종류의 커널 객체 정보를 구해오는 함수
NtQueryObject() API - 다양한 종류의 개체 정보를 검색 |
__kernel_entry NTSYSCALLAPI NTSTATUS NtQueryObject( [in, optional] HANDLE Handle, [in] OBJECT_INFORMATION_CLASS ObjectInformationClass, [out, optional] PVOID ObjectInformation, [in] ULONG ObjectInformationLength, [out, optional] PULONG ReturnLength ); |
출처 : MSDN
두 번째 파라미터 OBJECT_INFORMATION_CLASS ObjectInformationClass에 원하는 값을 입력하고 API를 호출하면, 세 번째 파라미터 PVOID ObjectInformation에 관련 정보의 구조체 포인터를 리턴
ObjectAllTypeInformation(3) 항목을 이용하여 시스템의 모든 객체 정보를 구하고 그중 DebugObject의 존재 확인
NtQueryObject() API 사용법
- 커널 객체(Kernel Object) 정보 리스트의 크기 얻기
- 메모리 할당
- 커널 객체 정보 리스트 구하기
- DebugObject 객체 타입 확인
회피방법 - ZwQueryObject의 호출 부분을 찾아 두 번째 파라미터의 값을 변경하면 해결된다.
5. ZwSetInformationThread()
디버기가 강제로 디버거를 떼어내는(Detach) 기법
스레드에게 정보를 세팅하는 System Native API이다.
ZwSetInformationThread() API - 스레드의 우선순위를 설정 |
NTSYSAPI NTSTATUS ZwSetInformationThread( [in] HANDLE ThreadHandle, [in] THREADINFOCLASS ThreadInformationClass, [in] PVOID ThreadInformation, [in] ULONG ThreadInformationLength ); |
출처 : MSDN
첫 번째 파라미터 ThreadHandle에 현재 스레드의 핸들을 넘겨주고, 두 번째 파라미터 ThreadInformationClass에 ThreadHideFormDebugger(0x11) 값을 입력하면 디버거 프로세스가 분리된다. -> 일반 실행의 경우 아무런 영향을 주지 않지만, 디버거 실행인 경우에는 디버거를 종료시킨다.(자신의 프로세스도 같이)
회피 방법 - 두 번째 파라미터 ThreadHideFromDebugger(0x11)을 0으로 변경
ZwSetInformationThread() API의 원리는 Thread가 숨김(Hide) 생태가 되어 디버깅이 불가능해지는 것으로 XP이후로는 DebugActiveProcessStop() API가 추가되었다.
6. DebugActiveProcessStop()
디버거에서 디버깅을 멈추고 싶을 때 호출
Detach 시 주체에 따른 호출 API | |
디버기가 디버깅 멈추기 | ZwSetInformationThread() API |
디버거가 디버깅 멈추기 | DebugActiveProcessStop() API |
7. TLS 콜백 함수
자체로는 안티 디버깅 기법이라고 볼 수 없지만 프로그램의 EntryPoint코드보다 먼저 실행되는 특징 때문에 안티 디버깅에 많이 활용된다.
TLS 콜백 함수 내에서 IsDebuggerPresent() 등의 함수로 간단히 디버깅 여부를 판별하여 프로그램의 실행 여부를 결정할 수 있다.
8. ETC
현재의 시스템이 (일반적인 시스템이 아니라) 리버싱을 위한 전용 시스템이라는 판단으로 실행을 멈추는 것도 좋은 방법이다. 이를 위해서 시스템에서 쉽게 구할 수 있는 일반적인 정보(Process, File, Window, Registry, host name, computer name, user name, Environment Variable 등)들을 활용하는 다양한 안티디버깅 기법들이 존재
대부분의 경우 Win32 API를 이용하여 구현되어 있다.
- OllyDbg 창 검색 ←FindWindow()
- OllyDbg 프로세스 검색 ← CreateToolhepl32Snapshot()
- 컴퓨터 이름이 “TEST”, “ANALYSIS” 등의 이름인지 확인 ← GetComputerName()
- 프로그램 실행 경로를 검사하여 “TEST”, “SAMPLE” 등의 이름인지 확인 ← GetCommandLine()
- 가상 머신에서 실행 중인지 확인(가상 머신 특유의 프로세스 이름 확인 ← VMWareService.exe, VMWareTray.exe, VMWareUser.exe 등)
회피방법 - FindWindow()에서 찾는 문자열의 주소로 이동하여 Window Class 이름 문자열 버퍼를 NULL로 덮어씌우거나 함수를 실행하지 않도록 JMP로 명령어를 수정, 파라미터로 사용되는 이전 함수의 리턴 값을 NULL로 수정, API 후킹 등의 방법들이 있다.
회피 방법이 어렵지 않아 유명 Protector들은 쓰지 않는다.
하지만 의외로 잘 알려지지 않은 Protector/Packer에서 사용되는 경우가 종종 있다.
'리버싱 핵심원리' 카테고리의 다른 글
디버깅 실습3 - PE Image Switching 개념 및 동작 원리 (0) | 2024.01.31 |
---|---|
디버깅 실습2 - Self-Creation (JIT 디버깅) (0) | 2024.01.16 |
Advanced 안티 디버깅 (0) | 2024.01.10 |
Dynamic 안티 디버깅 (0) | 2024.01.10 |
디버깅 실습1 - 서비스 (서비스 프로세스 디버깅) (2) | 2024.01.10 |