Android中Bionic模块

Android中Bionic模块

Bionic包含了系统中最基本的lib库,包括libc、libm、libdl、libstd++、libthread_db和一个linker程序;
libc库是C语言最基础的库文件,提供了所有系统的基本功能(对系统调用的封装),是应用和Linux内核交互的桥梁。
libm库是数学函数库,提供了常见的数字函数和浮点运算功能。
libdl库原本是用于动态库的装载,但是Android的libdl库中dlopen、dlclose等函数的实现只是一个空壳。
libstd++库是标准的c++库。
linker的功能是装载动态库以及用于库的重定位,相当于Linux中的ld.so。

系统调用

Linux的系统调用就是Linux内核提供给用户进程使用的一套接口。
系统调用是通过中断从用户态切换到内核态,实现内核提供的某些服务。
中断一般分为三类:由计算机硬件异常或故障引起的中断,称为内部异常中断;由程序中执行了引起中断的指令而造成的中断,称为软中断;由外部设备请求引起的中断,称为外部中断。

内存管理

对于32位处理器来说,寻址空间最大为4GB,其中0~3GB的地址空间分配给用户进程使用,3-4GB由内核使用。
Linux由两种方式来申请和释放内存空间,一种是使用系统调用brk;另一种是使用系统调用mmap和munmap。
Linux内核通常会将用户空间划分为一些大的区域,如代码区、数据区、堆、栈等。
系统调用brk的作用是调整堆的高地址边界,分配内存时把边界推高,释放内存时把边界拉低。
系统调用mmap用来分配大块的内存空间,mmap会在堆区和栈区之间寻找一块合适的空间分配给用户进程使用。
使用完成后可以通过munmap释放内存空间。

Futex同步机制

Futex是Linux的一个基础组件,可以用来构建各种更高级别的同步机制,比如锁或者信号量等等。
传统的进程间同步机制都是通过对内核对象进行操作来完成的,这个内核对象在需要同步的进程中都是可见的。

Futex的解决思路是:在无竞争的情况下操作完全在用户空间进行,不需要系统调用,仅在发生竞争
的时候进入内核完成相应的处理(wait或者wake up)。
Futex系统调用的函数是futex();
Futex的同步逻辑
Futex变量值的状态:
0表示无锁状态;
1表示有锁无竞争状态;
2表示有竞争状态;

ELF文件格式

(分析文件格式的目的,是为了了解可执行文件的装载过程)
Android的可执行文件和动态库就是Linux的ELF文件格式,但是,由于Android使用了自己的linker,因此和普通的Linux系统不完全兼容。

ELF文件以节(section)的方式组织在一起的,节描述了文件的各项信息,例如:代码、数据、符号表等。
可执行文件被装载进内存后,是以段(segment)的方式来组织,如:代码段、数据段、动态段等。
ELF格式的文件有三种:
可执行文件、动态库文件(.so文件)、重定位文件(.0文件)
可执行文件和动态库会有程序头部表,但是重定位文件没有。
EIF文件中还有一个节区头部表,描述文件中各个节区的内容。
ELF文件头
程序头部表的作用是记录文件中各种段的地址、大小等信息,在程序装载、链接时都需要它。

Bionic中的Linker模块

可执行文件的创建流程:
首先,C代码(.c)经过编译器预处理,编译成汇编代码(.asm),然后,经汇编器处理生成目标代码(.o),
然后,通过链接器来链接相应的库生成可执行(.out),最后OS将可执行文件加载到内存里执行。

可执行文件的装载

Linux系统上有两种并不完全相同的可执行文件程序:
1.一种是静态链接的可执行程序:包含了运行所需要的所有函数,可以不依赖任何外部库来运行。
2.一种是动态链接的可执行程序:不会包含所依赖的库文件,文件大小相对会小很多。
静态可执行程序用在一些特殊的场合,例如系统初始化时,这是整个系统未准备好,动态链接程序还无法使用,系统的启动程序init就是一个静态可执行程序。

在Android中,生成一个静态可执行程序的方法是再编译脚本增加如下配置:
LOCAL_FORCE_STATIC_EXECUTABLE :=true
Linux执行一个可执行文件的过程是:
父进程执行fork后,在fork出的子进程执行execve函数,这个函数会将可执行文件装载进内存,准备好运行环境后就跳到可执行文件入口开始执行。通常可执行程序的入口是_start_main()函数。

静态链接
在静态链接时,Android会给程序自动加上2个重定位文件。

动态链接
在动态连接时,execve系统调用会分析可执行文件的文件头来寻找链接器。Linux文件中是ld.so,而Android则是linker。
execve会将linker装载进可执行文件的空间,然后执行linker的_start函数。linker完成动态库的装载和符号的重定位后再去运行真正的可执行文件的代码。

可执行程序的初始化
可知性程序的初始化是通过_linker_init()函数完成的。

inker如何替换掉libdl.so
背景:Linux装载一个动态库时需要使用dlopen函数。dlopen原本位于libdl.so中,而libdl.so中的函数,如
dlopen,dlclose,dldsys等Google并没有直接实现,真正的实现在linker中。
在可执行文件的装载过程中,所有装载进来的动态库对应的soinfo结构都会放到一个链表中,当新装载一个动态库时,会首先检查它是否已经存在于链表中,如果不存在才会继续装载。而linker伪造了一个libdl.so的soinfo结构,并放在了链表第一个元素的位置,因此程序中链接的libdl.so并不会真正的装载。

调试器-ptrace和Hook API

ptrace系统调用通常用在调试器软件中,调试器利用ptrace函数达到控制目标进程运行的目的。
一些Android的安全管家就是通过ptrace函数把自带的动态库插入到系统或者别的进程中,从而达到监控系统运行的目的。
Hook API技术,在操作系统未能提供所需功能的情况下,利用 Hook API手段来实现某种有用的
功能。
Hook API的原理是利用ptrace函数把一小段代码注入目标程序中,这小段代码的任务是:装载自己开发的动态库到目标进程中,然后查找目标进程中特定函数在全局偏移表中的位置,替换成自己动态库的函数地址。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值