Post

(kernel) virt\_to\_phys

virt_to_phys

직접 호출하고 싶다면 kernel mode에서 실행해야 하기 때문에 LKM 등을 사용해야 하며, 어느 아키텍쳐든 최종 인터페이스로 다음 함수를 제공하기 때문에 이를 사용하는 편이 좋다.

1
2
static inline phys\_addr\_t virt\_to\_phys(volatile void \*address)

x86 ( not x86_64 )

http://elixir.free-electrons.com/linux/v4.13/source/arch/x86/mm/physaddr.c#L70

1
2
3
4
5
6
7
8
9
unsigned long \_\_phys\_addr(unsigned long x)
{

unsigned long phys\_addr = x - PAGE\_OFFSET;
....

return phys\_addr;
}

arm

기본적으로 다음과 같은 방식으로 동작한다. http://elixir.free-electrons.com/linux/v4.13/source/arch/arm/include/asm/memory.h#L255

1
2
3
4
5
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이 좀 달라진다. http://elixir.free-electrons.com/linux/v4.13/source/arch/arm/include/asm/memory.h#L223

PAGE_OFFSET

출력되는 주소는 virtual address다.

x86
1
2
3
/ # cat /boot/config-4.9.0-kali3-686-pae | grep PAGE\_OFFSET
CONFIG\_PAGE\_OFFSET=0xC0000000

arm (armv7)
1
2
3
/ $ zcat /proc/config.gz | grep PAGE
CONFIG\_PAGE\_OFFSET=0x80000000

get virtual symbol

2017/10/14 - [System/Exploit] - [kernel] get & hook sys_call_table / page protection

/proc/iomem : PHYS_OFFSET

virtual address로 변환하는 것은 아키텍쳐마다 다른데, virt\_to\_phys()를 반대로 수행하면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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

1
2
3
4
5
6
7
8
9
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
1
2
3
4
5
6
7
/ $ 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

1
2
3
4
5
6
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으로 접근 시 다음 체크를 수행한다.

1
2
int devmem\_is\_allowed(unsigned long phys\_page\_number)

  • devmem\_is\_allowed(phys\_page\_number) checks to see if /dev/mem access to a certain address is valid.
  • x86에서는 first megabyte of RAM( 0x00100000 )과 non-kernel-ram area에만 접근할 수 있음.

실제로 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//pagemap

https://github.com/torvalds/linux/blob/v4.9/Documentation/vm/pagemap.txt

This post is licensed under CC BY 4.0 by the author.