问题的提出是前一阵和lgx聊天发现,一个被strip的module也可以被成功的insmod,当时知道一些insmod 的原理觉得不太可能,因为一个正常的module文件其实就是标准的ELF格式object文件,如果将他的 symtab strip掉的话,那些printk这类的symbol将不能被正常的解析,理论上是不可能加载成功的,于是 做了一个简单的module在turbo7上测试了一把,modutils的版本是2.4.6,出人意料的是竟然成功的被加 载,真是觉得真是不可思议,因此觉得有必要研究一下insmod的具体实现,最好的方法当然是go to the source
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的所有项。
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;
/* 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. */
/* 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;
}
}
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);
...
/*
* 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;
}
}
...
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;
}
}
}
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;
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;
}
}
}
/*
* 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");
}
}
参考资料:
《linux内核源代码情景分析》 毛德操,胡希明
linux-2.4.8 kernel source
modutils-2.4.6 source
modutils-2.4.10 source作者: t920 时间: 2005-04-11 14:49 标题: [转]LKM backdoor研究linux系列--insmod源码分析篇 go to the source ... ...go to the source ... ...go to the source ... ...
(头疼中... ...)作者: flighttop 时间: 2005-04-12 05:29 标题: [转]LKM backdoor研究linux系列--insmod源码分析篇 3Q for your article. I would like to learn more about kernel analysis and programming. Up!作者: zsl_tdr 时间: 2005-07-29 16:58 标题: [转]LKM backdoor研究linux系列--insmod源码分析篇 请教斑竹 insmod.c 在那里有?作者: frosty 时间: 2005-07-29 18:02 标题: [转]LKM backdoor研究linux系列--insmod源码分析篇 我头大了
3Q a lot作者: 山东大葱 时间: 2006-12-03 18:38
忒深了!
俺一只脚伸下去探探,结果掉进去还没够到底。作者: accessory 时间: 2010-11-05 22:53
好贴要顶下。。。作者: bsea 时间: 2012-04-24 22:26
time is precious!作者: ulovko 时间: 2012-07-25 21:20
多谢分享 美文 ^_^