ELF란?
실행 가능한 바이너리 또는 오브젝트 파일 등의 형식을 규정한 것이다.
ELF파일 = ELF헤더 + 프로그램 헤더 테이블 + 섹션 헤더 테이블
오프젝트 파일은 세가지 종류가 있다.
- 재배치 가능한 파일(relocatable file)
: 코드와 데이터를 다른 오브젝트 파일과 링킹될 수 있도록 함
- 실행 파일(executable file)
: 코드와 데이터를 타겟 운영체제에서 실행시킬 수 있도록 함
- 공유 오브젝트 파일(shared object file)
: 재할당 가능한 데이터를 정적 혹은 동적으로 다른 공유 오브젝트들과 공유할 수 있도록 함
Linking View Execution View
* readelf로 elf 파일 형식 확인
간단히 설명하자면,
1. ELF 헤더 : 파일의 구성을 나타내는 로드맵과 같은 역할을 하며, 첫 부분을 차지
2. 섹션 : 링킹을 위한 object 파일의 정보를 다량으로 가지고 있으며, 이에 해당하는 것으로는 명령, 데이터, 심볼 테이블, 재배치 정보 등이 들어간다.
3. 프로그램 헤더 테이블(옵션) : 시스템에 어떻게 프로세스 이미지를 만들지를 지시
프로세스의 이미지를 만들기 위해서 사용되는 파일은 반드시 프로그램 헤더 테이블을 가져야하며, 재배치 가능 파일의 경우엔 가지지 않아도 된다.
4. 섹션 헤더 테이블 : 파일의 섹션들에 대해서 알려주는 정보를 포함
모든 섹션은 이 테이블에 하나의 엔트리(entry)를 가져야 한다.
각각의 엔트리는 섹션 이름이나, 섹션의 크기와 같은 정보를 제공해 준다.
만약 파일이 링킹하는 동안 사용된다면, 반드시 섹션 헤더 테이블을 가져야 하며, 다른 object파일은 섹션 헤더 테이블을 가지고 있지 않을 수도 있다.
이에 해당하는 정보들은 리눅스의 경우 ~/include/linux/elf.h에서 찾을 수 있다.
1. ELF Header : readelf -h
ELF헤더 구조
간단히,
자세히,
e_ident[] : 파일의 내용을 해석하고 디코딩하기 위해서 사용되는 기계 독립적인 데이터를 제공하며, 파일이 object파일임을 나타낸다.
-EI_MAG0 0 : 파일 식별
-EI_MAG1 1 : 파일 식별
-EI_MAG2 2 : 파일 식별
-EI_MAG3 3 : 파일 식별
-> EI_MAG0에서 EI_MAG3까지는 ELF파일을 가르키는 매직 번호가 들어온다.
- ELFMAG0 0x7F : EI_MAG
- ELFMAG1 'E'
- ELFMAG2 'L'
- ELFMAG3 'F'
-EI_CLASS 4 : 파일 클래스나 용량을 나타냄
- ELFCLASSNONE 0 : Invalid class
- ELFCLASS32 1 : 32bit objects
- ELFCLASS64 2 : 64bit objects
- ELFCLASSNUM 3 : ELF class number
-EI_DATA 5 : 데이터의 인코딩 방식
- ELFDAT ANONE 0 : Invalid data encoding
- ELFDAT A2LSB 1 : LSB가 가장 낮은 주소를 차지한다.
- ELFDAT A2MSB 2 : MSG가 가장 낮은 주소를 차지한다.
-EI_VERSION 6 : 파일의 버전, 현재로는 EV_CURRENT
-EI_PAD 7 : 패딩 바이트의 시작, 예약된 byte로 0으로 설정된다.
따라서 object파일을 읽어들인 프로그램은 이것을 무시할 수 있다.
만약 새로운 것이 추가된다면 EI_PAD는 바뀌게 될 것이다.
e_type : Object 파일의 타입을 표시한다.
- ET_NONE 0 : 파일의 타입이 없음
- ET_REL 1 : 재배치 가능 파일
- ET_EXEC 2 : 실행 파일
- ET_DYN 3 : 공유 object 파일
- ET_CORE 4 : core 파일
- ET_LOPROC 0xff00 : 프로세서에 의존적인 파일
- ET_HIPROC 0xffff : 프로세서에 의존적인 파일
e_machine : 이 파일을 사용하기 위해 필요한 architecture 정보를 나타낸다.
- EM_NONE 0 : 특정 machine을 구분하지 않음
- EM_M32 1 : AT&T WE32100
- EM_SPARC 2 : SPARC
- EM__386 3 : Intel 80386
- EM_68K 4 : Motorola 68000
- EM_88K 5 : Motorola 88000
- EM_486 6 : 사용되지 않음
- EM_860 7 Intel 80860
- EM_MIPS : MIPS R3000 (offcially, big-endian only)
- EM_MIPS_RS4_BE 10 : MIPS R4000 big-endian
- EM_PARISC 15 : HPPA
- EM_SPARC32PLUS 18 : Sun's "v8lpus"
- EM_PPC 20 : PowerPC
- EM_SH 42 : SuperH
- EM_SPARCV9 43 : SPARC v9 64-bit
- EM_IA_64 50 : HP/Intel IA-64
- EM_x8664 62 : AMD x86-64
- EM_ALPHA 0x9026 : Alpha
- EM_S390 0xA390 : IBM S390
e_version : Object 파일의 버전 정보를 나타낸다.
- EV_NONE 0 : Invalid 버전
- EV_CURRENT 1 : 현재 버전
- EV_NUM 2 : 다음에 주어지는 버전
e_entry : 시스템이 실행하기 위해 제어를 옮길 때 어디로 옮겨야 하는지를 가르쳐주는 가상 주소를 가진다.
만약 파일이 진입점(entry point)를 가지지 않는다면 0을 가질 것이다.
e_phoff : 프로그램 헤더 테이블의 파일 옵셋을 byte단위로 나타낸다.
프로그램 헤더 테이블이 없다면 당연히 0을 가질 것이다.
e_shoff : 섹션 헤더 테이블의 파일 옵셋을 byte단위로 나타낸다.
역시 섹션 헤더 테이블을 가지지 않는다면 0을 가질 것이다.
e_flags : 파일과 관련되서 프로세서에 특수한(specific) flag를 가진다.
flag를 나타내는 형태는 EF_[machine_flag]이 될 것이다. 이것을 알기위해서 기계 의존적인 부분을 봐야 할 것이다.
e_ehsize : ELF 헤더 크기를 가진다.
e_phentsize : 파일의 프로그램 헤더 테이블에 있는 한 엔트리의 크기를 byte단위로 표시한다. 모든 엔트리는 동일한 크기를 가진다.
e_phnum : 프로그램 헤더 테이블에 들어있는 모든 엔트리의 수를 나타낸다.
따라서, 전체 프로그램 헤더 테이블의 크기는 앞에 나온 e_phentsize와 e_phnum의 곱이 될 것이다.
마찬가지로 프로그램 헤더 테이블이 없다면 0을 가진다.
e_shentsize : 섹션 헤더의 크기를 byte단위로 나타낸다.
섹션 헤더는 섹션 헤더 테이블의 하나의 엔트리를 차지하며, 모두 크기가 동일한다.
e_shnum : 섹션 헤더 테이블에 있는 엔트리의 수를 가진다.
따라서, e_shentsize와 e_shnum의 곱으로 섹션 헤더 테이블의 전체 크기를 byte단위로 알 수 있다.
만약 섹션 헤더 테이블이 없다면, 당연히 0을 가질 것이다.
e_shstrndx : 섹션의 이름을 나타내는 스트링(string)의 테이블과 관련된 엔트리의 섹션 헤더 테이블 인덱스를 가지며,
만약 섹션 이름을 나타내는 테이블이 없다면 SHN_UNDEF 값을 가질 것이다.
2. Program Header : readelf -l
프로그램 헤더 테이블은 ELF헤더의 e_phoff로 지정된 오프셋에서 시작하고
e_phentsize와 e_phnum으로 정해진 크기를 갖는 테이블이다.
프로그램 헤더 테이블의 전체 크기 = e_phnum * e_phentsize (byte)
세그먼트 타입
3. Section Header : readelf -S
간단히,
자세히,
sh_name : 섹션의 이름을 가진다. 이 값을 섹션 헤더 스트링 테이블 섹션에 대한 인덱스로 사용할 수 있으며,
NULL로 끝나는 스트링의 위치를 알려준다.
sh_type : 섹션의 내용들이나 혹은 의미(semantics)를 구분짓는다.
- SHT_NULL 0 : 섹션 헤더가 비활성화(inactive)인 것. 즉, 관련된 섹션을 가지고 있지 않음을 알려준다.
다른 섹션 헤더의 멤버는 정의되지 않은 값을 가진다.
- SHT_PROGBITS 1 : 프로그램에서 정의한 정보를 가지는 부분이다. 형식과 의미 역시 프로그램이 어떻게 하느냐에 따른다.
- SHT_SYMTAB 2 : 심벌의 테이블을 가지는 섹션이다.
현재 object파일은 각각의 타입에 대해서 단지 하나의 섹션을 가질 수 있지만 나중에 가서는 이것이 풀릴 것이다.
SHT_SYMTAB는 링크 에디터를 위해서 심볼들을 제공하고 있지만, 동적인 링킹에도 사용될 수 있다.
완전한 심볼 테이블은 동적 링킹을 위해서 필요치 않은 많은 심볼들을 가질 수 있다.
- SHT_STRTAB 3 : 스트링 테이블을 가진다. Object파일은 여러개의 스트링 심벌 테이블을 가질 수 있다.
- SHT_RELA 4 : 섹션이 명시적인 가수를 가지는 재배치 엔트리를 가지고 있다. Object파일은 여러개의 재배치 섹션을 가질 수 있다.
- SHT_HASH 5 : 섹션이 심볼 해시 테이블을 가진다. 동적인 링킹에 참여하는 모든 object들은 심볼 해시 테이블을 가져야 하며,
현재 object 파일은 단지 하나의 해시 테이블을 가지지만, 나중에는 이러한 제약이 풀릴 것이다.
- SHT_DYNAMIC 6 : 섹션이 동적인 링킹을 위한 정보를 가진다. 현재 object파일은 단지 하나의 동적인 섹션을 가질 수 있지만, 이 역시 제약사항이 풀릴 것이다.
- SHT_NOTE 7 : 파일에 대한 표식(note)을 가지는 섹션을 말한다.
- SHT_NOBITS 8 : 아무런 공간을 차지하지 않는 섹션이다. 아무런 공간도 가지지 않는 섹션이지만 sh_offset에는 파일의 개념적인 오프셋을 가질 수 있다.
- SHT_REL 9 : 명시적인 가수(addend)가 없는 재배치 엔트리에 대한 정보를 가지는 섹션이다. Object 파일은 여러개의 재배치 섹션을 가질 수 있다.
- SHT_SHLIB 10 : 명확한 의미를 가지지는 않는 섹션 타입으로 프로그램은 이 타입에 대해서 ABI가 정하는 것을 따르지 않아도 상관없다.
- SHT_DYNSYM 11 : Object파일은 SHT_SYMTAB이외에 공간(space)을 절약하기 위해서, 동적인 링킹 심벌들의 최소 집합을 가지는 SHT_DYNSYM을 가질 수 있다.
- SHT_NUM 12
- SHT_LOPROC 0x70000000 : 프로세서에 의존적인 의미를 가진다.
- SHT_HIPROC 0x7fffffff : 역시 프로세서에 의존적인 정보를 가진다.
- SHT_LOUSER 0x80000000 : 응용 프로그램을 위해서 예약된 인덱스의 하위 한도(lower bound)를 명시한다.
- SHT_HIUSER 0xffffffff : 응용 프로그램을 위해서 예약된 인덱스의 상위 한도(upper bound)를 명시하는 부분으로 SHT_LOUSER와 SHT_HIUSER의 사이에 있는 섹션 타입들은
응용 프로그램에서, 현재나 미래의 시스템에서 정의한 섹션 타입들과 상충없이 사용될 수 있다.
// 위에 것은 ELF 1.1에서 정의한 타입, 이하의 것은 확장된 것
- SHT_MIPS_LIST 0x70000000
- SHT_MIPS_CONFLICT 0x70000002
- SHT_MIPS_GPTAB 0x70000003
- SHT_MIPS_UCODE 0x70000004
sh_flags : 여러가지 특성을 나타내는 flag로 사용된다.
- SHF_WRITE 1 : 프로세스 실행중에 write가능한 데이터를 섹션이 포함하고 있다는 뜻이다.
- SHF_ALLOC 2 : 프로세스 실행중에 차지하는 메모리를 나타내는 섹션이다. 어떤 제어 섹션들은 object파일의 메모리 이미지에 있지 않을 수도 있는데,
이러한 경우 이 특성(attribute)는 꺼져(off) 있을 것이다.
- SHF_EXECINSTR 4 : 이 섹션은 실행가능한 기계어 명령들을 가진다.
- SHF_MASKPROC 0xf0000000 : 프로세서에 의존적인 의미를 가지며 예약되어 있다.
// 위에 것은 ELF 1.1에서 정의한 타입, 아래는 LINUX에서 추가된 것
- SHF_MIPS_GPREL 0x10000000
sh_addr : 만약 섹션이 프로세스의 메모리 이미지로서 나타나게 된다면, 섹션의 첫번째 byte가 위치해야하는 주소를 알려준다.
그렇지 않다면 0을 가진다.
sh_offset : 섹션의 첫 byte주소를 파일의 처음에서 어디에 위치해 있는지를 알려준다.
SHT_NOBIT와 같은 타입을 가지는 섹션은 파일에서 자리를 차지 하지 않으므로 sh_offset은 단지 파일에서 개념적인 위치만을 알려준다.
sh_size : 섹션의 크기를 byte단위로 알려준다. SHT_NOBITS가 아닌 섹션 타입에 대해서 sh_size만큼을 파일에서 차지한다.
SH_NOBITS에 대해서 0이 아닌 값을 가질 수 있으나, 실제적으로는 파일에서 아무런 자리를 차지 하지 않는다.
sh_link : 섹션 헤더 테이블 인덱스의 링크를 가지는 부분으로 이 필들에 대한 해석은 섹션 헤더 타입에 따른다.
sh_info : 부가적인 정보를 가지는 부분으로 역시 섹션 타입에 의존적이다.
sh_addralign : 어떤 섹션에 대해서는 주소가 정렬되어야 하는데, 예를 들어서 섹션이 doubleword를 가진다면 전체 섹션에 대해서 doubleword로 시스템이 정렬해줘야 한다. 즉, sh_addralign값의 modulo값인 0에 sh_addr이 맞춰져야 할 것이다. 현재 0이나 2의 제곱승에 대해서만 이 값을 나타낼 수 있으며, 0이나 1은 섹션이 정렬될 필요가 없음을 의미한다.
sh_entsize : 심벌 테이블과 같은 섹션들은 고정된 크기의 엔트리만을 가진다.
그러한 섹션들에 대해서 sh_entsize는 각각의 엔트리의 크기를 byte단위로 표시한다.
만약 고정된 크기의 엔트리를 가지지 않는다면 0값을 가진다.
섹션 헤더 테이블 엔트리 0에 대한 설정
또한 sh_link와 sh_info는 섹션 타입에 따라 특별한 의미를 가지는데,
섹션들은 ELF header와 프로그램 헤더, 섹션 헤더 테이블을 제외한 모든 object 파일에 있는 정보들을 포함하고 있으며,
다음과 같은 여러 조건들을 맞춘다.
- Object 파일 내의 모든 섹션은 그것을 기술하는 정확히 하나의 섹션 헤더를 가진다.
하지만, 섹션 헤더는 섹션을 가지지 않을 수도 있다.
- 각각의 섹션은 파일내에서 하나의 연속된(혹은 빈) byte들을 점유한다.
- 파일내의 섹션은 겹쳐질 수 없으며, 파일내의 어떠한 byte도 둘 이상의 섹션에 존재하지 못한다.
- Object 파일은 비활성(inactive)인 공간을 가질 수 있으며, 다양한 헤더나 섹션들도 object파일의 모든 byte를 다 커버(cover)하지 않을 수 있다.
비활성인 데이터의 내용은 명시되지 않는다.
섹션의 종류
.bss : 초기화 되지 않은 데이터를 가지는 섹션으로, 프로그램의 메모리 이미지에 기여하고 있다.
프로그램이 시작할 때 여기에 있는 데이터들은 0으로 초기화 된다.
이 섹션은 파일 공간을 차지 하지 않기에 SHT_NOBITS의 섹션 타입을 가진다.
.comment : 버전 제어 정보를 가진다.
.data, .data1 : 초기화된 데이터를 가지는 섹션으로, 프로그램의 메모리 이미지에 기여한다.
.debug : 심볼릭 디버깅을 위한 정보를 가지는 섹션이다. 내용은 명시되지 않는다.
.dynamic : 동적 링킹 정보를 가지는 섹션이다. 섹션의 성질은 SHF_ALLOC bit을 가질 것이다.
SHF_WRITE bit이 설정되는 것은 프로세서 의존적이다.
.dynstr : 동적 링킹을 위해 필요한 스트링들을 가지는 섹션이다.
심볼 테이블의 엔트리와 관련된 이름을 표시하는 스트링이 가장 일반적이다.
.fini : 프로세스가 종료 코드에 기여하는 실행 가능 명령들을 가지는 섹션이다.
즉, 프로그램이 일반적인 종료를 할 때, 시스템은 이 섹션에 있는 코드를 실행하기 위해서 준비할 것이다.
.got : global offset table, 전역 오프셋 테이블을 가지는 섹션이다.
.hash : 심볼 해시 테이블을 가지는 섹션이다.
.init : 프로그램이 초기화 코드에 기여하는 실행 가능 명령들을 가지는 섹션이다.
즉, 프로그램이 실행을 시작할 때, 시스템은 주(main) 프로그램의 진입점(entry point)를 부르기 전에 이 섹션에 있는 코드를 수행하기 위해서 준비할 것이다.
.interp : 프로그램 해석기(interpreter)의 경로명(path name)을 가지는 섹션이다.
만약 파일이 적재 가능한 세그먼트를 가지고 있고, 이 세그먼트가 섹션을 가질 때, 섹션의 성질(attributes)는 SHF_ALLOC bit를 포함할 것이며,
그렇지 않다면 이 bit이 OFF될 것이다.
.line : 심볼 디버깅을 위한 라인 정보를 가지는 섹션으로, 기계어 코드와 프로그램의 소스사이의 일치하는 부분을 기술한다.
섹션의 내용은 정해지지 않았다.
.note : "Note Section"의 형식으로 들어있는 정보를 가지는 섹션이다.
.plt : 프로시저(procedure, 함수와 유사한 뜻)의 링크 테이블을 가지는 섹션이다.
.rel"NAME", .rela"NAME" : 재배치 정보를 가지는 섹션이다.
만일 파일이 적재가능한 세그먼트를 가지며, 이 세그먼트가 재배치 및 섹션의 성질을 포함하고 있다면, SHF_ALLOC bit이 설정될 것이다.
만일 그렇지 않다면, 이 bit는 OFF될 것이다. 일반적으로 "NAME"은 재배치가 적용되는 섹션에 의해서 제공될 것이다.
예를 들어 .text에 대해 재배치 섹션은 일반적으로 .rel.text나 .rela.text라는 이름을 가지게 된다.
.rodata, .rodata1 : read-only 데이터를 가지는 섹션으로 프로세스의 쓰기가 되지 않는 세그먼트 이미지를 만드는데 기여한다.
.shstrtab : 섹션들의 이름들을 가지는 섹션이다.
.symtab : 이 섹션에 있는 "Symbol Table"이 기술하는 것과 같은 심볼 테이블을 가지는 섹션이다.
만약 파일이 적재가능한 세그먼트를 가지고, 그 세그먼트가 심벌 테이블을 포함한다면, 섹션의 성질들은 SHF_ALLOC bit를 포함할 것이다.
그렇지 않다면 이 bit는 OFF된다.
.text : "text"를 가지는 섹션으로 프로그램의 실행 가능한 명령어를 가지고 있다.
'시스템 > 등드등' 카테고리의 다른 글
Wannacry 1차 정리본 (0) | 2019.01.26 |
---|---|
pwntools 간단 정리 (0) | 2019.01.11 |
간단한 정리 (0) | 2018.11.19 |
Format String Bug (0) | 2018.11.10 |
ARM Assembly 간단 정리 (0) | 2018.11.05 |