Wednesday, January 27, 2010

QEMU小实验:手工遍历所有进程的方法

QEMU小实验:手工遍历所有进程的方法

在内核中已经提供了遍历所有进程的方法,比如用for_each_process宏。但是如果你想加深对这部分的了解,那么可以不用这个宏,完全手工遍历一遍。下面介绍了在QEMU中,利用QEMU MONITOR,手工找出所有PROCESS的方法

环境:
QEMU 0.9.1
QEMU VM: CENT OS 5.3 (LINUX 2.6.18)
ARCH :X86-32

主要思路:
我们已经知道LINUX中的所有进程都对应一个TASK_STRUCT. 同时这些TASK_STRUCT里面有一个成员叫做TASKS。它的类型是一个LIST_HEAD(有NEXT, PRE2个成员,构成一个双向链表)。所有的进程就是通过这个TASK_STRUCT里的TASKS连接在一起的。

想遍历进程的话,可以找到一个上面说的结构,然后顺着链表一个一个找下去。另外,还有一些重要的信息在TASK_STRUCT中。常用的有PID(线程ID), TGID(进程ID), COMM(进程的命令参数)。
(关于PID, TGID,可以看下面这个帖子:http://linux.chinaunix.net/bbs/thread-1155667-1-1.html

如果是在GDB里,那么找到了TASK_STRUCT之后,可以直接用 p task->pid 的方法来把PID之类的打印出来。但是我们的目的就是不用GDB的功能,而只用QEMU MONITOR的功能,纯手工的找出PID这些成员。

这样一来,我们需要自己计算下PID, TGID等成员在TASK_STRUCT中的偏移量。怎么算呢?最天真的办法是按照 .H 文件里TASK_STRUCT的声明,自己一个一个算过去,比如一个CHAR占一个字节,一个INT 4个字节等。但是由于TASK_STRUCT是一个很大的结构(包括几十或者上百个成员),同时考虑到编译的时候还有对齐的问题。这样手工算可以说既费力又不一定对。

最厉害的办法是自己写个类似的C编译器,直接把编译后的输出打印出来。感兴趣的可以看看CIL(C Intermediate Language)。但是CIL还是有点麻烦的。它是用OCAML语言写的。我不是很熟悉。

于是我采用了一个比较折中的办法:自己写个KERNEL MODULE, 在这个MODULE里直接定义一个TASK_STRUCT,然后把感兴趣的那些成员的地址和基地址都打印出来。或者直接把偏移量打印出来。

我就是用上述办法得到了几个关键成员的偏移量:
(前面是TASK_STRUCT里的成员,后面是它距离TASK_STRUCT基地址的偏移量)
pid = 0Xbc
tgid = 0Xc0
comm = 0X1AC
tasks = 0X80

还有一点,我们怎么找到第一个TASK_STRUCT呢?可以看这篇文章(如何找出CURRENT):
http://linux.chinaunix.net/bbs/viewthread.php?tid=1147973&extra=

好了,万事具备,开始行动吧。
1) 运行一个QEMU VM。我是在WINDOWS HOST上跑的QEMU. 在LINUX上也可以跑QEMU。但是LINUX上的QEMU有一点不好,那就是它的QEMU MONITOR的大小是固定的。太小了。而在WINDOWS上面,QEMU MONITOR可以变得很大。

2) 按ALT+CTRL+2, 切换到QEMU MONITOR. 输入STOP. 这样QEMU 就会暂停下来了。这样的好处是你可以花很多时间来遍历进程。而不用担心某个进程结束后,带来的地址无效的问题。

3) p $esp :找出当前的ESP。我实验的时的输出是 0xc3a94f78

4) 找出THREAD_INFO:也就是把ESP与上0XFFFFE000。得到0xc3a94000

5) x /20w 0xc3a94000。显示的这个内容的第一个指针(前4个字节)就是当前进程的TASK_STRUCT。因为THREAD_INFO里的第一个成员就是struct task_struct *task
我这里得到的结果是0xc3aaa370。

(注意:有的时候这个地方显示的数据全都是0。我怀疑是因为进程正在切换中,内核栈刚刚清空。碰到这种情况,可以在QEMU MONITOR里输入c, 让QEMU继续跑一段时间,然后输入stop. 从头开始。有时要多试几次)

6) 找到了TASK_STRUCT之后,根据前面找到的偏移量,可以方便的找到PID, TGID, COMM, TASKS 等成员的值。比如PID的位置在0xc3aaa42c (TASK+0XBC)。
注意,在看COMM的时候,要用下面这个命令: x /20b 0xc3aaa51c. 否则的话,QEMU会把数据当成DWORD处理,并自动转换Endian. 使得看到的字符串的顺序是反过来的。

7) 这样一来,当前进程的信息已经知道了 。在我的输出中,PID,TGID=0,COMM=SWAPPER. 我们开始找下一个。下一个进程是通过TASK_STRUCT->TASKS 连接在一起的。有一点要注意,TASK_STRUCT->TASKS->NEXT中的地址不是下个TASK_STRUCT的起始地址,而是下个TASK_STRUCT中的TASKS的地址。所以有了这个地址后,要先减去0X80才得到基地址。然后就可以用上面的方法找出PID, TGID, COMM来了。

Linux中的task,process, thread 简介

本文的主要目的是介绍在Linux内核中,task,process, thread这3个名字之间的区别和联系。并且和WINDOWS中的相应观念进行比较。如果你已经很清楚了,那么就不用往下看了。

LINUX版本:2.6.18
ARCH: X86

首先要明确的是,按照LKD 2里面的说法,LINUX和其他OS 比如WINDOWS, SOLARIS之间一个很大的不同是没有严格定义的线程(thread)。那么你也许会问,如果LINUX中没有线程,那么如何来表示类似WINDOWS 线程的那种执行观念呢?答案是LINUX中,PROCESS(进程)可以当作线程。

那么你也许又会问,WINDOWS中的多线程程序在LINUX中是怎样表示的呢?具体来说,LINUX中的PROCESS有2种。一种是独立的 PROCESS。自己有自己的地址空间,资源列表,代码等。另外一种PROCESS是和其他PROCESS共享一个地址空间,资源列表的。这种 PROCESS就类似于WINDOWS中的线程。

在看LINUX内核代码的时候,你会同时看到process, task, thread这3个名字。下面简要介绍下他们之间的区别:

1、task 可以理解为一个LINUX PROCESS。最著名的定义TASK的数据结构叫做struct task_struct, 在linux\sched.h中。我觉得这个名字起得不好。因为大家都已经对PROCESS, THREAD之类得观念很熟悉了。现在又冒出来个TASK,很容易让人搞混。不过也许是历史原因吧。这个TASK一直保留着。

在 task_struct 中有一堆的成员。其中有PID 和TGID. PID实际上类似于WINDOWS中的THREAD ID。而TGID (thead group id) 对应于WINDOWS中的PID。PID对于独立的PROCESS来说,就是它的PID。这时PID == TGID。对于和其他PROCESS共享地址空间的PROCESS来说,每个都有独立的PID,但是他们的TGID是一样的。

2、thead虽然说LINUX不支持THREAD. 但是在内核代码里又可以看到THREAD这个名字。这时可以把他们和WINDOWS中的THREAD对应起来。一个比较著名的是thread_info 结构。

3、kernel thread在LINUX中,kernel thread是一个专门的名词。它的特点是没有独立的地址空间(MM结构为NULL). 他们只运行在KERNEL SPACE.不能切换到USER SPACE。

最后,总结下,在LINUX中,一个PROCESS即可能是一个WINDOWS PROCESS类似的观念,也可能是一个与WINDOWS THREAD类似的观念。而且有时还被叫做TASK(感觉有点乱)。不过最常用的还是与WINDOWS PROCESS类似的观念。比如在内核代码中有一个for_each_process宏。它就是只遍历那些主要的,独立的PROCESS。

参考资料:
1、LKD 2
2. http://blog.csdn.net/pppjob/archive/2009/02/05/3864020.aspx

Tuesday, January 12, 2010

ACPI tools on Linux

http://ftp.kernel.org/pub/linux/kernel/people/lenb/acpi/utils/
http://www.columbia.edu/~ariel/acpi/acpi_howto.txt
http://pank.org/blog/archives/000320.html
http://www.wangchao.net.cn/bbsdetail_1415962.html

pmtest does not work on CentOS 5.3

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.