绕过CONFIG_MODVERSIONS

来源:互联网 发布:盟军苏军将领知乎 编辑:程序博客网 时间:2024/06/06 02:03

最近买了个手机乐视1pro,没有内核源码,想插入一些自己编译的模块来扩展内核,于是找到了硬件相近的一加2的内核源码,把编译得到的模块拷到手机上,发现无法加载。经过摸索,发现原因是内核开启了CONFIG_MODVERSIONS,但是没开启CONFIG_MODULE_FORCE_LOAD. 内核symbol有crc校验,不同源码树编译出来的内核,symbol的crc可能不一样。
要想编译出crc一致的模块,需要用到编译内核时生成的Module.symvers这个文件,厂商没有开放内核源码,更不可能提供这个文件给你了。不过,既然内核会对模块引用到的symbol验证crc,说明内核本身就保存有一份crc,我们只要提取出来就行了。
我们只要编写如下模块,就可完成提取工作:
symcrc.c

#include <linux/module.h>#include <linux/init.h>#include <linux/device.h>#include <linux/string.h>#include <linux/slab.h>static char symcrc[256];static ssize_t symcrc_write(struct class *cls, struct class_attribute *attr, const char *_buf, size_t _count){    const struct kernel_symbol *ksymbol;    char *symname;    struct module *owner;    const unsigned long *crc = NULL;    bool gplok = true;    bool warn = true;    symname = kstrndup(_buf,strlen(_buf)-1,GFP_KERNEL);    ksymbol = find_symbol (symname, &owner, &crc, gplok, warn);    if (ksymbol != NULL && crc != NULL)    snprintf (symcrc, ARRAY_SIZE(symcrc), "0x%lx\t%s\tvmlinux\tEXPORT_SYMBOL\n", *crc, ksymbol->name);    else{        symcrc[0] = '\0';    }    kfree(symname);    return _count;}static ssize_t symcrc_read(struct class *cls, struct class_attribute *attr, char *_buf){    return sprintf(_buf, "%s", symcrc);}static struct class *symcrc_class = NULL;static CLASS_ATTR(writesym, 0220, NULL, symcrc_write);static CLASS_ATTR(getcrc, 0440, symcrc_read, NULL);int __init find_symbol_init (void){    int ret;    symcrc_class = class_create(THIS_MODULE, "symcrc");     if (IS_ERR(symcrc_class))     {           printk("Create class symcrc_class failed.\n");        return -ENOMEM;    }    ret =  class_create_file(symcrc_class, &class_attr_getcrc);    ret =  class_create_file(symcrc_class, &class_attr_writesym);    return 0;}void __exitfind_symbol_exit (void){  class_remove_file(symcrc_class, &class_attr_getcrc);  class_remove_file(symcrc_class, &class_attr_writesym);  class_destroy(symcrc_class);  symcrc_class = NULL;}module_init (find_symbol_init);module_exit (find_symbol_exit);MODULE_AUTHOR("Mr Pang");MODULE_LICENSE ("GPL");

编译这个模块,可以看到自动生成的symcrc.mod.c

#include <linux/module.h>#include <linux/vermagic.h>#include <linux/compiler.h>MODULE_INFO(vermagic, VERMAGIC_STRING);struct module __this_module__attribute__((section(".gnu.linkonce.this_module"))) = {    .name = KBUILD_MODNAME,    .init = init_module,#ifdef CONFIG_MODULE_UNLOAD    .exit = cleanup_module,#endif    .arch = MODULE_ARCH_INIT,};static const struct modversion_info ____versions[]__used__attribute__((section("__versions"))) = {    { 0x48f7bf25, __VMLINUX_SYMBOL_STR(module_layout) },    { 0xeb6cd1a5, __VMLINUX_SYMBOL_STR(class_destroy) },    { 0xe8efe986, __VMLINUX_SYMBOL_STR(class_remove_file) },    { 0x276c87ab, __VMLINUX_SYMBOL_STR(class_create_file) },    { 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },    { 0x8637fc77, __VMLINUX_SYMBOL_STR(__class_create) },    { 0x91715312, __VMLINUX_SYMBOL_STR(sprintf) },    { 0x37a0cba, __VMLINUX_SYMBOL_STR(kfree) },    { 0x28318305, __VMLINUX_SYMBOL_STR(snprintf) },    { 0x8b58b0d, __VMLINUX_SYMBOL_STR(find_symbol) },    { 0xaf6ae696, __VMLINUX_SYMBOL_STR(kstrndup) },    { 0x98cf60b3, __VMLINUX_SYMBOL_STR(strlen) },};static const char __module_depends[]__used__attribute__((section(".modinfo"))) ="depends=";

里面的static const struct modversion_info ____versions[]列举出了本模块引用到的symbol以及对应的crc。因为这个模块使用的是一加内核源码树编译的,所以部分crc与1pro内核的不一致,以至于无法加载。
为了解决这个问题,我们另外写一个模块,尽可能少的直接引用内核的symbol,
firststep.c

#include <linux/module.h>#include <linux/init.h>const struct kernel_symbol *(*find_symbol1)(const char *name, struct module **owner, const unsigned long **crc, bool gplok, bool warn) = (const struct kernel_symbol *(*)(const char *name, struct module **owner, const unsigned long **crc, bool gplok, bool warn))0xffffffc00027cab4;int (*printk1)(const char *fmt, ...) = (int (*)(const char *fmt, ...))0xffffffc000be3e94;static const char *sym_array[] = {  "module_layout",  "class_destroy",  "class_remove_file",  "class_create_file",    "printk",  "__class_create",  "sprintf",  "kfree",  "up",  "snprintf",  "down",    "find_symbol",  "kstrndup",  "strlen",};int __initfind_symbol_init (void){  const char *name;  int index;  const struct kernel_symbol *ksymbol;  struct module *owner;  const unsigned long *crc;  bool gplok = true;  bool warn = true;  for (index = 0; index < ARRAY_SIZE (sym_array); index++)    {      name = sym_array[index];      ksymbol = find_symbol1 (name, &owner, &crc, gplok, warn);      if (ksymbol != NULL)    printk1 (" %s", ksymbol->name);      else    printk1 ("<0>Failed to find symbol %s\n", name);      if (crc != NULL)    printk1 ("*crc : 0x%lx\n", *crc);      else    printk1 ("\n");    }  return 0;}void __exitfind_symbol_exit (void){}module_init (find_symbol_init);module_exit (find_symbol_exit);MODULE_LICENSE ("GPL");MODULE_AUTHOR("Mr Pang");

这个模块引用到的两个symbol是printk和find_symbol,我们通过其绝对地址来调用,就可以避免crc的问题了。绝对地址可以查看/proc/kallsyms获得,如果/proc/kallsyms地址全是0,可以echo 0 > /proc/sys/kernel/kptr_restrict
在我的手机上,find_symbol地址是0xffffffc00027cab4,printk地址是0xffffffc000be3e94.
static const char *sym_array[] 保存的是前一个模块symcrc引用到的symbol,可以查看symcrc.mod.c获得。
编译这个模块,查看自动生成的firststep.mod.c

#include <linux/module.h>#include <linux/vermagic.h>#include <linux/compiler.h>MODULE_INFO(vermagic, VERMAGIC_STRING);struct module __this_module__attribute__((section(".gnu.linkonce.this_module"))) = {    .name = KBUILD_MODNAME,    .init = init_module,#ifdef CONFIG_MODULE_UNLOAD    .exit = cleanup_module,#endif    .arch = MODULE_ARCH_INIT,};static const struct modversion_info ____versions[]__used__attribute__((section("__versions"))) = {    { 0x48f7bf25, __VMLINUX_SYMBOL_STR(module_layout) },};static const char __module_depends[]__used__attribute__((section(".modinfo"))) ="depends=";

发现这个模块依然引用了一个symbol–“module_layout”.这个symbol在所有模块上都可以查到,所以并不要紧,只需要提取手机上的任意一个模块,通过命令modprobe –show-modversions xxx.ko就可以找到module_layout的crc。人工修改firststep.mod.c文件,然后人工编译firststep.mod.c并人工链接firststep.ko。具体命令可以在编译模块时指定V=1查看。就两行长长的命令,复制粘贴而已。
此时生成的firststep.ko就是可以加载的了。加载之后通过dmesg查看打印出的crc,同样人工修改symcrc.mod.c里面的crc,再人工编译出symcrc.ko.
插入symcrc.ko之后,可以通过以下命令提取内核的Module.symvers:

for i in `cat /proc/kallsyms|awk '{print $3}'`;doecho $i > /sys/class/symcrc/writesym;cat /sys/class/symcrc/getcrc >> /sdcard/Module.symvers;done

拿到Module.symvers之后编译模块就方便多了。
首先要make clean
然后make nconfig选择需要编译的模块
然后通过make modules KBUILD_EXTRA_SYMBOLS=/path/to/Module.symvers
编译模块,生成的模块就是带有正确crc的模块,可以直接载入。

0 0
原创粉丝点击