문제 : 이 프로그램은 디버거 프로그램을 탐지하는 기능을 갖고 있다. 디버거를 탐지하는 함수의 이름은 무엇인가
실행파일을 실행해본다.
디버거 모드가 아닌 일반 모드에서 실행시켰기 때문에 저렇게 정상 문자열이 반복적으로 출력된다.
그러면 Olly-Dbg로 열었을 때는?
이렇게 디버깅 당함 문자열이 반복 출력된다.
이전 3번 문제와 마찬가지로 함수 이름을 묻는 문제이기 때문에
우클릭 > Search for > All intermodular calls 로 함수 목록을 볼 필요가 있다.
가장 눈에 띄는 함수는 IsDebuggerPresent이다.(지금 현재 Debugger 상태인가?)
위 함수를 더블클릭해서 해당 위치로 이동한다.
해당 위치에 BP를 걸고 F9를 눌러서 실행시킨다.
해당 위치까지 실행되었지만, 아무것도 발생하지 않았다.
추측에 따르면, 아마 저 함수가 실행되고 난 후에 어떤 반환값을 반환받고,
그 다음 줄에 있는 CMP(Compare) 명령을 실행한다.
비교되는 값에 따라서 "정상" 문자열인지 "디버깅 당함" 이라는 문자열이 출력 될 것이다.
F8을 눌러서 Step over를 실행하자.
Step over이후, EAX에 반환값으로 00000001이 입력되었다.
하지만, 예상과 다르게 콘솔 창에 "디버깅 당함"이라는 문자열은 출력되지 않았다.
F8로 계속 실행시켜보면,
딱 저 부분에서 "디버깅 당함"이라는 문자열이 출력된다.
문자열 출력 함수로 보이는데, 저기 Arg1을 인자로 받아서 출력하는 것 같다.
문자열을 코드 부분에서 찾으면 의미가 없고, 좌측 하단에 Hex값을 보여주는 부분에서 찾아본다.
(참고) Ctrl + G 단축키는 해당 주소로 이동시켜주는 단축키이다.
Ctrl + G 에 00431024를 입력해보면,
00431024 위치에 "디버깅 당함"이라는 문자열이 저장되어 있었다.
그럼 함수 00408190은 단순히 콘솔창에 문자열을 출력시켜주는 함수임을 알 수 있다.
F8로 계속 진행하면,
이 부분이 반복 실행된다.
대충 살펴보면
1. 1초 쉰다.(1000ms sleep)
2. CALL 00408210 실행
3. IsDebuggerPresent로 디버그 모드인지 판별
4. CALL 00408210 실행
5. "정상" 혹은 "디버깅 당함" 문자열 받아옴
("디버깅 당함" 문자열은 00431024위치에 있다는 것을 확인. "정상" 문자열은 0043101C 위치에 있을 것으로 추정)
6. 00408190 함수 실행 : 문자열을 콘솔 창에 출력해주는 함수
여기서 내가 궁금한 부분은, 00408210 위치에 있는 함수가 어떤 일을 하는지였다.
그래서 F7(Step into)로 들어가서 살펴보았는데, 별 중요한 함수는 아닌 것으로 판단하였다.
또 다른 궁금점은, 디버깅 모드에서 "정상" 문자열을 출력할 수 있는가였다.
그래서 IsDebuggerPresent가 종료된 후, EAX 값을 0으로 바꿔보았다.
예상대로 "정상" 문자열을 출력하였다.
그럼 IsDebuggerPresent에서는 무슨 일을 할까?
위 이미지는 IsDebuggerPresent의 내부이다.
딱 세 줄이 있는데,
1. MOV EAX, DWORD PTR FS:[30]
2. MOVZX EAX, BYTE PTR DS:[EAX+2]
3. RETN
이다.
(참고)
MOVZX
1. byte나 word를 word나 double-word 목적지에 전송
2. 0비트로 목적지 피연산자의 왼쪽 비트들을 채움.
첫번째, FS:[30]을 보면,
FS는 세그먼트 레지스터이다.
FS:[0x00]
4
Win9x and NT
현재 Structured Exception Handling (SEH) 프레임
FS:[0x04]
4
Win9x and NT
스택 베이스 / 스택의 바닥 (높은 주소)
FS:[0x08]
4
Win9x and NT
스택 한계 / 스택의 천장 (낮은 주소)
FS:[0x0C]
4
NT
SubSystemTib
FS:[0x10]
4
NT
Fiber data
FS:[0x14]
4
Win9x and NT
임의 데이터 슬롯
FS:[0x18]
4
Win9x and NT
Thread Environment Block
---- NT subsystem 독립적인 부분 끝 ----
FS:[0x1C]
4
NT
Environment Pointer
FS:[0x20]
4
NT
프로세스 ID (몇몇 윈도우 버전에서 이 필드는 'DebugContext'로 쓰인다.)
FS:[0x24]
4
NT
현재 스레드 ID
FS:[0x28]
4
NT
활동중인 RPC 핸들
FS:[0x2C]
4
Win9x and NT
thread-local storage 배열의 선형 주소
FS:[0x30]
4
NT
Process Environment Block (PEB)의 선형 주소
FS:[0x34]
4
NT
마지막 오류 번호
FS:[0x38]
4
NT
Count of owned critical sections
FS:[0x3C]
4
NT
CSR 클라이언트 스레드의 주소
FS:[0x40]
4
NT
Win32 스레드 정보
FS:[0x44]
124
NT, Wine
Win32 클라이언트 정보 (NT), user32 private data (Wine), 0x60 = LastError (Win95), 0x74 = LastError (WinME)
FS:[0xC0]
4
NT
Reserved for Wow64. Wow64에서 FastSysCall에 대한 포인터를 포함한다.
FS:[0xC4]
4
NT
Current Locale
FS:[0xC8]
4
NT
FP Software Status Register
FS:[0xCC]
216
NT, Wine
Reserved for OS (NT), kernel32 private data (Wine)
herein: FS:[0x124] 4 NT Pointer to KTHREAD (ETHREAD) structure
FS:[0x1A4]
4
NT
예외 코드
FS:[0x1A8]
18
NT
Activation context stack
FS:[0x1BC]
24
NT, Wine
Spare bytes (NT), ntdll private data (Wine)
FS:[0x1D4]
40
NT, Wine
Reserved for OS (NT), ntdll private data (Wine)
FS:[0x1FC]
1248
NT, Wine
GDI TEB Batch (OS), vm86 private data (Wine)
FS:[0x6DC]
4
NT
GDI Region
FS:[0x6E0]
4
NT
GDI 펜
FS:[0x6E4]
4
NT
GDI 브러시
FS:[0x6E8]
4
NT
Real Process ID
FS:[0x6EC]
4
NT
Real Thread ID
FS:[0x6F0]
4
NT
GDI cached process handle
FS:[0x6F4]
4
NT
GDI 클라이언트 프로세스 ID (PID)
FS:[0x6F8]
4
NT
GDI 클라이언트 스레드 ID (TID)
FS:[0x6FC]
4
NT
GDI thread locale information
FS:[0x700]
20
NT
Reserved for user application
FS:[0x714]
1248
NT
Reserved for GL
FS:[0xBF4]
4
NT
마지막 상태 값
FS:[0xBF8]
532
NT
Static UNICODE_STRING buffer
FS:[0xE0C]
4
NT
deallocation stack에 대한 포인터
FS:[0xE10]
256
NT
TLS 슬롯, 슬롯 당 4 byte
FS:[0xF10]
8
NT
TLS 링크 (LIST_ENTRY structure)
FS:[0xF18]
4
NT
VDM
FS:[0xF1C]
4
NT
Reserved for RPC
FS:[0xF28]
4
NT
스레드 에러 모드(RtlSetThreadErrorMode)
FS 레지스터의 구조이다.
(출처 : https://ko.wikipedia.org/wiki/Win32_%EC%8A%A4%EB%A0%88%EB%93%9C_%EC%A0%95%EB%B3%B4_%EB%B8%94%EB%A1%9D (위키피디아))
우리가 찾는[30]은 Pointer to PEB라는 것임을 알 수 있는데,
Pointer to PEB를 구글에 검색해보면,
BeingDebugged
프로세스가 디버깅 당하는 중인지
마이크로소프트는 이 필드 대신 공식적인 Win32를 사용할 것을 권장한다. CheckRemoteDebuggerPresent () [2]
Ldr
로드된 모듈에 대한 정보를 제공하는 PEB_LDR_DATA 구조체를 가리키는 포인터
kernel32 와 ntdll 의 베이스 주소가 들어 있다.
ProcessParameters
로드된 모듈에 대한 정보를 제공하는 RTL_USER_PROCESS_PARAMETERS 구조체를 가리키는 포인터
RTL_USER_PROCESS_PARAMETERS 구조체는 또한 대부분이 문서화되어 있지 않으며, 여러 버전에서 사용할 수 있다는 보장이 없다.[4]
PostProcessInitRoutine
DLL 초기화 이후, 메인 실행 코드가 발동되기 이전에 호출되는 콜백 함수에 대한 포인터
이 콜백 함수는 윈도우 2000 에서 사용되지만, 윈도우 NT 이후 버전에서 사용될 수 있다는 보장은 없다.[2]
SessionId
프로세스를 부분으로 갖는 터미널 서비스 세션의 세션 ID
NtCreateUserProcess () 시스템 호출은 커널 내부의 MmGetSessionId () 함수를 호출함으로써 초기화한다.[3]
출처 : https://ko.wikipedia.org/wiki/%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4_%ED%99%98%EA%B2%BD_%EB%B8%94%EB%A1%9D (위키피디아)
위 PEB 구조를 통해 안티 디버깅을 할 수 있다.
우선적으로 FS:[30]으로 디버깅 모드인지 아닌지 확인한다(BeingDebugged)
- 0 : 디버깅 모드가 아님(False)
- 1 : 디버깅 모드임(True)
그리고 MOVZX EAX, BYTE PTR DS:[EAX+2] 로 해당 값을 가져온다.
여기서 EAX = 003BA000, [EAX+2] = [003BA002] = 01
즉, 01값이 리턴된다.(앞에는 0으로 채워서 00000001)
그러므로 IsDebuggerPresent 함수의 RETN 값으로 00000001이 되는 것이고,
이는 디버깅 모드임을 알려주는 값이라고 볼 수 있다.