首页
论坛
课程
招聘
[原创] 浅谈ELF可执行文件的启动流程
2019-11-17 17:11 4544

[原创] 浅谈ELF可执行文件的启动流程

2019-11-17 17:11
4544

博客:www.wireghost.cn

浅谈ELF可执行文件的启动流程

源码分析

无论是动态链接还是静态链接的原生程序,在链接阶段都会传入一个链接脚本。根据链接时指定参数的不同,所传入的链接脚本也不一样。在 NDK 目录下检索 ldscripts,所有的链接脚本都在该路径中。默认情况下,会传入armelf_linux_eabi.x脚本文件。

在默认的链接脚本中,声明了可执行文件在进程中的基地址为0x00008000,并将"_start"指定为入口函数。这里,我们可以动手用IDA调试一个原生程序进行对比确认。。

其中,_start函数定义在Android源码路径中的/bionic/libc/arch-arm/bionic/Crtbegin.c

#include "../../bionic/libc_init_common.h"
#include <stddef.h>
#include <stdint.h>

__attribute__ ((section (".preinit_array")))
void (*__PREINIT_ARRAY__)(void) = (void (*)(void)) -1;

__attribute__ ((section (".init_array")))
void (*__INIT_ARRAY__)(void) = (void (*)(void)) -1;

__attribute__ ((section (".fini_array")))
void (*__FINI_ARRAY__)(void) = (void (*)(void)) -1;

__LIBC_HIDDEN__ void _start() {
  structors_array_t array;
  array.preinit_array = &__PREINIT_ARRAY__;
  array.init_array = &__INIT_ARRAY__;
  array.fini_array = &__FINI_ARRAY__;

  void* raw_args = (void*) ((uintptr_t) __builtin_frame_address(0) + sizeof(void*));
  __libc_init(raw_args, NULL, &main, &array);
}

#include "__dso_handle.h"
#include "atexit.h"

_start方法中调用了_libc_init,并将main函数的地址和指向preinit_array、init_array、fini_array数组的指针作为参数传入。再来看下_libc_init的实现,这部分在Android源码中的路径为/bionic/libc/bionic/libc_init_static.cpp及/bionic/libc/bionic/libc_init_dynamic.cpp,分别对应静态链接和动态链接的程序。
这里,让我们先看下静态链接的部分。在__libc_init中会先进行一些初始化工作,再调用preinit_array、init_array,最后调用由参数slingshot传入的main函数。

__noreturn void __libc_init(void* raw_args,
                            void (*onexit)(void),
                            int (*slingshot)(int, char**, char**),
                            structors_array_t const * const structors) {
  KernelArgumentBlock args(raw_args);
  __libc_init_tls(args);
  __libc_init_common(args);

  apply_gnu_relro();

  // Several Linux ABIs don't pass the onexit pointer, and the ones that
  // do never use it.  Therefore, we ignore it.

  call_array(structors->preinit_array);
  call_array(structors->init_array);

  // The executable may have its own destructors listed in its .fini_array
  // so we need to ensure that these are called when the program exits
  // normally.
  if (structors->fini_array != NULL) {
    __cxa_atexit(__libc_fini,structors->fini_array,NULL);
  }

  exit(slingshot(args.argc, args.argv, args.envp));
}

静态链接的程序在启动时不需要额外加载其他的动态库,不过这类程序相对较少,Android系统已知的有init、adbd、linker等程序。实际我们遇到的大多数可执行文件都是动态链接的,它的情况较上面的描述稍有不同。动态链接的程序在运行前还需要做一些初始化工作,如运行所依赖的动态库需要先被载入内存。当执行动态链接的程序时,系统会解析该ELF文件,并找到.interp段中所保存的程序解释器,默认是"/system/bin/linker"。然后先执行linker,linker会加载该程序的所依赖的一系列so,最后再调用该可执行程序。
值得注意的是,linker的入口函数_start并不在Crtbegin.c中。在源码/bionic/linker/Android.mk文件中,linker指定了自己的启动函数所在路径,即/bionic/linker/arch/arm/begin.S

    .text
    .align 4
    .type _start,#function
    .globl _start

_start:
    mov r0, sp
    mov r1, #0
    bl  __linker_init

    /* linker init returns the _entry address in the main image */
    mov pc, r0

    .section .ctors, "wa"
    .globl __CTOR_LIST__
__CTOR_LIST__:
    .long -1

首先,调用_linker_init函数完成linker的"自举",并进行一些初始化工作,最后会返回原native程序的入口函数地址(根据native程序的文件头获取)。
至于"mov pc, r0"这条语句,它的作用则是跳转到native程序的入口函数(_start)去执行。
再往后的调用过程与之前已经描述过的一样,都是_start调用_libc_init。此外,前面还提到过,动态链接程序的__libc_init是定义在libc_init_dynamic.cpp文件中。

// This function is called from the executable's _start entry point
// (see arch-$ARCH/bionic/crtbegin_dynamic.S), which is itself
// called by the dynamic linker after it has loaded all shared
// libraries the executable depends on.
//
// Note that the dynamic linker has also run all constructors in the
// executable at this point.
__noreturn void __libc_init(void* raw_args,
                            void (*onexit)(void),
                            int (*slingshot)(int, char**, char**),
                            structors_array_t const * const structors) {

  KernelArgumentBlock args(raw_args);

  // Several Linux ABIs don't pass the onexit pointer, and the ones that
  // do never use it.  Therefore, we ignore it.

  // The executable may have its own destructors listed in its .fini_array
  // so we need to ensure that these are called when the program exits
  // normally.
  if (structors->fini_array) {
    __cxa_atexit(__libc_fini,structors->fini_array,NULL);
  }

  exit(slingshot(args.argc, args.argv, args.envp));
}

可以发现,这段代码比静态链接程序的__libc_init还要简单些,这是因为一些初始化工作由linker完成了。

总结

无论是静态链接还是动态链接,native程序的入口函数都是_start。里面存在main方法和执向init_array、fini_array等数组的指针,它们对应初始化函数和析构函数。其中,动态链接的程序在执行_start之前,会先由linker加载所需的依赖库,并进行一些初始化工作。此外,可执行文件在进程中的基地址为0x00008000,关于这点可以做下备忘。。


第五届安全开发者峰会(SDC 2021)议题征集正式开启!

收藏
点赞2
打赏
分享
最新回复 (9)
雪    币: 8331
活跃值: 活跃值 (2410)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2019-11-18 10:03
2
0
漂亮 , 可见用心了,赞,这样写出来能分享知识,又能深化记忆和理解, 两者兼得,  如果在linker方面在深入的话,可以从查找符号表,解析重定位,从demo入手符号的生成过程,以及可以tricky(比如常见的脱壳,加壳点,双linker,反调试点,为什么要这么做)的点,再加入自己的理解总结,也可以
雪    币: 460
活跃值: 活跃值 (699)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
_air 活跃值 2019-11-20 12:17
3
0
学习了!多谢分享!
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
糊涂先生 活跃值 2020-4-13 14:19
4
0
学习了,多谢分享
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
糊涂先生 活跃值 2020-4-13 14:26
5
0
LowRebSwrd 漂亮 , 可见用心了,赞,这样写出来能分享知识,又能深化记忆和理解, 两者兼得, 如果在linker方面在深入的话,可以从查找符号表,解析重定位,从demo入手符号的生成过程,以及可以tricky( ...
版主,双linker有学习的文章和资料吗?
雪    币: 8331
活跃值: 活跃值 (2410)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2020-4-14 10:09
6
0
糊涂先生 版主,双linker有学习的文章和资料吗?
这方面没有主要讲这个的,不过了解linker的加载过程和熟悉源代码自然就知道双linker的运行原理了,还是多了解loader的代码吧。https://segmentfault.com/a/1190000007008889
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
糊涂先生 活跃值 2020-4-14 11:12
7
0
LowRebSwrd 这方面没有主要讲这个的,不过了解linker的加载过程和熟悉源代码自然就知道双linker的运行原理了,还是多了解loader的代码吧。https://segmentfault.com/a/11900 ...
谢谢版主~~
雪    币: 8331
活跃值: 活跃值 (2410)
能力值: (RANK:200 )
在线值:
发帖
回帖
粉丝
LowRebSwrd 活跃值 4 2020-4-14 11:15
8
0
糊涂先生 谢谢版主~~
https://www.twblogs.net/a/5bb03e142b7177781a0fe408/zh-cn  还有一篇
雪    币: 217
活跃值: 活跃值 (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
糊涂先生 活跃值 2020-4-14 11:24
9
0
LowRebSwrd https://www.twblogs.net/a/5bb03e142b7177781a0fe408/zh-cn 还有一篇
我要加倍学习了~再次感谢!
雪    币: 213
活跃值: 活跃值 (169)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lzgking 活跃值 2020-7-12 17:44
10
0
LowRebSwrd https://www.twblogs.net/a/5bb03e142b7177781a0fe408/zh-cn 还有一篇
这个在论坛内发过 https://bbs.pediy.com/thread-226667.htm
游客
登录 | 注册 方可回帖
返回