반응형


악성코드 분석을 위한 리버스 엔지니어링 - PE 구조



WinNT.h

위의 파일에서 직접 PE파일과 비교해서 찾아보면 더욱 더 좋다.


PE파일 포맷이란?

PE 포맷(Portable Executable)은 윈도우 운영 체제에서 사용되는 실행 파일, DLL, object 코드, FON 폰트 파일 등을

위한 파일 형식이다. PE 포맷은 윈도우 로더가 실행 가능한 코드를 관리하는데 필요한 정보를 캡슐화한 데이터 구조체이다.

 이것은 링킹을 위한 동적 라이브러리 참조, API 익스포트와 임포트 테이블, 자원 관리 데이터 그리고 TLS 데이터를 포함한다

윈도우 NT 운영체제에서, PE 포맷은 EXE, DLL, SYS (디바이스 드라이버), 그리고 다른 파일 종류들에서 쓰인다.

 통일 확장 펌웨어 인터페이스 (EFI) 설명서는 PEEFI 환경에서 표준 실행 파일 형식이라고 언급한다

윈도우 NT 운영 체제에서, PE는 현재 IA-32, IA-64, x86-64 (AMD64/Intel64), 그리고 

ARM instruction set architectures (ISAs)를 지원한다. 윈도우 2000 이전에서, 윈도우 NT(그리고 PE) MIPS, Alpha,

리고 파워PC ISAs를 지원했다. PE가 윈도우 CE에서 사용되므로 이것은 MIPS, ARM (Thumb 포함), 그리고

 SuperH ISA의 변형들을 지원하고 있다.



PE파일을 왜 만들었는가?

디스크에 있는 바이너리파일을 규칙을 가지고 메모리에 올리기 위해
마이크로소프트에서 PE파일을 만듬


PE파일의 종류

종류

확장자

실행파일

Exe,scr

라이브러리 파일

Dll, ocx,cpl,drv

오브젝트 파일

Obj

드라이버 파일

Sys,vxd






PE파일 포맷 구조
대표적으로 PE파일 구조를 보게해주는 툴인 PEView가 있다.

IMAGE_DOS_HEADER

DOS Header
- PE
파일 포맷을 만들 때 그 당시에 쓰던 도스 파일에 대한 하위 호환성을 고려
-
중요한 멤버는 첫 번째 e_magic(DOS Signature)와 마지막 멤버인 e_lfanew(NT Header시작주소) 두 가지임
-
e_magicDOS 시절 실행 파일을 설계한 Mark Zbikowaski의 이니셜로 만든 것
-
e_lfanewNT Header의 시작 주소를 나타냄
- DOS Header
e_magic, e_lfanew값 중 하나라도 변경된다면 프로그램이 실행되지 않음

4D 5A : DOS Signature
E0 00 00 00 : NT시작 주소를 나타냄


MS-DOS Stub Program

DOS Stub
- DOS Stub
의 존재 여부는 옵션이며 크기도 일정치 않음
-
있어도 되고 없어도 되는 부분
-
크기가 정해져 있는 것이 아니라 DOS stub에 악성코드를 심기도 함
- DOS Stub
에는 어셈블리어 코드와 This program cannot be run in dos mode라는
문자열이 있음
- DOS
모드에서 해당 파일을 실행하면 위의 문자열을 출력하고 종료

IMAGE_NT_HEADERS

NT Header
- NT Header
구조체
- DOS Header
의 마지막 멤버가 가르키는 주소에서 시작함
- 3
개의 멤버로 구성되어 있음
Signature – File Header – Optional Header

Signature50450000h [PE..]의 값을 가지고 나머지 두 멤버는 또 다른 구조체임

File Header 주요 멤버
1. Machine
CPU별 고유한 값, Intel x86 = 0x014c, intel x64=0x0200의 값
2. NumberOfSections
섹션의 개수를 나타냄
3. SizeOfOptionalHeader
Optional Header의 크기를 나타냄
4. Characteristics
파일의 속성을 나타냄 // 실행이 가능한지 또는 DLL 파일인지 등의 정보가 있음




Optional Header의 주요 멤버

1.Magic
Magic넘버는 32비트면 10B, 64비트면 20B를 가짐
2.AddressOfEntryPoint
EPRVA(Relatvie Virtual Address)값을 가지고 있음
프로세스의 시작 위치 주소값(RVA)
3.ImageBase
PE
파일이 로딩되는 시작 주소임
파일을 메모리에 로딩 후 EIP레지스터의 값 = ImageBase+AddressOfEntryPoint
메모리에서의 기준주소
4.SectionAlignment
메모리에서 섹션의 최소단위
5.FileAlignmnet
파일에서 섹션의 최소단위
6.SizeOfImage
PE
파일이 메모리에 로딩되었을 때의 크기
7.SizeOfHeader
PE
헤더의 전체 크기
8.Subsystem
GUI,CUI
버전인지 드라이브파일인지나타냄
1=
드리이버, 2=GUI, 3=CUI
9.NumberOfRvaAndSizes
DataDirectory배열의 개수를 나타냄
10.DataDirectory
IMAGE_DATA_DIRECTORY
구조체의 배열, 각 항목은 정해진 값을 가지고 각 항목 table이 어디에 위치해 있는지
RVA
값과 크기를 나타냄

0B 01 : Magic
89 36 00 00 : AddressOfEntryPoint
00 00 00 01 : ImageBase
00 10 00 00 : SectionAlignment
00 02 00 00 : FileAlignment
00 00 03 00 : SizeOfImage
00 04 00 00 : SizeOfHeader
02 00 : Subsystem
10 00 00 00 : NumberOfRvaAndSizes

Image Base + AddressOfEntryPoint = EIP
실행시 위의 주소로 그대로 EIP가 찍히진 않는다.
OptionalHeader에 DllCharacteristics이 ASLR과 관련되어 있기때문이다.
OptionalHeader에서 DllCharacteristics값을 바꿔 ASLR을 사용안하는 방법과
Section Header에 가서 NumberOfRelocations을 조정하면 고정된 위치로 나온다고 함.



SECTION

Section Header의 주요 멤버
1.VirtualSize
메모리에서 섹션이 차지하는 크기
2.VirtualAddress
메모리에서 섹션의 시작 주소(RVA)
3.SizeOfRawData
파일에서 섹션이 차지하는 크기
4.PointerToRawData
파일에서 섹션의 시작위치
5.Characteristics
섹션의 속성(Bit OR)


8C A6 00 00 : VirtualSize
00 10 00 00 : VirtualAddress
00 A8 00 00 : SizeOfRawData
00 04 00 00 : PointerToRawData
20 00 00 60 : Characteristics


.text 섹션

컴파일된 코드, 코드, 실행, 읽기 속성, 컴파일 후의 결과가 저장되는 섹션

.data섹션

읽기/쓰기가 가능하고 초기화된 전역변수, 상수가 존재한느 섹션

.rdata섹션

읽기만 가능하고 문자열 상수나 const로 선언된 변수처럼 읽기만 가능한 읽기 전용 섹션

.idata섹션

읽기/쓰기모두 가능하고 IAT와 관련된 정보가 들어가있는 섹션

.edata섹션

읽기만 가능하고 EAT와 관련된 정보가 들어 있는 섹션

.bss섹션

읽기/쓰기가 가능하고 초기화 되지 않은 전역 변수

.rsrc섹션

읽기만 가능하고 리소스가(아이콘 등)이 저장되는 섹션











BOUND IMPORT Directory Table, BOUND IMPORT DLL Names

.exe(바이너리파일)은 라이브러리를 가지고 있다. 일일이 코딩하기 귀찮으니까 가져다 쓰는 것이다.
리눅스는 .so가 윈도우는 .dll이 비슷한 역할을 한다.

A.exe와 B.exe가 있고 .dll이 있으면 A와 B에서 .dll을 다 쓸 수 있다.

.dll에서는 쓸 수 있는 함수들이 있을 것이다. 그 함수들을 일부분만 쓰게 해놓지 않는 경우가 대부분이다.

.dll도 실행파일이다. A.exe에서 .dll이 쓰는 함수를 받아온다. 그리고 .dll을 한개만 쓰는게 아니라 여러개의 .dll을 사용할 수 있다.

Directory Table에 있는 함수를 전부 쓰는 것이 아니다. 확장성을 위해서 놔둔 것이다.

A.exe에서 직접 .dll에 있는 함수를 쓸 수 없으니 포인터 형태로 가지고 온다.

A.exe가 .dll을 읽어올 수 있는 구조체를 불러와서 .dll의 함수 주소를 가져오는 것을 IAT라고 한다.

.dll에 있으니까 그 안에 어떤 함수를 쓰는지 확인해야한다. 특정 .dll을 쓴다고 악성코드라고 단정지을순 없다.

.dll에 있는 어떤 함수를 쓰는지 파악해야 그 파일의 성질을 알 수 있다.

IAT는 dll의 정보를 가지고 있는게 아니라 .dll을 향하는 어드레스를 리스트로 가지고 있는 것이다.

.dll은 A.exe와 B.exe등 여러 곳에서 사용할 수 있다. 이때 어느 바이너리에서 연동되어 사용되는지 알 필요가 있다.

그래서 EAT가 필요하다. EAT는 구조체 형식이다. 

IMPORT_DESCRIPTOR검색해서 OriginalFirstThunk 이것을 꼭 기억해두자

import name table을 다른 말로 INT라고 한다. 이 INT의 시작주소를 나타낸다.
NAME : 라이브러리의 이름

IMPORT_BY_NAME
네임값 받아오면 거기다가 반영을 하고 체크를 한다.

받아오는 과정
IAT

1. IID(Image_Import_Descriptor)의 NAME 멤버를 읽어서 라이브러리의 이름을 알아냄
ex) Kernel32.dll 
(( RUN32DLL.EXE로도 dll파일을 Running시킬 수 있다. ))

2. LoadLibrary 함수를 이용하여 DLL 로딩
ex) LoadLibrary(Kernel32.dll)

3. IID의 OriginalFirstThunk멤버를 읽어서 INT(Image_Import_Descriptor)주소를 알아냄

4. INT에서 배열들의 값을 하나씩 읽어 들여 해당 Image_Import_By_Name 구조체주소(RVA)값을 알아낸다.

5. Image_Import_By_Name에서 Original값과 함수 이름을 알아내고 해당 함수의 시작 주소를 알아냄

6. IDD의 FirstThunk의 멤버를 읽어서 IAT주소를 알아냄

7. 해당 IAT 배열 값에 5번에서 알아낸 함수 시작주소를 입력함

8. INT 배열이 끝날때 까지 (NULL 부분이 올때까지) 4~7번 과정을 반복한다. 

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기