tistoryBlogName: lmxx
tistoryTitle: “[TOOR] 7. Lazy Binding & Now Binding”
tistoryTags: “”
tistoryVisibility: “0”
tistoryCategory: “1189856”
tistorySkipModal: true
tistoryPublished: “”
tistoryPostId: “28”
tistoryPostUrl: https://lmxx.tistory.com/28
no relro 했는데 왜 Full Relro
norelro 로 컴파일 해서 최초 실행인데 왜 바인딩이 돼있지?
partial Relro 로 하려고 -z relro 옵션으로 컴파일 했는데 계속 Full Relro 여서 보니까 PIE 가 켜져있으면 Full Relro 가 된다.
PIE 끄면 partial 이 된다.
PLT, GOT
Dynamic Linking 에서 라이브러리 함수의 실제 주소를 가져오기 위해서 사용한다.
PLT
Procedure Linkage Table
외부 라이브러리 함수를 사용할 수 있도록 주소를 연결 해주는 테이블
GOT
Global Offset Table
PLT에서 호출하는 resolve()함수를 통해 구한 라이브러리 함수의 절대 주소가 저장되어 있는 테이블
Lazy Binding 간단 요약
동적으로 링크 된 바이너리에서 라이브러리 함수인
printf
를 호출한다고 가정하자.
call printf@plt
형태로 라이브러리 함수인printf()
호출- PLT에 접근하여 GOT로 점프
- 처음 호출한 경우 링커가
_dl_resolve()
함수를 통해printf()
의 실제 주소를 알아내어 GOT에 쓴다. - GOT에 있는
printf()
의 실제 주소를 이용해printf()
호출
이후 호출에는 3번 과정에서 바로 실제 함수의 주소로 접근할 수 있다
detail
우선 library 함수를 호출하게 되면
plt로 가게 된다.
처음 호출된 경우 해당 함수의 got 에는 plt의 주소가 적혀있는데
이로 인해서 Lazy Binding 이 시작된다.
0x8은 reloc_offset 이다.
이 reloc_offset 은 구조체의 시작 주소를 찾기 위해서 사용한다.
DL_FIXUP_VALUE_TYPE attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE _dl_fixup ( # ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS ELF_MACHINE_RUNTIME_FIXUP_ARGS, # endif struct link_map *l, ElfW(Word) reloc_arg)
_dl_fixup 를 보면 인자로 link_map 구조체와 reloc_arg 가 들어간다.
이제 다시 보면 0x8049030 로 jmp 해주는데
여기서 push 로 어떤 값을 넣어준다.
이 값은 link_map 구조체 포인터이다.
그리고 jmp 해주는데 이 함수는 _dl_runtime_resolve
이다.
보면 call 하는 부분이 있는데 이게 _dl_fixup
함수이다.
인자로 들어가는 것은
eax = link_map 구조체 포인터
edx = reloc_offset 이다.
_dl_fixup
함수 안에서 link_map 구조체를 이용해서 STRTAB의 주소를 알아 낸다.
보통 [link_map + 0x34] 로 STRTAB의 시작 주소를 찾을 수 있다.
STRTAB
strtab 은 프로그램 내에서 쓰이는 각종 심볼들의 string 이 들어있는 테이블이다.
알아낸 STRTAB의 주소를 보면 심볼들의 string 이 있는 걸 볼 수 있다.
이런식으로 주소를 구한다
또한 link_map 을 이용해서 JMPREL 주소를 구한다.
보통 [link_map + 0x7c] 로 구할 수 있다.
JMPREL
재배치 정보를 담고 있는 재배치 테이블 Elf32_Rel 구조체로 이루어져 있다.
Elf32_Rel 구조체는 GOT의 주소와 재배치 정보들로 이루어져 있다.
하나의 entry 크기는 8바이트 처음 4byte는 GOT의 주소 다음 4byte의 첫번째 1byte는 재배치 타입, 나머지 byte가 DYNSYM 테이블에서의 index를 나타낸다.
이렇게 찾을 수 있고
link_map 에 0x7c를 더해도 된다.
오른쪽의 3바이트는 DYNSYM 의 index 이고 1바이트는 type이다.
이 과정을 통해 구한게 JMPREL 이고
JMPREL에 reloc_offset 을 더하면 해당 함수가 사용하는 Elf32_REL 구조체의 주소가 된다.
이게 Elf32_REL 이다.
왼쪽에 있는 4바이트는 GOT
오른쪽 의 3바이트는 DYNSYM 의 index
나머지 1바이트는 type 이다.
지금까지 구한 STRTAB 과 DYNSYM 으로 함수 이름을 구한다.
DYNSYM
동적 심볼(섹션) 테이블
처음 16byte는 0으로 세팅된다.
import 및 export하는 모든 심볼의 정보가 담겨있다.
ELF32_SYM구조체로 이루어져 있다.
중요한 값은 첫 번째와 다섯 번째 값이다.
readelf -S
명령어나
gef, pwndbg 등에서 elf 명령을 사용하면 .dynsym 주소를 알 수 있다.
0x00000010 0x00000000 0x00000000 0x00 0x00 0x0012
중요한 값은 첫 번째와 다섯 번째 값이다.
첫 번째 값은 offset이고
다섯 번째 값은 3과 &연산을 해서 0이냐 아니냐로 이미 로딩 된 함수인지 아닌지를 판단한다
0x00은 3과 &연산 했을 때 0이 나오는 값이므로 로딩되지 않은 함수이다.
아까 찾은 STRTAB 에 offset 을 더해주면 해당 함수의 이름이 나온다.
이렇게 함수 이름을 구하고 _dl_lookup_symbol_x
를 호출한다.
_dl_lookup_symbol_x
함수가 종료되면 레지스터에 라이브러리 시작주소와 SYMTAB의 주소가 쓰여진다.
부르는 함수에 해당하는 offset이 적한 주소를 찾아온다.
함수 종료 후 레지스터 상태_dl_lookup_symbol_x
함수가 종료되면 eax에 라이브러리의 시작주소가 쓰여있고, ebp-0x1c에 SYMTAB주소가 쓰여있다.
이후에
함수의 실제 주소를 가져왔음을 알 수 있다.
SYMTAB에 적혀 있는 offset중에서 부르는 함수에 해당하는 offset이 적힌 주소를 찾아온다.
이렇게 구해온 라이브러리 시작 주소와 SYMTAB 내에 있는 실제 함수의 오프셋을 더해서 실제 함수의 주소를 구하고 이것을 GOT에 기록한다.
_dl_fixup
에서 got 에 실제 주소가 써졌다.
이후에 _dl_runtime_resolve
에 의해 실제 함수로 넘어간다.
Now Binding
Full Relro 가 걸리게 되면 Now Binding 으로 링킹을 하게 되는데 Lazy Binding 과는 달리 프로그램이 실행 될 때 링킹을 하고 got 에 쓰기 권한을 제거한다.
이렇게 하면 got overwrite 공격을 보호할 수 있다.
앞선 실습때 사용한 것에 Full Relro 만 추가했다
실행 전에 섹션을 확인해보면 got 영역에 쓰기 권한이 있음을 알 수 있다.
실행 전에는 값이 안 적혀 있지만
실행하자마자 확인하면 실제 주소가 적혀있음을 알 수 있다.
또한 vmmap 으로 확인해보면 쓰기 권한이 없다.
이 글은 옵시디언을 이용해서 작성되었습니다.
'TOOR' 카테고리의 다른 글
[TOOR] 9.1. ASLR (0) | 2023.09.20 |
---|---|
[TOOR] 8,10 공유 라이브러리 & Lazy Binding & Now Binding (0) | 2023.09.20 |
[TOOR] 6.1. Canary & canary write_up (0) | 2023.09.19 |
[TOOR] 5.4. shell_basic write_up (0) | 2023.09.19 |
[TOOR] 5.3. Shell Code (0) | 2023.09.19 |