PLT and GOT
PLT=Procedure Linkage Table.
==> 외부 프로시저를 연결해주는 테이블이고 PLT를 통해 다른 라이브러리에 있는 프로시저를 호출해
사용할 수 있다.
GOT=Global Offset Table.
==> PLT가 참고하는 테이블, 프로시저들의 주소가 들어있다.
흔히 PLT & GOT는 "함수를 호출하면(PLT를 호출하면) GOT으로 점프하는데 GOT에는 함수의 실제 주소가 쓰여있다.
첫번째 호출이라면 GOT는 함수의 주소를 가지고 있지 않고 과정을 거쳐 주소를 알아낸다.
두번째 호출부터는 첫 번째 호출 때 알아낸 주소로 바로 점프한다.
1.PLT와 GOT가 왜 필요가 있는 건가?
2.함수의 주소로 바로 점프를 왜 안할까?
3.PLT와 GOT가 왜 필요가 있는 건가?
4.GOT가 행동하는 과정은 뭐일까?
1번 Q & A
일단 링커(Linker)를 알아야 한다.
예를 들어 임의의 코드를 작성한다고 해본다. 소스 안에는 prinf 함수를 호출, include한 헤더파일에는 printf의
선언이 있다. 물론 소스파일을 실행파일로 만들기 위해서는 컴파일을 해야한다.
컴파일을 통해 오프젝트 파일이 생성된다. 하지만 오브젝트 파일은 그 자체로 실행이 가능하지는 않다.
printf의 구현 코드를 모르기 때문이다. printf를 호출 했을 때 어떤 코드를 실행해야 하는지,
우리가 작성한 코드만 가지고서는 아무것도 알 수 없다.
오브젝트 파일을 실행 가능하게 만들기 위해서 printf의 실행 코드를 찾아서 오브젝트 파일과 연결시켜야 한다.
printf의 실행 코드는 printf의 구현 코드를 컴파일한 오브젝트 파일로, 오브젝트 파일들이 모여있는 곳을 라이브러리라고 한다.
즉 라이브러리 필요한 오브젝트 파일들을 연결시키는 작업을 링킹(Linking)이라고 한다.
이렇게 링크 과정까지 마치면 최종적인 실행파일이 생긴다.
일단 주요 사항은 PLT & GOT이니까 PLT & GOT로 다시 집중한다.
위의 C코드를 보면 간단히 printf랑 put를 사용하고 있다.
해당 파일을 컴파일하고, gdb를 통해 확인해본다.
위를 보면 printf와 puts의 call하는 부분을 확인 할 수 있다.
자세히 보면 printf puts가 아니라 printf@plt, puts@plt라고 적혀있는 것을 알 수 있다.
일단 printf와 puts에 들어간 문자들이 저장된 곳이 각각 0x8048510, 0x8048510다.
결국 printf와 puts가 호출한 게 같다.
PLT을 붙은 이유를 알아보겠다. 일단 printf@plt의 위치에 브레이크 포인트를 걸고 call하는 걸 확인해보겠다.
printf@plt의 위치로 이동해보겠다.
마지막 어셈블리어의 jmp 0x8048300으로 이동해본다.
계속 들어가면 _dI_runtime_resolve로 이동하는 것을 알 수 있고 중간에 _dI_fixup으로 이동하는 것을 볼 수 있다.
_dI_fixup으로 들어가 보면, _dI_lookup_symbol_x에 들어가는 것을 볼 수 있다.
쭉 실행해보면 다시 _dI_fixup으로 이동한다. 중간에 다른 곳으로 호출되는 부분이 있긴 하지만
돌아가지는 않는다.
_dI_fixup에서 나오면 __printf로 이동하게 된다.
또한 _x86.get_pc_thunk.bx에 들어간 후 다시 나오게 된다. 또 _IO_vfprintf_internal에 들어간 후 다시 돌아온다.
_printf에서 RET를 통해 빠져나오면서 main으로 돌아온다. 그리고 printf에 넣었던 인자인 PLT & GOT이 출력된다.
또한 puts@PLT 또한 printf@PLT와 똑같이 수행한다.
그럼 printf를 계속해서 사용한다면 계속해서 과정을 반복할까?
소스 코드를 조금 바꿔서 확인해본다.
printf만 사용해서 출력해봤다. gdb로 분석해보겠다.
1번째 printf는 여러군데 들어갔다가 main으로 돌아온다.
2번째 printf는 아래와 같다.
동일하게 printf@plt로 이동했다. jmp을 통해 0x804a00c로 이동하고 있다.
위치에 있는 값을 확인해본다.
해당 주소로 점프를 하는데 이동해본다.
해당 위치로 이동한 결과 _printf로 이동하게 되었다.
또한 Ret을 통해 다시 main으로 돌아오게 된다.
중간의 _dI_runtime_resolve와 _dI_looup_symbol_x를 거치지 않는다.
즉 printf@PLT의 실행 주소는 아래와 같다.
즉
1.PLT에서 GOT로 이동한다.
2.처음 실행되는 함수는 _dI_runtime_resolve와 _dI_lookup_symbol_x에 들어가고
GOT에 함수의 주소를 저장한다.
3.두 번째 실행되는 함수는 바로 GOT로 넘어간다.
출처. https://www.youtube.com/watch?v=kUk5pw4w0h4&list=PLhixgUqwRTjxglIswKp9mpkfPNfHkzyeN&index=19
'#Tip' 카테고리의 다른 글
The heap 2. (0) | 2018.04.07 |
---|---|
The heap. (0) | 2018.04.07 |
Function Prologue & Epilogue. (0) | 2018.04.06 |
Gdb basic command. (0) | 2018.04.05 |
Windows에서 VMware의 Ubuntu에 SSH로 접속. (0) | 2018.03.31 |