IA-32 Instruction
- 32bit Instruction(32비트 명령어)
hex를 보고 명령어로 해석
32비트 명령어는 instruction Prefixes, opcode, ModR/M, SIB, Displacement/ Immediate
이렇게 구성되고 여기서 필수는 Opcode
hex를 보고 opcode 표에서 찾고 prefix인경우 다음코드가 opcode, ModR/M이 필요한 명령어의 경우 다음 hex가 ModR/M
이런식으로 해석하는걸 반복해서 Hex보고 명령어로 해석할 수 있으면 좋다.
안티 디버깅
-Static
주로 디버거를 탐지하여 프로그램이 정상적으로 실행 안되게 하는 방법.
디버거에 올리면 Run이 안되는 경우
-Dynamic
디버거를 방해해서 원본 코드와 데이터를 확인할 수 없게 만드는 방법
static 안티 디버깅의 목적
-디버기 프로세스가 자신이 디버깅 당하고 있는지 파악
디버거 탐지, 디버깅 환경 탐지, 디버거 강제 분리 등의 방법
회피 방법
탐지 코드에서 얻어오는 정보를 변경
PEB구조체의 BeingDebugged, Ldr, ProcessHeap, NtGlobalFlag를 사용해서 탐지.
BeingDebugged(+0x2) - 디버깅 중일때 1, 아닐때 0 - IsDebbugerPresent함수를 통해 얻기 가능
회피 방법 - BeingDebugged 값을 0으로 변경
Ldr(+0xc)
PEB_LDR_DATA 구조체를 가르키는 포인터 인데 이 구조체가 힙 메모리에 생성됨.
디버깅의 경우 힙 메모리의 안쓰는 영역이 0XFEEEFEEE로 채워짐(XP 이전의 경우에만)
Ldr은 77F55D80인데
WIN10이라서 00으로 채워짐
회피법 0XFEEEFEEE를 00으로 변경
ProcessHeap(0x18)
HEAP 구조체의 Flags(c)멤버 값이 정상의 경우 0x2, ForceFlags(10) 멤버의 값이 정상의 경우 0임
Flags의 값이 0x2
ForceFlags값이 13500A4임
이 값들도 (XP에서 효과가 있음 내건 WIN10이라 의미 없음)
회피 방법 - 정상 값으로 수정
NtGlobalFlag(+68)
디버깅 중일떄 70으로 세팅됨
70으로 세팅 된 모습
회피 방법 0으로 수정
IsDebuggerPresent() - PEB BeingDebugged 값 사용
CheckRemoteDebbugerPresent() - 내부에서 NtQueryInformationProcess 사용
NtQueryInformationProcess() 함수로 현재 디버깅 중인지 확인 가능
회피 방법 - 위 함수를 후킹해서 바꿔버리면 됨.
NtQuerySystemInformation() - 디버깅 환경을 체크할 수 있음.
회피 방법 - OS를 일반모드로 부팅하면 됨.
NtQueryObject() - 디버깅 중이면 DebugObject가 생성되는데 이를 탐지
회피 방법 - API 후킹으로 바꿔버리면 됨.
ZwSetInformationThread() - 디버기에서 강제로 디버거를 떼어네는 방법
회피 방법 - API 후킹으로 바꿔버리면 됨.
TLS 콜백 함수 - TLS 콜백함수에서 체크해서 꺼버림
ETC - 현재 환경 파악해서 디버깅 하는거같으면 꺼버림
Dynamic 안티 디버깅
목적 - 내부 코드와 데이터를 감추고 보호
방법
예외(SEH 활용) - 예외를 이용해서 안티디버깅 함.
정상적으로 실행된 경우 SEH를 타서 처리가 되지만 디버깅 중인 경우 디버거에서 예외 처리가 일어남.
회피 방법 - 디버거가 예외를 무시하게 한다.
SetUnhandledExceptionFilter - SEH에서 예외가 처리되지 않은 경우 시스템의 UnhandledExceptionFilter()가 호출 됨. 이 함수 내부에서 Last ExceptionFilter라고 불리는 시스템의 마지막 예외 처리기를 실행함. 보통 프로세스 종료.
UnhandledExceptionFilter함수 내부에서 NtQueryInforamtionProcess를 사용해서 디버깅 중인지 체크해서 일반 실행이면 마지막 예외 처리기를 실행하고 디버깅 중이면 디버거에게 예외를 넘겨줌.
SetUnhandledExceptionFilter함수를 통해 마지막 예외 처리기를 바꿀 수 있음.
고의로 예외를 발생시키고 UnhandledExceptionFilter를 호출하게 해서 디버깅 실행을 판별해서 실행흐름을 바꿀 수 있음.
회피 방법 - NtQueryInforamtionProcess 함수 후킹해서 무력화, SetUnhandledExceptionFilter 를 따라가서 마지막 예외 처리기에 bp설치
Timing Check -디버깅 중일때 코드 실행 시간이 길어진다는 것을 이용해서 코드 실행 시간 차이를 체크해서 디버깅 여부 판별하는 방법
RDTSC - x86 CPU에는 TSC 레지스터가 존재하는데 매순간 Clock Cycle을 카운트해서 TSC에 저장함. RDTSC는 그 TSC값을 읽어오는 어셈블리 명령어.
RDTSC를 이용해서 간격을 두고 두번 호출해서 차이를 계산.
회피방법 - 트레이싱 하지말고 해당 코드 넘기기
RDTSC 결과 조작
조건 분기 명령어 조작
커널 드라이버로 RDTSC 명령어 무력화
Trap Flag - EFLAGS 레지스터의 9번째 비트
TF 값을 1로 세팅하면 CPU가 single Step모드로 변경됨.
하나의 명령어를 실행 시킨 후 Single Step 예외를 발생시키고 TF값을 0으로 초기화.
SEH기법과 결합해서 디버거 탐지.
TF가 1로 세팅된 후 일반 실행의 경우 SEH를 타고 정상 코드가 실행
디버깅 중이라면 SEH로 못가고 명령어 계속 실행(이상한 곳으로 빠짐)
회피 방법 - Single Step 예외를 무시하고 SEH에 BP설치
INT 2D - 커널 모드에서 작동하는 BP임. 유저 모드에서도 예외를 발생시킴. 그러나 디버깅 실행의 경우 예외 발생하지 않고 넘어감.
특징
INT 2D명령어 뒤 1바이트가 무시됨.
회피 방법
Single Step 예외 무시하게 한 후, INT 2D명령어에서 TF를 1로 바꾸고 정상적인 SEH에 BP를 걸고 f9
(INT 2D가 디버거에서 예외가 발생하지 않으니까 예외를 만들어서 SEH를 탈 수 있게 만듦)
0xCC detection - BP를 탐지함. 탐지 방법이 0xcc를 탐지하면 Opcode가 아닌 0xcc를 탐지할 수 있으니 탐지 방법을 고민해야함.
API 첫 instruction에 BP를 거는 경우가 많으니 첫 부분이 0xcc인지 검사
회피 방법 - API의 중간에 bp설치
Check Sum 비교 - software bp를 걸면 코드가 바뀌니 하나라도 bp걸면 checksum이 달라짐 이를 이용해서 0xcc탐지
회피 방법 - checksum 비교구문 패치