zoukankan      html  css  js  c++  java
  • Android逆向之旅---SO(ELF)文件格式详解(转)

    第一、前言

    从今天开始我们正式开始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文件的数据结构类:

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

     

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

     

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

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

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

    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段的位置

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

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

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

     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   
    14 private static elf32_shdr parseSectionHeader(byte[] header){  
    15     ElfType32.elf32_shdr shdr = new ElfType32.elf32_shdr();  
    16     /** 
    17      *  public byte[] sh_name = new byte[4]; 
    18             public byte[] sh_type = new byte[4]; 
    19             public byte[] sh_flags = new byte[4]; 
    20             public byte[] sh_addr = new byte[4]; 
    21             public byte[] sh_offset = new byte[4]; 
    22             public byte[] sh_size = new byte[4]; 
    23             public byte[] sh_link = new byte[4]; 
    24             public byte[] sh_info = new byte[4]; 
    25             public byte[] sh_addralign = new byte[4]; 
    26             public byte[] sh_entsize = new byte[4]; 
    27      */  
    28     shdr.sh_name = Utils.copyBytes(header, 0, 4);  
    29     shdr.sh_type = Utils.copyBytes(header, 4, 4);  
    30     shdr.sh_flags = Utils.copyBytes(header, 8, 4);  
    31     shdr.sh_addr = Utils.copyBytes(header, 12, 4);  
    32     shdr.sh_offset = Utils.copyBytes(header, 16, 4);  
    33     shdr.sh_size = Utils.copyBytes(header, 20, 4);  
    34     shdr.sh_link = Utils.copyBytes(header, 24, 4);  
    35     shdr.sh_info = Utils.copyBytes(header, 28, 4);  
    36     shdr.sh_addralign = Utils.copyBytes(header, 32, 4);  
    37     shdr.sh_entsize = Utils.copyBytes(header, 36, 4);  
    38     return shdr;  
    39 }  

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

     

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


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

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

     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   
    15 private static elf32_phdr parseProgramHeader(byte[] header){  
    16     /** 
    17      *  public int p_type; 
    18             public int p_offset; 
    19             public int p_vaddr; 
    20             public int p_paddr; 
    21             public int p_filesz; 
    22             public int p_memsz; 
    23             public int p_flags; 
    24             public int p_align; 
    25      */  
    26     ElfType32.elf32_phdr phdr = new ElfType32.elf32_phdr();  
    27     phdr.p_type = Utils.copyBytes(header, 0, 4);  
    28     phdr.p_offset = Utils.copyBytes(header, 4, 4);  
    29     phdr.p_vaddr = Utils.copyBytes(header, 8, 4);  
    30     phdr.p_paddr = Utils.copyBytes(header, 12, 4);  
    31     phdr.p_filesz = Utils.copyBytes(header, 16, 4);  
    32     phdr.p_memsz = Utils.copyBytes(header, 20, 4);  
    33     phdr.p_flags = Utils.copyBytes(header, 24, 4);  
    34     phdr.p_align = Utils.copyBytes(header, 28, 4);  
    35     return phdr;  
    36   
    37 }

     

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

    5、验证解析结果

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

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

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

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

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

     

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

     

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

    第五、总结

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

    转至:Android逆向之旅---SO(ELF)文件格式详解

  • 相关阅读:
    asp.net core3.1修改代码以支持windows服务发布
    .NET Core3.1IFreeSql使用FreeSql.Generator自动生成实体类
    西南大学2021年秋《线性代数》参考答案
    西南大学2021年秋形势与政策2 阶段一参考答案
    送给程序员的礼物
    西南大学2021年秋形势与政策2 阶段三参考答案
    拿 M 奖真的那么难吗?这份美赛攻略请收下!
    前端开发必会实战项目,Vue Router 实战来啦!​​
    你喜欢哪款 Linux 桌面?萝莉风?御姐风?
    Python 挑战,你通关了吗?速来看题解!
  • 原文地址:https://www.cnblogs.com/1024Planet/p/6272620.html
Copyright © 2011-2022 走看看