zoukankan      html  css  js  c++  java
  • insmod 源码分析

    问题的提出是前一阵和lgx聊天发现,一个被strip的module也可以被成功的insmod,当时知道一些insmod 的原理觉得不太可能,因为一个正常的module文件其实就是标准的ELF格式object文件,如果将他的 symtab strip掉的话,那些printk这类的symbol将不能被正常的解析,理论上是不可能加载成功的,于是 做了一个简单的module在turbo7上测试了一把,modutils的版本是2.4.6,出人意料的是竟然成功的被加 载,真是觉得真是不可思议,因此觉得有必要研究一下insmod的具体实现,最好的方法当然是go to the source 
    先说说关于module的几个系统调用,主要有 sys_create_module,sys_init_module,sys_query_module,sys_delete_module。我们简单的分析一 下module的创建过程,有一个重要地数据结构必然要提到,那就是struct module,定义如下。 
    struct module 

            unsigned long size_of_struct;  /* == sizeof(module) */ 
            struct module *next; 
            const char *name; 
            unsigned long size; 
            union 
            { 
                    atomic_t usecount; 
                    long pad; 
            } uc;                          /* Needs to keep its size - so says rth */ 
            unsigned long flags;            /* AUTOCLEAN et al */ 
            unsigned nsyms;        symbol的个数 
            unsigned ndeps; 
            struct module_symbol *syms;        此module实现的对外输出的所有symbol 
            struct module_ref *deps; 
            struct module_ref *refs; 
            int (*init)(void); 
            void (*cleanup)(void); 
            const struct exception_table_entry *ex_table_start; 
            const struct exception_table_entry *ex_table_end; 
    #ifdef __alpha__ 
            unsigned long gp; 
    #endif 
            /* Members past this point are extensions to the basic 
                module support and are optional.  Use mod_opt_member() 
                to examine them.  */ 
            const struct module_persist *persist_start; 
            const struct module_persist *persist_end; 
            int (*can_unload)(void); 
    }; 
    而kernel中有一个全局变量module_list为所有的module的list,在系统初始化是module_list只有一项为 
    struct module *module_list = &kernel_module; 
    kernel_module中的syms为__start___ksymtab即为kernel对外输出的symbol的所有项。 
    sys_create_module负责分配空间生成一个module结构,加入module_list中。而sys_init_module是将insmod 在用户空间地module结构复制到由sys_create_module创建地空间中。 
    好了,我们现在分析一下insmod的主要工作流程(modtuils-2.4.6版) 
    1.get_kernel_info函数负责取得kernel中先以注册的modules,放入module_stat中,并将kernel实现的各个 symbol放入ksyms中,个数为ksyms。 
    最终调用new_get_kernel_info 
    //type的三种类型,影响get_kernle_info的流程。 
    #define K_SYMBOLS 1 /* Want info about symbols */ 
    #define K_INFO 2 /* Want extended module info */ 
    #define K_REFS 4 /* Want info about references */ 
    static int new_get_kernel_info(int type) 

        struct module_stat *modules; 
        struct module_stat *m; 
        struct module_symbol *syms; 
        struct module_symbol *s; 
        size_t ret; 
        size_t bufsize; 
        size_t nmod; 
        size_t nsyms; 
        size_t i; 
        size_t j; 
        char *module_names; 
        char *mn; 
        drop(); 
        /* 
        * Collect the loaded modules 
        */ 
        module_names = xmalloc(bufsize = 256); 
        //取得现有module的名称,ret返回个数,module_names返回各个module名称,字符0分割 
        while (query_module(NULL, QM_MODULES, module_names, bufsize, &ret)) { 
            if (errno != ENOSPC) { 
                error("QM_MODULES: %m "); 
                return 0; 
            } 
            module_names = xrealloc(module_names, bufsize = ret); 
        } 
        module_name_list = module_names; 
        l_module_name_list = bufsize; 
        n_module_stat = nmod = ret; 
        module_stat = modules = xmalloc(nmod * sizeof(struct module_stat)); 
        memset(modules, 0, nmod * sizeof(struct module_stat)); 
        /* Collect the info from the modules */ 
        //循环取得各个module的信息,QM_INFO的使用。 
        for (i = 0, mn = module_names, m = modules; 
            i ;name = mn; 
            if (query_module(mn, QM_INFO, &info, sizeof(info), &ret)) { 
                if (errno == ENOENT) { 
                /* The module was removed out from underneath us. */ 
                    m->;flags = NEW_MOD_DELETED; 
                    continue; 
                } 
                /* else oops */ 
                error("module %s: QM_INFO: %m", mn); 
                return 0; 
            } 
            m->;addr = info.addr; 
            if (type & K_INFO) {//取得module的信息 
                m->;size = info.size; 
                m->;flags = info.flags; 
                m->;usecount = info.usecount; 
                m->;modstruct = info.addr; 
            }//将info值传给module_stat结构 
            if (type & K_REFS) {//取得module的引用关系 
                int mm; 
                char *mrefs; 
                char *mr; 
                mrefs = xmalloc(bufsize = 64); 
                while (query_module(mn, QM_REFS, mrefs, bufsize, &ret)) { 
                    if (errno != ENOSPC) { 
                        error("QM_REFS: %m"); 
                        return 1; 
                    } 
                    mrefs = xrealloc(mrefs, bufsize = ret); 
                } 
                for (j = 0, mr = mrefs; 
                    j ;nrefs += 1; 
                            m->;refs = xrealloc(m->;refs, m->;nrefs * sizeof(struct module_stat **)); 
                            m->;refs[m->;nrefs - 1] = module_stat + mm; 
                            break; 
                        } 
                    } 
                } 
                free(mrefs); 
            } 
            if (type & K_SYMBOLS) { /* 取得symbol信息,正是我们要得*/ 
                syms = xmalloc(bufsize = 1024); 
                while (query_module(mn, QM_SYMBOLS, syms, bufsize, &ret)) { 
                    if (errno == ENOSPC) { 
                        syms = xrealloc(syms, bufsize = ret); 
                        continue; 
                    } 
                    if (errno == ENOENT) { 
                        /* 
                        * The module was removed out 
                        * from underneath us. 
                        */ 
                        m->;flags = NEW_MOD_DELETED; 
                        free(syms); 
                        goto next; 
                    } else { 
                        error("module %s: QM_SYMBOLS: %m", mn); 
                        return 0; 
                    } 
                } 
                nsyms = ret; 
                //syms是module_symbol结构,ret返回symbol个数 
                m->;nsyms = nsyms; 
                m->;syms = syms; 
                /* Convert string offsets to string pointers */ 
                for (j = 0, s = syms; j ;name += (unsigned long) syms; 
            } 
            next: 
        } 
        if (type & K_SYMBOLS) { /* Want info about symbols */ 
            /* Collect the kernel's symbols.  */ 
            syms = xmalloc(bufsize = 16 * 1024); 
            //name为NULL,返回kernel_module的symbol。 
            while (query_module(NULL, QM_SYMBOLS, syms, bufsize, &ret)) { 
                if (errno != ENOSPC) { 
                    error("kernel: QM_SYMBOLS: %m"); 
                    return 0; 
                } 
                syms = xrealloc(syms, bufsize = ret); 
            } 
            //将值返回给nksyms和ksyms两个全局变量存储。 
            nksyms = nsyms = ret; 
            ksyms = syms; 
            /* name原来只是一个结构内的偏移,加上结构地址为真正的字符串地址 */ 
            for (j = 0, s = syms; j ;name += (unsigned long) syms; 
        } 
        return 1; 

    2.set_ncv_prefix(NULL);判断symbolname中是否有前缀,象_smp之类,这里没有。 
    3.检查是否已有同名的module。 
    ... 
        for (i = 0; i 

    4.obj_load,将。o文件读入到struct obj_file结构f中。 

    struct obj_file *obj_load (int fp, Elf32_Half e_type, const char *filename) 

      struct obj_file *f; 
      ElfW(Shdr) *section_headers; 
      int shnum, i; 
      char *shstrtab; 
      /* Read the file header.  */ 
      f = arch_new_file();//生成新的obj_file结构 
      memset(f, 0, sizeof(*f)); 
      f->;symbol_cmp = strcmp;//设置symbol名的比较函数就是strcmp 
      f->;symbol_hash = obj_elf_hash;//设置计算symbol hash值的函数 
      f->;load_order_search_start = &f->;load_order;//?? 
      gzf_lseek(fp, 0, SEEK_SET); 
      if (gzf_read(fp, &f->;header, sizeof(f->;header)) != sizeof(f->;header)) 
        {//取得object文件的ELF头结构。 
          error("cannot read ELF header from %s", filename); 
          return NULL; 
        } 
      if (f->;header.e_ident[EI_MAG0] != ELFMAG0 
          || f->;header.e_ident[EI_MAG1] != ELFMAG1 
          || f->;header.e_ident[EI_MAG2] != ELFMAG2 
          || f->;header.e_ident[EI_MAG3] != ELFMAG3) 
        {//判断ELF的magic,是否是ELF文件 
          error("%s is not an ELF file", filename); 
          return NULL; 
        } 
      if (f->;header.e_ident[EI_CLASS] != ELFCLASSM//i386的机器上为ELFCLASS32,表示32bit 
          || f->;header.e_ident[EI_DATA] != ELFDATAM//此处值为ELFDATA2LSB,表示编码方式 
          || f->;header.e_ident[EI_VERSION] != EV_CURRENT//此值固定,表示版本 
          || !MATCH_MACHINE(f->;header.e_machine))//机器类型 
        { 
          error("ELF file %s not for this architecture", filename); 
          return NULL; 
        } 
      if (f->;header.e_type != e_type && e_type != ET_NONE)//类型必为ET_REL 
        { 
          switch (e_type) { 
          case ET_REL: 
        error("ELF file %s not a relocatable object", filename); 
        break; 
          case ET_EXEC: 
        error("ELF file %s not an executable object", filename); 
        break; 
          default: 
        error("ELF file %s has wrong type, expecting %d got %d", 
            filename, e_type, f->;header.e_type); 
        break; 
          } 
          return NULL; 
        } 
      /* Read the section headers.  */ 
      if (f->;header.e_shentsize != sizeof(ElfW(Shdr))) 
        { 
          error("section header size mismatch %s: %lu != %lu", 
            filename, 
            (unsigned long)f->;header.e_shentsize, 
            (unsigned long)sizeof(ElfW(Shdr))); 
          return NULL; 
        } 
      shnum = f->;header.e_shnum;//section个数 
      f->;sections = xmalloc(sizeof(struct obj_section *) * shnum); 
      memset(f->;sections, 0, sizeof(struct obj_section *) * shnum); 
      section_headers = alloca(sizeof(ElfW(Shdr)) * shnum);//section表的大小 
      gzf_lseek(fp, f->;header.e_shoff, SEEK_SET); 
      if (gzf_read(fp, section_headers, sizeof(ElfW(Shdr))*shnum) != sizeof(ElfW(Shdr))*shnum) 
        {//获得section表内容 
          error("error reading ELF section headers %s: %m", filename); 
          return NULL; 
        } 
      /* Read the section data.  */ 
      for (i = 0; i 
          f->;sections = sec = arch_new_section();//分配内存给每个section 
          memset(sec, 0, sizeof(*sec)); 
          sec->;header = section_headers;//设置section表项地址 
          sec->;idx = i;//section表中第几个 
          switch (sec->;header.sh_type)//section的类型 
        { 
        case SHT_NULL: 
        case SHT_NOTE: 
        case SHT_NOBITS: 
          /* ignore */ 
          break; 
        case SHT_PROGBITS: 
        case SHT_SYMTAB: 
        case SHT_STRTAB: 
        case SHT_RELM://将以上各种类型的section内容读到结构中。 
          if (sec->;header.sh_size >; 0) 
            { 
              sec->;contents = xmalloc(sec->;header.sh_size); 
              gzf_lseek(fp, sec->;header.sh_offset, SEEK_SET); 
              if (gzf_read(fp, sec->;contents, sec->;header.sh_size) != sec->;header.sh_size) 
            { 
              error("error reading ELF section data %s: %m", filename); 
              return NULL; 
            } 
            } 
          else 
            sec->;contents = NULL; 
          break; 
        //描述relocation的section 
    #if SHT_RELM == SHT_REL 
        case SHT_RELA: 
          if (sec->;header.sh_size) { 
            error("RELA relocations not supported on this architecture %s", filename); 
            return NULL; 
          } 
          break; 
    #else 
        case SHT_REL: 
          if (sec->;header.sh_size) { 
            error("REL relocations not supported on this architecture %s", filename); 
            return NULL; 
          } 
          break; 
    #endif 
        default: 
          if (sec->;header.sh_type >;= SHT_LOPROC) 
            { 
              if (arch_load_proc_section(sec, fp) 
          error("can't handle sections of type %ld %s", 
            (long)sec->;header.sh_type, filename); 
          return NULL; 
        } 
        } 
      /* Do what sort of interpretation as needed by each section.  */ 
    //shstrndx存的是section字符串表的索引值,就是第几个section 
    //shstrtab就是那个section了。 
      shstrtab = f->;sections[f->;header.e_shstrndx]->;contents; 
      for (i = 0; i ;sections; 
          sec->;name = shstrtab + sec->;header.sh_name; 
        }//根据strtab,取得每个section的名字 
      for (i = 0; i ;sections; 
          /* .modinfo and .modstring should be contents only but gcc has no 
          *  attribute for that.  The kernel may have marked these sections as 
          *  ALLOC, ignore the allocate bit. 
          */也就是说即使modinfo和modstring有此标志位,也去掉。 
          if (strcmp(sec->;name, ".modinfo") == 0 || 
          strcmp(sec->;name, ".modstring") == 0) 
        sec->;header.sh_flags &= ~SHF_ALLOC;//ALLOC表示此section是否占用内存 
          if (sec->;header.sh_flags & SHF_ALLOC) 
        obj_insert_section_load_order(f, sec);//确定section load的顺序 
                //根据的是flag的类型加权得到优先级 
                
          switch (sec->;header.sh_type) 
        { 
        case SHT_SYMTAB://符号表 
          { 
            unsigned long nsym, j; 
            char *strtab; 
            ElfW(Sym) *sym; 
            if (sec->;header.sh_entsize != sizeof(ElfW(Sym))) 
              { 
            error("symbol size mismatch %s: %lu != %lu", 
                  filename, 
                  (unsigned long)sec->;header.sh_entsize, 
                  (unsigned long)sizeof(ElfW(Sym))); 
            return NULL; 
              } 
            //计算符号表表项个数,nsym也就是symbol个数 
            nsym = sec->;header.sh_size / sizeof(ElfW(Sym)); 
            //sh_link是符号字符串表的索引值 
            strtab = f->;sections[sec->;header.sh_link]->;contents; 
            sym = (ElfW(Sym) *) sec->;contents; 
            /* Allocate space for a table of local symbols.  */ 
            //为所有符号分配空间 
            j = f->;local_symtab_size = sec->;header.sh_info; 
            f->;local_symtab = xmalloc(j *= sizeof(struct obj_symbol *)); 
            memset(f->;local_symtab, 0, j); 
            /* Insert all symbols into the hash table.  */ 
            for (j = 1, ++sym; j ;st_name)//有值就是strtab的索引值 
              name = strtab+sym->;st_name; 
            else//如果为零,此symbolname是一个section的name,比如.rodata之类的 
              name = f->;sections[sym->;st_shndx]->;name; 
            //obj_add_symbol将符号加入到f->;symbab这个hash表中 
            obj_add_symbol(f, name, j, sym->;st_info, sym->;st_shndx, 
                      sym->;st_value, sym->;st_size); 
              } 
          } 
        break; 
        } 
        } 
      /* second pass to add relocation data to symbols */ 
      for (i = 0; i ;sections; 
          switch (sec->;header.sh_type) 
        { 
        case SHT_RELM: 
          {//找到描述重定位的section 
            unsigned long nrel, j; 
            ElfW(RelM) *rel; 
            struct obj_section *symtab; 
            char *strtab; 
            if (sec->;header.sh_entsize != sizeof(ElfW(RelM))) 
              { 
            error("relocation entry size mismatch %s: %lu != %lu", 
                  filename, 
                  (unsigned long)sec->;header.sh_entsize, 
                  (unsigned long)sizeof(ElfW(RelM))); 
            return NULL; 
              } 
        //算出rel有几项,存到nrel中 
            nrel = sec->;header.sh_size / sizeof(ElfW(RelM)); 
            rel = (ElfW(RelM) *) sec->;contents; 
            //rel的section中sh_link相关值是符号section的索引号 
            symtab = f->;sections[sec->;header.sh_link]; 
            //而符号section中sh_link是符号字符串section的索引号 
            strtab = f->;sections[symtab->;header.sh_link]->;contents; 
            /* Save the relocate type in each symbol entry.  */ 
            //存储需要relocate的符号的rel的类型 
            for (j = 0; j ;r_info); 
            if (symndx) 
              { 
                extsym = ((ElfW(Sym) *) symtab->;contents) + symndx; 
                if (ELFW(ST_BIND)(extsym->;st_info) == STB_LOCAL) 
                  {//local类型 
                /* Local symbols we look up in the local table to be sure 
                  we get the one that is really intended.  */ 
                intsym = f->;local_symtab[symndx]; 
                  } 
                else 
                  {//其他类型,从hash表中取 
                /* Others we look up in the hash table.  */ 
                const char *name; 
                if (extsym->;st_name) 
                  name = strtab + extsym->;st_name; 
                else 
                  name = f->;sections[extsym->;st_shndx]->;name; 
                intsym = obj_find_symbol(f, name); 
                  } 
                intsym->;r_type = ELFW(R_TYPE)(rel->;r_info); 
              } 
              } 
          } 
          break; 
        } 
        } 
      f->;filename = xstrdup(filename); 
      return f; 

    ... 
    struct obj_symbol * 
    obj_add_symbol (struct obj_file *f, const char *name, unsigned long symidx, 
            int info, int secidx, ElfW(Addr) value, unsigned long size) 

      struct obj_symbol *sym;//计算符号的hash值 
      unsigned long hash = f->;symbol_hash(name) % HASH_BUCKETS; 
      int n_type = ELFW(ST_TYPE)(info); 
      int n_binding = ELFW(ST_BIND)(info); 
        //开始symtab为空的所以肯定找不到,一项一项向里加。 
      for (sym = f->;symtab[hash]; sym; sym = sym->;next) 
        if (f->;symbol_cmp(sym->;name, name) == 0) 
          {//找到符号对应的值,保存原有的值。 
        int o_secidx = sym->;secidx; 
        int o_info = sym->;info; 
        int o_type = ELFW(ST_TYPE)(o_info); 
        int o_binding = ELFW(ST_BIND)(o_info); 
        /* A redefinition!  Is it legal?  */ 
        if (secidx == SHN_UNDEF) 
          return sym; 
        else if (o_secidx == SHN_UNDEF) 
          goto found; 
        else if (n_binding == STB_GLOBAL && o_binding == STB_LOCAL) 
          { 
            /* Cope with local and global symbols of the same name 
              in the same object file, as might have been created 
              by ld -r.  The only reason locals are now seen at this 
              level at all is so that we can do semi-sensible things 
              with parameters.  */ 
            struct obj_symbol *nsym, **p; 
            nsym = arch_new_symbol();//生成一个新的sym加到hash表中 
            nsym->;next = sym->;next; 
            nsym->;ksymidx = -1; 
            /* Excise the old (local) symbol from the hash chain.  */ 
            for (p = &f->;symtab[hash]; *p != sym; p = &(*p)->;next) 
              continue; 
            *p = sym = nsym; 
            goto found; 
          } 
        else if (n_binding == STB_LOCAL) 
          { 
            /* Another symbol of the same name has already been defined. 
              Just add this to the local table.  */ 
            sym = arch_new_symbol(); 
            sym->;next = NULL; 
            sym->;ksymidx = -1;//加到本地结构中 
            f->;local_symtab[symidx] = sym; 
            goto found; 
          } 
        else if (n_binding == STB_WEAK) 
          return sym; 
        else if (o_binding == STB_WEAK) 
          goto found; 
        /* Don't unify COMMON symbols with object types the programmer 
          doesn't expect.  */ 
        else if (secidx == SHN_COMMON 
            && (o_type == STT_NOTYPE || o_type == STT_OBJECT)) 
          return sym; 
        else if (o_secidx == SHN_COMMON 
            && (n_type == STT_NOTYPE || n_type == STT_OBJECT)) 
          goto found; 
        else 
          { 
            /* Don't report an error if the symbol is coming from 
              the kernel or some external module.  */ 
            if (secidx 
      /* Completely new symbol.  */ 
      //开始的时候都会走到这里来 
      sym = arch_new_symbol();//创建新的sym结构加入到hash表中 
      sym->;next = f->;symtab[hash]; 
      f->;symtab[hash] = sym; 
      sym->;ksymidx = -1; 
      if (ELFW(ST_BIND)(info) == STB_LOCAL && symidx != -1) { 
        if (symidx >;= f->;local_symtab_size) 
          error("local symbol %s with index %ld exceeds local_symtab_size %ld", 
            name, (long) symidx, (long) f->;local_symtab_size); 
        else 
          f->;local_symtab[symidx] = sym;//如果是文件内使用的加入到此结构中 
      } 
    found://以后用kernel中的symbol解析时会走到此处,value会添上正确的值 
      sym->;name = name; 
      sym->;value = value; 
      sym->;size = size; 
      sym->;secidx = secidx; 
      sym->;info = info; 
      sym->;r_type = 0;    /* should be R_arch_NONE for all arch */ 
      return sym; 

    ... 

    5.比较kernel版本和module的版本。 
    ... 
        /* Version correspondence?  */ 
        k_version = get_kernel_version(k_strversion); 
        m_version = get_module_version(f, m_strversion); 
        if (m_version == -1) { 
            error("couldn't find the kernel version the module was compiled for"); 
            goto out; 
        } 
        k_crcs = is_kernel_checksummed();//kernel的symbol是否含有RXXXXXXX的校验 
        m_crcs = is_module_checksummed(f);//module的symbol是否含有RXXXXXX的校验 
        if ((m_crcs == 0 || k_crcs == 0) &&//如果都含有,就不必比较version了 
            strncmp(k_strversion, m_strversion, STRVERSIONLEN) != 0) { 
            if (flag_force_load) {//-f选项会强行加载 
                lprintf("Warning: kernel-module version mismatch " 
                      " %s was compiled for kernel version %s " 
                    " while this kernel is version %s", 
                    filename, m_strversion, k_strversion); 
            } else { 
                if (!quiet) 
                    error("kernel-module version mismatch " 
                          " %s was compiled for kernel version %s " 
                          " while this kernel is version %s.", 
                          filename, m_strversion, k_strversion); 
                goto out; 
            } 
        } 
        if (m_crcs != k_crcs)//两者比一样 
        //就不使用strcmp比较符号名字,而用ncv_strcmp比较使得printk和pirntk_RXXXXX 
        //是相同的。还有重新创建hash表 
            obj_set_symbol_compare(f, ncv_strcmp, ncv_symbol_hash); 
    ... 
    6.add_kernel_symbols替换。o中的symbol为ksyms中的符号值 
    ... 
    static void add_kernel_symbols(struct obj_file *f) 

        struct module_stat *m; 
        size_t i, nused = 0; 
        /* 使用已有的module中的symbol */ 
        for (i = 0, m = module_stat; i ;nsyms && 
                add_symbols_from(f, SHN_HIRESERVE + 2 + i, m->;syms, m->;nsyms)) 
                m->;status = 1 /* used */, ++nused; 
        n_ext_modules_used = nused; 
        /* 使用kernel export的symbol  */ 
        if (nksyms) 
            add_symbols_from(f, SHN_HIRESERVE + 1, ksyms, nksyms); 

    /* 
    * Conditionally add the symbols from the given symbol set 
    * to the new module. 
    */ 
    static int add_symbols_from(struct obj_file *f, int idx, 
                    struct module_symbol *syms, size_t nsyms) 

        struct module_symbol *s; 
        size_t i; 
        int used = 0; 
        for (i = 0, s = syms; i ;name); 
            if (sym && !ELFW(ST_BIND) (sym->;info) == STB_LOCAL) { 
                sym = obj_add_symbol(f, (char *) s->;name, -1, 
                      ELFW(ST_INFO) (STB_GLOBAL, STT_NOTYPE), 
                            idx, s->;value, 0); 
                /*将hash表中的待解析的symbol的value添成正确的值 
                * Did our symbol just get installed? 
                * If so, mark the module as "used". 
                */ 
                if (sym->;secidx == idx) 
                    used = 1; 
            } 
        } 
        return used; 

    ... 
    7.create_this_module(f, m_name)生成module结构,加入module_list中。 
    ... 
    static int create_this_module(struct obj_file *f, const char *m_name) 

        struct obj_section *sec; 
        //创建一个.this节,节大小为struct module的大小 
        sec = obj_create_alloced_section_first(f, ".this", tgt_sizeof_long, 
                              sizeof(struct module)); 
        memset(sec->;contents, 0, sizeof(struct module)); 
        //向hash表中添加一个__this_module的符号 
        obj_add_symbol(f, "__this_module", -1, ELFW(ST_INFO) (STB_LOCAL, STT_OBJECT), 
                  sec->;idx, 0, sizeof(struct module)); 
        //创建.kstrtab节 
        obj_string_patch(f, sec->;idx, offsetof(struct module, name), m_name); 
        return 1; 

    ... 
    8.obj_check_undefineds检查是否还有un_def的symbol 
    ... 
    obj_check_undefineds(struct obj_file *f, int quiet) 

      unsigned long i; 
      int ret = 1; 
      for (i = 0; i ;symtab; sym ; sym = sym->;next) 
        if (sym->;secidx == SHN_UNDEF) 
          { 
            if (ELFW(ST_BIND)(sym->;info) == STB_WEAK) 
              { 
            sym->;secidx = SHN_ABS; 
            sym->;value = 0; 
              } 
            else if (sym->;r_type) /* assumes R_arch_NONE is 0 on all arch */ 
              { 
            if (!quiet) 
                error("unresolved symbol %s", sym->;name); 
            ret = 0; 
              } 
          } 
        } 
      return ret; 

    ... 
    9.add_archdata添加结构相关的section,不过i386没什么用。 
    10.add_kallsyms如果symbol使用的都是kernel提供的,就添加一个.kallsyms section 
    11.obj_load_size计算module的大小 
    ... 
    /* Module has now finished growing; find its size and install it.  */ 
    m_size = obj_load_size(f);    /* DEPMOD */ 
    ... 
    obj_load_size (struct obj_file *f) 

      unsigned long dot = 0; 
      struct obj_section *sec; 
      /* Finalize the positions of the sections relative to one another.  */ 
      for (sec = f->;load_order; sec ; sec = sec->;load_next) 
        {//按照装载的顺序,计算module的大小。 
          ElfW(Addr) align; 
          align = sec->;header.sh_addralign; 
          if (align && (dot & (align - 1))) 
        dot = (dot | (align - 1)) + 1; 
          sec->;header.sh_addr = dot; 
          dot += sec->;header.sh_size; 
        } 
      return dot; 

    ... 
    12.create_module调用sys_create_module系统调用创建模块,分配module的空间 
    13.obj_relocate 
    ... 
    int 
    obj_relocate (struct obj_file *f, ElfW(Addr) base) 
    {//base就是create_module时分配的地址m_addr 
      int i, n = f->;header.e_shnum; 
      int ret = 1; 
      /* Finalize the addresses of the sections.  */ 
    //将各个section的sh_addr(原来是相对地址)加上此基地址 
      arch_finalize_section_address(f, base); 
      /* And iterate over all of the relocations.  */ 
      for (i = 0; i 
          relsec = f->;sections;//找到重定位section 
          if (relsec->;header.sh_type != SHT_RELM) 
        continue; 
        //relse的sh_link指向.symtab节,sh_info指向.text节 
          symsec = f->;sections[relsec->;header.sh_link]; 
          targsec = f->;sections[relsec->;header.sh_info]; 
          //symtab节的sh_link指向符号字符串那节。 
          strsec = f->;sections[symsec->;header.sh_link]; 
        //获得相应section的内容 
          rel = (ElfW(RelM) *)relsec->;contents; 
          relend = rel + (relsec->;header.sh_size / sizeof(ElfW(RelM))); 
          symtab = (ElfW(Sym) *)symsec->;contents; 
          strtab = (const char *)strsec->;contents; 
          for (; rel 
          /* Attempt to find a value to use for this relocation.  */ 
        //根据rel的描述找到需要重定位的符号索引值 
          symndx = ELFW(R_SYM)(rel->;r_info); 
          if (symndx) 
            {//同上,找到符号的描述,存入intsym变量中。 
              /* Note we've already checked for undefined symbols.  */ 
              extsym = &symtab[symndx]; 
              if (ELFW(ST_BIND)(extsym->;st_info) == STB_LOCAL) 
            { 
              /* Local symbols we look up in the local table to be sure 
                we get the one that is really intended.  */ 
              intsym = f->;local_symtab[symndx]; 
            } 
              else 
            { 
              /* Others we look up in the hash table.  */ 
              const char *name; 
              if (extsym->;st_name) 
                name = strtab + extsym->;st_name; 
              else 
                name = f->;sections[extsym->;st_shndx]->;name; 
              intsym = obj_find_symbol(f, name); 
            } 
    //虽然有些函数的symbol的value已经被填写过了,但是rel中还有象.rodata节这样的需要 
    //relocate的符号,因此他的value是0+section[.rodata]->;header->;sh_addr的值 
              value = obj_symbol_final_value(f, intsym); 
            } 
    #if SHT_RELM == SHT_RELA 
    #if defined(__alpha__) && defined(AXP_BROKEN_GAS) 
          /* Work around a nasty GAS bug, that is fixed as of 2.7.0.9.  */ 
          if (!extsym || !extsym->;st_name || 
              ELFW(ST_BIND)(extsym->;st_info) != STB_LOCAL) 
    #endif 
          value += rel->;r_addend; 
    #endif 
          /* Do it! *///此函数将.text中需要relocate的地方都填写正确value 
          //非常重要的一个函数 
          switch (arch_apply_relocation(f,targsec,symsec,intsym,rel,value)) 
            {//f:objfile结构,targsec:.text节,symsec:.symtab节,rel:.rel结构,value:绝对地址 
            case obj_reloc_ok: 
              break; 
            case obj_reloc_overflow: 
              errmsg = "Relocation overflow"; 
              goto bad_reloc; 
            case obj_reloc_dangerous: 
              errmsg = "Dangerous relocation"; 
              goto bad_reloc; 
            case obj_reloc_unhandled: 
              errmsg = "Unhandled relocation"; 
              goto bad_reloc; 
            case obj_reloc_constant_gp: 
              errmsg = "Modules compiled with -mconstant-gp cannot be loaded"; 
              goto bad_reloc; 
            bad_reloc: 
              if (extsym) 
            { 
              error("%s of type %ld for %s", errmsg, 
                (long)ELFW(R_TYPE)(rel->;r_info), 
                strtab + extsym->;st_name); 
            } 
              else 
            { 
              error("%s of type %ld", errmsg, 
                (long)ELFW(R_TYPE)(rel->;r_info)); 
            } 
              ret = 0; 
              break; 
            } 
        } 
        } 
      /* Finally, take care of the patches.  */ 
      if (f->;string_patches) 
        { 
          struct obj_string_patch_struct *p; 
          struct obj_section *strsec; 
          ElfW(Addr) strsec_base; 
          strsec = obj_find_section(f, ".kstrtab"); 
          strsec_base = strsec->;header.sh_addr; 
          for (p = f->;string_patches; p ; p = p->;next) 
        { 
          struct obj_section *targsec = f->;sections[p->;reloc_secidx]; 
          *(ElfW(Addr) *)(targsec->;contents + p->;reloc_offset) 
            = strsec_base + p->;string_offset; 
        } 
        } 
      if (f->;symbol_patches) 
        { 
          struct obj_symbol_patch_struct *p; 
          for (p = f->;symbol_patches; p; p = p->;next) 
        { 
          struct obj_section *targsec = f->;sections[p->;reloc_secidx]; 
          *(ElfW(Addr) *)(targsec->;contents + p->;reloc_offset) 
            = obj_symbol_final_value(f, p->;sym); 
        } 
        } 
      return ret; 

    ... 
    14.init_module初始化有create_module生成的module,是由sys_init_module系统调用实现的。 
    ... 
    static int init_module(const char *m_name, struct obj_file *f, 
                  unsigned long m_size, const char *blob_name, 
                  unsigned int noload, unsigned int flag_load_map) 
    {//一般情况下后三个参数都是0 
        struct module *module; 
        struct obj_section *sec; 
        void *image; 
        int ret = 0; 
        tgt_long m_addr; 
    //找到原来用create_this_module生成的.this节 
        sec = obj_find_section(f, ".this"); 
        module = (struct module *) sec->;contents; 
        m_addr = sec->;header.sh_addr;//base基地址 
        module->;size_of_struct = sizeof(*module);//module结构大小 
        module->;size = m_size;//module总共的大小 
        module->;flags = flag_autoclean ? NEW_MOD_AUTOCLEAN : 0; 
        sec = obj_find_section(f, "__ksymtab"); 
        if (sec && sec->;header.sh_size) {//模块自己export的symbol 
            module->;syms = sec->;header.sh_addr; 
            module->;nsyms = sec->;header.sh_size / (2 * tgt_sizeof_char_p); 
        } 
        if (n_ext_modules_used) {//填写此module依靠的模块 
            sec = obj_find_section(f, ".kmodtab"); 
            module->;deps = sec->;header.sh_addr; 
            module->;ndeps = n_ext_modules_used; 
        } 
        //填写init_module,cleanup_module的地址 
        module->;init = obj_symbol_final_value(f, obj_find_symbol(f, "init_module")); 
        module->;cleanup = obj_symbol_final_value(f, 
            obj_find_symbol(f, "cleanup_module")); 
        //exception_table_entry的地址,一般没有 
        sec = obj_find_section(f, "__ex_table"); 
        if (sec) { 
            module->;ex_table_start = sec->;header.sh_addr; 
            module->;ex_table_end = sec->;header.sh_addr + sec->;header.sh_size; 
        } 
        sec = obj_find_section(f, ".text.init"); 
        if (sec) {//这个runsize不知道是什么东西,一般没有 
            module->;runsize = sec->;header.sh_addr - m_addr; 
        } 
        sec = obj_find_section(f, ".data.init"); 
        if (sec) { 
            if (!module->;runsize || 
                module->;runsize >; sec->;header.sh_addr - m_addr) 
                module->;runsize = sec->;header.sh_addr - m_addr; 
        } 
        sec = obj_find_section(f, ARCHDATA_SEC_NAME); 
        if (sec && sec->;header.sh_size) { 
            module->;archdata_start = sec->;header.sh_addr; 
            module->;archdata_end = module->;archdata_start + sec->;header.sh_size; 
        } 
        sec = obj_find_section(f, KALLSYMS_SEC_NAME); 
        if (sec && sec->;header.sh_size) { 
            module->;kallsyms_start = sec->;header.sh_addr; 
            module->;kallsyms_end = module->;kallsyms_start + sec->;header.sh_size; 
        } 
        if (!arch_init_module(f, module))//i386此函数直接返回 
            return 0; 
        /* 
        * Whew!  All of the initialization is complete. 
        * Collect the final module image and give it to the kernel. 
        */ 
        image = xmalloc(m_size); 
        obj_create_image(f, image);//现在用户空间分配module的image的内存,然后将 
        //各个section的内容拷入image中,包括原文件中的section和后来构造的section 
        if (flag_load_map) 
            print_load_map(f); 
        if (blob_name) { 
            int fd, l; 
            fd = open(blob_name, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 
            if (fd 
        if (ret == 0 && !noload) {//调用系统调用完成最后的insmod工作。 
            fflush(stdout);        /* Flush any debugging output */ 
            ret = sys_init_module(m_name, (struct module *) image); 
            if (ret) { 
                error("init_module: %m"); 
                lprintf("Hint: insmod errors can be caused by incorrect module parameters, " 
                    "including invalid IO or IRQ parameters"); 
            } 
        } 
        free(image); 
        return ret == 0; 

    ... 
    最后清理一下思路。 
    1.get_kernel_info函数负责取得kernel中先以注册的modules,放入module_stat中. 
    2.set_ncv_prefix(NULL);判断symbolname中是否有前缀,象_smp之类,一般没有。 
    3.检查是否已有同名的module。 
    4.obj_load,将。o文件读入到struct obj_file结构f中。 
    5.比较kernel版本和module的版本,在版本的判断中,不是想象中的那样简单,还有是否有checksum的逻 辑关系。 
    6.add_kernel_symbols替换.o中的symbol为ksyms中的符号值 
    7.create_this_module(f, m_name)生成module结构,加入module_list中。 
    8.obj_check_undefineds检查是否还有un_def的symbol 
    9.add_archdata添加结构相关的section,不过i386没什么用。 
    10.add_kallsyms如果symbol使用的都是kernel提供的,就添加一个.kallsyms section 
    11.obj_load_size计算module的大小 
    12.create_module调用sys_create_module系统调用创建模块,分配module的空间 
    13.obj_relocate重定位module文件中.text中的地址 
    14.init_module先在用户空间创建module结构的image影响,是由sys_init_module系统调用实现向kernel的 copy。 
    经过对insmod主要流程的分析,发现原来的理解没有偏差,那问什么会被成功加载呢?后来仔细用gdb跟 了一把,发现所有对symtab的操作都被调过,因为它一直为NULL嘛,可是发现在没有symtab的情况 下,2.4.6竟然一个判断都没有,一路平稳的跑了下来,然后在module结构init和cleanup指针都为NULL的情 况下被加载成功。 
    而2.4.10的modutils中的insmod对strip后的module加载后有除零的异常.在obj_load函数实现中有 
    278                nsyms = symtab->;header.sh_size / symtab->;header.sh_entsize; 
    由于module中根本没有symtab所以这个结构中的所有值都为0。 
    后话: 
        其实将这篇文章加到backdoor研究中有些牵强,但是前一阵和backend讨论内核方式后门实现时讨论 了kernel image的方式,但只是限于理论上的讨论,考虑到以后有这方面实现的可能,那时有可能要自己 实现一个简单的insmod流程完成对symbol的解析和重定位,关于那三个module方面的系统调用在《情景 分析》中有较详细的讨论,就不班门弄斧了。

  • 相关阅读:
    linux C++ 通讯架构(二)linux相关
    TCP/IP协议分层详解
    公网Ip和私网ip
    IP地址的含义
    IP地址,子网掩码、默认网关,DNS服务器是什么意思?
    Iptables&Firewalld防火墙
    Linux服务器性能评估与优化
    Linux性能优化
    基于mysql-proxy实现mysql读写分离
    linux下搭建NFS服务器
  • 原文地址:https://www.cnblogs.com/heyp/p/3403976.html
Copyright © 2011-2022 走看看