系统加载so,在完成装载、映射和重定向以后,就首先执行.init和.init_array段的代码.
首先来看看加载so的流程:http://androidxref.com/4.4_r1/xref/bionic/linker/linker.cpp
soinfo* do_dlopen(const char* name, int flags) {
if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL)) != 0) {
DL_ERR(“invalid flags to dlopen: %x”, flags);
return NULL;
}
set_soinfo_pool_protection(PROT_READ | PROT_WRITE);
soinfo* si = find_library(name);
if (si != NULL) {
si->CallConstructors();
}
set_soinfo_pool_protection(PROT_READ);
return si;
}
find_library 会判断so是否已经加载,如果没有加载,对so进行加载,完成一些初始化工作
static soinfo* find_library_internal(const char* name) {
if (name == NULL) {
return somain;
}
soinfo* si = find_loaded_library(name);
if (si != NULL) {
if (si->flags & FLAG_LINKED) {
return si;
}
DL_ERR(“OOPS: recursive link to \”%s\””, si->name);
return NULL;
}
TRACE(“[ ‘%s’ has not been loaded yet. Locating…]”, name);
si = load_library(name);
if (si == NULL) {
return NULL;
}
// At this point we know that whatever is loaded @ base is a valid ELF
// shared library whose segments are properly mapped in.
TRACE(“[ init_library base=0x%08x sz=0x%08x name=’%s’ ]”,
si->base, si->size, si->name);
if (!soinfo_link_image(si)) {
munmap(reinterpret_cast<void*>(si->base), si->size);
soinfo_free(si);
return NULL;
}
782 return si;
783}
find_loaded_library:判断so文件是不是已经被加载了,如果是直接返回句柄,否则就是加载
load_library:加载so,包括打开so文件,读取so文件的一部分信息,给so文件分配一个soinfo类型的结构体
soinfo_link_image:加载so,对结构体变量进行赋值
void soinfo::CallConstructors() {
if (constructors_called) {
return;
}
// We set constructors_called before actually calling the constructors, otherwise it doesn’t
// protect against recursive constructor calls. One simple example of constructor recursion
// is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
// 1. The program depends on libc, so libc’s constructor is called here.
// 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
// 3. dlopen() calls the constructors on the newly created
// soinfo for libc_malloc_debug_leak.so.
// 4. The debug .so depends on libc, so CallConstructors is
// called again with the libc soinfo. If it doesn’t trigger the early-
// out above, the libc constructor will be called again (recursively!).
constructors_called = true;
if ((flags & FLAG_EXE) == 0 && preinit_array != NULL) {
// The GNU dynamic linker silently ignores these, but we warn the developer.
PRINT(“\”%s\”: ignoring %d-entry DT_PREINIT_ARRAY in shared library!”,
name, preinit_array_count);
}
if (dynamic != NULL) {
for (Elf32_Dyn* d = dynamic; d->d_tag != DT_NULL; ++d) {
if (d->d_tag == DT_NEEDED) {
const char* library_name = strtab + d->d_un.d_val;
TRACE(“\”%s\”: calling constructors in DT_NEEDED \”%s\””, name, library_name);
find_loaded_library(library_name)->CallConstructors();
}
}
}
TRACE(“\”%s\”: calling constructors”, name);
// DT_INIT should be called before DT_INIT_ARRAY if both are present.
CallFunction(“DT_INIT”, init_func);
CallArray(“DT_INIT_ARRAY”, init_array, init_array_count, false);
}
这里的DT_INIT和DT_INIT_ARRAY到底是什么呢?
init_func和init_array都是结构体soinfo的成员变量。在soinfo_link_image加载so的时候进行赋值.
#define DT_INIT 12 /* Address of initialization function */
#define DT_INIT_ARRAY 25 /* Address of initialization function array */
case DT_INIT:
si->init_func = reinterpret_cast<linker_function_t>(base + d->d_un.d_ptr);
DEBUG(“%s constructors (DT_INIT) found at %p”, si->name, si->init_func);
break;
case DT_INIT_ARRAY:
si->init_array = reinterpret_cast<linker_function_t*>(base + d->d_un.d_ptr);
DEBUG(“%s constructors (DT_INIT_ARRAY) found at %p”, si->name, si->init_array);
break;
进入CallFunction:
void soinfo::CallFunction(const char* function_name UNUSED, linker_function_t function) {
if (function == NULL || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
return;
}
TRACE(“[ Calling %s @ %p for ‘%s’ ]”, function_name, function, name);
function();
TRACE(“[ Done calling %s @ %p for ‘%s’ ]”, function_name, function, name);
// The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures
// are still writable. This happens with our debug malloc (see http://b/7941716).
set_soinfo_pool_protection(PROT_READ | PROT_WRITE);
}
结论:系统加载so,在完成装载、映射和重定向以后,就首先执行.init和.init_array段的代码,之后如果存在JNI_OnLoad就调用该函数.我们要对一个so进行分析,需要先看看有没有.init_array section和.init section,so加壳一般会在初始化函数进行脱壳操作。
如何在.init和.init_array段添加我们的函数
[1] 共享构造函数,在函数声明时加上"__attribute__((constructor))"属性 void __attribute__((constructor)) init_function(void); 对应有共享虚构函数,在程序exit()或者dlclose()返回前执行 void __attribute__((destructor)) fini_function(void); [2]c++ 静态构造函数
在.init和.init_array下断点
1.首先把/system/bin/的linker文件拿出来用IDA打开,找到我们刚刚所说的调用 function() 的位置。
可以直接搜索字符串Calling %s @ %p for ‘%s’
然后来到字符串所在的位置:
图中BLX R4就是我们要找的调用function() 的位置。加下文件偏移:0x272C
2.附加调试程序
找到linker模块加载的基地址:0xB6F9F000
然后加上刚刚的文件偏移:0xB6F9F000+0x272C=0xB6FA172C
定位到该地址,下好断点:
jdb -connect com.sun.jdi.SocketAttach:port=8700,hostname=localhost
继续F9:
现在看到在加载libexec.so的时候,断在了我们的断点上,F7进去就是初始化段的地方了。
Permalink
不太懂 “首先把/system/bin/的linker文件拿出来用IDA打开,找到我们刚刚所说的调用 function() 的位置。
可以直接搜索字符串Calling %s @ %p for ‘%s’”.怎么知道function位置可以通过搜字符串”Calling %s @ %p for ‘%s”来得到?
Permalink
和源码对应的