Fascination
article thumbnail

Background: Library - Static Link vs. Dynamic Link


 

# 라이브러리

  • 라이브러리는 컴퓨터 시스템에서, 프로그램들이 함수나, 변수를 공유해서 사용할 수 있게 함
  • 대개의 프로그램은 서로 공통으로 사용하는 함수들이 많음
  • ex) printf, scanf, strlen, memcpy, malloc 등 많은 C 프로그래머들이 코드를 작성하면서 사용하는 함수
  • C언어를 비롯하여 많은 컴파일 언어들은 자주 사용되는 함수들의 정의를 묶어서 하나의 라이브러리 파일로 만들고, 이를 여러 프로그램이 공유해서 사용할 수 있도록 지원하고 있음
  • 라이브러리를 사용하면 같은 함수를 반복적으로 정의해야하는 수고를 덜 수 있어 코드 개발의 효율이 높아짐
  • 각 언어에서 범용적으로 많이 사용되는 함수들은 표준 라이브러리가 제작되어 있어서 개발자들은 쉽게 해당 함수들을 사용할 수 있음
  • C의 표준 라이브러리인 libc는 우분투에 기본으로 탑재된 라이브러리이며, 실습환경에서는 /lib/x86_64-linux-gnu/libc-2.27.so에 있음
  • ex) printflibc에 이미 정의되어 있음
  • Figure 1. /lib/x86_64-linux-gnu/libc-2.27.so에 정의된 printf
__printf:
   0x0000000000064f00 <+0>:     sub    rsp,0xd8
   0x0000000000064f07 <+7>:     test   al,al
   0x0000000000064f09 <+9>:     mov    QWORD PTR [rsp+0x28],rsi
   0x0000000000064f0e <+14>:    mov    QWORD PTR [rsp+0x30],rdx
   0x0000000000064f13 <+19>:    mov    QWORD PTR [rsp+0x38],rcx
   0x0000000000064f18 <+24>:    mov    QWORD PTR [rsp+0x40],r8
   0x0000000000064f1d <+29>:    mov    QWORD PTR [rsp+0x48],r9
   0x0000000000064f22 <+34>:    je     0x64f5b <__printf+91>
   0x0000000000064f24 <+36>:    movaps XMMWORD PTR [rsp+0x50],xmm0
   0x0000000000064f29 <+41>:    movaps XMMWORD PTR [rsp+0x60],xmm1
   0x0000000000064f2e <+46>:    movaps XMMWORD PTR [rsp+0x70],xmm2
   0x0000000000064f33 <+51>:    movaps XMMWORD PTR [rsp+0x80],xmm3
   0x0000000000064f3b <+59>:    movaps XMMWORD PTR [rsp+0x90],xmm4
   0x0000000000064f43 <+67>:    movaps XMMWORD PTR [rsp+0xa0],xmm5
   0x0000000000064f4b <+75>:    movaps XMMWORD PTR [rsp+0xb0],xmm6
   0x0000000000064f53 <+83>:    movaps XMMWORD PTR [rsp+0xc0],xmm7
   0x0000000000064f5b <+91>:    mov    rax,QWORD PTR fs:0x28
   0x0000000000064f64 <+100>:   mov    QWORD PTR [rsp+0x18],rax
   ...

 

 

# 링크

1. 링크

  • 링크(Link): 컴파일의 마지막 단계
  • 프로그램에서 어떤 라이브러리의 함수를 사용한다면, 호출된 함수와 실제 라이브러리의 함수가 링크 과정에서 연결됨
  • Figure 2. hello-world
// Name: hello-world.c
// Compile: gcc -o hello-world hello-world.c
#include <stdio.h>
int main() {
  puts("Hello, world!");
  return 0;
}
// Path: /usr/include/stdio.h
...
/* Write a string, followed by a newline, to stdout.
   This function is a possible cancellation point and therefore not
   marked with __THROW.  */
extern int puts (const char *__s);
...
  • 리눅스에서 C 소스 코드는 전처리, 컴파일, 어셈블 과정을 거쳐 ELF 형식을 갖춘 오브젝트 파일(Object file)로 번역됨
  • 다음 명령어로 hello-world.c를 어셈블할 수 있음
$ gcc -c hello-world.c -o hello-world.o
  • 오브젝트 파일은 실행 가능한 형식을 갖추고 있지만, 라이브러리 함수들의 정의가 어디 있는지 알지 못하므로 실행은 불가능함
  • 다음 명령어를 실행해보면, puts의 선언이 stdio.h에 있어서 심볼(symbol)로는 기록되어 있지만, 심볼에 대한 자세한 내용은 하나도 기록되어 있지 않음
    • 심볼과 관련된 정보들을 찾아서 최종 실행 파일에 기록하는 것이 링크 과정에서 하는 일 중 하나임
$ readelf -s hello-world.o | grep puts
    11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts
  • 예제를 완전히 컴파일하고 다음 명령어를 통해 링크되기 전과 비교해볼 것
    • libc에서 puts의 정의를 찾아 연결한 것을 확인할 수 있음

  • libc를 같이 컴파일하지 않았음에도 libc에서 해당 심볼을 탐색한 것은, libc가 있는 /lib/x86-64-linux-gnu/ 가 표준 라이브러리 경로에 포함되어 있기 때문임
  • gcc는 소스 코드를 컴파일 할 때 표준 라이브러리의 라이브러리 파일들을 모두 탐색함
  • Figure 3. 표준 라이브러리 경로
    • 해당 명령어로 표준 라이브러리의 경로를 확인할 수 있음
    • 링크를 거치고나면 프로그램에서 puts를 호출할 때, puts의 정의가 있는 libc에서 puts의 코드를 찾고, 해당 코드를 실행하게 됨
$ ld --verbose | grep SEARCH_DIR | tr -s ' ;' '\n'
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu")
SEARCH_DIR("=/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu")
SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64")
SEARCH_DIR("=/usr/local/lib64")
SEARCH_DIR("=/lib64")
SEARCH_DIR("=/usr/lib64")
SEARCH_DIR("=/usr/local/lib")
SEARCH_DIR("=/lib")
SEARCH_DIR("=/usr/lib")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64")
SEARCH_DIR("=/usr/x86_64-linux-gnu/lib")

 

2. 라이브러리와 링크의 종류

  • 라이브러리는 크게 동적 라이브러리와 정적 라이브러리로 구분됨
  • 동적 링크(Dynamic Link)
    • 동적 라이브러리를 링크하는 것 
    • 동적 링크된 바이너리를 실행하면 동적 라이브러리가 프로세스의 메모리에 매핑됨
    • 실행 중에 라이브러리의 함수를 호출하면 매핑된 라이브러리에서 호출할 함수의 주소를 찾고, 그 함수를 실행함
  • 정적 링크(Static Link)
    • 정적 라이브러리를 링크하는 것
    • 바이너리에 정적 라이브러리의 모든 함수가 포함됨
    • 해당 함수를 호출할 때, 라이브러리를 참조하는 것이 아니라, 자심의 함수를 호출하는 것처럼 호출할 수 있음
    • 라이브러리에서 원하는 함수를 찾지 않아도 되니 탐색의 비용은 절감되는 듯하지만, 여러 바이너리에서 라이브러리를 사용하면 그 라이브러리의 복제가 여러 번 이루어지게 되므로 용량을 낭비하게 됨

 

3. 동적 링크 vs. 정적 링크

  • 먼저 앞의 hello-world.c를 정적 컴파일하여 static을, 동적 컴파일하여 dynamic을 생성함

  • 용량
    • 각각의 용량을 ls로 비교해보면 staticdynamic보다 100배 가까이 더 많은 용량을 차지하는 것을 확인할 수 있음

  • 호출 방법
    • static에서는 puts가 있는 0x410120을 직접 호출
    • 반면, dynamic에서는 putsplt 주소인 0x4003f0을 호출
    • 이러한 차이는 동적 링크된 바이너리는 함수의 주소를 라이브러리에서 찾아야하기 때문이며 이 과정에서 사용되는 테이블이 plt인 것

 

 

# PLT & GOT

1. PLT와 GOT

  • PLT(Procedure Linkage Table)와 GOT(Global Offset Table)는 라이브러리에서 동적 링크된 심볼의 주소를 찾을 때 사용하는 테이블
  • 바이너리가 실행되면 ASLR에 의해 라이브러리가 임의의 주소에 매핑됨
  • 이 상태에서 라이브러리 함수를 호출하면, 함수의 이름을 바탕으로 라이브러리에서 심볼들을 탐색하고, 해당 함수의 정의를 발견하면 그 주소로 실행 흐름을 옮기게 됨 
  • 이 과정을 통틀어 runtime resolve라고 함
  • 반복적으로 호출되는 함수의 정의를 매번 탐색해야 한다면 비효율적일 것임
  • 그래서 ELF는 GOT라는 테이블을 두고, resolve된 함수의 주소를 꺼내어 사용함
  • Figure 4. got 예제 코드
// Name: got.c
// Compile: gcc -o got got.c -no-pie
// dreamhack과 다르게 주어진대로 컴파일하면 Full Relro로 설정되어 옵션을 변경해줌
#include <stdio.h>
int main() {
  puts("Resolving address of 'puts'.");
  puts("Get address from GOT");
}

 

2. resolve 되기 전

  • got.c를 컴파일하고, 실행한 직후에 GOT를 확인해보면 puts의 주소를 찾기 전이므로, 함수의 주소가 아닌 puts@plt+6라는 PLT 내부의 주소가 적혀있음

  • puts@plt를 호출하는 지점에 중단점을 설정하고, 내부로 따라 들어가볼 것
  • PLT에는 먼저 puts의 GOT인 _GLOBAL_OFFSET_TABLE+24(0x60100+0x18 = 0x601018)에 쓰인 값으로 실행흐름을 옮김

_GLOBAL_OFFSET_TABLE_의 값 확인

  • 현재 GOT에는 puts@plt+6 의 주소가 쓰여있으므로, 바로 다음 줄의 코드를 실행하게 됨

  • 코드를 조금 더 실행시키면 dl_runtime_resolve_xsavec 라는 함수가 실행되는데, 이 함수에서 puts의 주소가 구해지고, GOT에 주소가 써짐

 

3. resolve 된 후

  • 두 번째로 puts@plt를 호출할 때는 GOT에 puts의 주소가 쓰여있어 바로 puts가 실행됨

 

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

  • PLT와 GOT는 동적 링크된 바이너리에서 라이브러리 함수의 주소를 찾고, 기록할 때 사용되는 중요한 테이블임
  • 시스템 해커의 관점에서 보면 PLT에서 GOT를 참조하여 실행흐름을 옮길 때, GOT의 값을 검증하지 않는다는 보안상의 약점이 보일 것임
  • 따라서 앞의 예에서 GOT에 저장된 puts의 주소를 공격자가 임의로 변경할 수 있으면, 두 번째로 puts가 호출될 때 공격자가 원하는 코드가 실행되게 할 수 있음
  • 이 공격 기법이 가능한지 gdb를 이용하여 간단한 실험을 해볼 수 있음
    • 앞의 got 바이너리의 두 번째 puts 호출 직전에 puts의 GOT 값을 "AAAAAAAA"로 변경하고 계속 실행시키면, 실제로 "AAAAAAAA"로 실행흐름이 옮겨지는 것을 확인할 수 있음
    • 이런 공격 기법을 GOT Overwrite라고 부르며, 임의 주소에 값을 쓸 수 있을 때, RCE를 하기 위한 방법으로 사용될 수 있음

 

 

# QUIZ

profile

Fascination

@euna-319

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!