공부해요/리버싱_핵심원리

52. Dynamic 안티 디버깅

yenas0 2025. 1. 29. 06:28
반응형

Dynamic 안티 디버깅의 목적

: 내부 코드와 데이터를 리버싱으로부터 감추고 보호하는 것, 원본 프로그램의 핵심 알고리즘 보호하기 위해 사용

 

예외

정상적으로 실행된 프로세스에서 예외 발생 시: SEH메커니즘에 의해 OS에서 예외를 받아 프로세스에 등록된 SEH를 호출

디버기에서 예외 발생시: 디버거에서 예외처리 담당

둘이 다르게 동작해서 안티 디버깅이 가능하다

 

 

SEH

대표적인 예외

EXCEPTION_DATATYPE_MISALIGNMENT       (0x80000002)
EXCEPTION_BREAKPOINT                  (0x80000003)
EXCEPTION_SINGLE_STEP                 (0x80000004)
EXCEPTION_ACCESS_VIOLATION            (0xC0000005)
EXCEPTION_IN_PAGE_ERROR               (0xC0000006)
EXCEPTION_ILLEGAL_INSTRUCTION         (0xC0000010)
EXCEPTION_NONCONTINUABLE_EXCEPTION    (0xC0000025)
EXCEPTION_INVALID_DISPOSITION         (0xC0000026)
EXCEPTION_ARRAY_BOUNDS_EXCEEDED       (0xC000008C)
EXCEPTION_FLT_DENORMAL_OPERAND        (0xC000008D)
EXCEPTION_FLT_DIVIDE_BY_ZERO          (0xC000008E)
EXCEPTION_FLT_INEXACT_RESULT          (0xC000008F)
EXCEPTION_FLT_INVALID_OPERATION       (0xC0000090)
EXCEPTION_FLT_OVERFLOW                (0xC0000091)
EXCEPTION_FLT_STACK_CHECK             (0xC0000092)
EXCEPTION_FLT_UNDERFLOW               (0xC0000093)
EXCEPTION_INT_DIVIDE_BY_ZERO          (0xC0000094)
EXCEPTION_INT_OVERFLOW                (0xC0000095)
EXCEPTION_PRIV_INSTRUCTION            (0xC0000096)
EXCEPTION_STACK_OVERFLOW              (0xC00000FD)

 

 

가장 대표적인 예외 -> BP

BP로 예외 실행 시 일반 실행에서는 등록된 SEH가 자동 실행되지만 디버깅 실행의 경우 실행 멈추고 디버거로 제어권이 넘어간다.

 

실습..

https://github.com/reversecore/book/blob/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SEH.exe

 

book/실습예제/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SEH.exe at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

올리디버거에서 열고 40100에 BP설치

위에 부분은 INT3 ㅇㅖ외를 이용한 안티 디버깅 예제이다

과정 살펴보면

1. 401011 - 40101D: SEH 설치

2. 401024: INT3 예외발생

3-1. 401025 - 40102A: 디버깅 실행 - 프로세스종료

3-2. 40102C - 40103F: 일반 실행 - SEH 실행

4. 401040 - 401047: SEH 제거

 

회피 방법

옵션을 수정하면 된다

INT3 breaks에 체크박스 표시를 하면 디버기에서 저 예외 무시하고 자체 SEH에서 처리되도록 할 수가 있음

 

 

SetUnhandledExceptionFilter()

SEH에서 처리 안되거나 등록된 SEH가 없다면?

-> 시스템의 kernel32!UnhandledExceptionFilter() API 호출 (함수 내부에서는 top level exception 실행)

-> 이 API는 정적 안티 디버깅 기법인 ntdll!NtQueryInformationProcess API를 호출

 

즉 kernel32!SetUnhandleExceptionFilter()API 사용하면 top level exception filter 변경이 가능

 

실습

https://github.com/reversecore/book/blob/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SUEF.exe

 

book/실습예제/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SUEF.exe at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

올리디버거에서 열고 401030주소에 BP를 설치했다.

 

코드 정리

1. 401030 - 40103D: 문자열 출력코드

2. 401040 - 40104B: SetUnhandledExceptionFilter() 호출해 새로운 top level exception filter 등록, 401000주소의 함수를 top level exception filter로 등록

3. 401050 - 401052: 강제 예외 발생

-> 401052 주소를 실행하면 정의되지 않은 프로세스 가상 메모리 주소에 값을 넣으려해서 예외 발생

-> 자동으로 BP 설치한 곳에 실행이 멈춤

 

회피 하려면?

위에 방식은 동적 정적이 합쳐있는 방식

-> 정적 먼저 없애: ntdll!NtQueryInformationProcess() API를 무력화

-> 동적 무력화하기: SetUnhandledExceptionFilter() API 호출에 의해등록된 exception filter 따라가 정상 실행 시에 분기된 주소 확인 

 

 

Timing check

디버깅 할때 트레이싱 한줄씩하면 오래걸리는데 이 실행 시간 차이를 측정해서 디버깅 여부를 판단하는 기법

(그럼 그냥 시간 조작해서 회피해버리면 된다. 근데 실제로는 보통 다른 안티 디버깅 방법이랑 같이 씀)

 

시간 간격 측정 방법

보통 방법은 두개있음 CPU 카운터 이용한 방법과 시스템의 실제 시간을 사용한것.

그 중 하나 RDTSC

 

RDTSC

cpu에는 TSC라는 64bit 레지스터 有, cpu는 매 순간 clock cycle을 카운트해 tsc에 저장한다.

RDTSC란 그 TSC값을 읽어오는 어셈블리 명령어다

 

실습

https://github.com/reversecore/book/blob/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_RDTSC.exe

 

book/실습예제/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_RDTSC.exe at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

 

401000으로 가서

코드 흐름

1. 40101C: 첫번째 RDTSC 호출

2. 40101E - 40101F: 결과 값을 스택에 저장

3. 401020 - 401028: 시간 소모용 loop

4. 40102A: 두번째 RDTSC 호출

5. 40102C - 40102D: 스택에서 첫번째 구했던 time stamp count 가져오기

6. 40102E - 401030:  count의 high order bits 비교

7. 401032 - 40103C: count의 low order bits 비교

8. 40103E - 401040: 비교문에서 걸린 경우 실행 - 프로세스 비정상 종료

9. 401042: 비교문을 무사히 통과한 경우 실행 계속

 

회피하려면?

1. 트레이싱 말고 run으로 그냥 쭉 해당 코드 넘어가기 (40102C에 BP 넣고 F9하면 됨)

2. 두번째 RDTSC 결과값 조작(첫번째 값이랑 비슷하게)

3. 조건 분기 명령어 조작(40103E주소로 안가게 디버거에서 flags값 변경시킴)

4. 커널 드라이버 사용(RDTSC 명령어 자체를 무력화)

 

 

 

Trap Flag

EFLAGS 레지스터의 9번째 비트

 

Single Step

TF == 1 : cpu는 single step 모드로 변경 후 하나의 명령어 실행 후 EXCEPTION_SINGLE_STEP 예외 발생하고 0으로 초기화

 

EXCEPTION_SINGLE_STEP 예외 : SEH 기법과 결합해 안티 디버깅에 사용

 

실습

https://github.com/reversecore/book/blob/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SingleStep.exe

 

book/실습예제/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_SingleStep.exe at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

401000주소로..

 

코드 흐름

1. 401011 - 40101D: SEH 등록

2. 401024 - 40102D: EFLAGS를 직접 수정할 수 없으므로 스택을 이용해 값을 변경

3. 40102E - 401034: 일반실행의 경우 SEH실행, 아니면 여기 실행

40102E에 nop 실행시키면 EXCEPTION_SINGLE_STEP 예외가 발생함을 알 수 있다

 

근데 책에서는 EFL이 212라는데난 206임 달라도 되는건가

아무튼 40102F에서 F7하고 401034 실행까지 하면 아래처럼 비정상 종료가 된다

 

회피하려면?

디버깅 옵션에서 저걸 체크해주면 된다.

그렇게 하고 SEH에 BP 놓고 40102E 실행하면 SEH의 BP에 멈추게됨. 새로운 EIP 주소에 다시 BP 설치하고 실행하면 정상적으로 실행이 된다.

 

 

INT 2D

커널 모드에서 작동하는 BP 예외 발생 명령어

근데 유저 모드에서도 예외 발생시키는데 이 때 디버깅 할 때는 예외를 발생하지 않음 -> 안티 디버깅에 사용할 수 있다

 

특징

1. 1바이트 무시

INT 2D실행하면 다음 명령어의 첫 바이트가 무시됨

2. BP까지 그대로 실행

INT 2D명령 트레이싱할 시, 다음 명령어 시작에서 안 멈춤 (F9처럼 BP까지 실행해버린다)

 

실습

401000으로 감

 

일반 실행의 경우: INT 2D에서 exception 발생 해 SEH로 이동해 최종적으로 not debugging이 출력됨

디버깅 실행의 경우: INT 2D에서 SEH로 못가고 한 바이트 뛰어서 401021주소의 MOV부터 시작해서 debugging 문자열로 출력됨

 

회피하려면?

코드패치로 해결

먼저 SEH로 쉽게 가야함

1. EXCEPTION_SINGLE_STEP 예외를 무시하도록 올리디버거 옵션 설정 후 40101E 주소까지 실행

2. INT 2D 명령어에서 멈춘 후 등록된 SEH에 BP 설치

3. TF 더블클릭하여 1로 세팅 (single step 모드로 변경)

 

 

 

0xCC Detection

프로그램 디버깅 시 BP 설치 多

BP의 x64 명령어는 0xCC

-> 이 값을 발견한다면 디버깅 여부 판단이 가능하다

 

API Break Point

보통 디버깅 할 때 API에 BP를 많이 설치하므로 API 코드 시작 부분의 첫 바이트가 CC인지 검사해서 디버깅 중인지 판단 할 수 있다.

 

실습

이렇게 API 찾아서 BP 설치하면 API 실제로 코드 시작 첫 바이트가 CC로 변경된다고 한다. 근데 디버거에서 확인은 못함

 

회피하려면?

1. 첫바이트 피해서 중간쯤~ 에 설치

2. 하드웨어 BP 설치

 

 

Checksum 비교

코드에 설치된 BP 확인하는 방법은 특정 코드 영역의 체크섬을 비교할 수도 잇다.

만약에 BP를 설치해서 0xCC가 생겼으면 체크섬이 원본이랑 달라지는데 이걸 비교해서 디버깅했는지 알 수가 있다.

 

실습

https://github.com/reversecore/book/blob/master/%EC%8B%A4%EC%8A%B5%EC%98%88%EC%A0%9C/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_Checksum.exe

 

book/실습예제/07_Anti_Debugging/52_Dynamic_Anti_Debugging/bin/DynAD_Checksum.exe at master · reversecore/book

리버싱 핵심원리 - 소스 코드 및 실습 예제. Contribute to reversecore/book development by creating an account on GitHub.

github.com

 

401000주소로 감

 

체크섬 구하는 루프

0040102A    MOVZX EBX,BYTE PTR DS:[ESI]
0040102E    ADD EAX,EBX
00401030    ROL EAX,1
00401032    INC ESI
00401033    LOOPD SHORT 0040102A

 

초기 값

ESI = 401000 (초기 데이터 시작 주소)

ECX = 70 (Loop Count)

 

Checksum 계산 루프

주소 범위 401000~40106F에서 1바이트씩 읽음

각 바이트 값을 ADD와 ROL 명령으로 계산하여 EAX에 저장

이 과정을 Loop Count(70번)만큼 반복

 

비교 조건분기

00401035    CMP EAX,DWORD PTR DS:[40BDC0]
0040103B    JE SHORT 00401044

두개가 다르면 코드가 패치되었거나 BP가 설치된 것이다.

 

회피하려면?

1. BP안걸거나 패치 안하기 (사실상 안티 디버깅이 원하는거)

2. CRC 비교 구문을 패치

3. 디버거에서 강제로 JMP 주소를 변경

 

반응형

'공부해요 > 리버싱_핵심원리' 카테고리의 다른 글

51. Static 안티 디버깅  (0) 2025.01.22
50. 안티 디버깅  (0) 2025.01.12
49. IA-32 Instruction  (0) 2025.01.12
48. SEH  (0) 2025.01.08
47. PEB  (1) 2025.01.02