참고 : https://wogh8732.tistory.com/228 https://dreamhack.io/learn/11#8 https://rninche01.tistory.com/entry/Linux-Binary-Execution-Flow 분석 대상 바이너리 : pwnable.xyz의 Dirty Turtle문제 바이너리 |
fini_array 가 뭔지 알아보려고 위 블로그를 토대로 main함수가 호출, 종료되는 과정을 살펴보았다.
1. ELF 헤더 확인
Entry point(EP) : 코드가 시작될 때 실행이 시작되는 파일, 프로그램의 시작점 또는 그 영역
Entry point 주소를 IDA에서 찾아보면 _start 함수임을 알 수 있다.
프로그램을 실행하면, _start 함수가 맨 처음에 호출이 된다.
2. _start 함수
_start 함수에서는 _libc_start_main함수를 호출한다.
이 함수는 바이너리 실행 과정에 필요한 여러 요소들을 초기화하기 위해 호출이 된다.
이 함수는 libc 안에 존재하는 함수이기 때문에, plt를 뒤져 got table에 등록을 한 다음, 호출이 된다.
3. _libc_start_main 함수
_libc_start_main 함수 내부에서 __libc_csu_init 을 호출한다.
3.1 __libc_csu_init 함수
size는 8 (init_array 크기)
size만큼 반복문을 돌면서 init_array에 저장된 함수 포인터들을 호출한다.
gdb로 보면 frame_dummy란 값으로 들어있다.
바로 이부분이 호출하는 과정..
그런데 이 바이너리는 하나만 호출하고 끝이 나더라.
그러고 다시 __libc_start_main으로 돌아간다.
여기서 이제 main이 호출이 된다!!
4. main이 종료되는 과정
main함수에서 ret를 하게 되면 __libc_start_main+231 로 간다.
그리고 __libc_start_main에서 exit 함수를 호출하게 된다. (드림핵에서는 __GI_exit 함수라고 한다.)
여기 인자로 0x0가 되어 있는 것을 확인할 수 있는데,
우리가 흔히 .c파일을 작성할 때 main() 마지막에 return 0; 으로 작성하는데 여기서 return값 0이 exit()의 파라미터로 들어가게 되는 것이라고 한다.
exit 함수 내부에서는 __run_exit_handlers를 호출한다.
__run_exit_handlers 함수 내부에서는 _dl_fini를 호출한다.
(__run_exit_handlers 함수는 exit_function 구조체 멤버 변수인 flavor 값에 따라서 함수를 호출하게 되는데, 기본 상태에서는 로더 라이브러리 내부에 존재하는 _dl_fini 함수를 호출한다고 한다.
_dl_fini 함수 내부에서 .fini_array 섹션을 호출한다.
(.fini_array배열에 들어있는 __do_global_dtors_aux()를 호출하는 것을 확인할 수 있다.)
(내가 .fini_array 주소에 0x41414141를 넣어놔서 0x41414141이 호출되는 것을 확인할 수 있다.)
.fini_array배열에 있는 함수들을 호출 한 후
다시 _dl_fini()로 돌아와 _fini()를 호출한다. (여기서는 딱히 하는 일이 없다.)
_dl_fini()가 리턴되고 다시 __run_exit_handlers()루틴으로 돌아오면 __GI__exit()를 호출하는데, 이는 앞서 이야기했던(드림핵에서는 _GI_exit()를 호출한다고 설명했던..) 것과는 다른 함수이다.
__GI__exit()에서는 rax값을 0xe7, rdi를 0x0으로 세팅하고
syscall하면서 이제 완전히 프로세스가 종료된다고 한다.
5. 정리
- elf파일의 entry point가 가리키는 _start()부터 호출하여 시작하게 된다.
- _start루틴(crt(C run time))에서는 커널로부터 받은 argc, argv인자를 저장하고 스택을 초기화한 후 glibc내에 정의된 __libc_start_main()를 호출한다.
- __libc_start_main()에서는 .init / .fini섹션 작업과 관련된 함수들을 호출하고 메인함수를 호출한다.(.init → main() → exit())
.fini_array는 프로그램이 종료된 후 참조되는 섹션이다.
따라서 RELRO 보호기법이 설정되어 있지 않고 임의 주소 쓰기가 가능하다면, .fini_array 섹션을 덮어 실행 흐름을 조작하는 것이 가능하다.
'System Hacking' 카테고리의 다른 글
type confusion (0) | 2021.06.28 |
---|---|
Double Staged FSB (0) | 2021.05.30 |
integer overflow 2 (0) | 2021.05.19 |
Sigreturn-oriented programming (SROP) (0) | 2021.05.10 |
Stack Pivoting (스택 피봇팅) (0) | 2021.05.03 |