Saturday, January 2, 2010

How to find the physical address of kernel text of Linux x86_64

How to find the physical address of kernel text of Linux x86_64

1. Goal:
I need to find the physical address of kernel text of Linux x86_64 (2.6.18). Why physical address? If you know DMA attack, you will understand why the physical address is useful. Basically, it may be used when you want to attack Linux x86_64.

BTW: for Linux x86_32, it is pretty easy to find the physical address of some kernel data structure. In x86_32, the kernel is normally mapped at 0xc0000000. Then the physical address is virtual address minus 0xc0000000.

2. Method:

Wrong method:

At first, virt_to_phys() function in the kernel came to my mind. The function name suggests that it is intended to be used to convert the virtual address to physical address. Also, it is easy to get the virtual address of the kernel text segment. You may use command : grep _text Symbol.

However, I find it got a wrong address when I used the above method. The physical address that obtained by virt_to_phys() is obvious wrong because it exceeds the amount of physical memory of my computer. After I read the description of virt_to_phys(), I found out that it only works for memories that are allocated by kmalloc().

Correct method:

After reading an article about linker script, I found out that the beginning physical address of the kernel is defined in the linker script. So let’s see what is inside this script.

First of all, the location of the script is in arch/kernel/x86_64 (or x86 for 2.6.32).

Following is a part of the script:
http://lxr.linux.no/#linux+v2.6.18/arch/x86_64/kernel/vmlinux.lds.S

SECTIONS
17{
18 . = __START_KERNEL;
19 phys_startup_64 = startup_64 - LOAD_OFFSET;
20 _text = .; /* Text and read-only data */
21 .text : AT(ADDR(.text) - LOAD_OFFSET) {

__START_KERNEL is a macro. So let’s find all the related definitions. There are in some .h files.


#define __PHYSICAL_START CONFIG_PHYSICAL_START
#define __START_KERNEL (__START_KERNEL_map + __PHYSICAL_START)
#define __START_KERNEL_map 0xffffffff80000000UL
#define LOAD_OFFSET __START_KERNEL_map

Following is a description of CONFIG_PHYSICAL_START:

http://lxr.linux.no/#linux+v2.6.18/arch/x86_64/Kconfig#L493

config PHYSICAL_START
494 hex "Physical address where the kernel is loaded" if (EMBEDDED CRASH_DUMP)
495 default "0x1000000" if CRASH_DUMP
496 default "0x200000"
497 help
498 This gives the physical address where the kernel is loaded. Normally
499 for regular kernels this value is 0x200000 (2MB). But in the case
500 of kexec on panic the fail safe kernel needs to run at a different
501 address than the panic-ed kernel.

From above, we can see that the default value of PHYSICAL_START is 0X200000. Also, the virtual address of the kernel starts from 0XFFFFFFFF80000000 (by using grep _text symbol file). Therefore, the contents of these two addresses should be the same. So the physical address 0x200000 correspond to the virtual address 0XFFFFFFFF80000000.

Last, let’s test the above theory on a virtual machine. I installed CentOS 5.3 x86_64 on QEMU (version 0.11.91, the older version such as 0.9.x or 0.010.x seem to have problems when install x86_64 CentOS). When QEMU virtual machine has focus, press alt+ctrl+2 to switch to QEMU monitor, then enter following two commands:
x /10x 0xffffffff80000000
xp /10x 0x200000
It turns out that the contents of these two addresses are the same. It means the theory is correct.

No comments: