首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第53期->安全文摘
期刊号: 类型: 关键词:
finding hidden modules in Redhat FC2

作者:coolq
出处:http://www.linuxforum.net/forum/showflat.php?Cat=&Board=security&Number=
日期:2004-11-05

coolq

Redhat Fedora Core 2默认的内核,添加了对PAE模式的支持,所以物理地址变成了36位,对页的访问也需要经过三级页表。
另外一点重要的不同,是打上了 Ingo Molnar的4G/4G补丁,也就是说,内核的内存布局不是原来的3G用户/1G内核,
而是4G用户/4G内核,使用两个不同的页目录,因此用户态/内核态切换时需要修改CR3寄存器。

/*******************************************
* name: module hunter 2.6 ver 1.1 9.6.04  *
*              (for RH FC2)               *
* orig author: madsys                     *
*                                         *
* rewrite: CoolQ                          *
*                                         *
* usage: cat /proc/showmodules            *
*                                         *
******************************************/


#undef KBUILD_MODNAME
#define KBUILD_MODNAME mh26


#include <linux/config.h>

#ifdef CONFIG_SMP
#define __SMP__
#endif /* CONFIG_SMP */

#if CONFIG_MODVERSIONS == 1
#define MODVERSIONS
#endif /* CONFIG_MODVERSIONS */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/mm.h>

#include <linux/proc_fs.h>

#include <linux/errno.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/fixmap.h>
#include <asm/page.h>
#include <asm/types.h>
#include <asm/atomic_kmap.h>


#define STEPPING (PAGE_SIZE/32)

typedef enum RAM_MODE{
    MODE_NORMAL_4K,
    MODE_NORMAL_4M,
    MODE_PAE_4K,
    MODE_PAE_2M
}MODE;

static MODE     mode;
static u32     cr3, cr4;

/*********************************************
* the following 4 funcs:                    *
* valid_addr_normal_4K,valid_addr_normal_4M *
* valid_addr_pae_4K, valid_addr_pae_2M      *
* do the dirty job separately               *
* return 1 for valid one                    *
* return 0 for invalid                      *
********************************************/

static int valid_addr_normal_4K(u32 address)
{
    u32     pde, pte;
    u32    cr3_tmp;
    u32    pde_idx, pte_idx;
    u32    addr;
    
    if (!address)
        return 0;
    
    cr3_tmp = cr3 & 0xfffffe00;
    pde_idx = address >> 22;
    pte_idx = (address & 0xfffff000) >> 12;
    
    pde = ((unsigned long *) __va(cr3_tmp))[pde_idx]; /* pde */
    
    if (pde & 1){

        addr = pde & 0xfffff000;
        address &= 0x003ff000;
        /* pte */
        pte = ((unsigned long *) __va(addr))[pte_idx];
        if (pte)
            return 1;
        else
            return 0;
    }else
        return 0;


}

static int valid_addr_normal_4M(u32 address)
{

    return 0;

}

static int valid_addr_pae_4K(u32 address)
{

    u64    pdpte, pde, pte, addr;
    u32    pdpte_idx, pde_idx, pte_idx;
    u32    cr3_tmp;
    
    if(!address)
        return 0;
    
    pdpte_idx = address >> 30;
    pde_idx = (address & 0x3fe00000) >> 21;
    pte_idx = (address & 0x1ff000) >> 12;
    
    cr3_tmp = cr3 & 0xffffe0;

    
    pdpte = ((u64*) __va(cr3_tmp))[pdpte_idx];
    if(pdpte & 1){
        addr = pdpte & 0xffffff000ULL;

        pde = ((u64*) __va(addr))[pde_idx];
        if(pde & 1){
            addr = pde & 0xffffff000ULL;

            pte = ((u64*) __va(addr))[pte_idx];
            if(pte)
                return 1;
            else
                return 0;
        }else
            return 0;
    }else
        return 0;

}

static int valid_addr_pae_2M(u32 address)
{
    return 0;
}

/**************************************************
* FuncName: valid_addr                           *
* Usage: tell if an address is valid             *
*        i.e. does the address have the specific *
*        page frame entry?                       *
* RetValue:                                      *
*        1 - address valid                       *
*        0 - address invalid                     *
* Details:                                       *
*        the func will call another routine      *
*        according to mode(NORMAL/PAE/4K/4M/2M)  *
*************************************************/
int valid_addr(u32 address)

{
    switch(mode){
        
        case MODE_NORMAL_4K:
            return valid_addr_normal_4K(address);
            break;

        case MODE_NORMAL_4M:
            return valid_addr_normal_4M(address);
            break;

        case MODE_PAE_4K:
            return valid_addr_pae_4K(address);
            break;

        case MODE_PAE_2M:
            return valid_addr_pae_2M(address);
            break;

        default:
            return 0;
            break;
    }
}

ssize_t showmodule_read(struct file *unused_file, char *buffer, size_t len, loff_t*off)

{

    struct module *p;


    printk("\naddress                 name     size"
        "      core_addr     flags  \n\n");

    /* brute force all the possible vms */
    for (p = (struct module *)VMALLOC_START;
         p <= (struct module*)(VMALLOC_START + VMALLOC_RESERVE - PAGE_SIZE);
         p = (struct module *)((u32)p + STEPPING)){
        
        if (valid_addr((u32)p + (u32)&((struct module *)NULL)->name)  )

            /* the valid module struct should match some rules */
            if ((     (p->name[0]>=0x30 && p->name[0]<=0x39)
                   || (p->name[0]> 0x41 && p->name[0]<=0x7a ))
                && (p->core_size < 1 <<20)
                && (p->core_size>= sizeof(struct module))
                && p->state <3
                && p->module_core > 0x02000000UL)

                printk("0x%p%18s   %6lu     0x%4p     %3d\n",
                    p, p->name, p->core_size,
                    p->module_core, p->state);
    }


    return 0;
}

static struct file_operations showmodules_ops = {

        read:showmodule_read,

};


int init_module(void)
{

    struct proc_dir_entry *entry;
    
    /* get cr3,cr4 registers and tell the VM mode */
    
    __asm__ __volatile__ ( "movl %%cr3, %0" : "=r"(cr3) );
    __asm__ __volatile__ ( "movl %%cr4, %0" : "=r"(cr4) );


    if(cr4 & (1 << 5)){
        
        u64    pdpte, pde;
        
        pdpte = ((u64 *) __va(cr3))[0];
        pde = ((u64 *) __va(pdpte))[0];
        
        if(pde & (1 << 7))
            mode = MODE_PAE_2M;
        else
            mode = MODE_PAE_4K;
        
    }else{

        u32    pde;
        
        pde = ((u32 *) __va(cr3))[0];
        
        if(pde & (1 << 7))
            mode = MODE_NORMAL_4M;
        else
            mode = MODE_NORMAL_4K;
    
    }

    /* add an entry to /proc */
    entry = create_proc_entry("showmodules", S_IRUSR, &proc_root);
    entry->proc_fops = &showmodules_ops;

    return 0;

}


void cleanup_module()
{

    remove_proc_entry("showmodules", &proc_root);

}



MODULE_LICENSE("GPL");
MODULE_AUTHOR("madsys madsys<at>ercist.iscas.ac.cn;qufuping<at>ercist.iscas.ac.cn");

coolq

init_module函数修改一下:
不知道为什么,CR3每次的内容都不一样(标准内核不一样,Redhat
FC2的就没有问题)。另外,FC2的默认内核,SMP的支持PAE,UP的是normal,不过两者都有4G的补




int init_module(void)
{

    struct proc_dir_entry *entry;
    u32    addr;
    /* get cr3,cr4 registers and tell the VM mode */
    
    __asm__ __volatile__ ( "movl %%cr3, %0" : "=r"(cr3) );
    __asm__ __volatile__ ( "movl %%cr4, %0" : "=r"(cr4) );


    addr = &__this_module;
    if(cr4 & (1 << 5)){
        
        u64    pdpte, pde;
        u32    pdpte_idx, pde_idx;

        pdpte_idx = addr >> 30;
        pde_idx = (addr & 0x3fffffff) >> 21;
        pdpte = ((u64 *) __va(cr3))[pdpte_idx];

        pde = ((u64 *) __va(pdpte))[pde_idx];
        
        if(pde & (1 << 7))
            mode = MODE_PAE_2M;
        else
            mode = MODE_PAE_4K;
        
    }else{

        u32    pde, pde_idx;
        
        pde_idx = addr >> 22;
        pde = ((u32 *) __va(cr3))[pde_idx];
        
        if(pde & (1 << 7))
            mode = MODE_NORMAL_4M;
        else
            mode = MODE_NORMAL_4K;
    
    }

    printk("cr3 = 0x%x\n", cr3);
    /* add an entry to /proc */

    printk("VM mode is %s\n", (mode == MODE_NORMAL_4K) ? "normal_4k" :
                (mode == MODE_NORMAL_4M) ? "normal_4M" :
                (mode == MODE_PAE_4K) ? "pae_4k" :
                (mode == MODE_PAE_2M) ? "pae_2M" :
                "unknown vm mode or error, better exit.");
    
    entry = create_proc_entry("showmodules", S_IRUSR, &proc_root);
    entry->proc_fops = &showmodules_ops;

    return 0;

}

madsys

标准2.6kernel中,每个进程的页目录(地址)是不一样的,表现为每个进程的当前cr3不同. 这是在进程切换时改变的. 具体见:

schedule()->context_switch()->switch_mm()

不过,系统启动时(见paging_init),和重起时(见machine_real_restart),还是要设置成初试页目录地址的.
版权所有,未经许可,不得转载