Iriton's log
1. 리버싱을 위한 기초 지식 본문
*본 포스트는 '리버싱 입문' 책을 참고하여 작성되었습니다.
1. 리버싱을 위한 프로그램 실행 구조
컴퓨터의 기본 요소
- CPU
- 메모리
- 하드디스크
실행할 수 있는 파일(exe)은 기본적으로 HDD에 저장된다.
윈도우 상에서 이 실행 파일을 PE(Portable Executable) 파일이라고 부른다.
PE 파일 구조
- Header: 중요 정보가 들어 있다. 해당 정보를 Loader가 분석해서 Body에 있는 Code와 Data를 메모리에 배치한다.
- Body: Code, Data가 들어 있다.
메모리 구조
- 코드 영역: 프로그램 코드가 들어간다.
- 데이터 영역: 정적 변수, 전역 변수가 들어간다.(고정된 값)
- 스택 영역: 함수 호출 시 사용되는 매개 변수, 지역 변수가 들어간다. (코드에 따라 변하는 값)
- 힙 영역: 동적 메모리 할당에 사용된다.
Loading 후 코드 영역과 데이터 영역에 자료가 들어간다.
프로그램 실행 후 스택 영역과 힙 영역에 데이터가 쌓인다.
OS는 메모리에 있는 PE 파일을 실행하기 위해 PE 헤더 정보에 있는 Entry Point를 찾아서 그곳부터 프로그램을 실행한다.
프로그램 안에 있는 명령어를 실행하는 건 CPU의 제어장치와 연상장치이다.
두 장치가 프로그램을 실행하는 데 필요한 데이터는 모두 CPU의 레지스터에 있어야 한다.
따라서, 메모리에 있는 값을 레지스터에 복사하는 과정이 필요하다.
컴퓨터는 하나의 CPU로 동시에 여러 개의 프로그램을 실행해야 한다.
이게 가능하려면 프로그램이 자신에게 필요한 데이터를 레지스터로 옮겨야 한다.
CPU 사용이 끝나면 다른 프로그램에게 CPU 사용 권한을 넘겨 사용하게 되는데 이때 자신이 사용하던 모든 레지스터를 메모리 영역으로 복사해 둔다. 이를 Context Switching이라고 한다.
2. 레지스터
레지스터: CPU에서 사용하는 고속 기억장치. CPU는 연산 수행을 위해 메모리에 있는 데이터를 CPU 내부의 레지스터로 가져온다.
Intel x86 CPU 기본 구조인 IA-32 아키텍처에서는 9개의 범용 레지스터를 제공한다.
프로그램에서 사용하는 레지스터: EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP
운영체제에서 사용하는 레지스터: EIP
EAX - Accumulator
: 곱셈과 나눗셈 명령에서 사용되며 함수의 반환값 저장
EBX - Base
: ESI나 EDI와 결합해 인덱스에 사용
ECX - Couter
: 반복 명령어에서 반복 카운터에 사용. 반복할 횟수 지정해 놓고 반복 작업 수행
EDX - Data
: EAX와 같이 사용. 부호 확장 명령 등에 활용
ESI - Source
: 데이터 복사나 조작할 때 소스 데이터 주소가 저장. ESI 레지스터가 가리키고 있는 주소엥 있는 데이터를 EDI 레지스터가 가리키는 주소로 복사하는 용도로 많이 사용된다.
EDI - Destination
: 복사 작업을 할 때 목적지 주소가 저장. 주로 ESI 레지스터가 가리키는 주소의 데이터가 저장되는 곳
EBP - Base
: 하나의 스택 프레임의 주소가 저장. 현재 사용되는 스택 프레임이 살아 있는 동안 EBP 값은 변하지 않는다. 현재 스택 프레임이 죽으면 이전에 사용되던 스택 프레임을 가리키게 된다.(상위 함수로 돌아가야 되니까)
ESP - Stack
: 하나의 스택 프레임의 끝 지점 주소가 저장 PUSH, POP 명령어에 따라서 ESP 값이 4 byte씩 변한다.(크기가 4byte라서)
EIP - Instruction
: 다음 실행할 명령어가 저장된 메모리 주소 저장. 현재 명령어를 모두 실행한 다음에 EIP 레지스터에 저장된 주소에 있는 명령어 실행.
32비트 레지스터는 용도에 따라서 8비트 단위로 나누어 사용할 수 있다.
3. 스택과 스택 프레임
스택
LIFO(Last In First Out) 방식으로 동작하는 구조
PUSH(데이터를 스택에 넣음), POP(데이터를 스택에서 꺼냄) 두 명령어를 가지고 제어한다.
프로그램에서 스택은
- 서브루틴으로 인자를 전달
- 서브루틴 내부에서 사용하는 지역변수가 저장되는 공간을 제공
- 서브루틴 종료 시 돌아갈 주소 저장하는 역할
OS가 이러한 메커니즘을 서브루틴이 호출되고 종료될 때 제어하고 있다.
스택 프레임
서브루틴이 가지는 자신만의 스택 영역
호출될 때 생겼다가 리턴될 때 소멸
복귀 주소를 스택에 집어넣고 시작(그래야 LIFO 방식에 의해 서브루틴의 데이터가 모두 POP 된 후에 복귀 주소로 돌아갈 수 있기 때문) 추가로, 이전의 EBP도 백업한다. 해당 EBP는 스택 프레임에서 데이터 참조를 위한 기준 주소인 Frame Pointer로 사용된다.
4. PE 파일
기본 개념
기계어는 파일로 만들어지는데 EXE나 DLL 같은 확장자로 만들어진다.
이런 파일을 PE 파일이라고 한다.
PE 파일은 포맷이 존재한다. 해당 포맷을 지키지 않으면 실행되지 않는다.
실행 계열: EXE, SCR
라이브러리 계열: DLL, OCX, CPL, DRV
드라이버 계열: SYS, VXD
오브젝트 파일 계열: DBJ
PE 파일은 PE Header, Section Header, Section Data로 구성된다.
여기서 PE Header + Section Header를 PE Header로 통칭하기도 한다.
PE 파일은 공통으로 PE Header를 가지고 있는데, Section Header는 PE 파일마다 가지고 있는 내용과 형태가 다르다.
1개 이상의 Section Header를 지닌다.
Section Data 역시 파일마다 내용, 형태가 다르며 1개 이상 가지고 있다.
Header에는 파일 구성 관련 정보가,
Data에는 코드와 데이터가 들어 있다.
- IMAGE_DOS_HEADER
: DOS 운영체제가 윈도우용 PE 파일을 실행했을 때 적절한 오류 메세지를 보여 준다.
실제 윈도우용 PE 헤더 위치를 가리키는 역할
- MS-DOS Stub Program
: DOS 운영체제가 윈도우용 PE 파일을 실행했을 때 보여 줄 오류 메세지를 저장하고 있다.
- IMAGE_OPTOINAL_HEADER
: PE 구조에서 핵심적인 역할을 한다.
Image Base는 메모리에 PE 파일이 저장되는 시작 주소이다.
PE 파일에서 확인할 수 있는 RVA(Relative Virtual Address)는 메모리에 로딩되면서 Image Base 값과 합산된 주소(VA: Virtual Address)에 저장된다.
Address of Entry Point는 프로그램 실행에 대한 제어권이 커널 영역에서 코드 영역으로 처음 넘어오는 주소인 엔트리 포인트를 가리킨다.
Base of Code는 코드 영역이 시작되는 주소(RVA)를 가리킨다. 디버거로 프로그램을 열었을 때 코드 영역의 맨 윗부분에 해당한다.
주소 지정 방법
- pFile: PE 파일 내부에서의 오프셋
- RVA: 메모리에 로드됐을 때, 기준값에서 얼마나 떨어져 있는지를 나타내는 상대 위치
- VA: 가상 메모리 상에 저장되는 실제 주소
RVA와 VA는 메모리에 저장되어 있을 때 의미 있는 반면 pFile은 HDD에 물리적으로 저장되어 있을 때 의미있음.
IAT
Import Address Table
어떤 라이브러리의 어떤 함수를 가져다 쓰는지 기록하는 정보
DLL을 사용할 수 있도록 지원
로더는 PE 파일을 메모리로 로딩할 때 IAT에 기록된 API 이름을 참조해서 실제 주소를 찾아 IAT 안에 API를 가리키는 주소를 적어 놓는다.
리버싱 도구
1. 기초 예제 abex crackme
2. 리버싱 바이블 Lena 강의
3. PE 파일 분석 프로그램 PEView와 Detect It Easy
4. 메모리 덤프를 위한 OllyDBG 플러그인 OllyDumpEx
5. 손상된 IAT 복구하는 LoadPE
'Reversing > Study' 카테고리의 다른 글
2. 리버싱 시작하기 - abex crackme #1 (0) | 2024.05.22 |
---|