아키텍쳐마다, OS마다 동작이 다르기 때문에 상황에 맞게 생각해야 한다.

다음 환경에서 테스트

```bash

root@kali32:~/add_syscall# uname -a

Linux kali32 4.13.0-kali1-686-pae #1 SMP Debian 4.13.4-1kali1 (2017-10-03) i686 GNU/Linux

```


get sys_call_table

sys_call_table의 각 entry는 syscall function의 address이며 ``c syscall()``은 이를 참조하기 때문에, 이를 변경해주면 victim syscall을 호출할 때 마다 변경된 syscall이 호출된다.
그러나 다음과 같은 제약이 있다.
  • SYS_CALL_TABLE의 주소를 알아야 한다.
  • SYS_CALL_TABLE entry에 접근하기 위해서는 kernel mode여야하기 때문에, kernel mode에서 sys_call_table을 변경할 방법이 필요하다.

1. kallsyms : kernel exported symbol

```bash

root@kali32:~# cat /proc/kallsyms | grep sys_call_table

root@kali32:~/add_syscall# cat /proc/kallsyms | grep getpid

c2072f60 T sys_getpid

// PAGE_OFFSET (0xc0000000) + phys_base (0x02000000) + offset

```

모든 공개 심볼을 조회할 수 있다.

kallsyms는 kASLR이 적용된 이후 reloc된 주소를 반환하기 때문에 kallsyms에 있는 주소가 해당 symbol이 실제로 위치한 virtual address다.


2.4.14 부터는 커널 컴파일 시 따로 옵션을 주거나, ``c EXPORT_SYMBOL()``이나 ``c EXPORT_SYMBOL_GPL()``로 직접 export해야 공개되면서 kallsyms에서 조회되기 때문에 조회되지 않을 수도 있다.

* export는 ``c extern``으로 사용할 수 있음을 의미한다.


2. System.map :  kernel symbol table

```bash
root@kali32:~/add_syscall# cat /boot/System.map-$(uname -r) | grep sys_call_table
c15d3160 R sys_call_table
root@kali32:~/add_syscall# cat /boot/System.map-$(uname -r) | grep getpid

c1072f60 T sys_getpid

// PAGE_OFFSET (0xc0000000) + static (0x01000000) + offset

```
커널 컴파일 시 생성되는 심볼 테이블 파일로, ``bash /boot/``디렉토리에 있지만 부팅 과정에서 사용되는게 아니라 디버깅에 사용된다.
kallsyms와 달리 System.map은 항상 고정값을 반환하기 때문에, 반환된 주소에 해당 symbol이 실제로 위치해 있을거라는 보장이 없다.

x86_64의 경우 같은 physical address를 가리키는 두 virtual address를 반환하는데, kallsyms는 Kernel text area를 반환하고 System.map은 direct mapping area를 반환한다. 따라서 kASLR이 없더라도 서로 다른 주소를 반환한다. 

구버전 kernel은 System.map으로 얻은 주소를 바로 사용해도 되는 경우가 있는 듯 하지만, 대체로 안된다.

LKM으로 조회해보면, kallsyms로 얻은 주소에는 binary가 제대로 찍혀 있는데, System.map으로 얻은 주소에는 이상한 값이 들어있다.

```bash

root@kali32:~/add_syscall# dmesg | tail -2

[ 9015.850279] kallsyms sys_getpid c2072f60 : 26748d3e

[ 9015.850280] System.map sys_getpid c1072f60 : 9a67ff72

```


그러나 kallsyms와 동시에 공개되어 있는 symbol (e.g., ``c getpid``)로 대조해 보고, 하위 몇 byte가 일치한다면 System.map의 sys_call_table 주소에서 offset을 얻어낼 수 있다.

```c

root@kali32:~/add_syscall# cat /boot/System.map-4.13.0-kali1-686-pae | grep sys_call_table

c15d3160 R sys_call_table

PAGE_OFFSET(0xc0000000) + phys_base (0x02000000) + offset(0x005d3160) = 0xc25d3160


root@kali32:~/add_syscall# dmesg | tail -3

[ 9015.850279] kallsyms sys_getpid c2072f60 : 26748d3e

[ 9015.850280] System.map sys_getpid c1072f60 : 9a67ff72

[ 9015.850280] sct[20(__NR_getpid)] c25d31b0 : c2072f60 : 26748d3e

```


3. dynamic : using exported symbol

kallsyms, System.map 모두 사용할 수 없을 때 사용할만한 방법.

근처에 있는 공개 심볼과, sys_call_table 내의 entry로 존재하는 공개 심볼(주로 `` sys_close``)을 이용해 반복문으로 값을 비교해가며 찾는다.

http://hkpco.kr/paper/crtlk.txt


exploit 활용도가 높지는 않은 방법인데, 이 방법을 사용할 수 있다는건

1. `` addr_limit``가 해제되어 있거나, 

2. 내가 작성한 코드를 kernel mode에서 실행시킬 수 있음을 의미한다.

후자의 경우, 그냥 그걸 이용해 exploit하면 되는거니까 굳이 sys_call_table을 찾을 필요가 없을 듯.


4. 64bit system call tracing

```
ENTRY(entry_SYSCALL_64)
    ...
    call    *sys_call_table(, %rax, 8)
```

``c SYSCALL``의 경우 ``c ENTRY(entry_SYSCALL_64)``에서 바로 ``c call *sys_call_table(, %rax, 8)``하기 때문에 사용할 수 있다.

MSR을 읽어오는 ``c rdmsr`` instruction을 user mode에서 호출 시 General Protection Fault가 발생하기 때문에

그냥 kernel mode에서 다음 매크로를 사용해 읽어온다.

```c

 #define rdmsrl(msr, val)    ((val) = native_read_msr((msr)))

```


32bit system call tracing
```c
int main(void){
    struct gdt_ptr idtr = {0,0};
    asm volatile("sidt %0" : "=m"(idtr));
    printf("%d %p\n", idtr.len, idtr.ptr);
    return 0;
}
```

``c int $0x80``에서 사용하는 `` IDTR``은 user mode에서도 읽어올 수 있지만

``c ENTRY(entry_INT80_32)`` 부터 system call handler까지 따라가는게 좀 많이 복잡하기도 하고,

커널 버전이 변경되면서 언제 바이너리 패턴이 변경될지 모르는거라서 사실상 쓰기가 조금 그렇다.

```c

idt → call do_int80_syscall_32 → call do_syscall_32_irqs_on → call ia32_sys_call_table

```

``c SYSENTER``도 system call handler까지 따라가는 과정이 ``c int $0x80``과 비슷해 사용하기가 좀 그렇다.


'OS > Kernel' 카테고리의 다른 글

[kernel] current 구조체 / cred 수정  (0) 2017.10.22
[kernel] hook sys_call_table  (0) 2017.10.20
[kernel] Page Protection  (0) 2017.10.16
[kernel] addr_limit : kernel space arg  (0) 2017.10.15
[kernel] LKM, Loadable Kernel Module / Kernel Compile  (0) 2017.10.14