what is debugging symbols & common symbols

문득 공부를 하다가 다음과 같은 의문이 생겼다.

glibc는

C
(pwn) amemoyoi ~$ file /lib/x86_64-linux-gnu/libc.so.6
/lib/x86_64-linux-gnu/libc.so.6: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=42c84c92e6f98126b3e2230ebfdead22c235b667, for GNU/Linux 3.2.0, stripped

다음과 같이 stripped 되어 있는데 왜 심볼들이 다 살아있지? 내가 아는 심볼의 정의가 틀렸던 것인가? 라는 생각이 들었다.

그래서 한번 정리 해보았다.

일단 우리가 일반적으로 바이너리를 컴파일해보자

C
(pwn) amemoyoi ~$ cat test.c
#include <stdio.h>

int global_var1 = 250;

int main(){
        int var_1 = 10;
	int var_2 = 50;
        
   	int input_var = 0;
	scanf("%d" , &input_var);

	if ( input_var == 1 )
		printf("%d!!! " , var_1 );
	else if (input_var == 2)
		 printf("%d!!! " , var_2 );
	else 
		 printf("%d!!! " , global_var1);

	printf("hello world!\n");

	return 0;

}
(pwn) amemoyoi ~$ gcc -o test test.c
(pwn) amemoyoi ~$ file test
test: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fa108236c1857fdbcd5bc8c61f68839c92f3ebad, for GNU/Linux 3.2.0, not stripped
(pwn) amemoyoi ~$ 

일반적으로 컴파일을 하면 다음과 같이 not stripped 라고 나온다.

C
(pwn) amemoyoi ~$ objdump -t ./test

./test:     file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l    df *ABS*	0000000000000000              Scrt1.o
000000000000038c l     O .note.ABI-tag	0000000000000020              __abi_tag
0000000000000000 l    df *ABS*	0000000000000000              crtstuff.c
00000000000010f0 l     F .text	0000000000000000              deregister_tm_clones
0000000000001120 l     F .text	0000000000000000              register_tm_clones
0000000000001160 l     F .text	0000000000000000              __do_global_dtors_aux
0000000000004014 l     O .bss	0000000000000001              completed.0
0000000000003da8 l     O .fini_array	0000000000000000              __do_global_dtors_aux_fini_array_entry
00000000000011a0 l     F .text	0000000000000000              frame_dummy
0000000000003da0 l     O .init_array	0000000000000000              __frame_dummy_init_array_entry
0000000000000000 l    df *ABS*	0000000000000000              test.c
0000000000000000 l    df *ABS*	0000000000000000              crtstuff.c
00000000000020f8 l     O .eh_frame	0000000000000000              __FRAME_END__
0000000000000000 l    df *ABS*	0000000000000000              
0000000000003db0 l     O .dynamic	0000000000000000              _DYNAMIC
000000000000201c l       .eh_frame_hdr	0000000000000000              __GNU_EH_FRAME_HDR
0000000000003fa0 l     O .got	0000000000000000              _GLOBAL_OFFSET_TABLE_
0000000000000000       F *UND*	0000000000000000              __libc_start_main@GLIBC_2.34
0000000000000000  w      *UND*	0000000000000000              _ITM_deregisterTMCloneTable
0000000000004000  w      .data	0000000000000000              data_start
0000000000000000       F *UND*	0000000000000000              puts@GLIBC_2.2.5
0000000000004014 g       .data	0000000000000000              _edata
0000000000001280 g     F .fini	0000000000000000              .hidden _fini
0000000000000000       F *UND*	0000000000000000              __stack_chk_fail@GLIBC_2.4
0000000000000000       F *UND*	0000000000000000              printf@GLIBC_2.2.5
0000000000004000 g       .data	0000000000000000              __data_start
0000000000000000  w      *UND*	0000000000000000              __gmon_start__
0000000000004008 g     O .data	0000000000000000              .hidden __dso_handle
0000000000002000 g     O .rodata	0000000000000004              _IO_stdin_used
0000000000004018 g       .bss	0000000000000000              _end
00000000000010c0 g     F .text	0000000000000026              _start
0000000000004014 g       .bss	0000000000000000              __bss_start
00000000000011a9 g     F .text	00000000000000d7              main
0000000000004010 g     O .data	0000000000000004              global_var1
0000000000000000       F *UND*	0000000000000000              __isoc99_scanf@GLIBC_2.7
0000000000004018 g     O .data	0000000000000000              .hidden __TMC_END__
0000000000000000  w      *UND*	0000000000000000              _ITM_registerTMCloneTable
0000000000000000  w    F *UND*	0000000000000000              __cxa_finalize@GLIBC_2.2.5
0000000000001000 g     F .init	0000000000000000              .hidden _init

하지만 다음과 같이 main 같은 심볼이 보이는 것을 알 수 있다. 심볼의 종류는 2가지로 나뉜다.

C
(pwn) amemoyoi ~$ readelf -S ./test
There are 31 section headers, starting at offset 0x3748:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000338  00000338
       0000000000000030  0000000000000000   A       0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000368  00000368
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000038c  0000038c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003b0  000003b0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
       00000000000000f0  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           00000000000004c8  000004c8
       00000000000000c8  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000000590  00000590
       0000000000000014  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          00000000000005a8  000005a8
       0000000000000050  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             00000000000005f8  000005f8
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000006b8  000006b8
       0000000000000060  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000050  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001070  00001070
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001080  00001080
       0000000000000040  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         00000000000010c0  000010c0
       00000000000001c0  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         0000000000001280  00001280
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       000000000000001b  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         000000000000201c  0000201c
       0000000000000034  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002050  00002050
       00000000000000ac  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003da0  00002da0
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003da8  00002da8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003db0  00002db0
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fa0  00002fa0
       0000000000000060  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000014  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004014  00003014
       0000000000000004  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003014
       000000000000002b  0000000000000001  MS       0     0     1
  [28] .symtab           SYMTAB           0000000000000000  00003040
       00000000000003c0  0000000000000018          29    18     8
  [29] .strtab           STRTAB           0000000000000000  00003400
       000000000000022d  0000000000000000           0     0     1
  [30] .shstrtab         STRTAB           0000000000000000  0000362d
       000000000000011a  0000000000000000           0     0     1

다음과 같이 섹션을 확인했을때

C
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
       00000000000000f0  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           00000000000004c8  000004c8
       00000000000000c8  0000000000000000   A       0     0     1

여기에 위치하는 재배치를 위한 심볼들과

C
  [28] .symtab           SYMTAB           0000000000000000  00003040
       00000000000003c0  0000000000000018          29    18     8
  [29] .strtab           STRTAB           0000000000000000  00003400
       000000000000022d  0000000000000000           0     0     1
 

여기에 위치하는 디버깅이나 리버싱을 위해 존재하는 심볼들이다.

.dynsym 에 대한 심볼들은 필수이다. 이 심볼들은 런타임과 로드가 될때 로더에 의해 재배치가 된다. 즉, 외부 라이브러리나 외부 파일에 대한 심볼들이다.

그에 반해 .symtab 여기에 존재하는 심볼들은 필수가 아니다. 이미 내부에 있는 함수들은 컴파일 시간에 위치가 전부 정해진다. 따라서, rip를 따라가는 특성을 가진 컴퓨터 입장에서는 심볼은 아무의미를 가지지 않는다. 즉, 컴퓨터 입장에서는 call “printf” 가 아닌 call 0x1e0(%rip)와 같은 철저히 이미 컴파일 시간에 계산된 rip를 따라갈 뿐이다. 따라서 굳이 필요없다.

그래서 내가 여기서 strip 명령어로 strip을 하면 저 .symtab 섹션을 지워버린다

C
(pwn) amemoyoi ~$ strip test
(pwn) amemoyoi ~$ readelf -S ./test
There are 29 section headers, starting at offset 0x3150:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000318  00000318
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.gnu.pr[...] NOTE             0000000000000338  00000338
       0000000000000030  0000000000000000   A       0     0     8
  [ 3] .note.gnu.bu[...] NOTE             0000000000000368  00000368
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .note.ABI-tag     NOTE             000000000000038c  0000038c
       0000000000000020  0000000000000000   A       0     0     4
  [ 5] .gnu.hash         GNU_HASH         00000000000003b0  000003b0
       0000000000000024  0000000000000000   A       6     0     8
  [ 6] .dynsym           DYNSYM           00000000000003d8  000003d8
       00000000000000f0  0000000000000018   A       7     1     8
  [ 7] .dynstr           STRTAB           00000000000004c8  000004c8
       00000000000000c8  0000000000000000   A       0     0     1
  [ 8] .gnu.version      VERSYM           0000000000000590  00000590
       0000000000000014  0000000000000002   A       6     0     2
  [ 9] .gnu.version_r    VERNEED          00000000000005a8  000005a8
       0000000000000050  0000000000000000   A       7     1     8
  [10] .rela.dyn         RELA             00000000000005f8  000005f8
       00000000000000c0  0000000000000018   A       6     0     8
  [11] .rela.plt         RELA             00000000000006b8  000006b8
       0000000000000060  0000000000000018  AI       6    24     8
  [12] .init             PROGBITS         0000000000001000  00001000
       000000000000001b  0000000000000000  AX       0     0     4
  [13] .plt              PROGBITS         0000000000001020  00001020
       0000000000000050  0000000000000010  AX       0     0     16
  [14] .plt.got          PROGBITS         0000000000001070  00001070
       0000000000000010  0000000000000010  AX       0     0     16
  [15] .plt.sec          PROGBITS         0000000000001080  00001080
       0000000000000040  0000000000000010  AX       0     0     16
  [16] .text             PROGBITS         00000000000010c0  000010c0
       00000000000001c0  0000000000000000  AX       0     0     16
  [17] .fini             PROGBITS         0000000000001280  00001280
       000000000000000d  0000000000000000  AX       0     0     4
  [18] .rodata           PROGBITS         0000000000002000  00002000
       000000000000001b  0000000000000000   A       0     0     4
  [19] .eh_frame_hdr     PROGBITS         000000000000201c  0000201c
       0000000000000034  0000000000000000   A       0     0     4
  [20] .eh_frame         PROGBITS         0000000000002050  00002050
       00000000000000ac  0000000000000000   A       0     0     8
  [21] .init_array       INIT_ARRAY       0000000000003da0  00002da0
       0000000000000008  0000000000000008  WA       0     0     8
  [22] .fini_array       FINI_ARRAY       0000000000003da8  00002da8
       0000000000000008  0000000000000008  WA       0     0     8
  [23] .dynamic          DYNAMIC          0000000000003db0  00002db0
       00000000000001f0  0000000000000010  WA       7     0     8
  [24] .got              PROGBITS         0000000000003fa0  00002fa0
       0000000000000060  0000000000000008  WA       0     0     8
  [25] .data             PROGBITS         0000000000004000  00003000
       0000000000000014  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           0000000000004014  00003014
       0000000000000004  0000000000000000  WA       0     0     1
  [27] .comment          PROGBITS         0000000000000000  00003014
       000000000000002b  0000000000000001  MS       0     0     1
  [28] .shstrtab         STRTAB           0000000000000000  0000303f
       000000000000010a  0000000000000000           0     0     1

다음과 같이 지워진다.

C
(pwn) amemoyoi ~$ file test
test: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fa108236c1857fdbcd5bc8c61f68839c92f3ebad, for GNU/Linux 3.2.0, stripped

그리고 stripped 이 된것을 알수 있다.

그럼 처음에 궁금했던 것을 해소해보자

libc는 당연히 .symtab 은 존재하지 않는다. stripped을 굳이 안할이유가 없기 때문이다.

C
(pwn) amemoyoi ~$ objdump -t /lib/x86_64-linux-gnu/libc.so.6

/lib/x86_64-linux-gnu/libc.so.6:     file format elf64-x86-64

SYMBOL TABLE:
no symbols

따라서 확인해보면 일반 심볼은 존재하지 않는다.

대신, 라이브러리 특성상 외부에서 자신의 함수를 알릴 이정표를 가지고 있어야 한다. 이를 .dynsym 에 넣어서 마치 api와 같은 역할을 한다. 이러한 재배치 심볼들을 우리는 읽고 사용한다. pwntools에서도 이 값을 읽고 사용한다.