Linux Kernel(4.19) Hacks

rousalome.egloos.com

포토로그 Kernel Crash


통계 위젯 (화이트)

96258
1323
114598


[리눅스커널] 시스템 콜: 커널 공간에서 시스템 콜 테이블 확인하기 11. System Call

이전 소절에서 알아본 시스템 콜 동작은 다음과 같이 정리할 수 있습니다.

     "유저 공간에서 전달된 시스템 콜 번호로 시스템 콜 테이블에 저장된 시스템 콜 핸들러 
     함수로 분기된다."

그런데 위 문장에서 '시스템 콜 테이블' 은 리눅스 커널 어느 코드일까요?

     "시스템 콜 테이블은 sys_call_table 심볼이다."

시스템 콜 테이블인 sys_call_table 심볼에 시스템 콜 핸들러 함수 주소가 저장돼 있습니다. 
그렇다면 시스템 콜 테이블에 저장된 시스템 콜 핸들러는 어떻게 확인할 수 있을까요? T32 프로그램을 활용하면 시스템 콜 테이블을 이해하기 쉽게 볼 수 있습니다.
d.v %y.l sys_call_table
________address||value______|symbol
NSD:80107FC4| 0x8012C6F4  \\vmlinux\kernel/signal\sys_restart_syscall
NSD:80107FC8| 0x801212C0  \\vmlinux\exit\sys_exit
NSD:80107FCC| 0x8011C100  \\vmlinux\fork\sys_fork
NSD:80107FD0| 0x8026AB24  \\vmlinux\read_write\sys_read
NSD:80107FD4| 0x8026ABC4  \\vmlinux\read_write\sys_write
NSD:80107FD8| 0x80268508  \\vmlinux\open\sys_open
NSD:80107FDC| 0x80267108  \\vmlinux\open\sys_close
NSD:80107FE0| 0x8013CC38  \\vmlinux\sys_ni\compat_sys_epoll_pwait
NSD:80107FE4| 0x80268558  \\vmlinux\open\sys_creat

Trace32 프로그램으로 sys_call_table 심볼을 unsigned int 단위로 캐스팅해 볼까요? 
v.v %i %h (unsigned int[100])*&sys_call_table = (
  [0] = 2148714228 = 0x8012C6F4,  // sys_restart_syscall
  [1] = 2148668096 = 0x801212C0, // sys_exit
  [2] = 2148647168 = 0x8011C100, // sys_fork
  [3] = 2150017828 = 0x8026AB24, // sys_read
  [4] = 2150017988 = 0x8026ABC4, // sys_write
  [5] = 2150008072 = 0x80268508, // sys_open
  [6] = 2150002952 = 0x80267108, // sys_close
  [7] = 2148781112 = 0x8013CC38, // compat_sys_epoll_pwait
  [8] = 2150008152 = 0x80268558, // sys_creat
  
위 디버깅 정보에서 1,2...8과 같은 배열 인덱스가 시스템 콜 번호이고 주석문으로 표시된 함수가 시스템 콜 핸들러 함수입니다.  3번 인덱스를 보면 sys_read() 함수가 보입니다. 이 정보는 다음 사실을 말해줍니다.

     "3번 시스템 콜 핸들러는 sys_read() 함수이다."

라즈베리파이에서 시스템 콜 번호 확인하기

라즈베리파이에서 다음 해더 파일에서도 시스템 콜 번호를 지정해놨음을 확인할 수 있습니다.  
[/usr/include/arm-linux-gnueabihf/asm/unistd.h]
#define __NR_restart_syscall (__NR_SYSCALL_BASE+  0)
#define __NR_exit (__NR_SYSCALL_BASE+  1)
#define __NR_fork (__NR_SYSCALL_BASE+  2)
#define __NR_read (__NR_SYSCALL_BASE+  3)
#define __NR_write (__NR_SYSCALL_BASE+  4)
#define __NR_open (__NR_SYSCALL_BASE+  5)
#define __NR_close (__NR_SYSCALL_BASE+  6)
...
#define __NR_mlock2 (__NR_SYSCALL_BASE+390)
#define __NR_copy_file_range (__NR_SYSCALL_BASE+391)
#define __NR_preadv2 (__NR_SYSCALL_BASE+392)
#define __NR_pwritev2 (__NR_SYSCALL_BASE+393)
#define __NR_pkey_mprotect (__NR_SYSCALL_BASE+394)
#define __NR_pkey_alloc (__NR_SYSCALL_BASE+395)
#define __NR_pkey_free (__NR_SYSCALL_BASE+396)

위 코드에서 선언된 매크로는 다음 규칙으로 해석할 수 있습니다.

     "#define __NR_’시스템 콜 함수’ (__NR_SYSCALL_BASE+ ‘시스템 콜 번호’)"

그렇다면 다음 매크로는 어떻게 해석할 수 있을까요?
#define __NR_exit (__NR_SYSCALL_BASE+  1)
#define __NR_fork (__NR_SYSCALL_BASE+  2)

우선 __NR_exit 시스템 콜 정보를 확인해 볼까요? __NR_SYSCALL_BASE 매크로는 시스템 콜 베이스 주소이며 오른쪽에 있는 1이 시스템 콜 번호입니다. 이 규칙으로 __NR_fork 매크로는 2번 시스템 콜 정보를 나타냅니다. 이 정보를 모으면 다음 사실을 알 수 있습니다.

     " ‘exit 시스템 콜 번호는 1’이고 ‘fork 시스템 콜 번호는 2이다’."

각 매크로는 다음 규칙으로 선언되어 있습니다.
매크로 리눅스 저수준 
표준함수 시스템 콜 핸들러
__NR_exit exit sys_exit
__NR_fork fork sys_fork
__NR_read read sys_read
__NR_write write sys_write
__NR_function function sys_function

그렇다면 시스템 콜 함수 별로 시스템 콜 핸들러 함수는 어떻게 알 수 있을까요?

     "매크로 앞에 붙은 __NR 스트링을 sys로 바꾸면 시스템 콜 핸들러 함수 이름을 알 수 
       있다."

이 규칙으로 소스 코드를 검색하면 됩니다.

또한 각 시스템 콜 핸들러 함수 선언부는 다음 해더 파일에서 확인할 수 있습니다.
[https://github.com/raspberrypi/linux/blob/rpi-4.19.y/include/linux/syscalls.h]
asmlinkage long sys_fork(void);
asmlinkage long sys_exit(int error_code);
asmlinkage long sys_read(unsigned int fd, char __user *buf, size_t count);
asmlinkage long sys_write(unsigned int fd, const char __user *buf,
  size_t count);


참고로 함수 선언부에 asmlinkage가 보이면 어셈블리 코드에서 바로 호출(브랜치)할 수 있다는 의미입니다.


여기까지 시스템 콜 테이블에 대해서 다음 내용을 살펴봤습니다.

     "첫째, 시스템 콜 테이블은 어디서 확인할 수 있을까?"
      시스템 콜 테이블은 sys_call_table 심볼이며 시스템 콜 핸들러 함수를 저장한다.

     "둘째, 시스템 콜 번호는 어떻게 알 수 있나?"
      sys_call_table 심볼을 보면 시스템 콜 번호를 알 수 있다.

여러분, 시스템 콜 테이블인 sys_call_table 심볼에 대해 공부하고 나니 어떤 생각이 드나요? 

     "시스템 콜 동작에서 중요한 역할을 수행하는구나."

대부분 이런 느낌이 들 것 입니다. 그런데 '검은 마음'으로 리눅스 커널 코드를 분석하는 부류가 있습니다. 이를 보통 '해커(크래커)'라고 부릅니다. 이들은 sys_call_table 심볼을 보면 분명히 다음과 같은 마음을 품을 가능성이 높습니다.

     "시스템 콜 테이블인 sys_call_table 심볼을 오염시키면 리눅스 시스템을 먹통으로 
       만들 수 있겠네?"

'해커(크래커)' 들에겐 '시스템 콜 테이블인 sys_call_table 심볼' 시스템을 오염시키는 먹잇감이었습니다. 하지만 다양한 기법으로 시스템 콜 테이블 오염을 방지하고 있습니다.

다음 절에서는 유저 공간에서 리눅스 저수준 함수인 open(), read() 그리고 write() 함수를 호출하면 커널 공간에선 어떤 실행 흐름으로 시스템 콜을 처리하는지 점검합니다. 

"혹시 궁금한 점이 있으면 댓글로 질문 남겨주세요. 아는 한 성실히 답변 올려드리겠습니다!" 

Thanks,
Austin Kim(austindh.kim@gmail.com)


#Reference: 시스템 콜
시스템 콜 주요 개념 소개
유저 공간에서 시스템 콜은 어떻게 발생할까
시스템 콜 핸들러는 어떤 동작을 할까? 
시스템 콜 실행 완료 후 무슨 일을 할까?
시스템 콜 관련 함수  
시스템 콜 디버깅  



    핑백

    덧글

    댓글 입력 영역