티스토리 뷰
본 포스팅은 간단한 디스어셈블을 위한 elf 파일 구조를 정리하기 위함입니다.
ELF (Executable and Linkable Format)
유닉스 계열 운영체제에서 사용되는 표준 바이너리 파일 포맷이다. ELF는 다양한 하드웨어 아키텍처 및 운영체제에서 사용되며, 리눅스의 기본 실행 파일 형식으로 널리 활용되고 있다.
파일 유형으로는 다음과 같다.
- 실행 파일 : 독립적으로 실행 가능한 바이너리 파일
- 오브젝트 파일 : 다른 오브젝트 파일과 링크되어야 실행 가능
- 공유 라이브러리 : 동적 라이브러리를 의미함
- 코어 덤프 : 프로세스의 메모리 상태를 저장하는 파일
보통 ELF 파일은 ELF Header + Program header table + Section header table로 구성된다.

- ELF header : ELF 파일의 시작 부분에 위치하며, 파일의 구조 및 속성을 정의하는 로드맵과 같은 역할
- Program header table : 실행 가능한 파일에서 메모리에 적재해야할 세그먼트 정보를 포함한다. 시스템에 프로세스 이미지를 어떻게 만들지 지시하는 역할
- Section header table : 링킹과 재배치 정보를 가지는 섹션 포함
섹션(Section) vs 세그먼트(Segment)
섹션은 링커(Linker)가 사용하며, 실행 파일을 구성하는 코드, 데이터, 심볼 테이블 등을 포함하는 세부적인 논리적 단위다. 예를 들어 위 ELF 파일 형식 그림에 있는 .text
, .rodata
, .data
하나하나가 다 섹션이다.
반면에, 세그먼트는 파일 실행 시 운영체제가 메모리에 적재하는 데이터의 논리적 단위다. 즉, 프로그램이 실행될 때 메모리에 로드되는 단위로, 주로 코드, 데이터, 스택 등의 정보가 포함된다. 하나의 세그먼트는 여러 개의 섹션을 포함할 수 있다.
예를 들어, 운영체제가 위 ELF 파일 형식으로 이루어 진 파일을 실행하기 위해 메모리에 올린다고 해 보자. 그럼 .text
, .rodata
를 하나하나 메모리에 올리는게 아니라, LOAD 라는 세그먼트 단위로 묶어서 올리게 된다.
ELF Header 구조
#define EI_NIDENT 16
typedef struct {
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
ElfN_Half e_type; /* Object file type */
ElfN_Half e_machine; /* Architecture */
ElfN_Word e_version; /* Object file version */
ElfN_Addr e_entry; /* Entry point virtual address */
ElfN_Off e_phoff; /* Program header table file offset */
ElfN_Off e_shoff; /* Section header table file offset */
ElfN_Word e_flags; /* Processor-specific flags */
ElfN_Half e_ehsize; /* ELF header size in bytes */
ElfN_Half e_phentsize; /* Program header table entry size */
ElfN_Half e_phnum; /* Program header entry count */
ElfN_Half e_shentsize; /* Section header table entry size */
ElfN_Half e_shnum; /* Section header table entry count */
ElfN_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
e_ident
: 파일의 내용을 해석하고 디코딩하기 위해 필요한 정보들- 0x00 ~ 0x03(File indentification) : ELF 파일임을 식별하기 위한 4바이트 매직 넘버로 0x7f, 45(E) ,4c(L), 46(F) 값이 할당
- 0x04(Class) : 파일의 클래스나 용량을 나타냄
- 0 = ELFCLASSNONE(Invalid class)
- 1 = ELFCLASS32(32bit)
- 2 = ELFCLASS64(64bit)
- 0x05 : Data encoding : 데이터의 인코딩 방식 정보
- 0 = ELFDATANONE(Invalid data encoding)
- 1 = ELFDATA2LSB(가장 오른쪽에 있는 비트가 가장 낮은 주소)
- 2 = ELFDATA2MSB(가장 왼쪽에 있는 비트가 가장 낮은 주소)
- 0x06 : File Version : ELF header version number로 현재로서는 EV_CURRENT = 1이 사용
- 0x07 : Start of padding bytes
e_type
: Object 파일의 타입- 0 = ET_NONE(No file type)
- 1 = ET_REL(Relocatable file)
- 2 = ET_EXEC(Executable file)
- 3 = ET_DYN(Shared object file)
- 4 = ET_CORE(Core file)
e_machine
: architecture 정보- 0 = EM_NONE(No machine)
- 1 = EM_M32(AT&TWE 32100)
- 2 = EM_SPARC(SPARC)
- 3 = EM_386(Intel 80386)
e_version
: Object 파일의 버전 정보- 0 = EV_NONE(Invalid version)
- 1 = EV_CURRENT(Current version)
e_entry
: 시스템이 실행하기 위해 제어를 옮길 때 참조하는 가상 주소(진입점)로 프로그램의 시작 주소 (main)를 찾을 수 있음e_phoff
: 프로그램 헤더 테이블의 파일 offsete_flags
: 파일과 관련해서 프로세서에 specific flag를 가짐e_ehsize
: ELF 헤더의 크기e_phentsize
: 프로그램 헤더 테이블에 있는 한 엔트리의 크기e_phum
: 프로그램 헤더 테이블에 있는 모든 엔트리의 수e_phentsize
,e_phum
= 프로그램 헤더 테이블의 크기e_shentsize
: 섹션 헤더의 크기e_shum
: 섹션 헤더 테이블에 있는 엔트리 수e_shstrndx
: 섹션의 이름을 나타내는 스트링 테이블과 관련된 엔트리의 섹션 헤더 테이블 인덱스
다음 명령어는 binary utililty로 해당 ELF 파일의 구조 정보를 보기 쉽게 나타낸다.
$ readelf 파일명
-h
: ELF 헤더 정보-l
: 프로그램 헤더 정보-s
: 섹션 헤더 정보-S
: 심볼 테이블 정보

multi라는 object 파일은 ELF 64bit 파일이며 little endian 구조라는 점을 간편하게 알 수 있다. 이를 알면 디스어셈블을 할 때 저장된 바이트 정보를 읽는 방법이나 레지스터를 읽을 때 참고할 수 있다.
또한 중요한 게 Entry point address
가 0x4003b2
라는 것인데, 이를 이용해 디스어셈블 시 첫 포인트(main)를 잡을 수 있다.
Program Header

잘린 부분이 있지만, 위 그림에 나와있는 정보만 분석해 보겠다.
- PHDR(value = 6) : 프로그램 헤더 테이블 자신의 위치와 크기 정보
- INTERP(value = 3) : 인터프리터 호출을 위한 내용
- LOAD(value = 1) : 로드 된 프로그램 세그먼트
- DYNAMIC(value = 2) : 동적 링크 정보
참고로 각 컬럼이 의미하는 아래 표와 같다.
| 데이터 타입 | 데이터명 | 설명 |
|-------------|----------|-------------------------|
| ElfN_Word | p_type | 세그먼트 타입. |
| ElfN_Off | p_offset | 세그먼트 파일 오프셋 |
| ElfN_Addr | p_vaddr | 세그먼트 가상 주소 |
| ElfN_Addr | p_paddr | 세그먼트 물리 주소 |
| ElfN_Word | p_filesz | 파일에서 세그먼트 크기 |
| ElfN_Word | p_memsz | 메모리에서 세그먼트 크기 |
| ElfN_Word | p_flags | 세그먼트 flags |
| ElfN_Word | p_align | 세그먼트 alignment |
Section Header


이 역시 위의 나와있는 섹션 정보만 정리해 보겠다.
- Interp : 인터프리터의 경로 명을 가지는 섹션
- Hash : 심볼 해쉬 테이블을 가지는 섹션
- Dynamic : 동적 링킹 정보를 가지는 섹션
- Dynstr : 동적 링킹을 위해 필요한 문자열
- Plt : procedure의 링크 테이블
- Got.plt : global offset table을 가지는 섹션
- Text : 프로그램의 실행 가능한 명령어 정보
- Data : 초기화된 데이터를 가지는 섹션
- Bss : 초기화되지 않은 데이터를 가지는 섹션
- Shstrtab : 섹션들의 이름들을 가지는 섹션
- Symtab : 심볼 테이블 정보
- Strtab : 문자열 테이블 정보
섹션 헤더의 컬럼들이 나타내는 의미는 아래 표와 같다.
| 데이터 타입 | 데이터명 | 설명 |
|-------------|--------------|--------------------------------------|
| ElfN_Word | sh_name | 섹션 이름 |
| ElfN_Word | sh_type | 섹션 타입 |
| ElfN_Word | sh_flags | 섹션 플래그 |
| ElfN_Addr | sh_addr | 실행할 섹션의 가상 주소 |
| ElfN_Off | sh_offset | 섹션의 오프셋 위치 |
| ElfN_Word | sh_size | 섹션 크기 |
| ElfN_Word | sh_link | 다른 섹션으로의 링크(테이블 인덱스) |
| ElfN_Word | sh_info | 섹션 추가 정보 |
| ElfN_Word | sh_addralign | 섹션 정렬 단위 |
| ElfN_Word | sh_entsize | 가지고 있는 고정크기의 entry들의 크기 |
Symbol table
사용한 함수나 변수들이 symbol table에 저장되어 있는데, 디스어셈블을 하기 전에 참고해 보면 도움이 된다.


References
'Security' 카테고리의 다른 글
Easy CrackMe 리버싱 (1) | 2019.05.08 |
---|---|
어셈블리로 구구단 짜기(nasm) (1) | 2019.04.02 |
RTL (Return to Libc) (0) | 2019.02.05 |
plt got (1) | 2019.02.03 |
리버싱 실습(Easy_ELF) (0) | 2019.01.24 |
- Total
- Today
- Yesterday
- Python Cookbook
- 선형 회귀
- Computer_Networking_A_Top-Down_Approach
- 스프링 mvc
- 파이썬 for Beginner 연습문제
- 지옥에서 온 git
- git merge
- Thymeleaf
- 생활코딩 javascript
- fetch join
- 쉘 코드
- 쉽게 배우는 운영체제
- Spring Data JPA
- 프로그래머스
- 방명록 프로젝트
- Do it! 정직하게 코딩하며 배우는 딥러닝 입문
- 스프링
- 스프링 테스트
- Spring
- Spring Boot
- 패킷 스위칭
- Gradle
- 스프링 컨테이너
- JPA
- 김영환
- git
- 운영체제 반효경
- git branch
- jsp
- 파이썬 for Beginner 솔루션
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |