Android ashmem hacking

/**********************************************************************
 *                     Android ashmem hacking
 * 声明:
 *     最近有些东西涉及到binder,而binder又涉及到ashmem,于是先跟一下这
 * 部分的内容。
 *
 *                                    2016-1-12 深圳 南山平山村 曾剑锋
 *********************************************************************/


/**
 * 参考文章:
 *     Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析
 *         http://blog.csdn.net/luoshengyang/article/details/6664554
 */


/*                                                                                                  
 * ashmem_area - anonymous shared memory area                                                       
 * Lifecycle: From our parent file's open() until its release()                                     
 * Locking: Protected by `ashmem_mutex'                                                             
 * Big Note: Mappings do NOT pin this structure; it dies on close()                                 
 */                                                                                                 
struct ashmem_area {                                                                                
    char name[ASHMEM_FULL_NAME_LEN];/* optional name for /proc/pid/maps */                          
    struct list_head unpinned_list;    /* list of all ashmem areas */                               
    struct file *file;        /* the shmem-based backing file */                                    
    size_t size;            /* size of the mapping, in bytes */                                     
    unsigned long prot_mask;    /* allowed prot bits, as vm_flags */                                
};                                                                                                  
                                                                                                    
/*                                                                                                  
 * ashmem_range - represents an interval of unpinned (evictable) pages                              
 * Lifecycle: From unpin to pin                                                                     
 * Locking: Protected by `ashmem_mutex'                                                             
 */                                                                                                 
struct ashmem_range {                                                                               
    struct list_head lru;        /* entry in LRU list */                                            
    struct list_head unpinned;    /* entry in its area's unpinned list */                           
    struct ashmem_area *asma;    /* associated area */                                              
    size_t pgstart;            /* starting page, inclusive */                                       
    size_t pgend;            /* ending page, inclusive */                                           
    unsigned int purged;        /* ASHMEM_NOT or ASHMEM_WAS_PURGED */                               
};                                                                                                  

module_init(ashmem_init);             ----------+                               
module_exit(ashmem_exit);                       |                          
                                                |                                                   
static int __init ashmem_init(void)   <---------+                                                   
{                                                                                                   
    int ret;                                                                                        
                                                                                                    
    // static struct kmem_cache *ashmem_area_cachep __read_mostly;                                  
    ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",                                     
                      sizeof(struct ashmem_area),                                                   
                      0, 0, NULL);                                                                  
    if (unlikely(!ashmem_area_cachep)) {                                                            
        printk(KERN_ERR "ashmem: failed to create slab cache\n");                                   
        return -ENOMEM;                                                                             
    }                                                                                               
                                                                                                    
    ashmem_range_cachep = kmem_cache_create("ashmem_range_cache",                                   
                      sizeof(struct ashmem_range),                                                  
                      0, 0, NULL);                                                                  
    if (unlikely(!ashmem_range_cachep)) {                                                           
        printk(KERN_ERR "ashmem: failed to create slab cache\n");                                   
        return -ENOMEM;                                                                             
    }                                                                                               
                                                                                                    
    ret = misc_register(&ashmem_misc);             -------------------+                             
    if (unlikely(ret)) {                                              |                             
        printk(KERN_ERR "ashmem: failed to register misc device!\n"); |                             
        return ret;                                                   |                             
    }                                                                 |                             
                                                                      |                             
    register_shrinker(&ashmem_shrinker);                              |                             
                                                                      |                             
    printk(KERN_INFO "ashmem: initialized\n");                        |                             
                                                                      |                             
    return 0;                                                         |                             
}                                                                     |                             
                                                                      |                             
static struct file_operations ashmem_fops = {      <-------+          |                             
    .owner = THIS_MODULE,                                  |          |                             
    .open = ashmem_open,                            -------*-+        |                             
    .release = ashmem_release,                             | |        |                             
        .read = ashmem_read,                               | |        |                             
        .llseek = ashmem_llseek,                           | |        |                             
    .mmap = ashmem_mmap,                    ---------------*-*--------*---------+                   
    .unlocked_ioctl = ashmem_ioctl,         ---------------*-*--------*-------+ |                   
    .compat_ioctl = ashmem_ioctl,                          | |        |       | |                   
};                                                         | |        |       | |                   
                                                           | |        |       | |                   
static struct miscdevice ashmem_misc = {           <-------*-*--------+       | |                   
    .minor = MISC_DYNAMIC_MINOR,                           | |                | |                   
    .name = "ashmem",                                      | |                | |                   
    .fops = &ashmem_fops,                          --------+ |                | |                   
};               +-------------------------------------------+                | |                   
                 V                                                            | |                   
static int ashmem_open(struct inode *inode, struct file *file)                | |                   
{                                                                             | |                   
    struct ashmem_area *asma;                                                 | |                   
    int ret;                                                                  | |                   
                                                                              | |                   
    ret = generic_file_open(inode, file);                                     | |                   
    if (unlikely(ret))                                                        | |                   
        return ret;                                                           | |                   
                                                                              | |                   
    // static struct kmem_cache *ashmem_area_cachep __read_mostly;            | |                   
    // ashmem_area_cachep = kmem_cache_create("ashmem_area_cache",            | |                   
    //                   sizeof(struct ashmem_area),                          | |                   
    //                   0, 0, NULL);                                         | |                   
    asma = kmem_cache_zalloc(ashmem_area_cachep, GFP_KERNEL);                 | |                   
    if (unlikely(!asma))                                                      | |                   
        return -ENOMEM;                                                       | |                   
                                                                              | |                   
    INIT_LIST_HEAD(&asma->unpinned_list);                                     | |                   
    // #define ASHMEM_NAME_PREFIX "dev/ashmem/"                               | |                   
    // #define ASHMEM_NAME_PREFIX_LEN (sizeof(ASHMEM_NAME_PREFIX) - 1)        | |                   
    // #define ASHMEM_FULL_NAME_LEN (ASHMEM_NAME_LEN + ASHMEM_NAME_PREFIX_LEN)| |                   
    memcpy(asma->name, ASHMEM_NAME_PREFIX, ASHMEM_NAME_PREFIX_LEN);           | |                   
    // #define PROT_MASK        (PROT_EXEC | PROT_READ | PROT_WRITE)          | |                   
    asma->prot_mask = PROT_MASK;                                              | |                   
    // can get this asma struct in other function                             | |                   
    file->private_data = asma;                                                | |                   
                                                                              | |                   
    return 0;     +-----------------------------------------------------------+ |                   
}                 |                                                             |                   
                  V                                                             |                   
static long ashmem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)|                   
{                                                                               |                   
    struct ashmem_area *asma = file->private_data;                              |                   
    long ret = -ENOTTY;                                                         |                   
                                                                                |                   
    switch (cmd) {                                                              |                   
    case ASHMEM_SET_NAME:                                                       |                   
        ret = set_name(asma, (void __user *) arg);      -------+                |                   
        break;                                                 |                |                   
    case ASHMEM_GET_NAME:                                      |                |                   
        ret = get_name(asma, (void __user *) arg);      -------*-----+          |                   
        break;                                                 |     |          |                   
    case ASHMEM_SET_SIZE:                                      |     |          |                   
        ret = -EINVAL;                                         |     |          |                   
        if (!asma->file) {                                     |     |          |                   
            ret = 0;                                           |     |          |                   
            asma->size = (size_t) arg;                         |     |          |                   
        }                                                      |     |          |                   
        break;                                                 |     |          |                   
    case ASHMEM_GET_SIZE:                                      |     |          |                   
        ret = asma->size;                                      |     |          |                   
        break;                                                 |     |          |                   
    case ASHMEM_SET_PROT_MASK:                                 |     |          |                   
        ret = set_prot_mask(asma, arg);                        |     |          |                   
        break;                                                 |     |          |                   
    case ASHMEM_GET_PROT_MASK:                                 |     |          |                   
        ret = asma->prot_mask;                                 |     |          |                   
        break;                                                 |     |          |                   
    case ASHMEM_PIN:                                           |     |          |                   
    case ASHMEM_UNPIN:                                         |     |          |                   
    case ASHMEM_GET_PIN_STATUS:                                |     |          |                   
        ret = ashmem_pin_unpin(asma, cmd, (void __user *) arg);|     |          |                   
        break;                                                 |     |          |                   
    case ASHMEM_PURGE_ALL_CACHES:                              |     |          |                   
        ret = -EPERM;                                          |     |          |                   
        if (capable(CAP_SYS_ADMIN)) {                          |     |          |                   
            struct shrink_control sc = {                       |     |          |                   
                .gfp_mask = GFP_KERNEL,                        |     |          |                   
                .nr_to_scan = 0,                               |     |          |                   
            };                                                 |     |          |                   
            ret = ashmem_shrink(&ashmem_shrinker, &sc);        |     |          |                   
            sc.nr_to_scan = ret;                               |     |          |                   
            ashmem_shrink(&ashmem_shrinker, &sc);              |     |          |                   
        }                                                      |     |          |                   
        break;                                                 |     |          |                   
    }                                                          |     |          |                   
                                                               |     |          |                   
    return ret;                                                |     |          |                   
}               +----------------------------------------------+     |          |                   
                V                                                    |          |                   
static int set_name(struct ashmem_area *asma, void __user *name)     |          |                   
{                                                                    |          |                   
    int ret = 0;                                                     |          |                   
                                                                     |          |                   
    mutex_lock(&ashmem_mutex);                                       |          |                   
                                                                     |          |                   
    /* cannot change an existing mapping's name */                   |          |                   
    if (unlikely(asma->file)) {                                      |          |                   
        ret = -EINVAL;                                               |          |                   
        goto out;                                                    |          |                   
    }                                                                |          |                   
                                                                     |          |                   
    if (unlikely(copy_from_user(asma->name + ASHMEM_NAME_PREFIX_LEN, |          |                   
                    name, ASHMEM_NAME_LEN)))                         |          |                   
        ret = -EFAULT;                                               |          |                   
    asma->name[ASHMEM_FULL_NAME_LEN-1] = '\0';                       |          |                   
                                                                     |          |                   
out:                                                                 |          |                   
    mutex_unlock(&ashmem_mutex);                                     |          |                   
                                                                     |          |                   
    return ret;                                                      |          |                   
}             +------------------------------------------------------+          |                   
              V                                                                 |                   
static int get_name(struct ashmem_area *asma, void __user *name)                |                   
{                                                                               |                   
    int ret = 0;                                                                |                   
                                                                                |                   
    mutex_lock(&ashmem_mutex);                                                  |                   
    if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0') {                           |                   
        size_t len;                                                             |                   
                                                                                |                   
        /*                                                                      |                   
         * Copying only `len', instead of ASHMEM_NAME_LEN, bytes                |                   
         * prevents us from revealing one user's stack to another.              |                   
         */                                                                     |                   
        len = strlen(asma->name + ASHMEM_NAME_PREFIX_LEN) + 1;                  |                   
        if (unlikely(copy_to_user(name,                                         |                   
                asma->name + ASHMEM_NAME_PREFIX_LEN, len)))                     |                   
            ret = -EFAULT;                                                      |                   
    } else {                                                                    |                   
        if (unlikely(copy_to_user(name, ASHMEM_NAME_DEF,                        |                   
                      sizeof(ASHMEM_NAME_DEF))))                                |                   
            ret = -EFAULT;                                                      |                   
    }                                                                           |                   
    mutex_unlock(&ashmem_mutex);                                                |                   
                                                                                |                   
    return ret;                                                                 |                   
}                                                                               |                   
                                                                                |                   
static int ashmem_mmap(struct file *file, struct vm_area_struct *vma)  <--------+                   
{                                                                                                   
    struct ashmem_area *asma = file->private_data;                                                  
    int ret = 0;                                                                                    
                                                                                                    
    mutex_lock(&ashmem_mutex);                                                                      
                                                                                                    
    /* user needs to SET_SIZE before mapping */                                                     
    if (unlikely(!asma->size)) {                                                                    
        ret = -EINVAL;                                                                              
        goto out;                                                                                   
    }                                                                                               
                                                                                                    
    /* requested protection bits must match our allowed protection mask */                          
    if (unlikely((vma->vm_flags & ~calc_vm_prot_bits(asma->prot_mask)) &                            
                        calc_vm_prot_bits(PROT_MASK))) {                                            
        ret = -EPERM;                                                                               
        goto out;                                                                                   
    }                                                                                               
    vma->vm_flags &= ~calc_vm_may_flags(~asma->prot_mask);                                          
                                                                                                    
    if (!asma->file) {                                                                              
        char *name = ASHMEM_NAME_DEF;                                                               
        struct file *vmfile;                                                                        
                                                                                                    
        if (asma->name[ASHMEM_NAME_PREFIX_LEN] != '\0')                                             
            name = asma->name;                                                                      
                                                                                                    
        /* ... and allocate the backing shmem file */                                               
        vmfile = shmem_file_setup(name, asma->size, vma->vm_flags);                                 
        if (unlikely(IS_ERR(vmfile))) {                                                             
            ret = PTR_ERR(vmfile);                                                                  
            goto out;                                                                               
        }                                                                                           
        asma->file = vmfile;                                                                        
    }                                                                                               
    // #define get_file(x)    atomic_long_inc(&(x)->f_count)                                        
    get_file(asma->file);                                                                           
                                                                                                    
    if (vma->vm_flags & VM_SHARED)                                                                  
        shmem_set_file(vma, asma->file);   --------------+                                          
    else {                                               |                                          
        if (vma->vm_file)                                |                                          
            fput(vma->vm_file);                          |                                          
        vma->vm_file = asma->file;                       |                                          
    }                                                    |                                          
    vma->vm_flags |= VM_CAN_NONLINEAR;                   |                                          
                                                         |                                          
out:                                                     |                                          
    mutex_unlock(&ashmem_mutex);                         |                                          
    return ret;                                          |                                          
}             +------------------------------------------+                                          
              v                                                                                     
void shmem_set_file(struct vm_area_struct *vma, struct file *file)                                  
{                                                                                                   
    if (vma->vm_file)                                                                               
        fput(vma->vm_file);                -------------+                                           
    vma->vm_file = file;                                |                                           
    vma->vm_ops = &shmem_vm_ops;                        |                                           
    vma->vm_flags |= VM_CAN_NONLINEAR;                  |                                           
}                                                       |                                           
                                                        |                                           
void fput(struct file *file)               <------------+                                           
{                                                                                                   
    if (atomic_long_dec_and_test(&file->f_count))                                                   
        __fput(file);                                                                               
}                                                                                                   

 

posted on 2016-01-12 10:48  zengjf  阅读(647)  评论(0编辑  收藏  举报

导航