Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

psj2867

PLT와 GOT의 간단한 동작 구조 본문

기타

PLT와 GOT의 간단한 동작 구조

psj2867 2023. 3. 28. 17:24

이상한 일이기는 하지만 취미나 흥미로 printf등 외부 함수를 따라가다 printf 가 아닌 plt 를 마주하게 되었다.
gcc 를 기본값으로 컴파일을 하면 gcc 는 기본적으로 동적 링킹을 우선적으로 채택하여 컴파일한다.

이때 사용되는 것이 plt 이다.
그래서 plt가 뭐지 검색을 하면 다음과 같은 정의가 나온다.

동적 링크 및 got, plt 에 관한 제대로 된 설명은 아래 참고
https://bpsecblog.wordpress.com/2016/03/07/about_got_plt_1/

PLT(Procedure Linkage Table): 외부 라이브러리 함수를 사용할 수 있도록 주소를 연결해 주는 테이블.
GOT(Global Offset Table): PLT에서 호출하는 resolve() 함수를 통해 구한 라이브러리 함수의 절대 주소가 저장되어 있는 테이블.

그리고 당연히 읽고도 이해가 되지 않았고 일단 무작정 어셈블리어를 따라가기 시작했다.
내부 구조가 간단하지는 않고 어셈블리어만 따라가다 보니 처음에는 어려웠다.
그리고 저 정의가 이해하고 보니 알겠지만 정의 때문에 더 헷갈렸다.

 

// 테스트 코드
#include <stdio.h>
int main(){        
    printf("%d12\n\n",1);
    puts("asdf");
    putchar('c');
}


plt - 테이블은 맞다. 다만 일종의 래핑 함수들이 들어있는 모음이라고 생각하는 것이 더 쉽다.
그저 아래와 같은 컴파일 시간에 생성된 변경되지 않는 함수(코드) 구역이다. 또는 (키(idx), 함수) 테이블

예)
   0x401030 <putchar@plt>:      jmp    QWORD PTR [rip+0x2fe2]        # 0x404018 <putchar@got.plt>
   0x401036 <putchar@plt+6>:    push   0x0
   0x40103b <putchar@plt+11>:   jmp    0x401020
   0x401040 <puts@plt>: jmp    QWORD PTR [rip+0x2fda]        # 0x404020 <puts@got.plt>
   0x401046 <puts@plt+6>:       push   0x1
   0x40104b <puts@plt+11>:      jmp    0x401020
   0x401050 <printf@plt>:       jmp    QWORD PTR [rip+0x2fd2]        # 0x404028 <printf@got.plt>
   0x401056 <printf@plt+6>:     push   0x2
   0x40105b <printf@plt+11>:    jmp    0x401020


got - 흔히 생각하는 테이블이다. 실제 함수 위치가 들어있는 테이블이다. 초기값으로 plt+6(plt+0 다음 instruction) 위치가 들어있다.
실제로 아래와 같은 위치가 들어있다. 또는 (키(idx), 함수 위치) 테이블

예)
  0x404018 <putchar@got.plt>:     0x0000000000401036      0x0000000000401046 # putchar, puts
  0x404028 <printf@got.plt>:      0x0000000000401056      0x0000000000000000 #printf, null


처음에는 plt가 위치가 들어있는 테이블이고 동적 링크로 나중에 메모리 값만 변경하면 되지 않나 라는 생각을 했지만 그런 간단한 일은 아니였다.
1. printf@plt 는 함수이다. printf@plt 는 호출 할 수 있는 read-only 코드 영역의 값이고 이 값을 변경할 수는 없다.
2. 그럼 실제 주소 값은 다른 data 영역에 있어야 한다. 그게 got 이다.

그래서 동작 구조는
1. printf@plt를 호출 plt+0에는 jump *(got+idx) 로 이동하는데 *(got+idx)의 초기 값은 printf@plt+6이다.
2. printf@plt+6 으로 돌아오면 내부에서 쓸 컴파일 시간에 결정된 idx 값을 스택에 넣어두고 printf@plt+11의 dl_runtime_resolve 관련 함수로 jump 한다.
3. 함수를 resolve 하여 got+idx 에 printf 함수 위치를 넣어두고 실행시킨다.
4. 다음 printf@plt를 호출하면 jump *(got+idx) 로 이동하는데  *(got+idx)에 printf의 값이 들어있다.

 

예)
(gdb) break printf@plt
(gdb) break putchar@plt
(gdb) run

(gdb) disassemble 

Dump of assembler code for function printf@plt:
=> 0x0000000000401030 <+0>:     jmp    QWORD PTR [rip+0x2fe2]        # 0x404028 <printf@got.plt>
   0x0000000000401036 <+6>:     push   0x0
   0x000000000040103b <+11>:    jmp    0x401020

(gdb) x/4xg $rip+0x2fe2

0x404018 <putchar@got.plt>:     0x0000000000401036      0x0000000000401046
0x404028 <printf@got.plt>:      0x0000000000401056      0x0000000000000000

## printf 와 puts 실행 후 putchar 처음 실행 중
(gdb) continue

(gdb) disassemble 

Dump of assembler code for function putchar@plt:
=> 0x0000000000401030 <+0>:     jmp    QWORD PTR [rip+0x2ff2]        # 0x404018 <putchar@got.plt>
   0x0000000000401036 <+6>:     push   0x0
   0x000000000040103b <+11>:    jmp    0x401020

(gdb) x/4xg $rip+0x2fe2

0x404018 <putchar@got.plt>:     0x0000000000401036      0x00007ffaa96b15f0 #putchar, puts
0x404028 <printf@got.plt>:      0x00007ffaa9691cf0      0x0000000000000000


plt 가 테이블이라는 단어를 사용해서 오히려 한참을 헤맸다.
흔히 생각하는 테이블은 (키, 데이터) 테이블이여서 plt에 바로 위치가 있으면 안 되나 라는 생각에 헤매었다.

 

'기타' 카테고리의 다른 글

punycode encoding/decoding 알고리즘(bootstring)  (0) 2023.05.09
iptables 정리  (0) 2023.04.18
hadoop mapreduce 정리  (0) 2023.02.16
elastic search 정리  (0) 2023.01.27
bash shell script 정리  (0) 2023.01.20
Comments