Android内核模块编译执行

摘要:
以“hello”驱动程序为例,本文简要介绍了编写和编译内核驱动程序的基本过程,包括内核模块的内置编译和动态加载模式的编译。在hello目录中,添加Makefile配置文件以进行编译。将hello.ko文件推入手机,并使用insmod在根权限下加载它。

Author: GeneBlue

0X01 前言

内核驱动是漏洞的高发区,了解Android驱动代码的编写是分析、利用驱动漏洞的基础。本文以一个“hello”驱动为例,简单介绍内核驱动编写、编译的基本过程,包括内核模块的内建编译动态加载方式的编译

0X02 编写

在 ./goldsifh/drivers 文件夹下新建hello目录,在hello目录中新建hello.c文件:

#include <linux/module.h>  
#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/miscdevice.h>  

MODULE_LICENSE("GPL");  
MODULE_AUTHOR("GeneBlue");  
MODULE_DESCRIPTION("Hello Kernel Device");  
MODULE_VERSION("1.0");  

#define CMD_COMMAND 0x1336  

long hello_ioctl(struct file *filp,  //ioctl函数
    unsigned int cmd,  
    unsigned long arg){  
    switch(cmd){  
    case CMD_COMMAND:  
        printk("Hello Module hello_ioctl() exced");  
        break;  
    default:  
        printk("Hello Module unknown ioctl cmd");  
    }  
    return 0;  
}  

struct file_operations hello_fops = {  //设备的操作函数指针表
    unlocked_ioctl: hello_ioctl  
};  

static struct miscdevice hello_device = {  //注册为misc设备的基本属性
    minor: MISC_DYNAMIC_MINOR,  
    name: "hello",  
    fops: &hello_fops,  
    mode: 777
};  

static int __init hello_begin(void){  
    int ret;  
    ret = misc_register(&hello_device);  //注册为misc设备
    if(ret)  
        printk("Failed to register misc device");  
    else  
        printk("Hello Module successfully loaded");  

    return ret;  
}  

static void __exit hello_exit(void){  
    int ret = misc_deregister(&hello_device);  //设备卸载
    if(ret)  
        printk("Hello module exit");  
}  

module_init(hello_begin);  //模块初始化函数
module_exit(hello_exit);  //模块卸载函数

写驱动模块时都要包含 module.h 头文件,该头文件定义了一些编写模块时常用宏或函数  kernel.h 提供常用的内核函数,如printk(); fs.h  提供文件表和文件结构,如file_operations; miscdevice.h 提供注册为misc设备的常用函数

0X03 编译

在编译之前,要确保下载下来的内核代码已经可以顺利地编译运行,具体可以参考这里

在hello目录中,要增加Makefile配置文件用于编译。在Makefile中添加:

obj-y +=  hello.o

表示内建编译,即直接编译到内核文件zImage中。然后在 goldfish/drivers/Makefile包含新建的驱动设备

obj-y += hello/

这样,再次编译内核后驱动设备即可包含在内核中


有的时候,我们需要在手机中编写可动态加载的驱动模块可动态加载模块非常便于调试,在kmsg可直接看到调试信息,这个时候需要重新编译手机内核开启内核的动态加载属性。在编译的内核的时候,使用

make menuconfig

命令,并在menuconfig中做如下配置:


menuconfig

menuconfig

如果不开启该选项就直接 insmod 加载hello.ko,一般情况下都会报如下错误:

insmod: init_module 'hello.ko' failed (Function not implemented)


[内建编译的情况]--编译完内核之后,还需要将内核拷贝到aosp的源码中替换掉默认的内核文件,在我的Android源码中默认的内核文件存放在如下路径中:

/android-4.4.4_r1/device/lge/hammerhead-kernel/zImage-dtb

编译Android源码生成新boot.img文件,然后只要将boot.img刷入到手机即可。


完成内核的准备工作之后,下面就要编译动态加载模块hello.ko,依然是上述的  hello.c  文件,在任意位置建一个目录,拷贝hello.c并编写  Makefile  如下:

obj-m := hello.o  

KERNELDIR := /home/geneblue/Android/Source/kernel/msm/  
PWD :=$(shell pwd)  
ARCH=arm  
CROSS_COMPILE=/home/geneblue/Android/Source/tsinghua_aosp/android-4.4.4_r1/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.6/bin/arm-linux-androideabi-  
CC=$(CROSS_COMPILE)gcc  
LD=$(CROSS_COMPILE)ld  
CFLAGS_MODULE=-fno-pic  

modules:  
    make -C $(KERNELDIR) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules    
clean:    
    rm *.o *.ko *.mod.c *.order *.symvers

注意,Makefile中要加上  CFLAGS_MODULE=-fno-pic  选项,不然insmod加载编译好的  hello.ko relocation节  会错误:

insmod: init_module 'hello.ko' failed (Exec format error)  
kmsg:  
<3>[ 1646.589131] hello: unknown relocation: 27  

最后,使用  make 命令即可编译生成正确的   hello.ko   文件。

0X04 运行

使用模拟器来加载新编好的内核,并在 adb shell  中,root权限下查看新编的驱动:

# root权限下
# ll /dev | grep hello

# dmesg | grep Hello
内建编译

这样,一个最简单的设备驱动已经可以正确运行了。


将 hello.ko 文件 push  到手机中,root权限下用 insmod 来加载即可。

# 加载内核模块
# insmod hello.ko

# 查看加载的内核模块
# lsmod
模块编译

0X05 参考


转载于:https://geneblue.github.io/2016/08/16/Android%E5%86%85%E6%A0%B8%E6%A8%A1%E5%9D%97%E7%BC%96%E8%AF%91%E6%89%A7%E8%A1%8C/

免责声明:文章转载自《Android内核模块编译执行》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇spark教程(14)-共享变量parted分区GPT格式下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

boost1.59编译安装

boost 1.59.0编译及使用 1、下载:        网址:http://sourceforge.net/projects/boost/files/boost/1.59.0/        选择:boost_1_59_0.7z或者boost_1_59_0.zip (Windows 8.1企业版操作系统);                    bo...

GCC编译器原理(一)------交叉编译器制作和GCC组件及命令

1.1 交叉编译器制作 默认安装的 GCC 编译系统所产生的代码适用于本机,即运行 GCC 的机器,但也可将 GCC 安装成能够生成其他的机器代码。安装一些必须的模块,就可产生多种目标机器代码,而且可通过命令行选择一种希望使用的代码。 1.1.1 目标机 从网站 http://gcc.gnu.org/install/specific.html 可以得到有可...

gcc 编译器参数

一、GCC编译过程 参考:http://hi.baidu.com/zengzhaonong/item/c00e079f500adccab625314f-------------------------------------    Pre-Processing   cpp        预处理    Compiling        ccl       ...

教你阅读 Cpython 的源码(一)

目录 第一部分-介绍 Cpython 源代码中有什么?如何编译 Cpython 代码编译器能做什么?为什么 Cpython 是用 C 语言而是 Python 编写的?Python 语言的规范Cpython 中的内存管理机制结论 第二部分-Python 解释器进程 建立运行时配置读取文件/输入词法解析和句法解析抽象语法树结论 第三部分- Cpython 的编...

linux下编译原理分析

linux下编译hello.c 程序,使用gcc hello.c,然后./a.out就能够执行;在这个简单的命令后面隐藏了很多复杂的过程,这个过程包含了以下的步骤: ====================================================================================== 预处理: 宏...

《Cython系列》2. 编译并运行Cython代码

楔子 Python和C、C++之间一个最重要的差异就是Python是解释型,而C、C++是编译型。如果开发Python程序,那么在修改代码之后可以立刻运行,而C、C++则需要一个编译步骤。而编译一个规模比较大的C、C++程序,那么可能会花费我们几个小时甚至几天的时间;而使用Python则可以让我们进行更敏捷的开发,从而更具有生产效率。 而Cython同C、...