자주 사용하는 함수들의 정의를 묶은 것
같은 함수를 반복적으로 정의해야하는 수고를 덜 수 있어 효율적이다.
호출된 함수와 실제 라이브러리의 함수가 링크 과정에서 연결된다.
함수를 호출하면 매핑된 라이브러리에서 호출할 함수의 주소를 찾고, 그 함수를 실행한다.
하나의 라이브러리를 여러 프로그램이 사용했다면 각 프로그램은 같은 라이브러리를 참조하므로 메모리 자원이 낭비되지 않는다.
둘 옵션으로 컴파일하여 비교해보면
정적링크로 된 방식은 해당 함수가 있는 주소를 직접 호출하는 반면,
동적링크로 된 방식은 해당 함수의 plt주소
를 호출한다! 함수의 주소를 라이브러리에서 찾아야 하기 때문!
PLT(Procedure Linkage Table)와 GOT(Global Offset Table)는 라이브러리에서 동적 링크된 심볼의 주소를 찾을 때 사용하는 테이블이다.
만약 반복적으로 호출되는 함수의 정의를 매번 탐색해야 한다면 비효율적일 것이다. 그래서 ELF는 GOT라는 테이블을 두고, resolve
된 함수의 주소를 해당 테이블에 저장한다!
plt에서 reslove함수를 통해 함수의 주소를 구하고 이를 got에 쓴다! PLT에는 함수의 주소가 resolve되지 않았을 경우, 함수의 주소를 구하고 실행하는 코드가 적혀있다.
실행한 직후 GOT를 확인해보면 아직 puts
의 주소를 찾기 전이므로, 함수의 주소가 아닌 puts@plt+6
라는 PLT 내부의 주소가 적혀져있다.
이 때 첫번째 puts가 실행되는 과정에서 resolve되어 GOT에 주소가 쓰여지고,
두 번재로 puts@plt를 호출할 때는 GOT에 puts
의 주소가 쓰여져있어서 바로 puts가 실행된다.
resolve 전
resolve 후
PLT를 이용해 GOT에값을 쓸 때 GOT 값을 검증하지 않는다!
-> 위의 예에서 GOT에 저장된 puts
의 주소를 공격자가 임의로 변경할 수 있으면, 두 번째로 puts
가 호출될 때 공격자가 원하는 코드가 실행되게 할 수 있다.
➥ 이렇게 GOT에서 puts의 주소가 매핑된 곳을 다른 값으로 세팅해주면!?
➥ 이렇게 바뀝니당!
그래서 두번째 puts를 실행시키면
GOT에 있던 0x41414141을 실행시키기 때문에... SEGFAULT가 나는 것을 확인할 수 있습니당~~
이런 공격기법을 GOT Overwrite
라고 부르며, RCE를 하기 위한 방법으로 사용된다!