android文件-so

android文件-so

寄存器接受参数在 R0 - R3 ,返回值在R0

so文件结构

  1. header
    ELF文件头是ELF文件中唯一一个固定位置的文件结构,保存段头表和节头表位置和大小信息
  2. section
  3. program
    • 段头表保存了ELF文件的加载过程中各节头表的内存映射,依赖库等信息
    • .dynstr .text .dynamic
      相同flg section 放在一起组成program load 段才会被加载到内存
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
1、.text段

一般C语言编译后的执行语句都编译成机器代码,保存在.text段。

2、.data段
已经初始化的全局变量和局部静态变量(虽然默认会初始化为0,或者手动初始化为0,都没有必要在数据段分配空间,直接放在.bss段,就默认值为0了)都保存在.data段。
大体来说,该section包含了在内存中的程序的初始化数据;data段包含三个部分:heap(堆)、stack(栈)和静态数据区。即.data还会存放其他类型的数据,比如局部变量。
数据段只是存放数据,变量名存放在字符串表中。

3、.bss段
未初始化的全局变量和局部静态变量都保存在.bss段。
大体来说该section包含了在内存中的程序的未初始化的数据。
由于程序加载(一般是指main之前)时,bss会被操作系统清零,所以未赋初值或初值为0的全局变量都在bss。.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间,这样可减少目标文件体积。
但程序运行时需为变量分配内存空间,故目标文件必须记录所有未初始化的静态分配变量大小总和(通过start_bss和end_bss地址写入机器代码)。当加载器(loader)加载程序时,将为BSS段分配的内存初始化为0。

4、.rodata段
存放只读数据,一般是程序里面的只读变量(如const修饰的变量),以及字符串常量(不一定,也可能放在.data中)。

5、.got段

GOT(Global Offset Table)表中每一项都是本运行模块要引用的一个全局变量或函数的地址。可以用GOT表来间接引用全局变量、函数,也可以把GOT表的首地址作为一个基 准,用相对于该基准的偏移量来引用静态变量、静态函数。由于加载器不会把运行模块加载到固定地址,在不同进程的地址空间中,各运行模块的绝对地址、相对位 置都不同。这种不同反映到GOT表上,就是每个进程的每个运行模块都有独立的GOT表,所以进程间不能共享GOT表。

6、.plt段

过程链接表用于把位置独立的函数调用重定向到绝对位置。通过 PLT 动态链接的程序支持惰性绑定模式。每个动态链接的程序和共享库都有一个 PLT,PLT 表的每一项都是一小段代码,对应于本运行模块要引用的一个全局函数。程序对某个函数的访问都被调整为对 PLT 入口的访问。
每个 PLT 入口项对应一个 GOT 项,执行函数实际上就是跳转到相应 GOT 项存储的地址,该 GOT 项初始值为 PLTn项中的 push 指令地址(即 jmp 的下一条指令,所以第 1 次跳转没有任何作用),待符号解析完成后存放符号的真正地址。动态链接器在装载映射共享库时在 GOT 里设置 2 个特殊值:在 GOT+4( 即 GOT[1]) 设置动态库映射信息数据结构link_map 地址;在 GOT+8(即 GOT[2])设置动态链接器符号解析函数的地址_dl_runtime_resolve。

每一个外部定义的符号在全局偏移表 (Global Offset Table GOT)中有相应的条目,如果符号是函数则在过程连接表(Procedure Linkage Table PLT)中也有相应的条目

下面来看张图就了解了.got段和.plt段的关系:



这个在我们之前说到Hook技术就是用着两个段来实现的。只要修改需要hook的函数地址,插入我们hook的函数,执行之后,再回来就可以了,这里只要修改.got和.plt表就可以了,相关知识大家去网上搜一下吧。这里就不在解释了。

readelf 使用

  1. readelf -h xxx.so (查看so文件的头部信息)
  2. readelf -S xxx.so (查看so文件的节(Section)头的信息)
  3. readelf -l xxx.so (查看so文件的程序段头信息(Program))
  4. readelf -a xxx.so (查看so文件所有信息)

010 Editor 运用