일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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
- UTF-16
- dotPeek
- PEImageSwitching
- 안티디버깅
- Exe2Aut
- DebugBlocker
- Self-Creation
- 디버깅
- CP949
- 리버싱핵심원리
- .net
- IL코드
- CodeEngn
- OllyDbg
- 리버싱
- Python
- RedLine Stealer
- PEImage
- Static안티디버깅
- Visual Studio
- JIT
- 분석 보고서
- ImageSwitching
- 서비스프로세스
- x32dbg
- Dynamic안티디버깅
- Advanced안티디버깅
- UTF-32
- Today
- Total
-榮-
Dynamic 안티 디버깅 본문
내부 코드와 데이터를 리버싱으로부터 감추고 보호하는 것이 목적으로, PE 프로텍터들에서 많이 사용된다.
원본 프로그램의 핵심 알고리즘을 보호하기 위해 사용
디버거로 프로그램을 실행할 수 있을지언정 원본 프로그램의 핵심 코드(OEP)를 트레이싱으로 찾지 못하도록 방해
1. 예외(Exception)
안티 디버깅의 단골 메뉴
예외가 발생하면 일반 실행은 SEH(Structured Exception Handling) 메커니즘에 의해 처리되고, 디버깅 중이면 디버거가 예외 처리를 담당한다. 이 특징을 이용해서 정상 실행 경우와 디버깅당하는 경우를 판별해서 서로 다른 동작을 수행할 수 있는 안티 디버깅 기법.
-1. SEH(Structured Exception Handling)
SEH의 대표적인 예외
EXCEPTION_DATATYPE_MISALIGNMRNT EXCEPTION_BREAKPOINT EXCEPTION_SINGLE_STEP EXCEPTION_ACCESS_BIOLATION EXCEPTION_IN_PAGE_ERROR EXCEPTION_ILLEGAL_INSTRTUCTION EXCEPTION_NONCONTINUABLE_EXCEPTION EXCEPTION_INVALID_DISPOSITION EXCEPTION_ARRAY_BOUNDS_EXCEEDED EXCEPTION_FLT_DENORMAL_OPERAND EXCEPTION_FLR_DIVIDE_BY_ZERO EXCEPTION_FLT_INEXACT_RESULT EXCEPTION_FLT_INVALID_OPERATION EXCEPTION_FLT_OVERATION EXCEPTION_FLT_STACK_CHECK EXCEPTION_FLT_UNDERFLOW EXCEPTION_INT_DIVIDE_BY_ZERO EXCEPTION_INT_OVERFLOW EXCPETION_PRIV_INSTRUCTION EXCEPTION_STACK_OVERFLOW |
(0x80000002) (0x80000003) (0x80000004) (0xC0000005) (0xC0000006) (0xC000001D) (0xC0000025) (0xC0000026) (0xC000008C) (0xC000008D) (0xC000008E) (0xC000008F) (0xC0000090) (0xC0000091) (0xC0000092) (0xC0000093) (0xC0000094) (0xC0000095) (0xC0000096) (0xC00000FD) |
가장 대표적인 예외는 BP(Break Point)
SEH에 Static 안티 디버깅을 삽입하여 프로그래밍 디버깅 중인지를 판단해 EIP를 변경하는 코드로 실행된다.
SS:[ESP+C] : SEH+C == 세 번째 파라미터 CONTEXT *pContext 구조체 포인터
DS:[EAX+B8] : pContext→Eip
회피 방법 - 디버거 프로그램의 INT3 예외를 무시하고 자체 SEH에서 처리되도록 옵션 설정
가끔 INT3 명령어를 트레이싱 중 디버거가 꼬이면서 비정상 종료가 되는 경우가 있다. 이 때는 SEH와 정상실행코드(SEH에서 지정해준 EIP)에 BP 설치 후 실행하면 된다.
- 디버깅 실전에서 매우 자주 사용된다.
-2. SetUnhandledExceptionFilter()
SEH에서 예외 처리가 되지 않았거나 등록된 SEH가 없을 경우, kernel32!UnhandledExceptionFilter() API가 호출된다. 이 함수 내부에서 Top Level Exception Filter(혹은 Last Exception Filter)라 불리는 시스템의 마지막 예외처리기를 실행 (마지막 예외처리기는 ‘작동이 중지되었습니다.’ 경고창을 띄운 후 종료)
kernel32!UnhandledExceptionFilter() 내부에서 Static 안티 디버깅 기법인 ntdll!NtQueryInformaionProcess(ProcessDebugPort) API를 호출하여 디버깅 여부 판별
→일반 실행이면 시스템 예외처리기에 넘기고, 디버깅 중이면 디버거에게 넘긴다.
SetUnhandledExceptionFilter() API |
LPTOP_LEVEL_EXCEPTION_FILTER SetUnhandledExceptionFilter( [in] LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter ); |
출처 : MSDN
kernel32!SetUnhandledExceptionFilter() API를 이용하면 시스템의 마지막 예외처리기(Top Level Exception Filter)를 변경할 수 있다.
lpTopLevelExceptionFilter 파라미터에 새로운 Top Level Exception Filter 함수 주소를 넘기면 된다.(SetUnhandledExceptionFilter() API의 파라미터/리턴값은 이전 Last Exception Filter 함수 주소)
안티 디버깅 기법은 고의로 예외를 발생시켜 새로 등록한 Last Exception Filter 내에서 일반 실행과 디버깅 실행을 판단하여 EIP 값을 변경 시킨다.
회피 방법 - SetUnhandledExceptionFilter() API를 이용한 안티 디버깅 기법은 Static & Dynamic 기법이 혼합된 형태이다.
1. Static 기법인 kernel32!UnhandledExceptionFilter() 내부에 호출되는 ntdll!ZwQueryInformationProcess() API를 (API 후킹 등의 방법으로) 무력화한다.
2. SetUnhandledExceptionFilter() API 호출에 의해 등록된 Exception Filter를 따라가서 정상 실행인 경우 어느 주소로 분기하는지 확인
2. Timing Check
트레이싱을 할 때 일반 실행보다 오래 걸리는 특성을 활용한 안티 디버깅 기법으로 실행 시간의 차이를 측정하여 디버깅 여부를 판별하는 기법
Timing Check 기법은 단순한 원리에 단순한 회피 기법을 가지고 있지만 보통 다른 안티 디버깅 기법들과 같이 사용되며 회피하는 것이 까다로울 수 있다.(특히 코드가 명확하게 눈에 보이지 않는 경우)
안티 에뮬레이팅(Anti-Emulation) 기법으로도 많이 사용된다. 에뮬레이터로 실행하면 일반 실행의 속도보다 훨씬 느리게 실행되기 때문에
시간 간격 측정 방법
1. Counter based method
- RDTSC
- kernel32!QueryPerformanceCounter
- kernel32!GetTickCount()
2. Time based method
- timeGetTime()
- _ftime()
CPU의 (Counter)를 이용하는 방법과 시스템의 실제 시간(Time)을 이용하는 두 가지 방법이 있다.
-1. RDTSC(Read Timp Stamp Counter)
x86 CPU에는 TSC(Time Stamp Counter)라는 64비트 레지스터가 존재
CPU는 매 순간 Clock Cycle을 카운트하여 TSC에 저장
RDTSC(ReaD Time Stamp Counter)는 그 TSC의 값을 EDX:EAX 형식으로 읽어오는 어셈블리 명령어 (상위 32bit - EDX, 하위 32bit - EAX)
회피 방법 -
1. 트레이싱하지 말고 RUN으로 해당 코드 넘기기 (BP 설치 후 RUN))
2. 두 번째 RDTSC 결과 값(EDX:EAX) 조작 (첫 번째 값과 비슷하게 조작하여 CMP 넘기기)
3. 조건 분기 명령어(CMP/Jcc) 실행을 조작 (Flags 값을 강제로 변경)
Jcc 명령어는 CF(Carry Flag)와 ZF(Zero Flag)의 영행을 받는다.
JA 명령어는 CF와 ZF가 모두 0인 경우 점프
4. 커널 드라이버를 이용하여 RDTSC 명령을 무력화 (커널 모드 드라이버를 이용해 원천젓으로 무력화시킬 수 있다. Olly Advanced PlugIn에 이러한 기능이 탑재되어 있다.)
실제로는 RDTSC 명령어나 CMP/Jcc 조건 분기 명령어들이 눈에 잘 보이지 않도록 교묘히 배치한다. 또한 다른 안티 디버깅(SEH, Static Method) 기법들과 조합되어 매우 강력한 힘을 발휘
3. Trap Flag
Trap Flag(TF)란 EFLAGS 레지스터의 9번째(Index 8) 비트
-1. Single Step
TF를 1로 세팅하면 CPU는 Sing Step 모드로 변경
CPU는 Single Step 모드에서 하나의 명령어를 실행 후 EXCEPTION_SINGLE_STEP 예외를 발생
이후 TF는 자동으로 최기화(0)
EXCEPTION_SINGLE_STEP 예외는 SEH 기법과 결합하여 디버거를 탐지하는 안티 디버깅 기법으로 사용
회피 방법 - EXCEPTION_SINGLE_STEP 예외를 디버기에서 직접 처리하도록 OllyDbg의 옵션을 변경 (EXCEPTION_SINGLE_STEP 예외 무시)
-2. INT 2D
INT 2D는 본래 커널 모드에서 작동하는 Break Point 예외를 발생시키는 명령어로 유저 모드에서도 예외를 발생시킨다.
디버깅 실행의 경우 무시하고 넘어간다.
INT 2D 명령어 디버깅 시 특징
1. 1바이트 무시
- 디버깅 모드에서 INT 2D를 실행(혹은 StepInto/StepOver)하면 다음 명령어의 첫 바이트는 무시되고, 그다음 바이트부터 새로운 명령어로 인식하여 실행 → 전혀 다른 명령어를 실행하는 셈
- INT 2D 기반의 안티 디버깅 기법은 일종의 Obfuscated Code 효과를 보여준다.
- 이런 식의 Code Byte Ordering을 변경해서 프로그램의 코드를 혼란스럽게 만드는 것은 Code Obfuscating 기법이라고 하며, Dynamic 안티 디버깅 기법으로 자주 사용된다.
2. BP까지 그대로 실행
- INT 2D 명령을 Trace하면, 그 다음 명령어 시작에서 멈추지 않는다.
- OllyDbg만의 특징이며, 디버거 별로 동작이 조금씩 다르게 나타난다.
- INT 2D 실행 과정에서 발생한 Code Byte Ordering이 깨지는 문제때문(일종의 버그)
회피 방법 - 실전에서는 SEH를 반드시 따라가서 코드를 한 줄씩 디버깅해야 하는 경우가 있다.
TF를 사용하여 SEH로 쉽게 가는 방법
1. EXCEPTION_SINFLE_STEP 예외를 무시하는 디버거 옵션 설정
2. SEH에 BP 설치
3. INT 2D 명령어 실행 전에 TF를 1로 세팅 (Single Step 예외를 사용)
4. 두 번 Trace (kernel 명령어이기 때문에 User 모드 디버거에서는 정상적인 명령으로 인식되지 않음)
5. SEH의 BP에 멈추고, TF = 0으로 변경됨
4. 0xCC Detection
Software BP(0xCC) 탐지
BP는 Opcode의 0xCC로 사용되지만, 프로세스 메모리의 코드 영역에서 단순히 0xCC를 검색하기에는 Displacement, Immediate, 데이터, 주소 등으로 사용될 수 있기 때문에 정확성이 매우 떨어진다.
-1. API Break Point
원하는 기능만 빠르게 디버깅하는 방법 중 하나는 해당 프로그램이 사용할 만한 API에 BP를 설치 후 실행하는 것이다. 만약 해당 BP에서 멈추면 저장된 복귀 주소(Return Address)를 확인해서 복귀 주소로 따라가 그 부분을 디버깅하는 식 → 디버깅 범위를 줄일 수 있다.
이와 같은 API에 설치된 BP를 확인해서 디버깅을 판단 (API 코드의 첫 바이트가 CC인지 검사)
리버서가 관심을 가지는 API 리스트 | ||
[프로세스] | ||
CreateProcess CreateThread EunmProcesses CreateToolhelp32Snapshot ShellExecuteA |
CreateProcessAsUser GetThreadContext EnumProcessModules Process32First WinExec |
CreateRemoteThread SetThreadContext OpenProcess Process32Next TerminateProcess |
[메모리] | ||
ReadProcessMemory VirtualAllocEx VirtualQuery |
WriteProcessMemory VirtualProtect VirtualQueryEx |
VirtualAlloc VirtualProtectEx |
[파일] | ||
CreateFile CopyFile MeveFile FindNextFile GetSystemDirectory SetFilePointer MapViewOfFileEx _write _tell |
ReadFile CreateDirectory MoveFileEx GetFileSize GetFileAttributes CreateFileMapping UnmapViewOfFile _read |
WriteFile DeleteFile FindFirstFile GetWindowsDirectory SetFileAttributes MapViewOfFile _open _lseek |
[레지스트리] | ||
RegCreateKeyEx RegEnumKeyEx RegSetValueEx |
RegDeleteKey RegQueryValueEx |
RegDeleteValue RegSetValue |
[네트워크] | ||
WSAStartup closedocket htons recv HttpSendRequst InternetConnet InternetOpenUrl |
socket getservbyname connet send HttpQueryInfo InternetGetConnetedState InternetReadFile |
inet_addr gethostbybname inet_htoa HttpOpenRequest InternetCloseHandle InernetOpen URLDownloadToFile |
[기타] | ||
OpenProcessToken OpenSCManager ControlService SetServiceStatus OpenMutex GetProcAddress OutputDebugString |
LookupPrivilegeValue CreateService DeleteService QueryServiceStatusEx FindWindow GetModuleFileNameA ... |
AdjustTokenPrivileges OpenService RegisterServiceCtrlHandler CreateMutex LoadLibrary GetCommandLine |
회피 방법 - 코드의 첫 바이트를 피해서 중간쯤 BP를 설치한다.
-2. Checksum 비교
특정 코드 영역의 Checksum 값을 비교
1byte라도 변경되면 바뀌는 Checksum 특징을 이용하여 특정 코드 내에서 CC 패치가 되었을 때 변경된 Checksum을 기존의 Checksum과 비교하여 디버깅 여부 판단
코드 버퍼에 대한 Checksum을 구하는 방법은 다양하다. 해당 메모리 영역이 기존과 동일한지 여부만 체크하면 되므로 다양한 알고리즘을 적용할 수 있다. 일반적으로 속도가 빠르고 충돌이 적은 CRC32(Cyclic Redundancy Check)를 많이 사용
회피 방법 - 원론적으로 CRC 계산 영역에 BP를 걸지 않거나 패치를 하지 않으면 무력화된다. → 디버깅이 어려워지므로 안티 디버깅의 노림수. 가장 좋은 방법은 CRC 비교 구문 패치.
but, 교묘하게 코드를 숨기고 수십~수백 개의 Checksum 비교 코드가 존재한다면, 디버깅하기 매우 까다롭다.
'리버싱 핵심원리' 카테고리의 다른 글
디버깅 실습3 - PE Image Switching 개념 및 동작 원리 (0) | 2024.01.31 |
---|---|
디버깅 실습2 - Self-Creation (JIT 디버깅) (0) | 2024.01.16 |
Advanced 안티 디버깅 (0) | 2024.01.10 |
Static 안티 디버깅 (1) | 2024.01.10 |
디버깅 실습1 - 서비스 (서비스 프로세스 디버깅) (2) | 2024.01.10 |