专栏首页Java架构师历程Android逆向之旅—SO(ELF)文件格式详解–反编译so

Android逆向之旅—SO(ELF)文件格式详解–反编译so

第一、前言

从今天开始我们正式开始Android的逆向之旅,关于逆向的相关知识,想必大家都不陌生了,逆向领域是一个充满挑战和神秘的领域。作为一名android开发者,每个人都想去探索这个领域,因为一旦你破解了别人的内容,成就感肯定爆棚,不过相反的是,我们不仅要研究破解之道,也要研究加密之道,因为加密和破解是相生相克的。但是我们在破解的过程中可能最头疼的是native层,也就是so文件的破解。所以我们先来详细了解一下so文件的内容下面就来看看我们今天所要介绍的内容。今天我们先来介绍一下elf文件的格式,因为我们知道Android中的so文件就是elf文件,所以需要了解so文件,必须先来了解一下elf文件的格式,对于如何详细了解一个elf文件,就是手动的写一个工具类来解析一个elf文件。

第二、准备资料

我们需要了解elf文件的格式,关于elf文件格式详解,网上已经有很多介绍资料了。这里我也不做太多的解释了。不过有两个资料还是需要介绍一下的,因为网上的内容真的很多,很杂。这两个资料是最全的,也是最好的。我就是看这两个资料来操作的:

第一个资料是非虫大哥的经典之作:

看吧,是不是超级详细?后面我们用Java代码来解析elf文件的时候,就是按照这张图来的。但是这张图有些数据结构解释的还不是很清楚,所以第二个资料来了。

第二个资料:北京大学实验室出的标准版

http://download.csdn.net/detail/jiangwei0910410003/9204051

这里就不对这个文件做详细解释了,后面在做解析工作的时候,会截图说明。

关于上面的这两个资料,这里还是多数两句:一定要仔细认真的阅读。这个是经典之作。也是后面工作的基础。

第三、工具

当然这里还需要介绍一个工具,因为这个工具在我们下面解析elf文件的时候,也非常有用,而且是检查我们解析elf文件的模板。

就是很出名的:readelf命令

不过Window下这个命令不能用,因为这个命令是Linux的,所以我们还得做个工作就是安装Cygwin。关于这个工具的安装,大家可以看看这篇文章:

http://blog.csdn.net/jiangwei0910410003/article/details/17710243

不过在下载的过程中,我担心小朋友们会遇到挫折,所以很贴心的,放到的云盘里面:

http://pan.baidu.com/s/1C1Zci

下载下来之后,需要改一个东西才能用:

该一下这个文件:

这个路径要改成你本地cygwin64中的bin目录的路径,不然运行错误的。改好之后,直接运行Cygwin.bat就可以了。

关于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文件的全部内容

还有很多命令用法,这里就不在细说了,网上有很多介绍的~~

第四、实际操作解析Elf文件(Java代码&C++代码)

上面我们介绍了elf文件格式资料,elf文件的工具,那么下面我们就来实际操作一下,来用Java代码手把手的解析一个libhello-jni.so文件。关于这个libhello-jni.so文件的下载地址:

http://download.csdn.net/detail/jiangwei0910410003/9204087

1、首先定义elf文件中各个结构体内容

这个我们需要参考elf.h这个头文件的格式了。这个文件网上也是有的,这里还是给个下载链接吧:

http://download.csdn.net/detail/jiangwei0910410003/9204081

我们看看Java中定义的elf文件的数据结构类:

[java] view plain copy

  1. package com.demo.parseso;
  2. import java.util.ArrayList;
  3. public class ElfType32 {
  4. public elf32_rel rel;
  5. public elf32_rela rela;
  6. public ArrayList<Elf32_Sym> symList = new ArrayList<Elf32_Sym>();
  7. public elf32_hdr hdr;//elf头部信息
  8. public ArrayList<elf32_phdr> phdrList = new ArrayList<elf32_phdr>();//可能会有多个程序头
  9. public ArrayList<elf32_shdr> shdrList = new ArrayList<elf32_shdr>();//可能会有多个段头
  10. public ArrayList<elf32_strtb> strtbList = new ArrayList<elf32_strtb>();//可能会有多个字符串值
  11. public ElfType32() {
  12.         rel = new elf32_rel();
  13.         rela = new elf32_rela();
  14.         hdr = new elf32_hdr();
  15.     }
  16. /**
  17.      *  typedef struct elf32_rel {
  18.           Elf32_Addr    r_offset;
  19.           Elf32_Word    r_info;
  20.         } Elf32_Rel;
  21.      *
  22.      */
  23. public class elf32_rel {
  24. public byte[] r_offset = new byte[4];
  25. public byte[] r_info = new byte[4];
  26. @Override
  27. public String toString(){
  28. return “r_offset:”+Utils.bytes2HexString(r_offset)+“;r_info:”+Utils.bytes2HexString(r_info);
  29.         }
  30.     }
  31. /**
  32.      *  typedef struct elf32_rela{
  33.           Elf32_Addr    r_offset;
  34.           Elf32_Word    r_info;
  35.           Elf32_Sword   r_addend;
  36.         } Elf32_Rela;
  37.      */
  38. public class elf32_rela{
  39. public byte[] r_offset = new byte[4];
  40. public byte[] r_info = new byte[4];
  41. public byte[] r_addend = new byte[4];
  42. @Override
  43. public String toString(){
  44. return “r_offset:”+Utils.bytes2HexString(r_offset)+“;r_info:”+Utils.bytes2HexString(r_info)+“;r_addend:”+Utils.bytes2HexString(r_info);
  45.         }
  46.     }
  47. /**
  48.      * typedef struct elf32_sym{
  49.           Elf32_Word    st_name;
  50.           Elf32_Addr    st_value;
  51.           Elf32_Word    st_size;
  52.           unsigned char st_info;
  53.           unsigned char st_other;
  54.           Elf32_Half    st_shndx;
  55.         } Elf32_Sym;
  56.      */
  57. public static class Elf32_Sym{
  58. public byte[] st_name = new byte[4];
  59. public byte[] st_value = new byte[4];
  60. public byte[] st_size = new byte[4];
  61. public byte st_info;
  62. public byte st_other;
  63. public byte[] st_shndx = new byte[2];
  64. @Override
  65. public String toString(){
  66. return “st_name:”+Utils.bytes2HexString(st_name)
  67.                     +“\nst_value:”+Utils.bytes2HexString(st_value)
  68.                     +“\nst_size:”+Utils.bytes2HexString(st_size)
  69.                     +“\nst_info:”+(st_info/16)
  70.                     +“\nst_other:”+(((short)st_other) & 0xF)
  71.                     +“\nst_shndx:”+Utils.bytes2HexString(st_shndx);
  72.         }
  73.     }
  74. public void printSymList(){
  75. for(int i=0;i<symList.size();i++){
  76.             System.out.println();
  77.             System.out.println(“The “+(i+1)+” Symbol Table:”);
  78.             System.out.println(symList.get(i).toString());
  79.         }
  80.     }
  81. //Bind字段==》st_info
  82. public static final int STB_LOCAL = 0;
  83. public static final int STB_GLOBAL = 1;
  84. public static final int STB_WEAK = 2;
  85. //Type字段==》st_other
  86. public static final int STT_NOTYPE = 0;
  87. public static final int STT_OBJECT = 1;
  88. public static final int STT_FUNC = 2;
  89. public static final int STT_SECTION = 3;
  90. public static final int STT_FILE = 4;
  91. /**
  92.      * 这里需要注意的是还需要做一次转化
  93.      *  #define ELF_ST_BIND(x)  ((x) >> 4)
  94.         #define ELF_ST_TYPE(x)  (((unsigned int) x) & 0xf)
  95.      */
  96. /**
  97.      * typedef struct elf32_hdr{
  98.           unsigned char e_ident[EI_NIDENT];
  99.           Elf32_Half    e_type;
  100.           Elf32_Half    e_machine;
  101.           Elf32_Word    e_version;
  102.           Elf32_Addr    e_entry;  // Entry point
  103.           Elf32_Off e_phoff;
  104.           Elf32_Off e_shoff;
  105.           Elf32_Word    e_flags;
  106.           Elf32_Half    e_ehsize;
  107.           Elf32_Half    e_phentsize;
  108.           Elf32_Half    e_phnum;
  109.           Elf32_Half    e_shentsize;
  110.           Elf32_Half    e_shnum;
  111.           Elf32_Half    e_shstrndx;
  112.         } Elf32_Ehdr;
  113.      */
  114. public class elf32_hdr{
  115. public byte[] e_ident = new byte[16];
  116. public byte[] e_type = new byte[2];
  117. public byte[] e_machine = new byte[2];
  118. public byte[] e_version = new byte[4];
  119. public byte[] e_entry = new byte[4];
  120. public byte[] e_phoff = new byte[4];
  121. public byte[] e_shoff = new byte[4];
  122. public byte[] e_flags = new byte[4];
  123. public byte[] e_ehsize = new byte[2];
  124. public byte[] e_phentsize = new byte[2];
  125. public byte[] e_phnum = new byte[2];
  126. public byte[] e_shentsize = new byte[2];
  127. public byte[] e_shnum = new byte[2];
  128. public byte[] e_shstrndx = new byte[2];
  129. @Override
  130. public String toString(){
  131. return “magic:”+ Utils.bytes2HexString(e_ident)
  132.                     +“\ne_type:”+Utils.bytes2HexString(e_type)
  133.                     +“\ne_machine:”+Utils.bytes2HexString(e_machine)
  134.                     +“\ne_version:”+Utils.bytes2HexString(e_version)
  135.                     +“\ne_entry:”+Utils.bytes2HexString(e_entry)
  136.                     +“\ne_phoff:”+Utils.bytes2HexString(e_phoff)
  137.                     +“\ne_shoff:”+Utils.bytes2HexString(e_shoff)
  138.                     +“\ne_flags:”+Utils.bytes2HexString(e_flags)
  139.                     +“\ne_ehsize:”+Utils.bytes2HexString(e_ehsize)
  140.                     +“\ne_phentsize:”+Utils.bytes2HexString(e_phentsize)
  141.                     +“\ne_phnum:”+Utils.bytes2HexString(e_phnum)
  142.                     +“\ne_shentsize:”+Utils.bytes2HexString(e_shentsize)
  143.                     +“\ne_shnum:”+Utils.bytes2HexString(e_shnum)
  144.                     +“\ne_shstrndx:”+Utils.bytes2HexString(e_shstrndx);
  145.         }
  146.     }
  147. /**
  148.      * typedef struct elf32_phdr{
  149.           Elf32_Word    p_type;
  150.           Elf32_Off p_offset;
  151.           Elf32_Addr    p_vaddr;
  152.           Elf32_Addr    p_paddr;
  153.           Elf32_Word    p_filesz;
  154.           Elf32_Word    p_memsz;
  155.           Elf32_Word    p_flags;
  156.           Elf32_Word    p_align;
  157.         } Elf32_Phdr;
  158.      */
  159. public static class elf32_phdr{
  160. public byte[] p_type = new byte[4];
  161. public byte[] p_offset = new byte[4];
  162. public byte[] p_vaddr = new byte[4];
  163. public byte[] p_paddr = new byte[4];
  164. public byte[] p_filesz = new byte[4];
  165. public byte[] p_memsz = new byte[4];
  166. public byte[] p_flags = new byte[4];
  167. public byte[] p_align = new byte[4];
  168. @Override
  169. public String toString(){
  170. return “p_type:”+ Utils.bytes2HexString(p_type)
  171.                     +“\np_offset:”+Utils.bytes2HexString(p_offset)
  172.                     +“\np_vaddr:”+Utils.bytes2HexString(p_vaddr)
  173.                     +“\np_paddr:”+Utils.bytes2HexString(p_paddr)
  174.                     +“\np_filesz:”+Utils.bytes2HexString(p_filesz)
  175.                     +“\np_memsz:”+Utils.bytes2HexString(p_memsz)
  176.                     +“\np_flags:”+Utils.bytes2HexString(p_flags)
  177.                     +“\np_align:”+Utils.bytes2HexString(p_align);
  178.         }
  179.     }
  180. public void printPhdrList(){
  181. for(int i=0;i<phdrList.size();i++){
  182.             System.out.println();
  183.             System.out.println(“The “+(i+1)+” Program Header:”);
  184.             System.out.println(phdrList.get(i).toString());
  185.         }
  186.     }
  187. /**
  188.      * typedef struct elf32_shdr {
  189.           Elf32_Word    sh_name;
  190.           Elf32_Word    sh_type;
  191.           Elf32_Word    sh_flags;
  192.           Elf32_Addr    sh_addr;
  193.           Elf32_Off sh_offset;
  194.           Elf32_Word    sh_size;
  195.           Elf32_Word    sh_link;
  196.           Elf32_Word    sh_info;
  197.           Elf32_Word    sh_addralign;
  198.           Elf32_Word    sh_entsize;
  199.         } Elf32_Shdr;
  200.      */
  201. public static class elf32_shdr{
  202. public byte[] sh_name = new byte[4];
  203. public byte[] sh_type = new byte[4];
  204. public byte[] sh_flags = new byte[4];
  205. public byte[] sh_addr = new byte[4];
  206. public byte[] sh_offset = new byte[4];
  207. public byte[] sh_size = new byte[4];
  208. public byte[] sh_link = new byte[4];
  209. public byte[] sh_info = new byte[4];
  210. public byte[] sh_addralign = new byte[4];
  211. public byte[] sh_entsize = new byte[4];
  212. @Override
  213. public String toString(){
  214. return “sh_name:”+Utils.bytes2HexString(sh_name)/*Utils.byte2Int(sh_name)*/
  215.                     +“\nsh_type:”+Utils.bytes2HexString(sh_type)
  216.                     +“\nsh_flags:”+Utils.bytes2HexString(sh_flags)
  217.                     +“\nsh_add:”+Utils.bytes2HexString(sh_addr)
  218.                     +“\nsh_offset:”+Utils.bytes2HexString(sh_offset)
  219.                     +“\nsh_size:”+Utils.bytes2HexString(sh_size)
  220.                     +“\nsh_link:”+Utils.bytes2HexString(sh_link)
  221.                     +“\nsh_info:”+Utils.bytes2HexString(sh_info)
  222.                     +“\nsh_addralign:”+Utils.bytes2HexString(sh_addralign)
  223.                     +“\nsh_entsize:”+ Utils.bytes2HexString(sh_entsize);
  224.         }
  225.     }
  226. /****************sh_type********************/
  227. public static final int SHT_NULL = 0;
  228. public static final int SHT_PROGBITS = 1;
  229. public static final int SHT_SYMTAB = 2;
  230. public static final int SHT_STRTAB = 3;
  231. public static final int SHT_RELA = 4;
  232. public static final int SHT_HASH = 5;
  233. public static final int SHT_DYNAMIC = 6;
  234. public static final int SHT_NOTE = 7;
  235. public static final int SHT_NOBITS = 8;
  236. public static final int SHT_REL = 9;
  237. public static final int SHT_SHLIB = 10;
  238. public static final int SHT_DYNSYM = 11;
  239. public static final int SHT_NUM = 12;
  240. public static final int SHT_LOPROC = 0x70000000;
  241. public static final int SHT_HIPROC = 0x7fffffff;
  242. public static final int SHT_LOUSER = 0x80000000;
  243. public static final int SHT_HIUSER = 0xffffffff;
  244. public static final int SHT_MIPS_LIST = 0x70000000;
  245. public static final int SHT_MIPS_CONFLICT = 0x70000002;
  246. public static final int SHT_MIPS_GPTAB = 0x70000003;
  247. public static final int SHT_MIPS_UCODE = 0x70000004;
  248. /*****************sh_flag***********************/
  249. public static final int SHF_WRITE = 0x1;
  250. public static final int SHF_ALLOC = 0x2;
  251. public static final int SHF_EXECINSTR = 0x4;
  252. public static final int SHF_MASKPROC = 0xf0000000;
  253. public static final int SHF_MIPS_GPREL = 0x10000000;
  254. public void printShdrList(){
  255. for(int i=0;i<shdrList.size();i++){
  256.             System.out.println();
  257.             System.out.println(“The “+(i+1)+” Section Header:”);
  258.             System.out.println(shdrList.get(i));
  259.         }
  260.     }
  261. public static class elf32_strtb{
  262. public byte[] str_name;
  263. public int len;
  264. @Override
  265. public String toString(){
  266. return “str_name:”+str_name
  267.                     +“len:”+len;
  268.         }
  269.     }
  270. }

这个没什么问题,也没难度,就是在看elf.h文件中定义的数据结构的时候,要记得每个字段的占用字节数就可以了。

有了结构定义,下面就来看看如何解析吧。

在解析之前我们需要将so文件读取到byte[]中,定义一个数据结构类型

[java] view plain copy

  1. public static ElfType32 type_32 = new ElfType32();
  2. byte[] fileByteArys = Utils.readFile(“so/libhello-jni.so”);
  3. if(fileByteArys == null){
  4.     System.out.println(“read file byte failed…”);
  5. return;
  6. }

2、解析elf文件的头部信息

关于这些字段的解释,要看上面提到的那个pdf文件中的描述

这里我们介绍几个重要的字段,也是我们后面修改so文件的时候也会用到:

1)、e_phoff

这个字段是程序头(Program Header)内容在整个文件的偏移值,我们可以用这个偏移值来定位程序头的开始位置,用于解析程序头信息

2)、e_shoff

这个字段是段头(Section Header)内容在这个文件的偏移值,我们可以用这个偏移值来定位段头的开始位置,用于解析段头信息

3)、e_phnum

这个字段是程序头的个数,用于解析程序头信息

4)、e_shnum

这个字段是段头的个数,用于解析段头信息

5)、e_shstrndx

这个字段是String段在整个段列表中的索引值,这个用于后面定位String段的位置

按照上面的图我们就可以很容易的解析

[java] view plain copy

  1. /**
  2.  * 解析Elf的头部信息
  3.  * @param header
  4.  */
  5. private static void  parseHeader(byte[] header, int offset){
  6. if(header == null){
  7.         System.out.println(“header is null”);
  8. return;
  9.     }
  10. /**
  11.      *  public byte[] e_ident = new byte[16];
  12.             public short e_type;
  13.             public short e_machine;
  14.             public int e_version;
  15.             public int e_entry;
  16.             public int e_phoff;
  17.             public int e_shoff;
  18.             public int e_flags;
  19.             public short e_ehsize;
  20.             public short e_phentsize;
  21.             public short e_phnum;
  22.             public short e_shentsize;
  23.             public short e_shnum;
  24.             public short e_shstrndx;
  25.      */
  26.     type_32.hdr.e_ident = Utils.copyBytes(header, 0, 16);//魔数
  27.     type_32.hdr.e_type = Utils.copyBytes(header, 16, 2);
  28.     type_32.hdr.e_machine = Utils.copyBytes(header, 18, 2);
  29.     type_32.hdr.e_version = Utils.copyBytes(header, 20, 4);
  30.     type_32.hdr.e_entry = Utils.copyBytes(header, 24, 4);
  31.     type_32.hdr.e_phoff = Utils.copyBytes(header, 28, 4);
  32.     type_32.hdr.e_shoff = Utils.copyBytes(header, 32, 4);
  33.     type_32.hdr.e_flags = Utils.copyBytes(header, 36, 4);
  34.     type_32.hdr.e_ehsize = Utils.copyBytes(header, 40, 2);
  35.     type_32.hdr.e_phentsize = Utils.copyBytes(header, 42, 2);
  36.     type_32.hdr.e_phnum = Utils.copyBytes(header, 44,2);
  37.     type_32.hdr.e_shentsize = Utils.copyBytes(header, 46,2);
  38.     type_32.hdr.e_shnum = Utils.copyBytes(header, 48, 2);
  39.     type_32.hdr.e_shstrndx = Utils.copyBytes(header, 50, 2);
  40. }

按照对应的每个字段的字节个数,读取byte就可以了。

3、解析段头(Section Header)信息

这个结构中字段见pdf中的描述吧,这里就不做解释了。后面我们会手动的构造这样的一个数据结构,到时候在详细说明每个字段含义。

按照这个结构。我们解析也简单了:

[java] view plain copy

  1. /**
  2.  * 解析段头信息内容
  3.  */
  4. public static void parseSectionHeaderList(byte[] header, int offset){
  5. int header_size = 40;//40个字节
  6. int header_count = Utils.byte2Short(type_32.hdr.e_shnum);//头部的个数
  7. byte[] des = new byte[header_size];
  8. for(int i=0;i<header_count;i++){
  9.         System.arraycopy(header, i*header_size + offset, des, 0, header_size);
  10.         type_32.shdrList.add(parseSectionHeader(des));
  11.     }
  12. }
  13. private static elf32_shdr parseSectionHeader(byte[] header){
  14.     ElfType32.elf32_shdr shdr = new ElfType32.elf32_shdr();
  15. /**
  16.      *  public byte[] sh_name = new byte[4];
  17.             public byte[] sh_type = new byte[4];
  18.             public byte[] sh_flags = new byte[4];
  19.             public byte[] sh_addr = new byte[4];
  20.             public byte[] sh_offset = new byte[4];
  21.             public byte[] sh_size = new byte[4];
  22.             public byte[] sh_link = new byte[4];
  23.             public byte[] sh_info = new byte[4];
  24.             public byte[] sh_addralign = new byte[4];
  25.             public byte[] sh_entsize = new byte[4];
  26.      */
  27.     shdr.sh_name = Utils.copyBytes(header, 0, 4);
  28.     shdr.sh_type = Utils.copyBytes(header, 4, 4);
  29.     shdr.sh_flags = Utils.copyBytes(header, 8, 4);
  30.     shdr.sh_addr = Utils.copyBytes(header, 12, 4);
  31.     shdr.sh_offset = Utils.copyBytes(header, 16, 4);
  32.     shdr.sh_size = Utils.copyBytes(header, 20, 4);
  33.     shdr.sh_link = Utils.copyBytes(header, 24, 4);
  34.     shdr.sh_info = Utils.copyBytes(header, 28, 4);
  35.     shdr.sh_addralign = Utils.copyBytes(header, 32, 4);
  36.     shdr.sh_entsize = Utils.copyBytes(header, 36, 4);
  37. return shdr;
  38. }

这里需要注意的是,我们看到的Section Header一般都是多个的,这里用一个List来保存

4、解析程序头(Program Header)信息

这里的字段,这里也不做解释了,看pdf文档。

我们按照这个结构来进行解析:

[java] view plain copy

  1. /**
  2.  * 解析程序头信息
  3.  * @param header
  4.  */
  5. public static void parseProgramHeaderList(byte[] header, int offset){
  6. int header_size = 32;//32个字节
  7. int header_count = Utils.byte2Short(type_32.hdr.e_phnum);//头部的个数
  8. byte[] des = new byte[header_size];
  9. for(int i=0;i<header_count;i++){
  10.         System.arraycopy(header, i*header_size + offset, des, 0, header_size);
  11.         type_32.phdrList.add(parseProgramHeader(des));
  12.     }
  13. }
  14. private static elf32_phdr parseProgramHeader(byte[] header){
  15. /**
  16.      *  public int p_type;
  17.             public int p_offset;
  18.             public int p_vaddr;
  19.             public int p_paddr;
  20.             public int p_filesz;
  21.             public int p_memsz;
  22.             public int p_flags;
  23.             public int p_align;
  24.      */
  25.     ElfType32.elf32_phdr phdr = new ElfType32.elf32_phdr();
  26.     phdr.p_type = Utils.copyBytes(header, 0, 4);
  27.     phdr.p_offset = Utils.copyBytes(header, 4, 4);
  28.     phdr.p_vaddr = Utils.copyBytes(header, 8, 4);
  29.     phdr.p_paddr = Utils.copyBytes(header, 12, 4);
  30.     phdr.p_filesz = Utils.copyBytes(header, 16, 4);
  31.     phdr.p_memsz = Utils.copyBytes(header, 20, 4);
  32.     phdr.p_flags = Utils.copyBytes(header, 24, 4);
  33.     phdr.p_align = Utils.copyBytes(header, 28, 4);
  34. return phdr;
  35. }

当然还有其他结构的解析工作,这里就不在一一介绍了,因为这些结构我们在后面的介绍中不会用到,但是也是需要了解的,详细参见pdf文档。

5、验证解析结果

那么上面我们的解析工作做完了,为了验证我们的解析工作是否正确,我们需要给每个结构定义个打印函数,也就是从写toString方法即可。

然后我们在使用readelf工具来查看so文件的各个结构内容,对比就可以知道解析的是否成功了。

解析代码下载地址:https://github.com/fourbrother/parse_androidso

上面我们用的是Java代码来进行解析的,为了照顾广大程序猿,所以给出一个C++版本的解析类:

[cpp] view plain copy

  1. #include<iostream.h>
  2. #include<string.h>
  3. #include<stdio.h>
  4. #include “elf.h”
  5. /**
  6.     非常重要的一个宏,功能很简单:
  7.     P:需要对其的段地址
  8.     ALIGNBYTES:对其的字节数
  9.     功能:将P值补充到时ALIGNBYTES的整数倍
  10.     这个函数也叫:页面对其函数
  11.     eg: 0x3e45/0x1000 == >0x4000
  12. */
  13. #define ALIGN(P, ALIGNBYTES)  ( ((unsigned long)P + ALIGNBYTES -1)&~(ALIGNBYTES-1) )
  14. int addSectionFun(char*, char*, unsigned int);
  15. int main()
  16. {
  17.     addSectionFun(“D:\libhello-jni.so”, “.jiangwei”, 0x1000);
  18. return 0;
  19. }
  20. int addSectionFun(char *lpPath, char *szSecname, unsigned int nNewSecSize)
  21. {
  22. char name[50];
  23. FILE *fdr, *fdw;
  24. char *base = NULL;
  25.     Elf32_Ehdr *ehdr;
  26.     Elf32_Phdr *t_phdr, *load1, *load2, *dynamic;
  27.     Elf32_Shdr *s_hdr;
  28. int flag = 0;
  29. int i = 0;
  30.     unsigned mapSZ = 0;
  31.     unsigned nLoop = 0;
  32.     unsigned int nAddInitFun = 0;
  33.     unsigned int nNewSecAddr = 0;
  34.     unsigned int nModuleBase = 0;
  35.     memset(name, 0, sizeof(name));
  36. if(nNewSecSize == 0)
  37.     {
  38. return 0;
  39.     }
  40.     fdr = fopen(lpPath, “rb”);
  41.     strcpy(name, lpPath);
  42. if(strchr(name, ‘.’))
  43.     {
  44.         strcpy(strchr(name, ‘.’), “_new.so”);
  45.     }
  46. else
  47.     {
  48.         strcat(name, “_new”);
  49.     }
  50.     fdw = fopen(name, “wb”);
  51. if(fdr == NULL || fdw == NULL)
  52.     {
  53.         printf(“Open file failed”);
  54. return 1;
  55.     }
  56.     fseek(fdr, 0, SEEK_END);
  57.     mapSZ = ftell(fdr);//源文件的长度大小
  58.     printf(“mapSZ:0x%x\n”, mapSZ);
  59.     base = (char*)malloc(mapSZ * 2 + nNewSecSize);//2*源文件大小+新加的Section size
  60.     printf(“base 0x%x \n”, base);
  61.     memset(base, 0, mapSZ * 2 + nNewSecSize);
  62.     fseek(fdr, 0, SEEK_SET);
  63.     fread(base, 1, mapSZ, fdr);//拷贝源文件内容到base
  64. if(base == (void*) -1)
  65.     {
  66.         printf(“fread fd failed”);
  67. return 2;
  68.     }
  69. //判断Program Header
  70.     ehdr = (Elf32_Ehdr*) base;
  71.     t_phdr = (Elf32_Phdr*)(base + sizeof(Elf32_Ehdr));
  72. for(i=0;i<ehdr->e_phnum;i++)
  73.     {
  74. if(t_phdr->p_type == PT_LOAD)
  75.         {
  76. //这里的flag只是一个标志位,去除第一个LOAD的Segment的值
  77. if(flag == 0)
  78.             {
  79.                 load1 = t_phdr;
  80.                 flag = 1;
  81.                 nModuleBase = load1->p_vaddr;
  82.                 printf(“load1 = %p, offset = 0x%x \n”, load1, load1->p_offset);
  83.             }
  84. else
  85.             {
  86.                 load2 = t_phdr;
  87.                 printf(“load2 = %p, offset = 0x%x \n”, load2, load2->p_offset);
  88.             }
  89.         }
  90. if(t_phdr->p_type == PT_DYNAMIC)
  91.         {
  92.             dynamic = t_phdr;
  93.             printf(“dynamic = %p, offset = 0x%x \n”, dynamic, dynamic->p_offset);
  94.         }
  95.         t_phdr ++;
  96.     }
  97. //section header
  98.     s_hdr = (Elf32_Shdr*)(base + ehdr->e_shoff);
  99. //获取到新加section的位置,这个是重点,需要进行页面对其操作
  100.     printf(“addr:0x%x\n”,load2->p_paddr);
  101.     nNewSecAddr = ALIGN(load2->p_paddr + load2->p_memsz – nModuleBase, load2->p_align);
  102.     printf(“new section add:%x \n”, nNewSecAddr);
  103. if(load1->p_filesz < ALIGN(load2->p_paddr + load2->p_memsz, load2->p_align) )
  104.     {
  105.         printf(“offset:%x\n”,(ehdr->e_shoff + sizeof(Elf32_Shdr) * ehdr->e_shnum));
  106. //注意这里的代码的执行条件,这里其实就是判断section header是不是在文件的末尾
  107. if( (ehdr->e_shoff + sizeof(Elf32_Shdr) * ehdr->e_shnum) != mapSZ)
  108.         {
  109. if(mapSZ + sizeof(Elf32_Shdr) * (ehdr->e_shnum + 1) > nNewSecAddr)
  110.             {
  111.                 printf(“无法添加节\n”);
  112. return 3;
  113.             }
  114. else
  115.             {
  116.                 memcpy(base + mapSZ, base + ehdr->e_shoff, sizeof(Elf32_Shdr) * ehdr->e_shnum);//将Section Header拷贝到原来文件的末尾
  117.                 ehdr->e_shoff = mapSZ;
  118.                 mapSZ += sizeof(Elf32_Shdr) * ehdr->e_shnum;//加上Section Header的长度
  119.                 s_hdr = (Elf32_Shdr*)(base + ehdr->e_shoff);
  120.                 printf(“ehdr_offset:%x”,ehdr->e_shoff);
  121.             }
  122.         }
  123.     }
  124. else
  125.     {
  126.         nNewSecAddr = load1->p_filesz;
  127.     }
  128.     printf(“还可添加 %d 个节\n”, (nNewSecAddr – ehdr->e_shoff) / sizeof(Elf32_Shdr) – ehdr->e_shnum – 1);
  129. int nWriteLen = nNewSecAddr + ALIGN(strlen(szSecname) + 1, 0x10) + nNewSecSize;//添加section之后的文件总长度:原来的长度 + section name + section size
  130.     printf(“write len %x\n”,nWriteLen);
  131. char *lpWriteBuf = (char *)malloc(nWriteLen);//nWriteLen :最后文件的总大小
  132.     memset(lpWriteBuf, 0, nWriteLen);
  133. //ehdr->e_shstrndx是section name的string表在section表头中的偏移值,修改string段的大小
  134.     s_hdr[ehdr->e_shstrndx].sh_size = nNewSecAddr – s_hdr[ehdr->e_shstrndx].sh_offset + strlen(szSecname) + 1;
  135.     strcpy(lpWriteBuf + nNewSecAddr, szSecname);//添加section name
  136. //以下代码是构建一个Section Header
  137.     Elf32_Shdr newSecShdr = {0};
  138.     newSecShdr.sh_name = nNewSecAddr – s_hdr[ehdr->e_shstrndx].sh_offset;
  139.     newSecShdr.sh_type = SHT_PROGBITS;
  140.     newSecShdr.sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
  141.     nNewSecAddr += ALIGN(strlen(szSecname) + 1, 0x10);
  142.     newSecShdr.sh_size = nNewSecSize;
  143.     newSecShdr.sh_offset = nNewSecAddr;
  144.     newSecShdr.sh_addr = nNewSecAddr + nModuleBase;
  145.     newSecShdr.sh_addralign = 4;
  146. //修改Program Header信息
  147.     load1->p_filesz = nWriteLen;
  148.     load1->p_memsz = nNewSecAddr + nNewSecSize;
  149.     load1->p_flags = 7;      //可读 可写 可执行
  150. //修改Elf header中的section的count值
  151.     ehdr->e_shnum++;
  152.     memcpy(lpWriteBuf, base, mapSZ);//从base中拷贝mapSZ长度的字节到lpWriteBuf
  153.     memcpy(lpWriteBuf + mapSZ, &newSecShdr, sizeof(Elf32_Shdr));//将新加的Section Header追加到lpWriteBuf末尾
  154. //写文件
  155.     fseek(fdw, 0, SEEK_SET);
  156.     fwrite(lpWriteBuf, 1, nWriteLen, fdw);
  157.     fclose(fdw);
  158.     fclose(fdr);
  159.     free(base);
  160.     free(lpWriteBuf);
  161. return 0;
  162. }

看了C++代码解析之后,这里不得不多说两句了,看看C++中的代码多么简单,原因很简单:在做文件字节操作的时候,C++中的指针真的很牛逼的,这个也是Java望成莫及的。。

C++代码下载:http://download.csdn.net/detail/jiangwei0910410003/9204139

第五、总结

关于Elf文件的格式,就介绍到这里,通过自己写一个解析类的话,可以很深刻的了解elf文件的格式,所以我们在以后遇到一个文件格式的了解过程中,最好的方式就是手动的写一个工具类就好了。那么这篇文章是逆向之旅的第一篇,也是以后篇章的基础,下面一篇文章我们会介绍如何来手动的在elf中添加一个段数据结构,尽情期待~~

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • python开发_tkinter_图片操作

    在java的swing中,我们可以找到一些有关图片的操作,对于python的tkinter类似,也有对于图片的相关操作

    Hongten
  • Install Node.js

    安装的话其实很容易,只要登陆上官网:https://nodejs.org/en/download/ 如下图:

    凌川江雪
  • 广播的最佳实践——实现强制下线功能(Android_Broadcast)

    其一,用于辅助统筹管理本项目的所有活动。调用ActivityCollector类的方法,如类中的onCreate和onDestroy方法,所有从父类派生出去的子...

    凌川江雪
  • 跨程序共享数据——Content Provider 之 ContentResolver基本用法 & 一个读取系统联系人的Demo

    本模块共有四篇文章,参考郭神的《第一行代码》,对Content Provider的学习做一个详细的笔记,大家可以一起交流一下:

    凌川江雪
  • 小结:greenDAO和LitePal的区别

    1. greenDAO的version等数据库属性设置都是在对应的模型类里面完成的,以Java class的属性变量的形式存储;而LitePal是在另外的一个x...

    凌川江雪
  • SLF4J和Logback日志框架详解

    SLF4J是一套简单的日志外观模式的Java API,帮助在项目部署时对接各种日志实现。

    DannyHoo
  • java.lang.IndexOutOfBoundsException and drawPosText

    郁闷。。报错的原因是数组超范围了。。 还以为是for语句写错了,看了半天。。。没错啊。。。

    凌川江雪
  • Android实战_note1(MyMirror_一款小型摄像处理的App)

    1.1 Activity.java全文: 注意代码中的注释,其中 handler.sendEmptyMessageDelayed(1,3000);...

    凌川江雪
  • Content Provider 之 最终弹 实战体验跨程序数据共享(结合SQLiteDemo)

    本模块共有四篇文章,参考郭神的《第一行代码》,对Content Provider的学习做一个详细的笔记,大家可以一起交流一下:

    凌川江雪
  • python开发_dbm_键值对存储_完整_博主推荐

    ============================================

    Hongten

扫码关注云+社区

领取腾讯云代金券