[kernel] get sys_call_table
아키텍쳐마다, 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의 주소를 알아야 한다.
- 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
c1072f60 T sys_getpid
// PAGE_OFFSET (0xc0000000) + static (0x01000000) + offset
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
근처에 있는 공개 심볼과, 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
``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)))
```
``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 |