[Dreamhack-system] Background: Library - Static Link vs. Dynamic Link

박정원·2023년 4월 27일

보안공부

목록 보기
21/43

1. 서론

  • 많은 정보가 저장되고, 처리되는 컴퓨터 시스템에도 현실 세계의 도서관과 비슷한 라이브러리(Library;도서관)라는 개념이 있음
  • 실제 도서관과의 유사점과 차이점에 주목하며 컴퓨터 시스템의 라이브러리가 무엇인지, 어떻게 사용되고, 시스템 해킹의 관점에서 라이브러리가 갖는 중요성은 무엇인지 알아보자.

2. 라이브러리

  • 라이브러리는 컴퓨터 시스템에서, 프로그램들이 함수나, 변수를 공유해서 사용할 수 있게 함
  • C언어를 비롯하여 많은 컴파일 언어들은 자주 사용되는 함수들의 정의를 묶어서 하나의 라이브러리 파일로 만들고, 이를 여러 프로그램이 공유해서 사용할 수 있도록 지원하고 있음
  • 또한, 각 언어에서 범용적으로 많이 사용되는 함수들은 표준 라이브러리가 제작되어 있어서 개발자들은 쉽게 해당 함수들을 사용할 수 있음
__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
   ...
- /lib/x86_64-linux-gnu/libc-2.27.so에 정의된 printf

3. 링크

링크

  • 링크는 많은 프로그래밍 언어에서 컴파일의 마지막 단계로 알려져 있으며, 프로그램에서 어떤 라이브러리의 함수를 사용한다면, 호출된 함수와 실제 라이브러리의 함수가 링크 과정에서 연결됨

    #include <stdio.h>
    int main() {
      puts("Hello, world!");
      return 0;
    }

    다음과 같은 코드를 오브젝트 파일로 변환한 후에 다음 명령어를 실행해본다.

    $ readelf -s hello-world.o | grep puts
        11: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND puts

    puts의 선언이 stdio.h에 있어서 심볼로는 기록되어 있지만, 심볼에 대한 자세한 내용은 하나도 기록되어 있지 않음 -> 심볼과 관련된 정보를 찾아서 최종 실행 파일에 기록하는 것이 링크 과정에서 하는 일 중 하나임

  • 예제를 완전히 컴파일하고 다음 명령어를 통해 링크되기 전과 비교해보면, libc에서 puts의 정의를 찾아 연결한 것을 확인할 수 있음

    $ gcc -o hello-world hello-world.c
    $ readelf -s hello-world | grep puts
        2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND  puts@GLIBC_2.2.5 (2)
       46: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND  puts@@GLIBC_2.2.5
    $ ldd hello-world
           linux-vdso.so.1 (0x00007ffec3995000)
           libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fee37831000)
           /lib64/ld-linux-x86-64.so.2 (0x00007fee37e24000)

    여기서 libc를 같이 컴파일하지 않았음에도 libc에서 해당 심볼을 탐색한 것은, libc가 있는 /lib/x86_64-linux-gnu/가 표준 라이브러리 경로에 포함되어 있기 때문임, gcc는 소스 코드를 컴파일할 때 표준 라이브러리의 라이브러리 파일들을 모두 탐색함

라이브러리와 링크의 종류

  • 라이브러리는 크게 동적 라이브러리와 정적 라이브러리로 구분되며, 동적 라이브러리를 링크하는 것을 동적 링크(Dynamic Link), 정적 라이브러리를 링크하는 것을 정적 링크(Static Link)라고 부름

동적 링크

  • 동적 링크된 바이너리를 실행하면 동적 라이브러리가 프로세스의 메모리에 매핑되고, 실행 중에 라이브러리의 함수를 호출하면 매핑된 라이브러리에서 호출할 함수의 주소를 찾고, 그 함수를 실행함

정적 링크

  • 정적 링크를 하면 바이너리에 정적 라이브러리의 필요한 모든 함수가 포함됨 -> 따라서 해당 함수를 호출할 때, 라이브러리를 참조하는 것이 아니라, 자신이ㅡ 함수를 호출하는 것처럼 호출할 수 있음

동적 링크 vs. 정적 링크

  • 앞의 hello-world.c를 정적 컴파일하여 static을, 동적 컴파일하여 dynamic을 생성해본다.
    $gcc -o static hello-world.c -static
    $gcc -o dynamic hello-world.c -no-pie

    용량

    각각의 용량을 ls로 비교해보면 static이 dynamic보다 100배 가까이 더 많은 용량을 차지하는 것을 확인할 수 있음
    $ ls -lh ./static ./dynamic
    -rwxr-xr-x 1 dreamhack dreamhack 8.2K Nov  9 10:24 dynamic
    -rwxr-xr-x 1 dreamhack dreamhack 825K Nov  9 10:23 static

    호출 방법

    static에서는 puts가 있는 0x410230을 직접 호출하는 반면, dynamic에서는 puts의 plt주소인 0ㅌ4003f0을 호출함

4. PLT와 GOT

  • PLT(Procedure Linkage Table)와 GOT(Global Offset Table)는 라이브러리에서 동적 링크된 심볼의 주소를 찾을 때 사용하는 테이블임
  • 바이너리가 실행되면 ASLR에 의해 라이브러리가 임의의 주소에 매핑되는데, 이 상태에서 라이브러리 함수를 호출하면, 함수의 이름을 바탕으로 라이브러리에서 심볼들을 탐색하고, 해당 함수의 정의를 발견하면 그 주소로 실행 흐름을 옮기게 됨 -> 이 전 과정을 통틀어 runtime resolve라고 함
  • 그런데 반복적으로 호출되는 함수의 정의를 매번 탐색해야 한다면 비효율적일 것임 -> 그래서 ELF는 GOT라는 테이블을 두고, resolve된 함수의 주소를 해당 테이블에 저장함
profile
보안, 프런트엔드 공부 중!

0개의 댓글