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
```

/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