--[ 4 - 需要注意的问题
--[ 4.1 SMP和CPU优化带来的麻烦
对于vmlinuz和内存中的.text进行逐字节比较,如果你的内核有SMP,或者使用了CPU的某
些优化(默认情况下有很多),即使内核是没有问题的,也会有内容不相同的情况!
先来看对SMP的支持,注意以下这个宏
#define wmb() alternative("lock; addl $0,0(%%esp)", "sfence", X86_FEATURE_XMM)
经测试,这一语句装载到内存中,内容会发生改变,具体的原因还不得而知
0xf0 0x83 0x44 0x24 0x00 -> 0x0f 0xae (0xe8|0xf0) 0x8d 0x76
如果有谁知道原因,请mail我。
另外,CPU优化,也造成磁盘和内存中的内容不一致
一个例子就是list_for_each_entry调用的prefetch()
634 extern inline void prefetch(const void *x)
635 {
636 alternative_input(ASM_NOP4,
637 "prefetchnta (%1)",
638 X86_FEATURE_XMM,
639 "r" (x));
640 }
#define GENERIC_NOP4 ".byte 0x8d,0x74,0x26,0x00\n"
#define K8_NOP4 ".byte 0x66,0x66,0x66,0x90\n"
#define K7_NOP4 ".byte 0x8d,0x44,0x20,0x00\n"
#ifdef CONFIG_MK8
#define ASM_NOP4 K8_NOP4
#elif defined(CONFIG_MK7)
#define ASM_NOP4 K7_NOP4
#else
#define ASM_NOP4 GENERIC_NOP4
出现不一致的情况就是上边的几种NOP4, 由于我对这一块不熟悉,不知有谁能指点一二?
--[ 4.2 2.4内核的差异
2.4与2.6相比,__ex_table的位置靠后了
17 _etext = .; /* End of text section */
18
19 .rodata : { *(.rodata) *(.rodata.*) }
20 .kstrtab : { *(.kstrtab) }
21
22 . = ALIGN(16); /* Exception table */
23 __start___ex_table = .;
24 __ex_table : { *(__ex_table) }
25 __stop___ex_table = .;
由于.rodata和.kstrtab都不会发生变化,因此,不影响本文的方法。
--[ 4.3 Fedora Core 2的4G补丁
Redhat的内核改动的地方很多,比较讨厌,4G补丁就是其中之一,这里需要注意的是,
打了4G补丁的内核,PAGE_OFFSET已经不是0xc000000,而是0x02000000,内核有自己单
独的4G空间了。
另外的一个影响是__ex_table中的异常处理指针,有一些发生异常的地方,指针是以
0xffff开头的,估计4G在处理异常的时候,有些特别的地方.
00181000 60 41 10 02 63 41 10 02 30 48 10 02 40 f3 27 02 |`A..cA..0H..@.'.|
00181010 33 48 10 02 49 f3 27 02 35 55 10 02 52 f3 27 02 |3H..I.'.5U..R.'.|
00181020 41 55 10 02 5b f3 27 02 66 32 ff ff 64 f3 27 02 |AU..[.'.f2..d.'.|
<--------->
00181030 67 32 ff ff 70 f3 27 02 6b 32 ff ff 7c f3 27 02 |g2..p.'.k2..|.'.|
<---------> <--------->
00181040 73 32 ff ff 8d f3 27 02 74 32 ff ff 99 f3 27 02 |s2....'.t2....'.|
<---------> <--------->
00181050 78 32 ff ff a5 f3 27 02 92 62 10 02 b6 f3 27 02 |x2....'..b....'.
<--------->
--[ 5 - 总结
本文介绍的方法,对于检查基于系统调用和异常表的木马是非常有效的,对于内核静态补丁,
也是管用的. 对于处理函数指针的情况,思想适用,但是需要对每种情况写一种扩展(利用
符号,判断函数指针是否在正常的.text范围内,包括内核与模块的.text)。
本文的方法,对于一种非常简单的静态补丁,是无效的: 木马将磁盘vmlinuz中的指令
修改,例如简单的将jz->jnz, jne->je。但如果系统使用的是发行版带的标准内核,我们
就能保证vmlinuz的完整性(很容易找到)。
--[ 6 - 参考
[1] Linux on-the-fly kernel patching without LKM
<
http://www.phrack.org/phrack/58/p58-0x07>
[2] Hijacking Linux Page Fault Handler
<
http://www.phrack.org/show.php?p=61&a=7>
[3] Static Kernel Patching
<
http://www.phrack.org/show.php?p=60&a=8>
[4] Linux kernel rootkits: protecting system's "Ring-zero"
<
http://www.giac.org/practical/GCUX/Raul_Siles_GCUX.pdf>
[5] 利用异常表处理 Linux 内核态缺页异常
<
http://www-900.ibm.com/developer ... /l-page/index.shtml>
[6] linux kernel source code
<
http://www.kernel.org>
[7] Intel用户手册
--------------------------------------------------------------------------------
leviathan.alan 回复于:2006-06-20 09:43:28
--[ 7 - 程序
下面的程序只考虑的前面的两种情况, 至于后面的两种, 由于比较特殊, 需要根据自己的
需要,自己添加功能,当然思想前面已经陈述,很简单.
--[ 7.1 dump.c(kernel module)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/file.h>
#include <linux/moduleparam.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include <asm/string.h>
#include <asm/unistd.h>
#define EOF (-1)
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
struct {
unsigned short limit;
unsigned int base;
} __attribute__ ((packed)) idtr;
struct {
unsigned short off1;
unsigned short sel;
unsigned char none,flags;
unsigned short off2;
} __attribute__ ((packed)) idt;
static unsigned int len = 0x800000;
module_param(len, uint, 0);
static char buffer[256];
struct file *klib_fopen(const char *filename, int flags, int mode);
void klib_fclose(struct file *filp);
int klib_fwrite(char *buf, int len, struct file *filp);
void *memmem(void *start, unsigned int s_len, void *find, unsigned int f_len);
struct file *klib_fopen(const char *filename, int flags, int mode)
{
struct file *filp = filp_open(filename, flags, mode);
return (IS_ERR(filp)) ? NULL : filp;
}
void klib_fclose(struct file *filp)
{
if (filp)
fput(filp);
}
int klib_fwrite(char *buf, int len, struct file *filp)
{
int writelen;
mm_segment_t oldfs;
if (filp == NULL)
return -ENOENT;
if (filp->f_op->write == NULL)
return -ENOSYS;
if (((filp->f_flags & O_ACCMODE) & (O_WRONLY | O_RDWR)) == 0)
return -EACCES;
oldfs = get_fs();
set_fs(KERNEL_DS);
writelen = filp->f_op->write(filp, buf, len, &filp->f_pos);
set_fs(oldfs);
return writelen;
}
void *memmem(void *start, unsigned int s_len, void *find, unsigned int f_len)
{
char *p, *q;
unsigned int len;
p = start, q = find;
len = 0;
while((p - (char *)start + f_len) <= s_len){
while(*p++ == *q++){
len++;
if(len == f_len)
return(p - f_len);
};
q = find;
len = 0;
};
return(NULL);
}
static int dump_init(void)
{
unsigned int addr_start = PAGE_OFFSET + 0x100000;
struct file *filep;
unsigned int sys_call_off, sct;
char *p;
/* Step 1: dump kernel memory */
filep = klib_fopen("./kernel.dat", O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(filep == NULL){
printk("Error create kernel.dat.\n");
return 0;
}
klib_fwrite((char*)addr_start, len, filep);
klib_fclose(filep);
printk("dump file to ./kernel.dat - OK.\n");
/* Step 2: Get syscall info */
filep = klib_fopen("./kernel.info", O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(filep == NULL){
printk("Error create kernel.info.\n");
return 0;
}
__asm__ ("sidt %0" : "=m" (idtr));
// printk("idtr base at 0x%X\n",(int)idtr.base);
memcpy(&idt,(void*)(idtr.base+8*0x80),sizeof(idt));
sys_call_off = (idt.off2 << 16) | idt.off1;
// printk("sys_call_off is at 0x%x\n", sys_call_off);
p = (char*)memmem ((void *)sys_call_off, 100, "\xff\x14\x85", 3);
if(p){
sct = *(unsigned*)(p+3);
// printk("syscall table at addr 0x%x, rel off 0x%x\n",
// sct, sct - (unsigned int)addr_start);
}else
;
// printk("syscall table not find?\n");
sprintf(buffer, ".text = 0x%x\n"
"idtr = 0x%x\n"
"sys_call_off = 0x%x\n"
"sys_call_table = 0x%x\n"
"sys_call_nr = %d\n",
addr_start, idtr.base, sys_call_off,
p ? sct : 0, NR_syscalls);
klib_fwrite(buffer, strlen(buffer), filep);
klib_fclose(filep);
return 0;
}
static void dump_exit(void)
{
return;
}
module_init(dump_init);
module_exit(dump_exit);
MODULE_LICENSE("GPL");