ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ELF 파일 포맷(2)
    Linux System 2019. 3. 11. 13:06

    ELF Program Header


    : 프로그램 로딩에 필요한 바이너리 세그먼트를 정의.


    세그먼트 

    디스크에 저장된 실행파일이 커널에 의해 로드되는 과정에서 어떤 메모리 구조로 매핑될 것인지를 정의

    (즉, 프로그램을 메모리에 로드하는 로더가 ELF 파일을 다룰 때의 단위. 각 구성은 Read Only, Read and Write, Write Only등의 속성에 세그먼트를 분리)


    - 실행 가능 파일에만 존재 한다. (물론 공유 Obejct 파일에도 존재한다.)


    - 여러 Linkable 파일들의 섹션들을 링커가 돌면서 통합해주고, 이것들에 대한 정보를 ELF Program Header에 갖는다.


    -> 즉, 섹션과 세그먼트는 완전히 서로 별개의 것이 아니다.

    일종의 유사한 섹션들을 모아 놓은 것이 세그먼트

    섹션 : 링킹 가능 파일을 링킹이 편리하게 하기 위해 사용.

    세그먼트 : 실행 가능 파일을 ELF-loader에 의해 로드 혹은 링크를 쉽게 하기 위해 사용


    즉, ELF는 ABI(Application Binary Interface)로써 각 용도에 따라 섹션이나 세그먼트를 나누어 두고 프로그램의 실행 또는 링킹을 원할하게 하기 위해 사용.


    -  ELF Program Header는 ELF Header의 e_phoff 멤버(Program header offset)를 통해 접근이 가능.

    - 이 테이블은 세그먼트의 타입, 파일 오프셋, 물리 주소, 가상 주소, 파일 크기, 메모리 이미지 크기, 정렬 방식 등을 정의.


    구조체 형식

    typedef strcut {

    uint32_t     p_type;        // 세그먼트 형식

    Elf32_Off    p_offset;      // 세그먼트 오프셋

    Elf32_Addr    p_vaddr;    // 세그먼트 가상 주소

    Elf32_Addr    p_paddr;    // 세그먼트 물리 주소

    uint32_t     p_filesz;        // 파일에서의 세그먼트 크기

    uint32_t     p_memsz;     // 메모리에서의 세그먼트 크기

    uint32_t     p_flags;        // 세그먼트 플래그 (실행 | 읽기 | 쓰기)

    uint32_t     p_align;        // 메모리에서의 세그먼트 정렬

    }Elf32_Phdr


    세그먼트 형식

    1) PT_LOAD (0x01)

    : 실행파일에는 PT_LOAD 형식의 세그먼트가 하나 이상 필요.

      로드 가능한 세그먼트 형식으로 메모리에 로드 또는 매핑된다.

    EX) 텍스트 세그먼트, 데이터 세그먼트

    - 이 형식의 세그먼트들은 p_align 값을 이용해 정렬된 후 메모리에 매핑된다.

    - 텍스트 세그먼트(코드 세그먼트)는 일반적으로 PF_X | PF_R (Read + Execute)라는 퍼미션을 가진다.

    -데이터 세그먼트는 일반적으로 PF_W | PF_R (Read + Write)라는 세그먼트퍼미션을 가진다.

    (여기서 악성코드에 감염된 경우, 텍스트 세그먼트의 퍼미션이 변경될 수 있다.

     예를 들어, PF_W 플래그가 p_flags에 추가되어 쓰기 가능한 상태가 될 수 잇다.)


    2) PT_DYNAMIC (0x02)

    : Dynamic 세그먼트 - Dynamic 링킹되는 실행파일에서 사용되며 동적 링커가 사용하는 정보가 담긴다.

    - 주로 사용하는 값

    ㄱ. 런타임 중에 링크되는 공유라이브러리 목록

    ㄴ. 전역 오프셋 테이블(GOT)의 주소/위치

    ㄷ. 재배열 엔트리 정보


    - 주로 사용되는 태그들.

    0x00. DT_NULL : 동적 세그먼트 리스트의 끝을 의미 (Ignored)

    0x01. DT_NEEDED  : 필요한 라이브러리의 이름의 문자열 테이블 오프셋(d_val)

    (이름은 STRTAB에 존재. 인덱스로 참조)

    0x02. DT_PLTRELSZ : PLT 재배열 테이블의 크기 (bytes)(d_val)

    0x03. DT_PLTGOT (d_ptr)

    0x04. DT_HASH : 심볼 해시 테이블 주소 (d_ptr)

    0x05. DT_STRTAB : 문자열 테이블 주소 (d_ptr)

    0x06. DT_SYMTAB : 심볼 테이블 주소 (d_ptr)

    0x07. DT_RELA : RELA 상대 주소 재배열 테이블 주소 (d_ptr)

    0x08. DT_RELASZ : RELA 상대 주소 재배열 테이블 크기 (d_val)

    0x09. DT_RELAENT : RELA 상대 주소 재배열 테이블 엔트리 크기 (d_val)

    0x0A. DT_STRSZ : 문자열 테이블 크기 (d_val)

    0x0B. DT_SYMENT : 심볼 테이블 엔트리 크기 (d_val)

    0x0C. DT_INIT : 초기화 함수 주소 (d_ptr)

    0x0D. DT_FINI : 종료 함수 주소 (d_ptr)

    0x0E. DT_SONAME : 공유 오브젝트 이름의 문자열 테이블 오프셋 (d_val)

    0x0F. DT_RPATH : 라이브러리 탐색 경로 문자열 테이블 (d_val)

    0x10. DT_SYMBOLIC : 프로그램 심볼 실행 전 링커의 공유 라이브러리 탐색 알람. (ignored)

    0x11. DT_REL : REL 상대 주소 재배열 테이블 주소 (d_ptr)

    0x12. DT_RELSZ : REL 상대 주소 재배열 테이블 크기 (d_val)

    0x13. DT_RELENT : REL 상대 주소 재배열 테이블 엔트리 크기 (d_val)

    0x14. DT_PLTREL : PTL(RELA or REL)가 가리키는 재배열 형식 (d_val)

    0x15. DT_DEBUG : 정의되지 않은 사용(디버깅용) (d_ptr)

    0x16. DT_TEXTREL : 이 태그가 없으면 모든 재배열 가능 세그먼트는 쓰기 가능한 상태 여야 한다. (ignored)

    0x17. DT_JMPREL : PLT가 사용하는 재배열 엔트리 주소 (d_ptr)

    0x18. DT_BIND_NOW : 프로그램이 실행되기 전 동적 링커가 모든 재배열을 처리하라고 명령. (ignored)

    - 각 Dynamic 세그먼트에는 Dynamic 링킹 정보(구조체)가 담겨 있다.

    -> ELF Dynamic 구조체


    typedef struct {

    Elf32_Sword d_tag;

    union {
        Elf32_Word d_val;

    Elf32_Addr d_ptr;

    }d_un;

    }Elf32_Dyn;


    3. PT_INTERP (0x03)

    : 프로그램 인터프리터의 정보를 가지고 있다.

      프로그램 인터프리터 : 동적 링커라고도 하며, 일반적으로 /lib/linux-ld.so.2에 위치한다.


    4. PT_NOTE (0x04)

    : PT_NOTE 형식의 세그먼트는 특정 벤더나 시스템에 관한 부가적인 정보를 포함한다.

     -> 벤더나 시스템 빌더는 SHT_NOTE 섹션 형식이나 PT_NOTE 프로그램 헤더를 통해 오브젝트 파일의 호환성 등의 정보를 기록할 수 있다.

    - 이 세그먼트는 OS에서만 참조하기 때문에, 실행파일이 실행되는 동안 꼭 필요한 정보가 아니며, 누락되어 있는 경우, 추정한 값을 이용한다.


    5. PT_PHDR (0x06)

    : ELF Program Header의 주소와 크기를 나타낸다.


    ※ 재배열 가능 객체 (ET_REL 형식)에는 프로그램 헤더가 존재하지 않는다.

    .o 파일은 실행 파일로 링크되는 파일이기 때문에, 그 자체로는 직접 메모리로 로드될 수가 없다.

    예외) 리눅스의 로드 가능한 커널 모듈은 ET_REL 객체이지만, 모듈 자체가 커널 메모리로 로드되고, 재배열되어 실행이 된다.



    예제를 통해 살펴보자


    - PHDR 세그먼트를 보면 offset 0x34에서 부터 Program Header가 시작됨을 알 수 있고, 전체 Size는 0x20 * 0xB = 0x160이 됨을 확인 할 수 있다.


    - INTERP 세그먼트를 보면 offset은 0x194에 존재한다.

    동적 링커가 '/lib/ld-linux.so.2'임을 알 수 있다.

    size도 문자열 총 길이가 0x13이 됨을 알 수 있다.


    - LOAD 세그먼트의 경우 여러 개 존재하는데, 플래그와 readelf의 매핑 정보를 통해 각 LOAD 세그먼트의 역할을 알 수 있다.

    -> 두번 째 LOAD 세그먼트를 보면, 플래그가 Read + Execute이고 mapping 정보를 보아도 .text가 매핑이 되어있다. 따라서 해당 세그먼트가 텍스트 세그먼트임을 알 수 있다.

    -> 네번 째 LOAD 세그먼트의 경우, 플래그가 Read + Write 이고, mapping 정보를 보아도 .data, .bss가 매핑이 되어 있다. 따라서 해당 세그먼트가 데이터 세그먼트임을 알 수 있다.


    -> 텍스트 세그먼트를 살펴본 결과, 0x1000에서 부터 instruction 코드가 있음을 알 수 있다. 그리고, .init 섹션에서부터 코드가 시작함을 알 수 있다.



    - Dynamic 세그먼트의 경우 .dynamic 섹션 하나에만 매핑이 된다. 그냥 .dynamic 섹션으로 봐도 무리가 없을 것 같다.

    위의 그림을 보면, d_tag - d_val 또는 d_tag - d_ptr 형태로 dynamic 구조체가 마지막에 NULL이 나올때까지 리스트로 존재하게 된다.



    'Linux System' 카테고리의 다른 글

    ELF 파일 포맷(3)  (0) 2019.03.14
    ELF 파일 포맷(1)  (0) 2019.03.11
    라이브러리  (0) 2019.03.11
    파일 디스크립터  (0) 2019.03.10

    댓글

Designed by Tistory.