[kernel] virt_to_phys
virt_to_phys
직접 호출하고 싶다면 kernel mode에서 실행해야 하기 때문에 LKM 등을 사용해야 하며,
어느 아키텍쳐든 최종 인터페이스로 다음 함수를 제공하기 때문에 이를 사용하는 편이 좋다.
```c
static inline phys_addr_t virt_to_phys(volatile void *address)
```
x86 ( not x86_64 )
```c
unsigned long __phys_addr(unsigned long x)
{
unsigned long phys_addr = x - PAGE_OFFSET;
....
return phys_addr;
}
```
arm
기본적으로 다음과 같은 방식으로 동작한다.
```c
static inline phys_addr_t __virt_to_phys_nodebug(unsigned long x)
{
return (phys_addr_t)x - PAGE_OFFSET + PHYS_OFFSET;
}
```
`` CONFIG_ARM_PATCH_PHYS_VIRT`` flag 있는 경우 define이 좀 달라진다.
PAGE_OFFSET
출력되는 주소는 virtual address다.
x86
```bash
/ # cat /boot/config-4.9.0-kali3-686-pae | grep PAGE_OFFSET
CONFIG_PAGE_OFFSET=0xC0000000
```
arm (armv7)
```bash
/ $ zcat /proc/config.gz | grep PAGE
CONFIG_PAGE_OFFSET=0x80000000
```
get virtual symbol
/proc/iomem : PHYS_OFFSET
virtual address로 변환하는 것은 아키텍쳐마다 다른데, ``c virt_to_phys()``를 반대로 수행하면 된다.
```c
root@kali32:/boot# cat /proc/iomem
00000000-00000fff : reserved
00001000-0009ebff : System RAM
0009ec00-0009ffff : reserved
000a0000-000bffff : PCI Bus 0000:00
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000ca000-000cafff : Adapter ROM
000cc000-000cffff : PCI Bus 0000:00
......
000dc000-000fffff : reserved
000f0000-000fffff : System ROM
00100000-3fedffff : System RAM // 이 range 내에서 좌측 1byte만 kASLR
04000000-04595ef1 : Kernel code(text) // left 2hex are PHYS_OFFSET
04595ef2-047df57f : Kernel data
048a1000-04912fff : Kernel bss
3fee0000-3fefefff : ACPI Tables
3feff000-3fefffff : ACPI Non-volatile Storage
3ff00000-3fffffff : System RAM
40000000-febfffff : PCI Bus 0000:00
40000000-febfffff : PCI Bus 0000:00
40008000-4000bfff : 0000:00:10.0
e5b00000-e5bfffff : PCI Bus 0000:22
......
fffe0000-ffffffff : reserved
```
e.g.,
x86
```c
root@kali32:~# cat /proc/kallsyms | grep setresuid
d1079030 T sys_setresuid
root@kali32:~# cat /proc/iomem
11000000-11595ef1 : Kernel code == PHYS_OFFSET
phy_addr = 11079030
```
arm
```c
/ $ cat /proc/kallsyms | grep sys_call_ta
8000e348 T sys_call_table
/ $ cat /proc/iomem
60008000-60485f3f : Kernel code
phys_addr = 6000e348
```
/dev/mem : access using physical address
```bash
root@kali32:~# cat /boot/config-4.13.0-kali1-686-pae | grep DEVMEM
CONFIG_DEVMEM=y
CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
CONFIG_STRICT_DEVMEM=y
CONFIG_IO_STRICT_DEVMEM=y
```
`` CONFIG_STRICT_DEVMEM``이 설정되어 있다면 `` /dev/mem``으로 접근 시 다음 체크를 수행한다.
```c
int devmem_is_allowed(unsigned long phys_page_number)
```
- ``c devmem_is_allowed(phys_page_number)`` checks to see if /dev/mem access to a certain address is valid.
- x86에서는 first megabyte of RAM( ``c 0x00100000`` )과 non-kernel-ram area에만 접근할 수 있음.실제로 ``c 0x000fffff``까지는 잘 읽히는데 여기를 넘어가자마자 읽히지 않는다.
- first megabyte of RAM에는 BIOS code와 X, dosemu 등 apps에서 사용하는 data region이 위치.
- non-kernel-ram area는 PCI mmio resource, potential bios/acpi data region을 포함하는 영역.
- disallowed 되어야 하는 영역의 경우 reject되는 대신 0으로 채워져 있을 수 있음.
Note ) 정의와 체크 위치는 아키텍쳐마다 다르지만 웬만한 아키텍쳐에서는 다 체크한다.
Note ) 이를 해제하려면 kernel을 recompile해야 한다.
/proc/<pid>/pagemap
https://github.com/torvalds/linux/blob/v4.9/Documentation/vm/pagemap.txt
'OS > Kernel' 카테고리의 다른 글
[kernel] addr_limit : kernel space arg (0) | 2017.10.15 |
---|---|
[kernel] LKM, Loadable Kernel Module / Kernel Compile (0) | 2017.10.14 |
[kernel] exploit (0) | 2017.10.06 |
Interrupt (0) | 2016.10.07 |
System call / vDSO, vsyscall (0) | 2016.09.18 |