본문 바로가기

2. Reversing (리버싱)

[2024.11.09]리버씽씽이_5주차 활동

5주차에는 유튜브 리버싱 강의를 15강~17강까지 학습하였다.

 

 

리버싱 강의

리버싱에 대해 공부합시다.

www.youtube.com

 

 

섹션헤더에 대해 잘 이해했다면 이제부터 PE파일이 메모리에 로딩되었을 때 각 섹션에서 메모리의 주소(RVA)와 오프셋(offset)을 잘 매핑할 수 있어야한다.

이러한 매핑을 일반적으로 RVA To RAW라고 부른다.

오프셋: 오차, 문자 A의 배열이 abcdef를 포함한다면 ‘c’ 문자는 A 시작점에서 2의 오프셋을 지닌다고 할 수 있음

 

RAW(File Offset)

RVA가 속해 있는 섹션을 찾는다.

공식을 이용해서 파일 오프셋(RAW)을 계산한다.

 

<공식>

RAW(File offset) - PointerToRawData(파일에서 섹션의 시작 위치) = RVA(상대주소) - VA(메모리에서 섹션이 차지하는 크기)

RAW(File offset) = RVA(상대주소) - VA(메모리에서 섹션이 차지하는 크기) + PointerToRawData(파일에서 섹션의 시작 위치)

 

#1. RVA = 3000일 때 RAW은 얼마인가?

RVA는 400보다 크고 53200보다 작다. -> RVA는 Section(“.text”)에 위치해 있음

PointerToRawData = 00000400

VA = 01001000

ImageBase = 01000000

VA - ImageBase = 1000(시작 주소를 맞춰줌)

RAW = 3000 – 1000 + 400 = 2400

(RVA + ImageBase = VA(절대주소)와 다름)

 

#2. RVA = BC123 RAW?

PTRD = B9C00

VA = BC000

RAW = B9D23

 

#3. RVA = ABA8 RAW?

Section(“.text”)에 위치

PTRD = 400

VA = 1000

RAW = 9FA8

 

Ex) 파일에서는 Section(“.data”)에 위치, 메모리에서는 .rsrc section에 위치

→ RVA에 대한 RAW를 정의할 수 없다

 

 

IAT (Import Address Table)

IAT는 windows os의 핵심개념인 process memory dll 구조 등에 근간(핵심)을 이해한다고 할 수 있다.

어떤 프로그램에 어떤 library에서 어떤 함수를 사용하고 있는지 기술한(서술한) Table(Array: 배열)이다.

 

DLL(Dynamic Linked Library)이란? ---> ‘동적 연결 라이브러리’

MS-DOS 16bit: DLL X, Library O

예시로 C언어에서 printf() -> stdio.h 바이너리 코드(2진수)를 긁어와서 필요한 프로그램 삽입시킨다.

window 멀티 태스킹(다중작업)

 

DLL 초기 구상

프로그램에 라이브러리를 포함시키지 말고 별도의 파일(DLL)로 구성하여 필요할때마다 호출하자

일단 한번 로딩된 DLL의 코드 리소스 메모리 매핑 기술로 다같이 나누어쓰자

라이브러리가 업뎃 했을 때 해당 DLL파일만 교체하면 되니까 간편하다

 

DLL 동적 연결 라이브러리

동적으로 라이브러리 함수를 가져와서 연결(파일형태)

 

DLL 로딩되는 방식이 크게 2가지

프로그램에서 사용되는 순간 딱 연결(로딩)하고 사용 끝나면 메모리에서 해제되는 방법

-> explicit Linking

2. 프로그램이 실행될 때 같이 연결(로딩)되어 프로그램 종료시 메모리에서 해제되는 방법

-> implicit Linking

 

Kernel은 admin(OS 관리자)이라고 생각하면 된다. 모든 것을 관리

 

DLL 재배치

어떤 파일의 IB가 10000인데 로딩을 하려고 봤더니 이미 다른 파일이 그 자리를 차지하고 있다면 다른 빈공간을 찾아서 로딩

 

 

 

PE파일은 자신이 어떤 라이브러리를 import(호출)하고 있는지 IAT구조체를 통해서 명시하고 있다.

 

OriginalFirstThunk : INT(Import Name Table) 주소(RVA)

Name : Library 이름 문자열 주소(RVA)

FirstThunk: IAT 주소를 가리킨다.(RVA)

(참고. INT와 IAT는 Long Type 배열이고 NULL로 끝난다. 그리고 IAT와 INT 값이 서로 같아야한다.)

 

PE로더가 임포트 함수를 IAT에 입력하는 기본적인 순서

[IAT 입력 순서]

IMAGE_IMPORT_DESCRIPTOR의 Name 멤버를 읽고 나서 library 이름 문자열(“Kernal32.dll”)

해당 라이브러리를 로딩한다. -> LoadLibrary(“Kernal32.dll”)

IMAGE_IMPORT_DESCRIPTOR의 OriginalFirstThunk 멤버를 읽어서 INT 주소를 얻는다

INT에서 값 읽어 해당 IMAGE_IMPORT_BY_NAME 주소(RVA)를 얻음

IMAGE_IMPORT_BY_NAME에서 Hint(고유번호) 또는 Name 항목 -> 함수 시작주소 얻음 (GetProcAddress() 함수로)

IAT 배열 값에 위에서 구한(4번) 함수 주소를 입력한다.

INT NULL일 때까지 4~6 반복

IMAGE_OPTIONAL_HEADER32.DataDirectory[1].VirtualAddress -> IMAGE_IMPORT_DESCRIPTOR

 

1.라이브러리 이름(Name)

Name 항목은 임포트 함수가 라이브러리에 파일의 이름 문자열 포인터다.

--> 위의 1번 과정

2. OriginalFirstThunk - INT(Import Name Table)

INT가 뭔지 알아보자! 임포트 하는 함수의 정보(Ordinal)가 담긴 구조체 포인터 배열이다.

이 정보는 필요로하는 프로세스 메모리에 로딩된 라이브러리에서 해당 함수의 시작주소를 정확히 구할 수 있다.

(나중에 EAT할 때 자세히 다룬다)

--> IMAGE_IMPORT_BY_NAME은 INT의 구조체라고 생각하면 됨

3. Ordinal은 고유번호인데 함수들이 변수들을 구별하기 위한 번호이다.

정리하자면 IMAGE_IMPORT_BY_NAME은 DLL에서 함수를 호출할 때 필요한 정보(이름, 고유번호)를 담고 있다.

(그 정보를 토대로 주소를 서치)

 

<중간정리>

OriginalFirstThunk -> INT를 가리킨다. 그리고 INT와 IMAGE_IMPORT_BY_NAME의 차이점은 INT는 함수 이름과 고유번호를 담고 있고 IMAGE_IMPORT_BY_NAME는 해당 함수에 대한 자세한 정보를 담고 있다. 그리고 NAME와 IMAGE_IMPORT_BY_NAME 차이점은 NAME은 (필요한) 가져와야하는 DLL 파일의 이름을 가리키고 IMAGE_IMPORT_BY_NAME DLL에서 내보내야하는 함수의 이름과 고유번호 값을 가지고 있는 구조체

4. FirstThunk – IAT(Import Address Table)

FirstThunk는 IAT를 가리키는 포인터다.

 

IAT란

실행파일(ex: DLL)이 다른 모듈(ex: exe)에서 사용하는 함수를 가리키는 포인터 배열이다.

간단하게 말하자면 IAT는 모든 함수의 주소들의 집합이다.