Hubeen's Home

main()는 리눅스에서 어떻게 실행이 될까?



시작하기에 앞서 리눅스에서의 실행파일이 무엇인지 알아본 뒤에 main 실행이 어떻게 되는지 깊숙히 파고들 예정입니다.
먼저 리눅스에서의 실행파일이 무엇인지 알아본 뒤에 실행 포맷 구조를 알아 본 뒤에 main에 대해 들어갈 예정입니다.
아마 내용이 축구공처럼 통통 튀기고 이리저리 갈 것 같으니 언제든지 이상한 부분은 댓글로 지적해주시면 감사하겠습니다.


What is ELF?



그래서 ELF는 무엇일까요?

게임 또는 애니메이션이나 소설에 나오는 엘프를 말하는 걸까요?

네, 하지만 여러분들이 생각하는 그런 엘프는 아니라 유감,


그렇다면 ELF에 대해 알아봅시다!

1999년 86open 프로젝트에 x86기반 유닉스 계열 시스템들의 표준 바이너리 파일 형식입니다.

ELF (Executable and Linkable Format)은 실행파일, 목적파일, 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식인데요.


음 그냥 간단히 윈도우에서의 .exe 파일과 비슷하다고 보시면 될 것 같습니다. 


ELF 포맷 구조를 보도록 하겠습니다.



그림판으로 그려서 못생긴 기분이 들지만...


대략 이렇게 생겨먹었습니다.

ELF파일은 ELF 헤더가 맨 앞에 위치하며, 프로그램 헤더 테이블과 섹션 헤더 테이블은 그 뒤에 위치합니다.


그렇다면 이제 elf 파일 형식을 직접 봐보도록 하겠습니다.



ELF Header View



ELF 파일 형식을 보기 위해서는 readelf 명령어를 이용합니다.

readelf 명령어 옵션에 대해 알아보고 싶으신 분은 아래의 링크를 참조바랍니다.

readelf - https://sourceware.org/binutils/docs/binutils/readelf.html


저희는 ELF Header를 보려고 하는 중이기 때문에

아래와 같은 옵션으로 넘겨줍니다.


-h

--file-header

Displays the information contained in the ELF header at the start of the file. 


$ readelf -h ./hello



ELF 헤더는 다음과 같은 구조를 가지고 있습니다.


#define EI_NIDENT 16


typedef struct {

        unsigned char   e_ident[EI_NIDENT];  // Magic number and other info

        Elf32_Half      e_type;  // Object file type

        Elf32_Half      e_machine;  // Architecture

        Elf32_Word      e_version;  // Object file version

        Elf32_Addr      e_entry;  // Entry point virtual address 

        Elf32_Off       e_phoff;  // Program header table file offset

        Elf32_Off       e_shoff;  // Section headr table file offset 

        Elf32_Word      e_flags;  // Processor-specific flags

        Elf32_Half      e_ehsize;  // ELF header size in bytes

        Elf32_Half      e_phentsize;  // Program header table entry size

        Elf32_Half      e_phnum;  // Program header entry count

        Elf32_Half      e_shentsize;  // Section header table entry size

        Elf32_Half      e_shnum;  // Sectino header table entry count 

        Elf32_Half      e_shstrndx;  // Sectino header string table index

} Elf32_Ehdr; // x86


typedef struct {

        unsigned char   e_ident[EI_NIDENT];  // Magic number and other info

        Elf64_Half      e_type;  // Object file type

        Elf64_Half      e_machine;  // Architecture

        Elf64_Word      e_version;  // Object file version

        Elf64_Addr      e_entry;  // Entry point virtual address 

        Elf64_Off       e_phoff;  // Program header table file offset

        Elf64_Off       e_shoff;  // Section headr table file offset 

        Elf64_Word      e_flags;  // Processor-specific flags

        Elf64_Half      e_ehsize;  // ELF header size in bytes

        Elf64_Half      e_phentsize;  // Program header table entry size

        Elf64_Half      e_phnum;  // Program header entry count

        Elf64_Half      e_shentsize;  // Section header table entry size

        Elf64_Half      e_shnum;  // Sectino header table entry count 

        Elf64_Half      e_shstrndx;  // Sectino header string table index

} Elf64_Ehdr;  // x86_64


참고 : http://www.sco.com/developers/gabi/2003-12-17/ch4.eheader.html


Program Header table



프로그램 헤더 테이블은 ELF헤더의 e_phoff로 지정된 오프셋에서 시작하고 e_phentsize와 e_phnum으로 정해진 크기를 갖는 테이블입니다.

프로그램 헤더 테이블의 전체 크기는 e_phnum * e_phentsize byte 입니다.


지금 현재는 프로그램 헤더 테이블을 보려는 중이니 아래와 같은 옵션으로 넘겨줍니다.


-l

--program-headers

--segments

Displays the information contained in the file's segment headers, if it has any. 


$ readelf -l ./hello



프로그램 헤더는 다음과 같은 구조를 갖고 있습니다.


typedef struct {

Elf32_Word p_type;  // Segment type 

Elf32_Off p_offset;  // Segment file offset 

Elf32_Addr p_vaddr;  // Segment virtual address 

Elf32_Addr p_paddr;  // Segment physical address

Elf32_Word p_filesz;  // Segment size in file 

Elf32_Word p_memsz;  // Segment size in memory

Elf32_Word p_flags;  // Segment flags 

Elf32_Word p_align;  // Segment alignment 

} Elf32_Phdr;  // x86


typedef struct {

Elf64_Word p_type;  // Segment type 

Elf64_Word p_flags;  // Segment flags 

Elf64_Off p_offset;  // Segment file offset 

Elf64_Addr p_vaddr;  // Segment virtual address 

Elf64_Addr p_paddr;  // Segment physical address

Elf64_Xword p_filesz;  // Segment size in file 

Elf64_Xword p_memsz;  // Segment size in memory

Elf64_Xword p_align;  // Segment alignment 

} Elf64_Phdr;  // x86_64


세그 먼트 타입에는 다음과 같이 있습니다.


PT_NULL 0

PT_LOAD 1

PT_DYNAMIC                 2

PT_INTERP 3

PT_NOTE 4

PT_SHLIB 5

PT_PHDR 6

PT_TLS 7

PT_LOOS 0x60000000

PT_HIOS 0x6fffffff

PT_LOPROC         0x70000000

PT_HIPROC         0x7fffffff


참고 : http://www.sco.com/developers/gabi/latest/ch5.pheader.html



Section header table



이 곳은 Section&Segment 오프셋이나 크기, 타입 등의 정보를 담고 있는 테이블입니다.

이 테이블은 오브젝트 파일 같이 relocation 가능한 ELF 오브젝트에는 반드시 있어야되는 녀석입니다.


지금 현재는 Section header table을 보려는 중이니 아래와 같은 옵션을 넘겨줍니다.


-S

--sections

--section-headers

Displays the information contained in the file's section headers, if it has any. 


$ readelf -S ./hello



섹션 헤더는 다음과 같은 구조를 갖고 있습니다.


typedef struct {

    uint32_t   sh_name;  // Section name

    uint32_t   sh_type;  // Section Type

    uint32_t   sh_flags;  // Section Flag

    Elf32_Addr sh_addr;  // Section virtual address 

    Elf32_Off  sh_offset;  // Section offset

    uint32_t   sh_size;  // section size

    uint32_t   sh_link;  // section index link

    uint32_t   sh_info;  // extra information

    uint32_t   sh_addralign;  // section alignment constraints

    uint32_t   sh_entsize;  // hold a table of fixed-sized

} Elf32_Shdr;  // x86


typedef struct {

    uint32_t   sh_name;  // Section name

    uint32_t   sh_type;  // Section Type

    uint64_t   sh_flags;  // Section Flag

    Elf64_Addr sh_addr;  // Section virtual address 

    Elf64_Off  sh_offset;  // Section offset

    uint64_t   sh_size;  // section size

    uint32_t   sh_link;  // section index link

    uint32_t   sh_info;  // extra information

    uint64_t   sh_addralign;  // section alignment constraints

    uint64_t   sh_entsize;  // hold a table of fixed-sized

} Elf64_Shdr;  // x86_64


참고 : http://linux.die.net/man/5/elf


ps. 오 세상에... 여기까지 귀찮게 다 작성했더니 마지막에 섹션 헤더에 대해 구글링을 하다가 엄청난 자료를 발견하였다. 무려 한글로 된 ... 고로 첨부합니다.


ELF File Format.doc


이렇게 리눅스에 ELF 포맷에 대해 알아보았습니다.

그렇다면 이제 어떻게 main을 불러오는지 알아보도록 하겠습니다!

오 할렐루야 이제야...



I want go to main() !



먼저 바이너리의 엔트리 포인트를 먼저 보도록 하겠습니다.



Entry point address는 0x8048320 이라고 합니다.

이제 이 주소에 뭐가 있는지 디버깅을 하면서 찬찬히 들어가보도록 하겠습니다.



Entry point address는 _start함수의 주소였습니다.

그렇다면 프로그램이 실행되면 _start 함수가 먼저 실행이 되겠네요.

_start 함수가 실행되면 스택 프레임은 아래와 같습니다.


Stack Top

0x804841d

 ...

esi

ecx

0x8048440

0x80484b0

edx

esp

eax


현재 이렇게 스택 프레임이 생겨먹었습니다.

여기서 그렇다면 궁금증이 몇가지가 생기셨을꺼라 생각됩니다.

뭐 저만 생긴 걸 수도 있지만요.


  1. 스택에 push된 저 값들은 무엇인가요?
  2. _start에서 0x8048310을 호출하는데 뭐지?



스택에 push된 저 값들은 무엇인가요?



먼저 스택에 push된 값들은 아래와 같습니다.

0x80484b0, 0x8048440, 0x804841d


0x804841d : main() 함수의 주소

0x8048440 : __libc_csu_init() 함수 주소

0x80484b0 : ??


들이라는 것을 알 수 있었습니다.



_start에서 0x8048310를 호출하는데 뭐지??



음 뭐 사실 뭐하는 녀석인지는 옆에 함수 명이 보이기 때문에 크게 생각하실 것은 없습니다.

0x0804833c <_start+28>: call   0x8048310 <__libc_start_main@plt>

__libc_start_main@plt 를 호출합니다.


plt를 호출하는 이유는 __libc_start_main를 처음 호출하는 것이라 바로 호출하지 않고 plt에서 got를 다이나믹 링킹하고 호출을 합니다.


아니 그러면 __libc_start_main은 뭘까요?

깊숙히 들어가보도록 하겠습니다.


  1. __libc_start_main은 뭔가요?


__libc_start_main은 뭔가요?



음...

현재 이 흐름은 libc의 손에 달려있습니다...!

__libc_start_main함수는 libc.so.6 안에 있거든요!



glibc 소스 코드에서 __libc_start_main를 발견할 수 있습니다.

코드는 아래와 같습니다!


int __cdecl __libc_start_main(int (__cdecl *main)(int, char **, char **), 

int argc, 

char **ubp_av, void (*init)(void), 

void (*fini)(void), 

void (*rtld_fini)(void), 

void *stack_end)



__libc_csu_init 호출



_init 호출



호로록



이 부분에선 gmon 프로파일링 시스템 초기화가 필요하다면 gmon_start를 호출하여 초기화합니다.




그 뒤에 다시 __libc_start_main으로 돌아오고 저희가 만든 main을 호출하게 됩니다.



이상입니다.


뭔가 덤성덤성 뛰어넘은 부분이 많지만 ...

도움이 되었으면 좋겠습니다!


길고 긴 글을 읽어주셔서 감사합니다.



Reference



Comment



'0x20 Security > 0x21 System' 카테고리의 다른 글

About LD_PRELOAD  (0) 2016.07.20
Use After Free (UAF)  (1) 2016.07.13
How main() is executed on linux  (2) 2016.07.04
System Hacking :: Memory Protection  (0) 2016.06.28
[SSA] 시스템 해킹 스터디 6주차  (8) 2015.11.09
[SSA] 시스템 해킹 스터디 5주차  (0) 2015.11.05

Comment 2