ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • PLT 와 GOT
    표튜터와 함께하는 Pwnable/Pwnable 개념 및 정리 2019. 2. 14. 15:10

    PLT와 GOT에 대해서 알아보도록 하겠다!!


    이해하기전에 어떤식으로 프로그램이 컴파일되고 실행되는지를 알아야한다.

    C언어 코드를 예로 들어보겠다.

    1
    2
    3
    4
    5
    6
    7
    #include <stdio.h>
     
    int main()
    {
        printf("Hello My name is Ho ~ ");
        return 0;
    }


    다음과 같은 소스코드가 있다면 이 소스 코드를 실행하기 위해서는 다음과 같은 과정이 필요하다.


    우리가 만약에 printf라는 코드를 실행하기 위해서는 우선 코드를 작성하고 컴파일을 해주어야한다.


    컴파일을 진행하면 obj파일이 생성되는데 생성된 obj파일은 스스로 실행을 할 수 없다. 


    그 이유는 소스코드 내부의 함수(printf)가 어떤식으로 구현되어있는지 obj파일은 알 수 없기 때문이다.


    이러한 obj파일을 실행하게 하기위해서는 함수(printf)의 실행코드 등, 필요한 obj파일을 찾아 연결시켜줘야한다. 


    이러한 과정을 Linking이라고 부르며 obj파일과 Linking하게 되는 obj파일의 모임을 LIB(라이브러리)라고 한다.


    라이브러리는 예를 들어 위의 printf 함수같이 어떠한 함수 등을 구현한 코드를 컴파일한 obj 파일들의 모임이라고 생각하면 된다.





    linking에는 두 가지 방식이 있는데 Static과 Dynamic방식이 존재한다.


    Static link 방식의 경우 파일 생성 시 라이브러리의 내용을 포함시켜 실행파일을 생성한다. 

    실행파일 내부에 모든 코드가 포함되기 때문에 라이브러리의 연동이 필요없으며

    필요한 라이브러리들이 내부에 있기 때문에 관리가 필요없다.

    하지만 그만큼 파일 용량이 커지며 동일한 라이브러리를 사용하게 되더라도

    해당 라이브러리를 사용하는 모든 프로그램들이 라이브러리내용을 메모리에 패밍시켜야한다는 단점이 존재한다.



    Dynamic link방식은 라이브러리를 하나의 메모리 공간에 매핑시켜서

    여러프로그램이 공유해서 사용하는 방식으로 진행된다.

    실행파일내부에 라이브러리 코드가 없기 때문에 static link방식에 비해 파일크기도 작고 실행 시에도

    적은 메모리를 이용한다. 또한 라이브러리 업데이트를 따로 진행할 수 있어서 편리하다.

    하지만 라이브러리를 의존하기 때문에 라이브러리가 없으면 실행이 불가하다는 단점이 있다.





    코드를 통해 Static link 방식과 Dynamic link 방식을 비교해보도록 하겠다.



    먼저 static link방식이다. 컴파일 옵션에 -static을 써주면 static방식으로 컴파일이 된다.

    뒤에 있는 -m32는 32bit 형식을 의미하는 옵션이다.

    컴파일된 파일의 정보를 보니 statically linked로 linking된 것을 확인 할 수 있었다.



    이번에는 dynamic link방식을 해보았다. 컴파일 옵션을 주지않게되면 

    아래의 그림처럼 dynamically linked로 진행된다.





    우리가 공부할 plt와 got는 Dynamic link방식에서 사용된다.

    그 이유는 static link 방식의 경우 사용할 라이브러리가 프로그램 내부에 있기 때문에

    함수의 주소를 알아오는 과정이 필요없지만 dynamic link의 경우는 컴파일 시

    라이브러리가 외부에 있기 때문에 주소를 알아오는 과정이 필요하기 때문이다.

    Dynamic link방식으로 프로그램을 컴파일하게되면 함수 호출 시 plt를 참조하게된다.

    plt는 got로 jmp하는데 got에는 라이브러리에 존재하는 실제 함수의 주소가 있어 함수를 호출하게 된다.


    하지만 처음 호출할 때와 두 번째 호출일때의 동작방식이 다르다.


    두 번째 호출시에는 바로 got에 실제 함수가 쓰여있어서 참조하면되지만

    첫 번째 호출이라면 got에는 실제함수의 주소가 있지 않다.


    그렇기 때문에 첫 번째 호출 시에는 linker가 dl_resolve라는 함수를 사용하여

    필요한 함수의 주소를 알아와서 got에 그 주소를 적어준 뒤 함수를 호출하게 된다.


    아까 컴파일한 소스코드를 이용해서 차이를 비교해보도록 하겠다.



    Static link파일의 경우 처음 printf 함수의 주소가 프로그램 실행 전후가 같다.

    <실행 전>

    printf 함수의 주소를 제대로 가리키고 있음을 볼 수 있다.



    <실행 후>

    그 이유는 프로그램 내부에 라이브러리가 존재하기 때문에 plt와 got를 이용하지 않는다.

    또한 printf함수 주소가 프로그램의 text영역 내에 있는 것으로 예상해볼 수 있었다.








    이번에는 Dynamic link파일이다. 

    <실행 전>

    실행 전의 주소를 기억해놓자. 현재 printf@plt를 가리키고 있다.



    <실행 후>

    프로그램의 실행 전과 실행 후의 주소값이 달라짐을 알 수 있다.

    실제의 printf 함수를 가리키게되고 이 주소는 프로그램 내부의 주소같아 보이지 않는다.

    위에서 설명한대로 프로그램 실행 전에는 함수의 실제주소를 가리키고 있지 않고

    실행 후에 linker의 dl_solve() 함수를 통해 got에 함수의 실제주소가 입력됨으로

    주소값이 실제주소로 달라짐을 확인할 수 있었다.



    * 깰꼼 정리!! *


    처음 함수 호출 시 함수의 got가 가리키고 있는 주소는 plt+6의 주소이며

    이 값은 reloc_offset이다. 이 reloc_offset을 push한 뒤 dynamic linker로 jump하게 되며

    jump 후 link_map 구조체 포인터를 push한다. push 후에는 dl_runtime_resolve함수를 호출하고

    이전에 push했던 reloc_offset과 link_map 구조체 포인터를 인자로 _dl_fixup함수를 호출한다.

    _dl_fixup에서는 프로그램에서 사용된 함수 이름들의 문자열이 저장된 STRTAB주소와

    GOT주소 및 재배치 정보를 담은 재배치 테이블인 JMPREL의 주소를 알아낼 수 있다.

    알아낸 STRTAB내에 있는 함수 이름의 주소를 넘기면서 _dl_lookup_symbol_x 함수를 호출하여

    라이브러리 시작주소와 라이브러리 함수 내에 있는 SYMTAB의 주소를 가져오게 된다.

    다시 _dl_fixup함수로 돌아오게 되면 SYMTAB 내의 실제 함수의 오프셋과 라이브러리 시작 주소를 더하여

    실제 함수의 주소를 알아낸 뒤 GOT에 기록하게된다.


    Dynamic Linker는 호출할 함수이름이 있는 메모리의 주소를 구하고 이를 이용해 최종적으로

    공유라이브러리에 있는 실제 함수의 주소를 구해오는 것이다.



    * SYMTAB

    실제 함수의 offs


    et이 존재함(나중에 libc_base와 더해서 got에 기록함)


    * STRTAB(문자열 테이블)

    프로그램내에 쓰이는 각종 심볼들의 문자열이 들어있다.


    * JMPREL

    재배치 정보를 담고 있는 재배치 테이블 ELF32_REL 구조체로 이루어져있다.


    * 재배치 타입

    어떤 비트를 변화시키고 그 값을 어떻게 계산할 것인지를 나타낸다.


    * SYMTAB(Symbol Table Pointer) : 심볼 테이블 포인터 

    전역변수와 함수에 대한 정보를 담고 있음


    plt는 code영역이기 때문에 실행권한이 있지만 Got는 data영역이기 때문에 실행권한이 없다.

    따라서 got overwrite할 때는 plt로 덮어씌워주어야 한다.


    참고 : https://bpsecblog.wordpress.com/2016/03/09/about_got_plt_2/



    PLT는 code섹션에 GOT는 data섹션에 만들어진다.

    (사진 추가예정)


    Lazy Binding 
    plt & got


    Dynamic Linking방식으로 컴파일이 된 ELF 바이너리는 공유 라이브러리 내에
    위치한 함수의 주소를 동적으로 알아오기 위한 GOT(Global Offset Table)테이블을 이용한다.

     처음 함수 호출 시 간단하게 plt와 got 동작 원리 설명 :
    plt영역의 got를 참조함 ->
     got가 plt+6의 주소를 가리킴으로 다시 plt로 이동함 -> 
    그 다음이 jump로 dl_runtime_resolve함수로 분기함 -> 
    링커(linker)가 dl_runtime_resolve함수 내부에 있는 _dl_fixup함수가 호출되고 got에 함수주소를 넣어줌

    이와 같은 모든 외부함수의 주소를 한 번에 로딩하지 않고 함수 호출 시점에서
    해당함수의 주소만 공유 라이브러리로부터 알아오는 것을 Lazy Binding이라고 하며
     이 방식은 plt와 got를 사용하여 실행 시 필요한 프로시저의 심볼만을 해석하여 접근하기 때문에
    더 빠르게 프로그램을 실행시킬 수 있다. 그러므로 plt와 got를 사용하는 것이다.  


    반응형

    댓글

Designed by Tistory.