iohyeon 2020. 9. 25. 18:37

I. Anti-Debugging

       : 디버깅을 방해하는 기술, 디버거와 OS에 따라 기법이 달라짐(높은 의존성)

       i. Static vs. Dynamic

Static Anti-Debugging vs. Dynamic Anti-Debugging from ReverseCore


                > Static Anti-Debugging

                     : 디버깅 시작할 때 한 번만 해체하면 해결되는 기법

                     : 디버거 탐지, 디버깅 환경 탐지 → 디버거에서 실행 x, 종료 코드 등 실행, 디버거 강제 분리

                     : String in Process Virtual Memory, Kernel Mode Driver, System Environment Targeting, OutputDebugString(), Memory Break Point, Filename Format String, ESI Value, (Guard Page - Memory Break Point) 사용

                    → 회피 방법 : 탐지 코드가 얻는 정보 변경

                > Dynamic Anti-Debugging

                     : 트레이싱하며 만날 때마다 해결하는 기법

                     : 디버거에서 트레이싱하여 원본 프로그램의 코드와 데이터 확인

                     : API Redirection, Guard Page, Virtual machine(자체 구현) 사용

 

II. Static Anti-Debugging

       i. PEB

             > IsDebuggerPresent() - PEB.BeingDebugged 멤버의 값이 1(True)인 경우

             > PEB.Ldr에서 힙 메모리의 사용되지 않는 영역이 0xFEEEFEEE로 채워짐

             > HAEP 구조체의 Flags 멤버와 Force Flags 멤버가 특정한 값으로 세팅

HEAP Structure from ReverseCore

                  → PEB.ProcessHeap 멤버

                   GetProcessHeap()

             > PEB.NtGlobalFlag가 0x70으로 세팅

 

       ii. NtQueryInformationProcess()

NtQueryInformationProcess() from ReverseCore

             : PROCESSINFOCLASS ProcessInformationClass에 원하는 정보 형식을 입력한 후 호출하면 PVOID ProcessInformation에 해당 정보 세팅

ProcessInformationClass Structure from ReverseCore

             > ProcessDebugPort : 파라미터에 값을 입력하면 Debug Port 얻을 수 있음, dwDebugPort에 디버깅 중이라면 0xFFFFFFFF, 아니라면 0이 세팅

ProcessDebugPort from ReverseCore

                     CheckRemoteDebuggerPresent( ) : 현재 프로세스와 다른 프로세스의 디버깅 여부 판단

             > ProcessDebugObjectHandle : 프로젝트가 디버깅될 때 Debug Object가 생성되기 때문에, 입력했을 때 프로세스가 디버깅 중이라면 Debug Object Handle이 존재하고, 아니라면 NULL이다.

ProcessDebugObjectHandle from ReverseCore

             > ProcessDebugFlags : 입력하여 디버깅 중이면 Debug Flags 값이 0(False), 아니면 1(True)

ProcessDebugFlags from ReverseCore

 

       iii. NtQuerySystemInformation()

             : SystemInformationClass 파라미터에 원하는 시스템 정보를 입력하고 SystemInformation 파라미터에 관련 구조체 주소를 넘겨주면 API가 리턴하며 그 구조체에 관련 정보를 채워줌

             > SystemKerneIDebuggerlnformation

                   : SystemInformationClass에 SystemKernelDebuggerInformation 값을 입력하고 Systemlnformtion에는 SYSTEM_KERNEL_DEBUGGER_INFORMAION 구조체 주소 입력하면, API가 리턴되면서 디버깅 중일 경우 SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerEnabled에 1(True), 아닐 경우 0(False) 세팅 (SYSTEM_KERNEL_DEBUGGER_INFORMATION.DebuggerNotPresent는 항상 1(True)로 세팅)

 

       iv. NtQueryObject()

             : 시스템에서 디버거가 다른 프로세스를 디버깅 중일 때 DebugObject 타입의 커널 객체가 생성되는데, 이 존재를 확인

NtQueryObject from ReverseCore

                  → ObjectInformationClass에 원하는 값을 입력한 후 API 호출하면 Object Information에 관련 정보의 구조체 포인터 리턴

 

       v. ZwSetInformationThread()

             : 강제 디버거 detach하는 API

                   ThreadHandle에 현재 스레드의 핸들, ThreadInformationClass에 ThreadHideFromDebugger 값을 입력하면 디버거 프로세스 분리(자신 프로세스도 같이 종료)

 

       vi. TLS Callback Function

             : Entry Point 코드보다 먼저 실행되어 IsDebuggerPresent() 등의 함수로 디버깅 여부를 판단하여 프로그램을 계속 실행할 지 말 지 결정

 

III. Dynamic Anti-Debugging

       i. Exception

             : 정상 실행된 프로세스에서의 발생 경우 SEH 매커니즘에 의해 OS에서 예외를 받아 프로세스에 등록된 SEH를 호출하지만, 디버기에서 예외 발생 경우 디버거에서 예외 처리를 하는 차이를 이용

                   > SEH

                           → EXCEPTION_BREAKPOINT : BP를 이용하여 예외를 발생시켰을 때 등록된 SEH 실행이 아닌 디버깅 실행의 경우 실행이 멈추고 제어권이 넘어오므로 예외 처리기 따라 들어가야 함

                   > SetUnhandledExceptionFilter : 프로세스 예외 발생 시에 SEH에서 처리되지 않거나 등록된 SEH가 없다면 시스템의 kemel32!UnhandledExceptionFilter()가 호출되고 이 함수 내부에서 Top Level Exception Filter(or Last Exception Filter)라는 시스템의 마지막 예외 처리기 실행

       ii. Timing Check

                  : 디버거를 이용했을 때 증가하는 실행 시간 측정

       iii. Trap Flag

                   : RFLAGS[8] 비트

                   : 1(True)로 설정할 경우 CPU가 Single Step 모드로 변경하면 하나의 명령어를 실행시킨 후 EXCEPTION_SINGLE_STEP 예외 발생(이 경우 TF는 자동으로 0으로 초기화)

                   : INT 2D 명령어는 커널 모드에서 작동하는 BP 예외를 발생시키지만 유저 모드에서도 예외를 발생시키는데, 디버깅 실행의 경우 무시하고 넘어감

       iv. 0xCC Detection

                  : BP의 x86 Instruction이 0xCC이므로 이 값을 탐지하거나 Checksum 값 변화 비교

 

IV. 고급 안티 디버깅

       i. Garbage Code

             : 의미 없는 코드를 대량으로 추가

       ii. Breaking Code Alignment

             : 디스어셈블러를 혼란시키기 위해 의도적으로 추가시킨 코드

              → StepInto(F7) 명령으로 이동하면 정상 코드 보임

       iii. Encryption/Decryption

             : 복호화해야 정상 코드 나타남, 코드 재생성

       iv. Stolen Bytes(Remove OEP)

             : 원본 코드의 일부(주로 OEP 코드)를 패커/프로텍터가 생성한 메모리 영역으로 옮김

       v. API Redirection

             : 주요 API들의 코드 전체 혹은 일부를 다른 메모리 영역으로 복사한 후 해당 API를 호출하는 코드를 패치시켜 원본 API 주소에 BP를 설치하여도 복사해둔 API가 실행되어 소용이 없게 만듦

       vi. Debug Blocker(Self Debugging)

             : 스스로 디버깅 모드로 실행하여 다른 디버거가 Attach될 수 없게 하고 디버기 프로세스를 제어