2011. 7. 1. 15:50

♧ ELF :: Executable and Linking Format 의 약자로,

실행 가능한 바이너리 또는 오브젝트 파일 등의 형식을 규정한 것.

▷ ELF 파일은 ELF헤더가 맨 앞에 위치.

▷ 프로그램 헤더 테이블과 섹션 헤더 테이블이 그 뒤에 위치.

▷ 이러한 헤더의 구조는 elf.h 참조

   

   

   

:: ELF 에서 사용하는 자료형

▷ ELF 바이너리에는 32bit와 64비트, 두 가지가 있다.

자료형

N = 32

N = 64

설명

ElfN_Half

uint16_t

uint16_t

부호 없는 16비트 값

ElfN_Word

uint32_t

uint32_t

부호 없는 32비트 값

ElfN_Sword

int32_t

int32_t

부호 있는 32비트 값

ElfN_Xword

uint64_t

uint64_t

부호 없는 64비트 값

ElfN_Sxword

int64_t

int64_t

부호 있는 64비트 값

ElfN_Addr

uint32_t

uint64_t

주소

ElfN_Off

uint32_t

uint64_t

오프셋

ElfN_Section

uint16_t

uint16_t

섹션 인덱스

ElfN_Versym

uint16_t

uint16_t

버전 심볼 정보

  

   

   

:: [ ELF 헤더 ] readelf 의 -h옵션 (--file-header)

▷ ELF헤더는 ELF파일 맨 앞에 반드시 존재하며, 그 파일이 ELF 파일임을 나타냄.

   

[ ELF헤더 구조 ] (/usr/include/elf.h)

   

▷ e_ident::

ELF의 매직 넘버(MAGIC NUMBER)와 기타 정보를 갖고 있다.

ELF 파일은 첫 4바이트에 다음과 같은 매직 넘버를 갖는다

| 0x7F | 0x45 | 0x4c | 0x46 | → "\177ELF"가 된다.

그 다음 바이트는 32비트인 경우 ELFCLASS32(1), 64바이트인 경우 ELFCLASS(2)가 된다.

다음 바이트는 엔디안을 나타냄.(리틀 엔디안인=ELFDATA2LSB(1),빅 엔디안인=ELFDATA2MSB(2))

나머지 바이트는 ELF버전과 OS, ABI 등의 정보를 1바이트씩을 사용해 나타냄.

   

▷ e_type:: 다음 타입 중 하나를 나타낸다.

타입

설명

ET_REL

1

재배치 가능한 파일

ET_EXEC

2

실행 가능한 파일

ET_DYN

3

공유 오브젝트 파일

ET_CORE

4

코어 파일

   

▷ e_machine:: 아키텍처 타입을 타나낸다. (EM_으로 시작하는 상수로 정의되어 있음.)

▷ e_version:: ELF 버전을 나타낸다. (위 예(/bin/ls)는 EV_CURRENT(1)이다)

▷ e_entry:: 이 ELF에서 실행 시작하는 가상 주소.

▷ e_ehsize:: ELF 헤더 자체의 크기.

▷ e_phoff, e_phentsize, e_phnum:: 프로그램 헤더 테이블의 위치, 크기, 헤더 개수.

▷ e_shoff, e_shentsize, e_shnum:: 섹션 헤더 테이블의 위치, 크기, 개수.

▷ e_shstrndx:: 섹션명의 스트링 테이블을 갖는 섹션 헤더 인덱스를 나타냄.

   

   

:: [ 프로그램 헤더 ] readelf의 -l 옵션 (--program-headers)

▷ 프로그램 헤더 테이블은 ELF 헤더의 e_phoff로 지정된 오프셋에서 시작.

▷ e_phentsize(헤더크기)와 e_phnum(프로그램 헤더 개수)으로 정해진 크기를 갖는 테이블.

(※ 즉, 프로그램 헤더 테이블의 전체 크기 = e_phentsize * e_phnum)

   

 [ 프로그램 헤더 구조 ]

   

▷ 『Program Headers:』의 각 행에 해당.

▷ p_type:: 다음과 같다.

p_type

설명

PT_LOAD

1

로드된 프로그램 세그먼트

PT_DYNAMIC

2

동적 링크 정보

PT_INTERP

3

프로그램 인터프리터

PT_NOTE

4

추가 정보

PT_PHDR

6

프로그램 헤더 테이블 자신

PT_TLS

7

스레드 지역 저장소

PT_GNU_EH_FRAME

0x6474e550

GNU .eh_frame_hdr 세그먼트

PT_GNU_STACK

0x6474e551

스택 실행 가능성

   

▷ 『Section to Segment mapping:』 이후의 행에 나타난 정보는

『Program Headers:』의 각 프로그램 헤더에 나타난 세그먼트에

그 세그먼트 메모리 범위를 포함하는 섹션명을 나열하고 있다.

   

(인덱스 00) 프로그램 헤더에 표시된 세그먼트는 PHDR 타입이고 그에 속하는 섹션은 없다.

(인덱스 01) 프로그램 헤더에 표시된 세그먼트는 INTERP 타입이고 그에 속하는 섹션은 .interp가 있다.

(인덱스 02) 프로그램 헤더에 표시된 세그먼트는 LOAD 타입이고 그 안에는 .interp, .note.ABI-tag, ...등등

▷ 『Section to Segment mapping:』 ↔ 『Program Headers:』 이 둘을 연결해서 봄.

type

segment

section

PHDR

00

없음.

INTERP

01

.interp

LOAD

02

.interp , .note.ABI-tag , .note.gnu.build-id, .hash ...[등등]

  

   

   

:: [ 섹션 헤더 ] readelf의 -S 옵션 (--section-headers)

▷ 섹션 헤더 테이블은 ELF 헤더의 e_shoff로 지정된 오프셋에서 시작.

▷ e_shentsize(섹션 헤더 크기)와 e_shnum(섹션 헤더 개수)으로 정해진 크기를 갖는 테이블.

(※ 즉, 섹션 헤더 테이블의 전체 크기 = e_shentsize * e_shnum)

   

[ 섹션 헤더 구조 ]

▷ 『Section Headers:』의 각 행에 해당.

▷ 섹션명(Section name)은

ELF 헤더의 e_shstrndx로 지정된 섹션에 포함된 스트링 테이블의 인덱스로 지정.

▷ 위의 /bin/ls 예의 경우,

e_shstrndx는 28이므로, 28번째 섹션 헤더가 그 스트링 테이블을 갖는 섹션이 된다.

- sh_offset이 '0x0192d4' 이고, 크기가 '0x0000f2' 바이트인 스트링 테이블(STRTAB)임을 알 수 있다.

   

▷ 섹션 타입(sh_type):

 섹션 타입

 값 

 설명

 SHT_PROGBITS

 1

 프로그램 데이터

 SHT_STRTAB

 2

 심볼 테이블

 SHT_STRTAB

 3

 스트링 테이블

 SHT_RELA

 4

 재배치 엔트리

 SHT_HASH

 5

 심볼 해시 테이블

 SHT_DYNAMIC

 6

 동적 링크 정보

 SHT_NOTE

 7

 Notes (호환 체크정보)

 SHT_NOBITS

 8

파일상에  데이터가 없는 부분 (.bss)

 SHT_REL

 9

 재배치 엔트리

 SHT_DYNSYM

 11

 동적 링크의 심볼테이블

 SHT_INIT_ARRAY

 14

 생성자 배열 (.init) 

 SHT_FINI_ARRAY

 15

 소멸자 배열 (.fini)

 SHT_GNU_verdef

 0x6ffffffd

 버전 정의 섹션

 SHT_GNU_verneed 

 0x6ffffffe

 필요한 버전 섹션

 SHT_GNU_versym

 0x6fffffff

 버전 심볼 테이블

  

   

   

:: [ 스트링 테이블 ]

▷ 스트링 테이블은 단순한 문자열 리스트이다.

▷ /bin/ls의 경우 다음 섹션이 스트링 테이블이다.

▷ 예를 들어 [28] .shstrtab의 오프셋은 '0x0192d4', 크기는 '0xf2'이므로

od를 사용하여 테이블을 확인하자.

# od --skip-bytes 0x192d4 --read-bytes 0xf2 -t x1z /bin/ls

▷ 이 경우 스트링 테이블은 다음과 같이 되어 있다.

인덱스

문자열

1

.shstrtab

11

.interp

19

.note.ABI-tag

즉, .shstrtab의 맨 앞부터의 오프셋이 스트링 테이블의 인섹스가 된다.

   

   

:: [ 심볼 테이블 ] readelf의 -s 옵션 (--syms)

▷ 심볼 테이블은 심볼과 그 값 등을 대응시키는 테이블이다.

▷ /bin/ls의 경우 strip되어 있으므로 동적 심볼 테이블만 있다.

...(중략)...

▷ 이를 ELF 헤더로부터 찾아 보자.

우선 섹션 헤더에서 .dynsym이라는 심볼 테이블이 있다는 것을 알 수 있다.

▷ 덤프해 보면 다음과 같다.

...(중략)...

▷ 심볼 테이블은 다음과 같은 구조 테이블이다.

32비트와 64비트 ELF 바이너리에서는 st_value의 정렬 순서에 따라 변한다.

▷ st_name=스트링 테이블의 인덱스, st_value=심볼 값, st_size=심볼크기.

▷ st_info의 하위 4비트는 심볼 테이블 등의 정보로 다음과 같은 것이 있다.

심볼 타입

설명

STT_OBJECT

1

데이터 오브젝트

STT_FUNC

2

실행 코드

STT_SECTION

3

섹션 관련

STT_FILE

4

오브젝트 관련 소스코드 파일

STT_COMMON

5

일반 데이터

STT_TLS

6

스레드 지역 저장 데이터

▷ 또한 st_info의 상위 4비트는 그 심볼의 바인딩 방법을 타나낸다.

심볼 바인딩

설명

STB_LOCAL

0

지역 심볼

STB_GLOBAL

1

전역 심볼

STB_WEAK

2

WEAK

   

▷ st_shndx는 관련된 섹션을 나타낸다.

섹션

설명

SHN_UNDEF

0

미정의

SHN_ABS

0xfff1

절대값을 갖는 심볼

SHN_COMMON

0xfff2

일반적인 심볼

   

/bin/ls의 예를 보자. 0번째 심볼 정보는 다음과 같이 비어 있다.

▷ /bin/ls의 위의 예에서 맨 마지막에 있는 심볼 정보를 알아 보자.

주소 0006104 부터 시작하는 이 바이트 열은 다음과 같은 의미를 갖는다.

( ※ 아키텍처(x86) 리틀 엔디안 이기 때문에 반대로 읽는다)

st_name = 0x2db

st_value = 0x08061320

st_size = 0x4

st_intfo = 0x11 = (STB_GLOBAL | STT_OBJECT)

st_other = 0

st_shndx = 0x1a = 26

   

▷ 여기서 심볼명을 가리키는 오프셋 값 st_name은 '0x2db'로 되어 있다.

.dynstr 섹션을 보면 스트링 테이블은 시작 위치로부터 '0xc54' 바이트 떨어진 위치에서 시작한다.

- 여기서 '0xc54'에 0x2db(st_name)를 더한 값인 '0xf2f' 위치에서 시작하는 문자열을 보면,

' optarg'라는 문자열을 찾을 수 있다.

- 이와 같이 st_name = '0x2db'에 해당하는 심볼은 'optarg'임을 알 수 있다.

   

▷ 이는 readelf -s 출력 결과에서 위의 심볼 정보를 확인 할 수 있다.

  

   

   

:: [ 재배치 정보 ]

▷ SHT_RELA 또는 SHT_REL 타입의 섹션은 재배치 정보를 갖는다.

▷ SHT_RELA는 다음과 같은 Rela 구조의 테이블을 갖는다.

   

▷ SHT_REL의 경우는 다음과 같이 r_addend가 없는 Rel 구조의 테이블을 갖는다.

   

▷ r_offset은 재배치를 해야 하는 위치를 가리키는, 섹션 첫 위치로부터의 오프셋이다.

▷ r_info는 재배치 타입 또는 심볼테이블의 인덱스 등의 정보를 포함한다.

▷ Rela의 경우 재배치할 경우에 항상 더해야 할 값을 r_addend로 갖고 있다.

  

Posted by devanix