[转]LKM backdoor研究linux系列--insmod源码分析篇

来源:互联网 发布:办公助手软件 编辑:程序博客网 时间:2024/04/29 07:50

转自:http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=527771

作者:jbtzhm <jbtzhm@nsfocus.com>;
主页:http://www.nsfocus.com/
日期:2002-08-16

问题的提出是前一阵和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.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 = 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) < 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;
sec->;name = shstrtab + sec->;header.sh_name;
}//根据strtab,取得每个section的名字

for (i = 0; i < shnum; ++i)
{
struct obj_section *sec = f->;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 < 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;
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; 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;//找到重定位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))
{//fbjfile结构,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