转载_LKM backdoor研究linux系列--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\n"); 
             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 < nmod; 
         ++i, ++m, mn += strlen(mn) + 1) { 
         struct module_info info; 
         //info包括module的地址,大小,flag和使用计数器。 
         m->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 < ret; 
                 ++j, mr += strlen(mr) + 1) { 
                 for (mm = 0; mm < i; ++mm) { 
                     if (strcmp(mr, module_stat[mm].name) == 0) { 
                         m->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 < nsyms; ++j, ++s) 
                 s->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 < nsyms; ++j, ++s) 
             s->name += (unsigned long) syms; 
     } 
  
     return 1; 

  
  
2.set_ncv_prefix(NULL);判断symbolname中是否有前缀,象_smp之类,这里没有 
。 
  
  
3.检查是否已有同名的module。 
... 
     for (i = 0; i < n_module_stat; ++i) { 
         if (strcmp(module_stat[i].name, m_name) == 0) { 
             error("a module named %s already exists", m_name); 
             goto out; 
         } 
     }//判断是否已经存在。 
... 
  
  
  
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 < shnum; ++i) 
     { 
       struct obj_section *sec; 
  
       f->sections[i] = sec = arch_new_section();//分配内存给每个 
section 
       memset(sec, 0, sizeof(*sec)); 
  
       sec->header = section_headers[i];//设置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) < 0) 
         return NULL; 
           break; 
         } 
  
       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 < shnum; ++i) 
     { 
       struct obj_section *sec = f->sections[i]; 
       sec->name = shstrtab + sec->header.sh_name; 
     }//根据strtab,取得每个section的名字 
  
   for (i = 0; i < shnum; ++i) 
     { 
       struct obj_section *sec = f->sections[i]; 
  
       /* .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 < nsym; ++j, ++sym) 
           { 
         const char *name; 
         if (sym->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 < shnum; ++i) 
     { 
       struct obj_section *sec = f->sections[i]; 
       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 < nrel; ++j, ++rel) 
           { 
         ElfW(Sym) *extsym; 
         struct obj_symbol *intsym; 
         unsigned long symndx;//取得这个需relocate的符号索引值 
         symndx = ELFW(R_SYM)(rel->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 <= SHN_HIRESERVE) 
           error("%s multiply defined", name); 
         return sym; 
       } 
       } 
  
   /* 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\n" 
                   "\t%s was compiled for kernel version %s\n" 
                 "\twhile this kernel is version %s", 
                 filename, m_strversion, k_strversion); 
         } else { 
             if (!quiet) 
  
                 error("kernel-module version mismatch\n" 
                       "\t%s was compiled for kernel version %s\n" 
                       "\twhile 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 < n_module_stat; ++i, ++m) 
         if (m->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 < nsyms; ++i, ++s) { 
         /* 
         * Only add symbols that are already marked external. 
         * If we override locals we may cause problems for 
         * argument initialization. 
         * We will also create a false dependency on the module. 
         */ 
         struct obj_symbol *sym; 
         //表中是否有需要此名字的的symbol 
         sym = obj_find_symbol(f, (char *) s->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 < HASH_BUCKETS; ++i) 
     { 
       struct obj_symbol *sym; 
       for (sym = f->symtab[i]; 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 < n; ++i) 
     { 
       struct obj_section *relsec, *symsec, *targsec, *strsec; 
       ElfW(RelM) *rel, *relend; 
       ElfW(Sym) *symtab; 
       const char *strtab; 
  
       relsec = f->sections[i];//找到重定位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 < relend; ++rel) 
     { 
       ElfW(Addr) value = 0; 
       struct obj_symbol *intsym = NULL; 
       unsigned long symndx; 
       ElfW(Sym) *extsym = 0; 
       const char *errmsg; 
  
       /* 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 < 0) { 
             error("open %s failed %m", blob_name); 
             ret = -1; 
         } 
         else { 
             if ((l = write(fd, image, m_size)) != m_size) { 
                 error("write %s failed %m", blob_name); 
                 ret = -1; 
             } 
             close(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内核源代码情景分析》    毛德操,胡希明 
     linux-2.4.8 kernel source 
     modutils-2.4.6 source 
     modutils-2.4.10 source 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值