Library & Link

RoughBoy·2023년 9월 9일
0

시스템 구조

목록 보기
7/11
post-thumbnail
post-custom-banner

라이브러리

라이브러리는 컴퓨터 시스템에서, 프로그램들이 함수나, 변수를 공유해서 사용할 수 있게 한다. 대개의 프로그램은 서로 공통으로 사용하는 함수들이 많다. 예를 들어, printf, scanf, strlen, memcpy, malloc등은 많은 C프로그래머들이 코드를 작성하면서 사용하는 함수이다.

C언어를 비롯하여 많은 컴파일 언어들은 자주 사용되는 함수들의 정의를 묶어서 하나의 라이브러리 파일로 만들고, 이를 여러 프로그램이 공유해서 사용할 수 있도록 지원하고 있다. 라이브러리를 사용하면 같은 함수를 반복적으로 정의해야 하는 수고를 덜 수 있어 코드 개발의 효율이 높아진다는 장점이 있다.

또한, 각 언어에서 범용적으로 많이 사용되는 함수들은 표준 라이브러리가 제작되어 있어서 개발자들은 쉽게 해당 함수들을 사용할 수 있다. 대표적으로, C의 표준 라이브러리인 libc는 우분투에 기본적으로 탑재된 라이브러리이며, 실습환경에서는 /lib/x86_64-lonux-gnu/libc-2.27.so에 있다.
(왠지 모르겠지만 내 lib에는 python3.10밖에 안보인다.)
처음 코딩을 공부할 때, printf를 정의한 적이 없지만printf("Hello world!")라는 예제를 컴파일해서, 실행할 수있는 것은 libc에 이 함수가 이미 정의되어 있기 때문이다.

링크

링크(Link)는 많은 프로그램 언어에서 컴파일의 마지막 단계로 알려져 있다. 프로그램에서 어떤 라이브러리의 함수를 사용한다면, 호출된 함수와 실제 라이브러리의 함수가 링크 과정에서 연결된다.
리눅스에서 c소스 코드는 전처리, 컴파일, 어셈블 과정을 거쳐 ELF형식을 갖춘 오브젝트 파일로 번역된다.

오브젝트 파일은 실행 가능한 형식을 갖추고 있지만, 라이브러리 함수들의 정의가 어디 있는지 알지 못하므로 실행은 불가능합니다. 다음 명령어를 실행해보면, puts의 선언이 stdio.h에 있어서 심볼로는 기록되어 있지만, 심볼에 대한 자세한 내용은 하나도 기록되어 있지 않다. 심볼과 관련된 정보들을 찾아서 최종 실행 파일에 기록하는 것이 링크 과정에서 하는 일 중 하나다.

라이브러리와 링크의 종류

라이브러리는 크게 동적 라이브러리와 정적라이브러리로 구분되며, 동적 라이브러리를 링크하는것을 동적링크, 정적 라이브러리를 링크하는 것을 정적 링크라고 부른다.

동적 링크

프로그램을 사람으로, 라이브러리를 도서관으로 비유했을 때, 동적 링크는 가장 자연스러운 도서관 사용 방법이다. 동적 링크된 바이너리를 실행하면 동적 라이브러리가 프로세스 메모리에 매핑된다. 그리고 실행 중에 라이브러리의 함수를 호출하면 매핑된 라이브러리에서 호출할 함수의 주소를 찾고, 그 함수를 실행한다. 이 과정은 사람이 도서관에 방문해서 원하는 책의 위치를 찾고, 그 책에서 정보를 습득하는 과정과 유사하다.

정적링크

마찬가지의 비유를 했을 때, 정적 링크는 도서관의 모든 책을 암기하는 것과 같다. 정적 링크를 하면 바이너리에 정적 라이브러리의 모든 함수가 포한된다. 따라서 해당 함수를 호출할 때, 라이브러리를 참조하는 것이 아니라, 자신의 함수를 호출하는 것처럼 호출할 수 있다. 라이브러리에서 원하는 함수를 찾지 않아도 되니 탐색의 비용이 절감되는 듯 하지만, 여러 바이너리에서 라이브러리를 사용하면 그 라이브러리의 복제가 여러번 이루어 지게 되므로 용량을 낭비하게 된다.

  1. 동적링크가 정적링크보다 더많은 용량을 차지한다.

  2. static에서는 코드를 직접 호출한다.
    dynamic에서는 코드의 plt 주소를 호출한다.
    이런 차이가 발생하는이유는, 동적 링크된 바이너리는 함수의 주소를 라이브러리에서 "찾아야"하기 때문이다.
    (plt는 이과정에서 사용되는 테이블이다)

PLT & GOT

PLT와 GOT는 라이브러리에서 동적 링크된 심볼의 주소를 찾을때 사용하는 테이블이다.

바이너리를 실행되면 ASLR에의해 라이브러리가 임의의 주소에 매핑된다. 이 상태에서 라이브러리 함수를 호출하면, 함수의 이름을 바탕으로 하이브러리에서 심볼들을 탐색하고, 해당 함수의 정의를 발견하면 그 주소로 실행 흐름을 옮기게 된다. 이 전 과정을 통틀어 runtime resolve라고 한다.

그런데 만약 반복적으로 호출되는 함수의 정의를 매번 탐색해야 한다면 비효율적일 것이다. 그래서 ELF는 GOT라는 테이블을 두고, resolve된 함수의 주소를 해당 테이블에 저장한다. 그리고 나중에 다시 해당 함수를 호출하면 저장된 주소를 꺼내서 사용한다.

시스템 해킹의 관점에서 본 PLT와 GOT

PLT와 GOT는 동적 링크된바이너리에서 라이브러리 함수의 주소를 찾고, 기록할때 사용되는 중요한 테이블이다. 그런데, 시스템 해커의 관점에서 보면 PLT에서 GOT를 참조하여 실행 흐름을 옮길 때, GOT의 값을 검증하지 않는다는 보안상의 약점이 있다.

따라서 GOT에 저장된 코드@@의 주소를 공격자가 임의로 변경할 수 있으면, 두 번째로 코드@@가 호출될때 공격자가 원하는 코드가 실행되게할 수 있다.

이런 공격기법을 GOT Overwrite라고 부르며, 임의 주소값을 쓸 수 있을때, RCE를 하기 위한 방법으로 사용될 수 있다.

profile
The day⋯ is doomed⋯. Thanks to⋯ the Rowdyruff Boys⋯.
post-custom-banner

0개의 댓글