前段时间遇到个怪问题,有个so,用readelf跟ida,或者直接写工具分析,都能找到导出函数名对应的符号表,info啊,之类的也都对。
但在手机上dlsym却不行,困扰了一下,后来发现是有人手动改了导出函数的名字,但是没有在hash节做处理。那么要如何更改hash节呢。
首先找到符号表段,然后根据符号表的name找到名字。然后更改。
但是这还没完,由于linker加载的时候是根据elfhash查找的名字,所以还要改hash段。
static ElfW(Sym)* soinfo_elf_lookup(soinfo* si, unsigned hash, const char* name, const SymbolLookupScope& lookup_scope) {
ElfW(Sym)* symtab = si->symtab;
const char* strtab = si->strtab;
TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd",
name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
ElfW(Sym)* s = symtab + n;
if (strcmp(strtab + s->st_name, name)) continue;
switch (ELF_ST_BIND(s->st_info)) {
case STB_GLOBAL:
case STB_WEAK:
if (s->st_shndx == SHN_UNDEF) {
continue;
}
TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
name, si->name, reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
return s;
case STB_LOCAL:
if (lookup_scope != SymbolLookupScope::kAllowLocal) {
continue;
}
TRACE_TYPE(LOOKUP, "FOUND LOCAL %s in %s (%p) %zd",
name, si->name, reinterpret_cast<void*>(s->st_value),
static_cast<size_t>(s->st_size));
return s;
default:
__libc_fatal("ERROR: Unexpected ST_BIND value: %d for '%s' in '%s'",
ELF_ST_BIND(s->st_info), name, si->name);
}
}
return NULL;
}
更改方法:
首先根据字符串节的偏移,对比符号表,找到这个符号在符号表中的偏移。每个符号表是0x10个字节,这个要注意。
然后用如下函数算出你更改名字后的hash。
static unsigned elfhash(const char* _name) {
const unsigned char* name = reinterpret_cast<const unsigned char*>(_name);
unsigned h = 0, g;
while (*name) {
h = (h << 4) + *name++;
g = h & 0xf0000000;
h ^= g;
h ^= g >> 24;
}
return h;
}
hash节 结构如下:
在数据中如下:
根据算好的hash值,用公式unsigned n = bucket[hash % nbucket]; unsigned n2 = chain[n]
n2就是改符号应该存在的符号表偏移,然后用这个偏移到符号表去找,如果正好就是这个,那么恭喜你。。运气真好。
如果是被手动修改了符号名字符串,那么一般都不会正好是这个偏移。
如果不是,就要继续,n2 = chain[n2] 如果这时的n2不等于0,就要继续n2 = chain[n2]直到等于0为止,然后在这个偏移写入之前查好的符号表偏移。
大功告成!