《Android软件安全权威指南》原生程序文件格式 阅读笔记

22 篇文章 0 订阅
21 篇文章 0 订阅

原生程序开发

JNI

JNI方法都在jni.h中定义(JNINativeInterface),该结构体中保存的是一些列JNI方法的指针,第一个参数为JNIEnv结构体指令,在这里插入图片描述
该指令的第一个参数就是一个名为funiction的JNINativeInterface。
在这里插入图片描述
JNI编程中,所有Java层数据类型都可以用jvalue表示,定义如下:
在这里插入图片描述
除了jobject,其他所有类型都在原生程序中有对应数据类型。
在这里插入图片描述
jobject在JNI的实现被定义为C++类,有多个子类,,分类包含jstring,jthrowable,jXXXarray(xxxx为所有的基础数据类型)
JNI方法按功能可以分为:
在这里插入图片描述

原生函数入口函数

会分别执行preinit_array_,init_array,程序执行结束,执行fini_array。如果是so只有后两个。
linker的call_constructors如下:
在这里插入图片描述
因此在分析so动态库,看i恶意通过查看.init_array是否有函数指针确定是否含初始化代码。如果通过dlopen()方法加载动态库,工作就完成了;如果在Java层通过System.loadLibrary()方法加载动态库,连接器在加载动态库时会查找动态库中是否有JNI初始化函数JNI_Onload(),有就调用。所有对于so动态库首先执行.init_array指定初始化函数执行函数指针数组,再执行JNI_OnLoad()函数。

原生程序文件格式

Android原生程序的编译工具链使用的是gcc和Clang,Clang使用的连接器是gcc的后端,因此所有原生程序的链接工作最终由编译工具链中ld命令和响应脚本完成的。
elf格式文档讲原生程序分成3类:
Relocatable File:可重定位文件,“.o”结尾。
Executable File:可执行文件。
Shared Object File:共享的目标文件。
在这里插入图片描述
不加-fPIE -pie参数编译出来是可执行文件,加上为共享的目标文件。
$cc在执行链接时会调用链接器来链接目标文件生成程序,传递参数会确定链接脚本,如-fPIE -pie,如果传递该参数会在链接时使用aarch64linux.xde链接脚本,没指定参数使用arrch64linux.x链接脚本(-T参数为连接器指定使用的链接脚本)。
在这里插入图片描述
–verbose参数可以产科支持链接的·1文件类型和默认链接脚本。
LSB是小端,MSB是大端。

AArch64 ELF文件格式

一个ELF文件分为3部分:

  • ELF Header,文件头。
  • Program Header Table:程序头表,包含多个Program Header与Segment段数据。
  • Section Header Table:节区头表,包含多个Section Header与Section(节区)数据。
    ELF Header描述了基本文件信息。有上面两个表在文件中偏移。Program Header Table定义的是Segment信息。米哦啊书一个地址段数据的读写执行属性及是否加载到内存。Program Header Table可以有多个Program Header,每个Program Header对应要给Segment,Segment个数由ELF Header给出。
    Section Header Table定义Section信息,Section与Segment关系为:一个Segment对应多个Section,每个Section通常只属于一个Segment,特殊情况可属于多个Segment,且Segment之间数据可以重叠。如图:
    在这里插入图片描述
    可看出segment包含运行时需要的信息,section包含链接时需要的信息。ELF在链接时通过Section Header Table寻找节区信息。运行时加载器通过Program Header Table寻找Segment并加载。
    .o文件通常不包括Program Header Table。
    在这里插入图片描述

ELF64_Ehdr与ELF32_Ehdr字段基本一样,判断是32还是64通过抵押给字段e_ident字段。EI_NIDENT的值16,其中EI_MAG0到3为0x7F、E、L、F。EI_CLASS表示ELF的文件类别:1为ELFCLASS32,是32为,2为ELFCLASS64,为64位。
在这里插入图片描述
e_type字段表示ELF文件类型,如下:
在这里插入图片描述
e_machine是机器架构。e_version描述文件版本,Android上有效值为1的EV_CURRENT。
e_entry指明ELF入口函数,指向ELF的_start导出符号地址。
e_phoff字段指明了Program Header Table在文件中的偏移。e_shoff为Section Header Table在文件中的偏移。
e_ehsize表示Elf64_Ehdr或Elf32_Ehdr结构体占用的字节数。
e_phentsize与e_phnum自动分别指定了Program Header Table每一项占用字节数与个数。e_shentize与e_shnum指定 Section Header Table每一项占用字节数与个数。
e_shstrndx是索引值,表示.shstrtab字符串表是第几个Section Header Table。

Program Header Table

在这里插入图片描述
p_type字段描述段类型(Segment Type)。
在这里插入图片描述
可执行与共享文件包括如下部分:
多个(一般2个)PT_LOAD类型的Segment。原生程序运行时,Segment会加载到内存中。
一个PT_DYNAMIC类型Segment。记录非常重要的动态链接信息,包括原生程序以来的动态链接库列表,初始化结构数组init_array的地址与大小,字符串表,地址重定位等。
一个PT_GNU_STACK类型的Segment,决定运行时栈是否可执行,默认可读写,不可执行。
一个PT_GNU_RELRO类型Segment,决定连接器执行重定位操作后,哪块内存区域被设置为只读。
一个PT_PHDR类型Segment,指向Program Header Table自身。
一个PT_INITERP类型Segment,指向可执行程序加载器路径。
如果使用-fexceptions开启异常支持,还会使用一个PT_ARM_EXIDX的Segment。
p_flags指定segment,rwx。
p_offset指定Segment在文件偏移。p_vaddr指定Segment在内存虚拟地址,p_paddr指定该Segment采用物理内存寻址的系统中加载后的物理地址。
p_filesz指定Segment在当前文件所占字节数。
p_memsz指定segment在内存字节数。
p_align指定Segment的内存对齐约束,64位原生程序:PT_LOAD指定可执行代码的Segment,内存对齐为65536。PT_DYNAMIC指定数据的Segment,对齐值8。

Section Header Table

Section Header如下:
在这里插入图片描述
sh_flags为Section标志信息,如SHF_ALLOC表示Section可分配内存,SHF_EXECINSTR表示该Section包含可执行机器指令。可以同时多个标志。
在这里插入图片描述

.dynamic

.dynamic节区调用soinfo::prelink_image()进行预链接处理,使用特定数据结构存放Section条目信息。
在这里插入图片描述
d_tag字段描述了区节的类型。影响d_un字段的类型与值。如下:
在这里插入图片描述

字符串表

在这里插入图片描述
##符号表
通常包含.symtab和.dynsym两个符号表,后者时所有需要分配内存的Section。所以可以优化掉前者。

got表和plt表

.got和.got.plt标志为3,表示会加载到内存并且可写,.plt标志位6,表示会加载到内存并且可执行。
这表的作用就是代码段不允许修改,只能先从代码段plt跳到.got,通过修改稿got实现调用真正函数。
参考:https://www.zhihu.com/question/21249496

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值