用戶態和內核共享內存----使用 /dev/mem & mmap


想法的來源是看到chinaunix上有人轉載了wheelz的博客,但是wheelz的代碼在我的實驗平台上是不能正常工作的,可能是wheelz的代碼太過久遠,我試驗的內核版本是:3.4.13。wheelz的源代碼如下:

// 內核模塊
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Wheelz");
MODULE_DESCRIPTION("mmap demo");

static unsigned long p = 0;

static int __init init(void)
{
        //分配共享內存(一個頁面)
        p = __get_free_pages(GFP_KERNEL, 0);
        SetPageReserved(virt_to_page(p));
        printk("<1> p = 0x%08x\n", p); 
      //p是內核中的虛擬地址    
        //在共享內存中寫上一個字符串
        strcpy(p, "Hello world!\n");
        return 0;

}

static void __exit fini(void)
{
        ClearPageReserved(virt_to_page(p));
        free_pages(p, 0);        
}

module_init(init);
module_exit(fini);

----------------------------------------------------------------------------------------------------------------------------------------
// 用戶態程序 #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #define PAGE_SIZE (4*1024) #define PAGE_OFFSET 0xc0000000 #define KERNEL_VIRT_ADDR 0xc5e3c000 int main() { char *buf; int fd; unsigned long phy_addr; fd=open("/dev/mem",O_RDWR); if(fd == -1) perror("open"); phy_addr=KERNEL_VIRT_ADDR - PAGE_OFFSET; //此處不太懂,不能理解物理地址phy_addr的計算方法 buf=mmap(0, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr); if(buf == MAP_FAILED) perror("mmap"); puts(buf);//打印共享內存的內容 munmap(buf,PAGE_SIZE); close(fd); return 0; }

在網上找了一些資料,導致這段代碼不工作的原因可能有一下幾個:

(1)在編譯內核時設置了CONFIG_STRICT_DEVMEM(某些版本中是CONFIG_NONPROMISC_DEVMEM),應該將此設置刪除。

(2)請求的地址沒有通過內核中devmem_is_allowed函數對/dev/mem的保護。

(3)物理地址phy_addr計算錯誤。(PS:wheelz的計算方法是怎么得到的?)

 

我對上面的幾個問題一一做了修改:

(1)修改了.config文件

# CONFIG_STRICT_DEVMEM is not set

(2)重寫arch/x86/mm/init.c下的devmem_is_allowed函數,這里我沒有做太細致的修改,只是讓函數一直返回1。當然這可能會存在一些問題。

/*
 * devmem_is_allowed() checks to see if /dev/mem access to a certain address
 * is valid. The argument is a physical page number.
 *
 *
 * On x86, access has to be given to the first megabyte of ram because that area
 * contains bios code and data regions used by X and dosemu and similar apps.
 * Access has to be given to non-kernel-ram areas as well, these contain the PCI
 * mmio resources as well as potential bios/acpi data regions.
 */
int devmem_is_allowed(unsigned long pagenr)
{

        return 1;
        if (pagenr <= 256)
                return 1;
        if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
                return 0;
        if (!page_is_ram(pagenr))
                return 1;
        return 0;
}

(3)修改物理地址的計算,這里我們直接使用內核中提供的轉換函數virt_to_phy()或者__pa()。

// 內核模塊
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mm.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("godjesse");
MODULE_DESCRIPTION("mmap demo");

static unsigned long p = 0;
static unsigned long pp = 0;

static int __init init(void)
{
        p = __get_free_pages(GFP_KERNEL, 0);

        if(!p)
        {
                printk("Allocate memory failure!/n");
        }
        else
        {
                SetPageReserved(virt_to_page(p));
// 使用virt_to_phys計算物理地址,供用戶態程序使用 pp
= (unsigned long)virt_to_phys((void *)p); printk("<1> page : pp = 0x%lx\n",pp); } strcpy((char *)p, "Hello world !\n"); return 0; } static void __exit fini(void) { printk("The content written by user is: %s/n", (unsigned char *)p); ClearPageReserved(virt_to_page(p)); free_pages(p, 0); printk(" exit \n"); } module_init(init); module_exit(fini);
---------------------------------------------------------------------------------------------------------------------------------------
// 用戶態程序 #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> //hard coding read after the module installed #define KERNEL_PHY_ADDR 0x3737c000 int main() { char *buf; int fd; unsigned long phy_addr; int pagesize = getpagesize(); phy_addr=KERNEL_PHY_ADDR; fd=open("/dev/mem",O_RDWR); if(fd == -1) perror("open"); buf=mmap(0, pagesize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr); if(buf == MAP_FAILED) { perror("mmap"); } printf("buf : %s\n",buf); // test the write buf[0] = 'X'; munmap(buf,pagesize); close(fd); return 0; }

 經過這些修改后,demo可以正常工作。

上文中提到修改devmem_is_allowed實際上是存在問題的,存在其他一些較為優雅的方法,如某牛人寫的博客:bypassing devmem_is_allowed with kernel probes,博客鏈接:

http://www.libcrack.so/2012/09/02/bypassing-devmem_is_allowed-with-kprobes/

相關資料:

http://stackoverflow.com/questions/11891979/accessing-mmaped-dev-mem 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM