Post

PLT, GOT

\*(func@plt + 2) == func@got다.

PLT : Procedure Linkage Table
GOT : Global Offset Table

같은 file 내의 procedure라면 PLT가 따로 필요 없지만, 외부 library func를 호출할 때 PLT가 필요하다. linker가 PLT를 보고 func를 찾게 되기 때문. windows의 IAT와 비슷하다고 생각하면 된다. PLT는 GOT를 참조하여 해당 주소로 점프한다.

dynamic linking

함수를 호출할 때는 항상 PLT를 이용한다.

* static 옵션으로 컴파일하면 바로 libc func 위치로 call하게 된다.

system함수를 disassemble 해보면 PLT 영역의 system함수 항목이 출력된다. 해당 항목의 첫부분, jmp *addr에서 addr이 어떤 값을 저장하고 있는지 출력해 보면 해당 주소는 GOT 영역의 주소이고, PLT 바로 다음 instruction 주소를 저장하고 있다는 것을 알 수 있다. 그리고 포인터를 사용했기 때문에, GOT 영역으로 jmp 하는게 아니라 PLT 다음 instruction으로 jmp 하게 된다. 즉 단순히 다음 instruction으로 넘어간다.

first call
PLT -> ( GOT ) -> PLT + 1instruction

그리고 push 0x08 한 후, jmp addr2 하게 되는데, addr2는 PLT 영역의 시작 주소를 가리킨다. <_init+48>

이 jmp를 따라 이동한 PLT영역의 시작 주소에서 push한 0x08을 인자로 받으며 _dl_runtime_resolve 함수가 호출된다.

_dl_runtime_resolve는 넘겨받은 값에 대응되는 함수의 실제 주소를 구한 후 GOT에 저장하고, 해당 함수로 jmp한다.

즉, reference를 resolve해준다.

GOT영역 ( 0x8049478 )에 저장된 값이 실제 주소로 변경되므로 2번째 호출 부터는 실제 주소로 바로 jmp *GOT 할 수 있다.

after first call
PLT -> ( GOT ) -> func real addr

system 함수의 주소를 출력해보면, PLT 주소가 출력됨을 알 수 있다.

프로세스를 실행시키면 dynamic linking이 진행된다.

일단 print로 출력되는 주소가 mapping된 library상의 system 함수 주소로 변경됐다.

system함수를 disassemble 해보면 PLT가 아니라 실제 system 함수의 assembly가 출력된다.

이런식으로 프로그램 run 시킨 다음에는 print나 disas 명령어로 출력되는 libc 주소 및 코드가 GOT의 실제 주소, 코드로 변경되지만, jmp *PLTaddr 로 메모리를 조사해 보면 PLT가 변경되지는 않은 상태다.

call instruction을 수행하면서 PLT에 GOT가 들어가게 된다.

static linking했다면 run하지 않아도 프로그램 상의 static library를 호출하게 되고 disas도 libc func의 코드를 출력하게 되지만 GOTaddr을 얻을 수는 없다.

resolution detail은 이런식

출처 : http://shayete.tistory.com/entry/6-Return-Oriented-Programming

참고

2016/12/09 - [System/LINUX & UNIX] - GOT( Global Offset Table ) Layout과 link_map structure

This post is licensed under CC BY 4.0 by the author.