八月的太阳晒得黄黄的,谁说这世界不是黄金?小雀儿在树荫里打盹,孩子们在草地里打滚。八月的太阳晒得黄黄的,谁说这世界不是黄金?金黄的树林,金黄的草地,小雀们合奏着欢畅的清音:金黄的茅舍,金黄的麦屯,金黄是老农们的笑声。 —— 徐志摩·八月的太阳
ilocker:关注 Android 安全(新入行,0基础) QQ: 2597294287
在生成 native 程序时,在链接阶段会传入一个链接脚本,在该脚本中指定了程序的入口函数。
可以看到,在默认的链接脚本 armelf_linux_eabi.x 中,入口函数指定为“_start”。
在 NDK 目录下搜索 ldscripts,所有的链接脚本都在该目录中。
_start 函数定义在 (libc 的 bionic 目录下) Crtbegin.c 中。
在 _start 中又调用了 __libc_init,并将 main 函数的地址作为参数传入。
再看 __libc_init 的源码 (在 libc 的 bionic 目录下的 Libc_init_static.cpp 文件中):
在 __libc_init 中先进行一些初始化工作,再调用 preinit_array、init_array,最后调用由参数 slingshot 传入的 main 函数。
上面的描述针对静态链接的程序,对于动态链接的程序还稍有不同。
对于动态链接 (在 gcc 命令行中指定 -Bdynamic),在生成的可执行文件的 .interp 中保存了程序解释器的文件路径,默认为“/system/bin/linker”。
当执行动态链接的程序时,系统会解析该 ELF 文件,并找到 .interp 中保存的程序解释器,也就是 android linker。然后先执行 linker,linker 会加载该程序的所依赖的一系列 so,并最后再调用该可执行程序。
linker 的入口函数 _start 不在 Crtbegin.c 中,而是在 linker 源码的 begin.S 文件中。
调用 __linker_init 函数完成 linker 的“自举”,并进行一些初始化工作,最后会返回原 native 程序的入口函数的地址 (从 native 程序的 ELF Header 中找到)。
mov pc, r0,跳转到 native 程序的入口函数 (_start) 去执行,接下来的执行过程与上面一样。_start 调用 __libc_init。动态链接程序的 __libc_init 定义在 Libc_init_dynamic.cpp 文件中 (静态链接程序的 __libc_init 定义在 Libc_init_static.cpp 文件中)。
看起来比静态链接程序的 __libc_init 简单点,因为一些初始化工作由 linker 完成了。
总结:静态链接与动态链接的 native 程序的入口函数都是 _start。动态链接的程序在执行 _start 之前,需要先由 linker 加载依赖库,并进行一些初始化工作。
学习资料: 《Android 软件安全与逆向分析》