2. PE – Portable Executable
• Portable Executable
– 16 bit VxD 를 제외한 win32 실행 파일 규격
– Exe, dll, ocx, sys 등
– windows 표준 규격이므로 머신 (CPU) 에 상관없이 windows 운
영체제 하에서 모두 실행 가능한 실행 파일 포맷
3. RVA
• RVA – Relative Virtual Address
PE
(Exe, dll)
RVA
매핑된 PE
이미지
가상 메모리 공간
가상 주소 0x00000000
RVA
로딩 시작 주소
(ImageBase)
RVA
가상 메모리상에서의 실제 번지 ( 가상 주소 ) = ImageBase + RVA
4. Section
• Section ?
실행 코드 , 리소스 데이터 , 읽기 전용 데이터 등 비슷한 용도의
데이터들을 페이지 단위로 매핑하기 위한 공간
코드 영역 .text 실행코드를 담는 섹션
데이터 영역 .data, .rdata, .bss 전역 데이터 , 문자열 , VMT 등의
영역
리소스 영역 .rsrc 리소스 , 아이콘 등의 영역
IAT .idata, .didat 임포트 데이터
EAT .edata 익스포트 데이터
기준 재 배치 .reloc 기준 재 배치 영역
5. DOS 헤더
PE 헤더
섹션 테이블
.text
.data
.rsrc
.reloc
File offset
DOS 헤더
PE 헤더
섹션 테이블
.text
.data
.rsrc
.reloc
0x00400000
메모리 증가 방향
메모리 매핑
PE 의 로딩 방식
• Memory mapped file
– 디스크에 존재하는 파일을 직접 가상메모리 공간에 매핑
– Windows 의 IPC ( 파이프 , 메일 슬롯 , RPC, WM_COPYDATA
등 ) 구현의 근간이 되는 개념
– Pagefile.sys ?
6. 가상 주소 공간
• 가상 주소 – Virtual Address
프로세스는 몽땅 사용할 수 있는 4GB 의 메모리를 제공받는다 .
10. IMAGE_FILE_HEADER
• WORD NumberOfSections;
IMAGE_SECTION_HEADER 의 개수
• WORD SizeOfOptionalHeader;
IMAGE_FILE_HEADER 다음에 오는 IMAGE_OPTIONAL_HEADER 구조체의 바이
트수 , OBJ 인 경우 0, 실행파일인 경우 sizeof(IMAGE_OPTIONAL_HEADER) 임 .
32 비트 PE 인 경우 0xE0 (224), 64 비트 PE 의 경우 0xF0 (240)
11. IMAGE_OPTIONAL_HEADER
typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
//
WORD Magic;
DWORD AddressOfEntryPoint; // 프로그램 엔트리 포인트
// WinMainCRTStartup or minaCRTStartup or
// DllMainCRTStartup
//
// NT additional fields.
//
DWORD ImageBase; // PE 가 가상 주소 공간에 매핑될 메모리상의 시작
// 주소 , 디폴트로 0x00400000 (exe),
// 0x10000000(dll)
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
DWORD NumberOfRvaAndSizes; // 바로 다음 필드인 DataDirectory[] 배열의
// 갯수 . 항상 16 이다 (0x00000010)
// 아래의 Direcotry Entries 박스
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16
14. Export section (.edata)
• Dll 처럼 특정 함수포인터 , symbol 등을 외부로 노출하는 경우 생성
됨
• IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY[IMAGE
_DIRECTORY_ENTRY_EXPORT] 구조체가 가리키는 곳이 export
섹션이다 .
• IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY
[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress
값이 .edata 섹션의 RVA 값이 됨
• .edata 섹션은 IMAGE_EXPORT_DIRECTORY 구조체로 이루어져
있음
15. IMAGE_EXPORT_DIRECTORY
• Name
– DLL 의 이름 문자열
• Base
– Export 된 함수들의 서수의 시작 번호 ( 임의의 숫자로 시작 가능 )
– 정확한 서수의 값을 계산하려면 AddressOfNameOrdinal 필드의 값에 Base 필드 값을 더해 주어야 함
• NumberOfFunctions
– AddressOfFunctions 필드가 가리키는 RVA 배열의 개수
– 서수의 끝 번호 – 시작 번호 + 1 의 값
– 불연속적인 서수를 사용하는 경우 NumberOfNames 와 다름
• NumberOfNames
– AddressOfNames 필드가 가리키는 RVA 배열의 개수
– 실제 export 된 함수의 정확한 개수를 나타내는 값임
• AddressOfFunctions
– Export 된 함수들의 함수 포인터를 가진
배열의 RVA
• AddressOfNames
– Export 된 함수들의 심볼을 가리키는
문자열 배열의 RVA
• AddressOfNameOrdinals
– WORD 타입의 배열에 대한 포인터 RVA
– WORD 타입 배열의 각 원소는 ordinal
값을 저장하고 있다 .
18. Import section (.idata)
• Import 섹션은 해당 PE 가 로드한 외부 모듈 / 함수에 대
한 정보를 보관하는 섹션이다 .
• IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY[IMAGE
_DIRECTORY_ENTRY_IMPORT] 구조체가 가리키는 곳이 import
섹션이다 .
• IMAGE_OPTIONAL_HEADER.IMAGE_DATA_DIRECTORY
[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress
값이 .idata 섹션의 RVA 값이 됨
• .edata 섹션은 IMAGE_IMPORT_DESCRIPTOR 구조체로
이루어져 있음
19. IMAGE_IMPORT_DESCRIPTOR
• OriginalFirstThunk
– IMAGE_THUNK_DATA 구조체 배열의 RVA
– 각 배열의 원소는 IMAGE_IMPORT_BY_NAME 구조체 의 RVA
• Name
– 임포트한 dll 의 이름 문자열
• FirstThunk ( 아주 중요 !)
– PE 가 디스크에 존재할
때는 OriginalFirstThunk
와 동일한 값
– 가상메모리상에서는
IAT 포인터 , 즉 실제
임포트한 함수의 주소
( 바인딩 되었다고 표현 )
20. IMAGE_THUNK_DATA
• 4 바이트 유니온
• ForwarderString
– 임포트한 함수가 포워딩 된 경우 ‘ dll.RtlGetWin32LastError’ 형태
의 문자열 RVA
• Ordinal
– 서수로 임포트 된 경우 해당
함수의 서수값
• AddressOfData
– 이름으로 임포트 된 경우 해당
함수의 함수 포인터
• Name or Ordinal?
– IMAGE_IMPORT_DESCRIPTOR.OrginalFirstThunk 의 최상위
비트가 설정된 경우 Ordinal 로 임포트 한 것임
21. IAT 바인딩 과정 - 1
• Disk 에 존재할 때의 Import section 구조
22. IAT 바인딩 과정 - 2
• 가상 주소 공간에 매핑된 후의 Import section
23. 볼랜드 계열 링커가 생성한 PE 의 IAT 분석
• 볼랜드 계열 링커가 생성한 PE 는 OrginalFirstThunk 필
드가 항상 0 이다 .
• 따라서 가상 메모리 상에서 임포트 한 DLL 및 함수의 서
수 혹은 이름을 역으로 분석해 낼 수 없다 .
• ImportREC 같은 툴로 IAT 복원이 안 된다 . !!
25. Injecting Technique -1
• Registry 를 이용한 dll 침투 기법
키에 dll 이름을 등록하면 user32.dll 이 로드되면서 이 DLL 들도 함께 로드한
다 .
• 단점
USER32.dll 을 사용하는 어플리케이션만 해당됨 ( 콘솔 APP 는 해당 안됨 )
NT, Win2K 만 해당됨
리부팅 필요
HKEY_LOCAL_MACHINESoftwareMicrosoftwindowsNTCurrentersionWindowsAppInit_DLLs
26. Injecting Technique -2
• SYSTEM-WIDE Windows Hook
– SetWindowsEx() 함수를 통해서 윈도우 메시지 핸들러를 통해
DLL 을 대상 프로세스 컨텍스트로 침투 시키는 방법
- 이 경우 침투된 dll 들은 서로 다른 프로세스 공간에 존재하므로
데이터 공유를 위해 IPC 메커니즘을 구현함
- 이 방법은 UnHookWindowsEx() 을 통해 dll 을 언로드
- Windows 9x 부터 최신의 windows 까지 모두 사용 가능
- 모든 프로세스의 windows message 를 처리하므로 부하를 가중
시킬 수 있음
27. STEP 1.
침투하고자 하는 프로세스의 핸들을 얻는다 .
STEP 2.
kernel32.dll 의 LoarLibrary() 함수의 포인터를 얻는다 .
STEP 3.
CreateRemoteThread() 를 통해서 타겟 프로세스에 스레드를 생성
한다 .
이때 lpParameter 는 “ hook.dll” 문자열의 포인터가 넘어갈 것이다
.
STEP 4.
타겟 프로세스는 LoadLibrary(“hook.dll”) 을 호출하는 것과 동일한
결과를 만든다 .
STEP 5.
hook.dll 의 Dll entry 는 타겟 프로세스의 컨텍스트 내에서 필요한
작업을 수행할 수 있다 .
Injecting Technique -3
• Injecting DLL by using CreateRemoteThread() API
– Win9x 계열에서는 사용할 수 없음
– 타겟 프로세스 공간에 스레드를 생성하는 API
생성된 스레드가 실행할 함수 포인터
LoadLibrary () 함수와 동일한 원형을 가진다 !!
29. Interception mechanisms - 2
• Window 서브 클래싱
– SetWindowLogPtr() 함수를 통해서 타겟 윈도우의 Window
procedure 를 가로채는 (?) 기법
• Proxy dll or Trojan dll
– Kernel32.dll 의 export function 을 동일하게 구현한 dll 을 타겟
프로세스에 침투 시키는 방법 -_-
• Spying by a debugger
– 디버깅 api 를 통해서 , 혹은 디버거를 통해서 타겟 프로세스에
dll 을 침투
32. Interception mechanisms - 4
• EAT hook
– 타겟 프로세스가 Import 한 DLL 들은 이미 타겟 프로세스 주소
공간에 매핑되어있는 상태
– 따라서 로드된 dll 들의 EAT 를 분석해서 주입된 hook.dll 의 주
소공간으로 바꿔치기 하면 됨
IAT 훅의 단점을 극복할 수 있다 . !!
33. Interception mechanisms - 5
• Inline hook
– 함수 내부의 코드를 patch 해서 특정 주소의 인스트럭션을 수행
하도록 flow 를 변경하는 기법
– IAT hook, EAT hook 의 모든 단점을 극복할 수 있는 기술
– 난이도가 높음
34. Interception mechanisms - 6
• Jump templates
– 한 부분만 수정해서 모든 콜에 대한 변조가 가능
– Inline hook 을 통해 구현하면 더욱 효과적 (more stealthy !)
Api_1()
Api_2()
Api_3()
.....
.
Api_x()
IDT vector
IDT vector
0x2E handler
IDT vector
IDT vector
IDT vector
Ntoskrnl 의 인터럽트 핸들러
Hook driver code