Android so文件进阶 一
0x00 前言
最近一段时间在弄android方面的东西,今天有人发了张截图,问:在要dump多大的内存?
一时之间我竟然想不起来ELF文件的哪个字段表示的是文件大小,虽然最后给出了解决方法,IDA CTRL+S,直接看Segements信息,可以得出整个文件的大小。但说明了自己对于ELF文件格式远不如PE文件那么熟悉,毕竟看PE文件格式是抱着《加密与解密》来来回回看了好几遍,而对于ELF文件只是在网上随便找了几遍博客,现在还记得大致的结构,对于一些具体的细节却并已经遗忘的差不多。于是想自动动手写篇关于ELF文件格式的文章,加深自己对ELF文件的理解和记忆。关于整个系列的文章中的名词都使用的是英文原名,避免产生歧义,就比如说section和segment,可能各个书中翻译过来都存在差异,对于一些专有名词,我个人还是比较喜欢阅读英文,也不必翻译。
0x01 ELF文件
关于ELF文件的起源,官方的一些东西就不在进行缀述了。直奔主题,先从elf文件的三种类型讲起,elf文件分为三种类型:
1) 可重定位的对象文件(Relocatable file),也就是平常说的目标文件,后缀为.o;
2) 可执行的对象文件(Executable file);
3) 可被共享的对象文件(Shared object file)。
而我们的主要精力就放在第3种,可被共享的对象文件上,也就是所谓的动态库文件,即.so文件,类似于Windows下的dll文件。而动态库文件在发挥作用的都要经过两个过程
①编译阶段。链接编辑器(link editor)拿它和其他Relocatable object file以及其他shared object file作为输入,经链接处理后,生存另外的 shared object file 或者 executable file。
②运行阶段。动态链接器(dynamic linker)拿它和一个executable file以及另外一些 shared object file 来一起处理,在Linux系统里面创建一个进程映像。
下面是elf文件的两种view,linking view 和execution view,Linking View主要是为了给Linker使用,而Execution View是为了给Loader使用。而我们关心的.so文件可以是这两种View并存的,只不过是同一个文件的两种划分方式,这样load完还可以relocate,而.so文件仅仅只是用来执行时,是允许没有section的,而关于section与segment的关系,相同属性的section会被映射到同一个segment中。
ELF Header结构的定义如下,各个字段的含义都有注释:
/* ELF Header */ typedef struct elfhdr {unsigned char e_ident[EI_NIDENT]; /* ELF Identification */Elf32_Half e_type; /* object file type */Elf32_Half e_machine; /* machine */Elf32_Word e_version; /* object file version */Elf32_Addr e_entry; /* virtual entry point */Elf32_Off e_phoff; /* program header table offset */Elf32_Off e_shoff; /* section header table offset */Elf32_Word e_flags; /* processor-specific flags */Elf32_Half e_ehsize; /* ELF header size */Elf32_Half e_phentsize; /* program header entry size */Elf32_Half e_phnum; /* number of program header entries */Elf32_Half e_shentsize; /* section header entry size */Elf32_Half e_shnum; /* number of section header entries */Elf32_Half e_shstrndx; /* section header table's "section header string table" entry offset */ } Elf32_Ehdr;
关于每个字段的含义后面的注释都很清楚,唯一需要说明的一点就是e_shstrndx,这个表示的是.shstrtab section的section header在section header table中的索引,或者恰当的说是偏移。
接下来就是两个header table ,Section header table 和 Program header table。下面分别介绍这两个table的entry的结构,先是section header table entry的结构
Section Header Table
/* Section Header */ typedef struct {Elf32_Word sh_name; /* name - index into section heade string table section */Elf32_Word sh_type; /* type */Elf32_Word sh_flags; /* flags */Elf32_Addr sh_addr; /* address */Elf32_Off sh_offset; /* file offset */Elf32_Word sh_size; /* section size */Elf32_Word sh_link; /* section header table index link */Elf32_Word sh_info; /* extra information */Elf32_Word sh_addralign; /* address alignment */Elf32_Word sh_entsize; /* section entry size */ } Elf32_Shdr;
这里简要的介绍下各个字段的含义
sh_name: 该Section的名字,类型是Elf32_Word,指向在串表中的偏移值;
sh_type: 按内容和意义将section分类,下面给出不同的值代表的不同的含义;
关于sh_type的类型,值和表示的含义如下表:
SHT_NULL |
0 |
section头是无效的;它没有相关的section |
SHT_PROGBITS |
1 |
该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身 |
SHT_SYMTAB |
2 |
保存着符号表 |
SHT_STRTAB |
3 |
保存着一个字符串表 |
SHT_RELA |
4 |
保存着具有明确加数的重定位入口 |
SHT_HASH |
5 |
保存着一个标号的哈希(hash)表 |
SHT_DYNAMIC |
6 |
保存着动态连接的信息 |
SHT_NOTE |
7 |
保存着其他的一些标志文件的信息 |
SHT_NOBITS |
8 |
在文件中不占空间,sh_offset成员包含了概念上的文件偏移量 |
SHT_REL |
9 |
保存着具有明确加数的重定位的入口 |
SHT_SHLIB |
10 |
类型保留但语意没有指明,包含这个类型的section的程序是不符合ABI的规定 |
SHT_DYNSYM |
11 |
保存着符号表 |
SHT_LOPROC |
0x70000000 |
在这范围之间的值为特定处理器语意保留 |
SHT_HIPROC |
0x7fffffff |
|
SHT_LOUSER |
0x80000000 |
为应用程序保留的索引范围的最小边界 |
SHT_HIUSER |
0xffffffff |
为应用程序保留的索引范围的最大边界 |
sh_flags: 表示该section的类型,下表包含了各个类型的解释;
关于sh_flags的值与含义:
SHF_WRITE | 0x1 | 该section包含了在进程执行过程中可被写的数据 |
SHF_ALLOC | 0x2 | 该section在进程执行过程中占据着内存 |
SHF_EXECINSTR | 0x4 | 该section包含了可执行的机器指令 |
SHF_MASKPROC | 0xf0000000 | 为特定处理语意保留的 |
sh_addr: 表示该section在内存中,相对于基址的偏移;
sh_offset: 表示该section到文件头部的字节偏移;
sh_size: 该section大小;
sh_link: 表示与当前section有link关系的section索引;
sh_info: 一些附加信息;
sh_addralign: section的地址对齐;
sh_entsize: section项的大小(bytes)。
讲了那么多也就只是原理的性的东西,为了加深理解,用一个实际的例子。
最后的一个字符串表索引节头就是之前提过的Elf32_Hder中的e_shstrndx成员,来看shstrtab的内容:
我们分析一个特定的节,选择.mytext节
开始的4个字节00 00 00 39 就是“.mytext”字符串在shstrtab中的偏移量(注意大小端的问题,这里是小端存储)。
这里给大家“安利”一个GitHub的开源项目010Editor-stuff,关于各种文件解析模板,超级好用。
还有就是关于android下so文件的问题:
这个是位于/obj/local/armeabi/目录下的so文件的section header :
下面是位于/libs/armeabi/下的so文件,也就是打包进apk的文件;
对比一下,发现很多的section都被删除了,主要是有关debug和.symtab,.strtab section 都被删除了,也就是说android下打包进apk的so文件只支持动态链接。
Program Header Table
接下来就Program header table entry的结构:
typedef struct {Elf32_Word p_type; /* Segment type */Elf32_Off p_offset; /* Segment offset in file */Elf32_Addr p_vaddr; /* Segment virtual address in memory*/ Elf32_Addr p_paddr; /* Segment physical address */Elf32_Word p_filesz; /* Segment size in file */Elf32_Word p_memsz; /* Segment size in memory */Elf32_Word p_flags; /* Segment flags */Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr;
每个Segment包含若干个section,只有对executable file和shared object file才能存在Program header。
p_type:表示该Program header所指的Segment的类型
PT_NULL | 0 | 该数组元素未使用;其他的成员值是未定义的 |
PT_LOAD | 1 | 该数组元素指定一个可载入的段,由 p_filesz 和 p_memsz 描述 |
PT_DYNAMIC | 2 | 该数组元素指定动态链接信息 |
PT_INTERP | 3 | 该数组元素指定辅助信息的位置和大小 |
PT_NOTE | 4 | 该段类型保留且具有未指定的语义,具有一个这种类型数组元素的程序并不遵守ABI 。 |
PT_SHLIB | 5 | 该段类型保留且具有未指定的语义 |
PT_PHDR | 6 |
该数组元素(如果出现),指定了程序头表本身的位置和大小(包括在文件中 |
PT_LOPROC | 0x70000000 | 保留用于特定处理器的语义 |
PT_HIPROC | 0x7fffffff |
p_offset:表示该Program header所指的Segment在文件中偏移量。
p_vaddr:表示该Program header所指的Segment在内存中的虚拟地址。
p_vaddr:表示该Program header所指的Segment在内存中的物理地址,一般可忽略。
p_filesz:表示该Program header所指的Segment在文件中的大小。
p_memsz:表示该Program header所指的Segment在内存中的大小,一般地,p_memsz>=p_filesz,对p_filesz不足的内存区域填0。
最后作为结尾写段代码加深对个格式的理解:
#include <stdio.h> #include <stdlib.h> #include <elf.h> #include <errno.h> #include <fcntl.h> #ifdef __x86_64#define Elf_Ehdr Elf64_Ehdr#define Elf_Shdr Elf64_Shdr#define Elf_Sym Elf64_Sym#define Elf_Rel Elf64_Rela#define ELF_R_SYM ELF64_R_SYM#define REL_DYN ".rela.dyn"#define REL_PLT ".rela.plt" #else#define Elf_Ehdr Elf32_Ehdr#define Elf_Shdr Elf32_Shdr#define Elf_Sym Elf32_Sym#define Elf_Rel Elf32_Rel#define ELF_R_SYM ELF32_R_SYM#define REL_DYN ".rel.dyn"#define REL_PLT ".rel.plt" #endif#define LOG(...) printf(__VA_ARGS__);static uint32_t get_module_base(pid_t pid, const char *module_path) {FILE *fp = NULL;char *pch = NULL;char filename[32];char line[512];uint32_t addr = 0;LOG("[+] get libc base...\n");if (pid < 0)snprintf(filename, sizeof(filename), "/proc/self/maps");elsesnprintf(filename, sizeof(filename), "/proc/%d/maps", pid);if ((fp = fopen(filename, "r")) == NULL) {LOG("[-]open %s failed!", filename);return 0;}while (fgets(line, sizeof(line), fp)) {if (strstr(line, module_path)) {pch = strtok(line, "-");addr = strtoul(pch, NULL, 16);break;}}fclose(fp);LOG("[+] libc base:0x%x...\n",addr);return addr; }int read_header(int d, Elf_Ehdr **header)//read elf header structure {*header = (Elf_Ehdr *)malloc(sizeof(Elf_Ehdr));if (lseek(d, 0, SEEK_SET) < 0)//seek to the begin of file {free(*header);return errno;}if (read(d, *header, sizeof(Elf_Ehdr)) <= 0)//read from begin,read sizof(Elf_Ehdr) bytes ==> header {free(*header);return errno = EINVAL;}return 0; }static int read_section_table(int d, Elf_Ehdr const *header, Elf_Shdr **table)//read elf header,find section header base address {size_t size;if (NULL == header)return EINVAL;size = header->e_shnum * sizeof(Elf_Shdr);//section numbers and total size*table = (Elf_Shdr *)malloc(size);if (lseek(d, header->e_shoff, SEEK_SET) < 0)//point to section header,offset 0 {free(*table);return errno;}if (read(d, *table, size) <= 0)//read section header structure to **table {free(*table);return errno = EINVAL;}return 0; }static int read_string_table(int d, Elf_Shdr const *section, char const **strings) {if (NULL == section)//section == > .dynstr sectionreturn EINVAL;*strings = (char const *)malloc(section->sh_size);if (lseek(d, section->sh_offset, SEEK_SET) < 0){free((void *)*strings);return errno;}if (read(d, (char *)*strings, section->sh_size) <= 0)//strings include all strings in .dynstr sections {free((void *)*strings);return errno = EINVAL;}return 0; }int main() {LOG("[+]Arm ELF32 reader...\n");uint32_t lic_base = get_module_base(-1,"/system/lib/libc.so");int descriptor = open("/system/lib/libc.so", O_RDONLY);//open libc.so,and return the handleElf_Ehdr *header = NULL;//elf headerElf_Shdr *section_header = NULL;//section header array ptrchar const *strings = NULL;//string table ptrread_header(descriptor,&header);LOG("[+]libc.so elf header:\n");LOG("[+]e_ident[EI_NIDENT]: %s\n",header->e_ident);LOG("[+]e_type:%d(ET_DYN:%d,DYN (Shared object file))\n",header->e_type,ET_DYN);LOG("[+]e_machine:%d(EM_ARM:%d,Advanced RISC Machines)\n",header->e_machine,EM_ARM);LOG("[+]e_shoff:%d bytes\n",header->e_shoff);LOG("[+]libc.so section header:\n");read_section_table(descriptor,header,§ion_header);read_string_table(descriptor,§ion_header[header->e_shstrndx], &strings);//header->e_shstrndx ==>the index of string section header in section headersint i = 0;for(i = 0;i<header->e_shnum;++i){LOG("Section[%d] name:%s,type:%d,addr:0x%x,offset:0x%x,size:%dbytes,etc...\n",i,&strings[section_header[i].sh_name],section_header[i].sh_type,section_header[i].sh_addr,section_header[i].sh_offset,section_header[i].sh_size);}close(descriptor);return 0; }
0x02 小结
只是总结了ELF文件的大体的结构,对于每个section的结构并没有深入,之后的学习会更加深入elf文件的学习,主要聚焦在android 下的.so文件,还会加上一些关于 so加密的知识。
转载于:https://www.cnblogs.com/lanrenxinxin/p/4898513.html
Android so文件进阶 一相关推荐
- 【我的Android进阶之旅】Android 混淆文件资源分类整理之二:将混淆文件拆分成更小粒度的混淆文件
在我2017年的文章[我的Android进阶之旅]Android 混淆文件资源分类整理中,我已经提及过. 之前将所有的混淆都配置在一个 proguard-rules.pro 这个Android Stu ...
- Android.mk文件语法规范
序言: ------------- 此文档旨在描述Android.mk文件的语法,Android.mk文件为Android NDK(原生开发)描述了你C/C++源文件. 为了明白下面的内容,你必须已经 ...
- Android.mk文件语法规范及使用模板 (转载)
2019独角兽企业重金招聘Python工程师标准>>> http://blogold.chinaunix.net/u3/99423/showart_2206760.html Andr ...
- Android下载文件(一)下载进度断点续传
Android下载文件(一)下载进度&断点续传 索引 Android下载文件(一)下载进度&断点续传 Android下载文件(二)单任务多线程并发&断点续传(待续) Andro ...
- Android资源文件 - 使用资源存储字符串 颜色 尺寸 整型 布尔值 数组
一. Android资源文件简介 1. Android应用资源的作用 (1) Android项目中文件分类 在Android工程中, 文件主要分为下面几类 : 界面布局文件, Java src源文件, ...
- Android.mk 文件语法详解
0. Android.mk简介: Android.mk文件用来告知NDK Build 系统关于Source的信息. Android.mk将是GNU Makefile的一部分,且将被Build Syst ...
- Xamarin Android布局文件没有智能提示
Xamarin Android布局文件没有智能提示 在Visual Studio 2015中,Android项目的Main.axml文件没有智能提示,不便于布局文件的编写.解决办法: (1)从Xama ...
- 打开别人Xamarin项目找不到android.jar文件
打开别人Xamarin项目找不到android.jar文件 错误信息:Could not find android.jar for API Level 23. 打开非本机创建的Xamarin项目,编译 ...
- Xamarin中打开别人项目找不到android.jar文件
Xamarin中打开别人项目找不到android.jar文件 错误信息:Could not find android.jar for API Level 23. 打开非本机创建的Xamarin项目,编 ...
- 【Android 应用开发】Android资源文件 - 使用资源存储字符串 颜色 尺寸 整型 布尔值 数组
. 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/19913755 . 一. Android资源文件简介 1 ...
最新文章
- TeamCity 和 Nexus 的使用
- 15个初学者必看的基础SQL查询语句
- eclipse安装maven插件
- 如何使用Python2和Python3
- Java:switch语句例子
- c 语言差错编码实验结果,C语言程序设计实验报告(四).doc11111111111111111.doc
- Centos7-Mysql-5.6.41一主两从的搭建
- 教你如何在短期内做好会员运营快速拉新用户
- SQLSERVER中CONNECTIONPROPERTY函数
- 树梅派应用27:通过USB蓝牙适配器连接BLE设备
- c语言将首字母变大写,c语言问题 将首字母变为大写
- C++的异常[转载]
- java null==null是否成立
- 论坛回顾|蚂蚁供应链安全建设实践
- 手机无线鼠标服务器.zip,无线鼠标遥控器_Remote Mouse 1.0双版 用手机遥控你的电脑...
- epic games 无法 下载 unreal engine5
- java kml_当Java遇上KML
- 计算机键盘分为哪几个键区,标准键盘一般分为哪四个键区
- 手写迷你SpringMVC框架
- 一纸学习思维导图 Mind Map