라이브러리는 컴퓨터 시스템에서, 프로그램들이 함수나, 변수를 공유해서 사용할 수 있게 한다.
대부분 프로그램은 prinf
, scanf
, strlen
, memcpy
, malloc
과 같이 공통으로 사용하는 함수들이 많은데, 이렇게 자주 사용되는 함수들의 정의를 묶어서 하나의 파일로 만들 것이 라이브러리 이다.
라이브러리를 사용하면, 같은 함수를 각각의 프로그램에서 반복적으로 정의하지 않아도 되므로 개발 효율이 높아진다.
C 표준 라이브러리인 libc
는 우분투에 기본으로 탑재된 라이브러리이다.
링크 는 많은 프로그래밍 언어에서 컴파일의 마지막 단계로 알려져 있다.
프로그램에서 어떤 라이브러리 함수를 사용한다면, 호출된 함수와 실제 라이브러리의 함수가 링크 과정에서 연결된다.
리눅스에서 C 소스 코드는 전처리
, 컴파일
, 어셈블
과정을 거쳐 실행 가능한 ELF 형식인 오브젝트 파일(object file) 로 번역된다.
오브젝트 파일은 실행 가능한 파일 형식 이지만, 라이브리리 함수들의 정의가 어디 있는지 알지 못하므로 실제 실행은 불가능 하다.
링크 과정은 해당 오브젝트 파일(object file) 에 필요한 함수에 대한 정보를 기록하는 것이다.
아래 명령어로 컴파일을 하게 되면 C 표준 라이브러리인 libc
를 같이 컴파일 하지 않았음에도 함수에 대한 정보가 파일에 포함된다.
gcc -o hello_world hello_world.c
그 이유는 libc
가 있는 /lib/x86_64-linux-gnu
가 표준 라이브러리 경로에 포함되어 있기 때문이다.
링크를 거치고 나면, 프로그램에서 라이브러리 함수를 호출할 때, 함수의 정의가 있는 libc 에서 해당 함수의 코드를 찾고, 해당 코드를 실행하게 된다.
라이브러리는 크게 동적 라이브러리 와 정적 라이브러리 로 구분되며, 동적 라이브러리를 링크하는 것은 동적 링크, 정적 라이브러리를 링크하는 것은 정적 링크 라고 한다.
실행 파일에는 라이브러리 함수에 대한 코드가 포함되어 있지 않음.
바이너리 실행 시 동적 라이브러리가 프로세스의 메모리에 매핑되는 방식
실행 중에 라이브러리 함수 호출 시, 매핑된 라이브러리에서 호출할 함수의 주소를 찾고, 그 함수를 실행
PLT(Procedure Linkage Table) 와 GOT(Global Offset Table) 는 라이브러리에서 동적 링크된 심볼의 주소를 찾을 때 사용하는 테이블이다.
바이너리 실행 시, ASLR에 의해 라이브러리가 임의의 주소에 매핑된다.
이 상태에서 라이브러리 함수를 호출하면, 함수의 이름을 바탕으로 라이브러리에서 심볼들을 탐색하고, 해당 함수의 정의를 발견하면 해당 주소로 실행 흐름을 옮긴다. (runtime resolve)
위 과정에서 발견한 함수(resolve 된 함수)의 주소를 GOT(Global Offset Table) 테이블에 저장하고, 추후 해당 함수가 다시 호출되면 GOT 에 저장된 주소를 꺼내서 사용한다.
PLT 와 GOT 는 동적 링크된 바이너리에서 라이브러리 함수의 주소를 찾고, 기록할 때 사용되는 중요한 테이블이다.
해당 테이블들을 시스템 해커의 관점에서 보면 PLT 에서 GOT 를 참조하여 실행 흐름을 옮길 때, GOT 의 값을 검증하지 않는다 는 보안상의 약점이 있다.
만약 GOT 에 저장된 함수의 주소를 공격자가 임의로 변경할 수 있다면, 추후 해당 함수가 다시 실행 될 때 공격자가 원하는 코드가 실행되게끔 할 수 있다.
위와 같은 공격 기법을 GOT Overwrite 라고 부르며, 임의 주소에 값을 쓸 수 있을 때, RCE 를 하기 위한 방법으로 사용될 수 있다.