syscall call table이 만들어지는 과정

dandb3·2023년 5월 22일
0

pwnable

목록 보기
1/17
  • system call의 동작 내부구조에 대해서 알아보자..

  • 일단 기본적인 시스템 콜 동작은 다음과 같다.

    • user mode에서 software interrupt 발생
    • kernel mode로 넘어가서 system call 실행
    • 실행 완료 후 다시 user mode로 돌아옴.
  • kernel mode에서 처리하는 과정..?

    • 일단 system call table이 존재. (/arch/x86/um/sys_call_table_64.c)

          asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
              [0 ... __NR_syscall_max] = &sys_ni_syscall,
              #include <asm/syscalls_64.h>
          };

      사실 배열 중간에 #include가 들어간다는 것이 잘 이해가 되지 않기는 하다.

    • 어쨌든 각 entry들은 sys_ni_syscall로 초기화가 되는데, 그러면 sys_ni_syscall은 무엇인가?

      	asmlinkage long sys_ni_syscall(void)
          {
              return -ENOSYS;
          }

      그냥 error상태를 반환하는데, syscall이 존재하지 않는다는 뜻임.
      기본적으로 모든 entry를 error함수로 채워놓고, 그 이후에 채운다.

    • 배열을 어떤 식으로 채울까?

      • 앞서 말했던 배열 중간에 #include <asm/syscalls_64.h>로 채우게 된다.
      • 이 헤더파일은 특수 스크립트에 의해 생성되는데, 생성시에 syscall table을 참고해서 생성하게 된다.
          	__SYSCALL_COMMON(0, sys_read, sys_read)
          	__SYSCALL_COMMON(1, sys_write, sys_write)
            __SYSCALL_COMMON(2, sys_open, sys_open)
            __SYSCALL_COMMON(3, sys_close, sys_close)
            __SYSCALL_COMMON(5, sys_newfstat, sys_newfstat)
            ...
            ...
        	...
        /arch/x86/um/sys_call_table_64.c에 아래의 내용이 define되어 있다.
        	#define __SYSCALL_COMMON(nr, sym, compat) __SYSCALL_64(nr, sym, compat)
        	#define __SYSCALL_64(nr, sym, compat) [nr] = sym,
        그러므로, 결론적으로
            asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
                [0 ... __NR_syscall_max] = &sys_ni_syscall,
                [0] = sys_read,
                [1] = sys_write,
                [2] = sys_open,
                ...
                ...
                ...
            };
        이런 꼴이 되어, 각 배열의 인덱스마다 알맞은 syscall함수가 배정되게 되고, 나머지 entry들은 예상대로 error를 리턴하는 함수가 채워짐을 알 수 있다.
    • 시스템 콜 호출하는 함수

          __visible noinstr void do_syscall_64(struct pt_regs *regs, int nr)
          {
              add_random_kstack_offset();													//커널 스택 ASLR
              nr = syscall_enter_from_user_mode(regs, nr);								//user mode -> kernel mode
      
              instrumentation_begin();
      		
              // 64비트 시스템 콜 실행 -> 실패 시 32비트 시스템 콜 실행.
              if (!do_syscall_x64(regs, nr) && !do_syscall_x32(regs, nr) && nr != -1) {
                  /* Invalid system call, but still a system call. */
                  regs->ax = __x64_sys_ni_syscall(regs);
              }
      
              instrumentation_end();
              syscall_exit_to_user_mode(regs);											//kernel mode -> user mode
          }
    • 64비트 시스템 콜 호출하는 함수

      	static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
          {
              /*
               * Convert negative numbers to very high and thus out of range
               * numbers for comparisons.
               */
              unsigned int unr = nr;
      
              if (likely(unr < NR_syscalls)) {
                  unr = array_index_nospec(unr, NR_syscalls);
                  regs->ax = sys_call_table[unr](regs);
                  return true;
              }
              return false;
          }
    • 32비트 시스템 콜 호출하는 함수

      	static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
          {
              /*
               * Adjust the starting offset of the table, and convert numbers
               * < __X32_SYSCALL_BIT to very high and thus out of range
               * numbers for comparisons.
               */
              unsigned int xnr = nr - __X32_SYSCALL_BIT;
      
              if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
                  xnr = array_index_nospec(xnr, X32_NR_syscalls);
                  regs->ax = x32_sys_call_table[xnr](regs);
                  return true;
              }
              return false;
          }
  • 출처

profile
공부 내용 저장소

0개의 댓글