Compile | 컴파일
컴파일은 인간이 이해할 수 있는 언어로 작성된 소스코드를 CPU가 이해할 수 있는 언어로 변환하는 작업
즉, 바이너리 형태로 변환하는 작업이다. (기계어)
- Pre-Processing : 전처리기 (Pre-processor)을 통해 소스 파일을 수정된 소스 파일 (전처리된 소스 파일)로 변환
- Compilation : 컴파일러 (Compiler)을 통해 수정된 소스 파일을 어셈블리 파일로 변환
- Assembly : 어셈블러 (Assembler)을 통해 어셈블리어 파일을 오브젝트 파일로 변환 - 오브젝트 파일부터 바이너리 형태
- Linking : 링커 (Linker)을 통해 오브젝트 파일들과 라이브러리, 헤더 파일들을 하나로 묶어 실행 파일로 변환
이때 Linking 과정에서 Static Linking (정적 링킹)과 Dynamic Linking (동적 링킹)으로 구분한다.
Static Link | 정적 링크
obj 파일에 정적 라이브러리 파일 (.a or .lib) 등을 합쳐 하나의 실행 파일을 만듦
- 실행 파일 내부에 라이브러리가 포함되어 있음
- 라이브러리 파일이 따로 없어도 독립적으로 동작 가능
- 정적 링크된 함수를 호출 할 때 소스파일 자체의 함수를 호출하는 것과 같이 호출할 수 있음
- 라이브러리에서 원하는 함수를 찾지 않아도 되어 탐색 비용이 절감됨
- 여러 바이너리에서 사용하면 복제가 이루어짐
- 실행 파일의 용량이 정적링크된 파일만큼 늘어남
Dynamic Link | 동적 링크
obj 파일에 동적 라이브러리 파일 (.a or .lib) 등을 따로 메모리에 올려, 실행파일을 실행시킬 때 불러옴
- 프로그램 실행 시 동적 링크된 곳에서 파일을 불러옴
- 바이너리 실행 시 동적 링크된 파일이 프로세스 메모리에 매핑됨
- 바이너리 실행 중 함수를 호출하면, 매핑된 곳에서 해당 함수의 주소를 찾음
- 윈도우의 경우 dll 파일 등으로 표현되어 있는 곳에서 exe 파일이 실행시 dll을 불러오는 형식
라이브러리 파일만 링크 되는 것은 아니다. 헤더파일 등도 동적 링크, 정적 링크 될 수 있음
[ 라이브러리란? ]
라이브러리는 컴퓨터 시스템에서 프로그램들이 함수나 변수를 공유해서 사용할 수 있게 해주는 것이다.
자주 사용되는 함수드의 정의를 묶어, 하나의 라이브러리 파일로 만들고, 여러 프로그램들이 공유해서 사용할 수 있도록 함
C언어의 표준 라이브러리 : libc
libc에는 printf, scanf, strlen, memcpy 등을 확인할 수 있음
Dynamic Link vs Statkc Link
#include <stdio.h>
int main() {
puts("Hello, world!");
return 0;
}
Hello, world!를 출력하는 프로그램을 동적링크, 정적링크 시켜서 비교해보겠다.
gcc -o static hello.c -static
gcc -o dynamic hello.c
정적, 동적으로 컴파일한다.
용량
ls -lh ./static ./dynamic
-rwxr-xr-x 1 parallels parallels 69K Sep 5 09:17 ./dynamic
-rwxr-xr-x 1 parallels parallels 621K Sep 5 09:17 ./static
약 10배 정도의 용량 차이가 난다.
정적 링크된 실행 파일의 용량 >> 동적 링크된 실행 파일의 용량
호출 방법
Static
0x00000000004007a0 <+0>: stp x29, x30, [sp, #-16]!
0x00000000004007a4 <+4>: mov x29, sp
0x00000000004007a8 <+8>: adrp x0, 0x454000 <read_encoded_value_with_base+160>
0x00000000004007ac <+12>: add x0, x0, #0x518
0x00000000004007b0 <+16>: bl 0x401660 <puts>
0x00000000004007b4 <+20>: mov w0, #0x0 // #0
0x00000000004007b8 <+24>: ldp x29, x30, [sp], #16
0x00000000004007bc <+28>: ret
puts가 있는 0x401660을 직접 호출함
Dynamic
0x0000000000400698 <+0>: stp x29, x30, [sp, #-16]!
0x000000000040069c <+4>: mov x29, sp
0x00000000004006a0 <+8>: adrp x0, 0x400000
0x00000000004006a4 <+12>: add x0, x0, #0x6d8
0x00000000004006a8 <+16>: bl 0x400540 <puts@plt>
0x00000000004006ac <+20>: mov w0, #0x0 // #0
0x00000000004006b0 <+24>: ldp x29, x30, [sp], #16
puts의 plt 주소인 0x400540을 호출함
plt는 동적 링크된 바이너리에서 함수의 주소를 라이브러리에서 찾는데 사용되는 테이블
PLT & GOT
PLT (Procedure Linkage Table), GOT (Gloabl Offset Table)은 라이브러리에서 동적 링크된 심볼 주소를 찾을 때 사용하는 테이블
PLT
- 라이브러리의 모든 함수의 주소가 매핑되어있음
- printf, puts 등등 PLT 테이블이 매핑되어 있음
- 바이너리는 정확히 사용하려는 함수가 PLT에 어디 위치에 있는지 실행하기 전에는 알 수 없음
GOT
- ELF (리눅스 바이너리 포맷)은 GOT 테이블을 두고, resolve된 함수의 주소를 해당 테이블에 저장
- 함수를 다시 호출할 때 PLT에서 다시 함수를 찾지 않아도 됨
- 함수의 다시 실행할 때는 PLT에 함수가 어디에 있는지 정확한 주소를 GOT에 이미 저장해둔 상태
runtime resolve
해당 과정을 통해 동적 링크된 심볼 주소를 찾음
- 라이브러리 함수 호출
- 함수의 이름을 바탕으로 라이브러리에서 심볼 탐색
- 함수 정의 발견 시, 그 주소로 실행 흐름을 옮김
resolve 전
puts@plt를 호출하는 부분을 중심으로 볼 예정
- plt의 주소인 0x400540으로 실행 흐름이 옮겨짐 (puts의 주소가 아님)
- _dl_runtime_resolve_fxsave라는 함수가 실행됨
- 해당 함수에서 puts의 주소를 구함
- GOT 엔트리에 puts의 주소를 적음
해당 GOT 엔트리에는 libc 영역 내 실제 puts 주소가 쓰여짐
resolve 후
두번째로 puts를 실행한다는 가정
- puts@plt를 call 함
- GOT에서 이미 매핑된 puts의 주소를 받아와 puts가 바로 실행됨
GOT Overwrite
취약점 : PLT에서 GOT를 참고하여 실행 흐름을 옮길 때 GOT 값을 검증하지 않음
→ GOT Overwrite 공격 기법 수행 가능
앞의 예시를 바탕으로, puts의 GOT 엔트리에 저장된 값을 임의로 변경하면, puts가 호출될 때 실행 흐름을 옮길 수 있음
reference
- Dreamhack SystemHacking > Background: Library - Static Link vs Dynamic Link
'CS > Computer Architecture' 카테고리의 다른 글
[Assembly] 산술 연산 (0) | 2024.09.27 |
---|---|
[Assembly] 데이터 이동 (0) | 2024.09.20 |
함수의 프롤로그 & 에필로그 (0) | 2024.08.22 |
Register (0) | 2024.08.16 |
[Assembly] Intel vs AT&T (2) | 2024.08.14 |