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文件的數據結構類:

[java] view plain copy
  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.                     +"\nst_value:"+Utils.bytes2HexString(st_value)  
  78.                     +"\nst_size:"+Utils.bytes2HexString(st_size)  
  79.                     +"\nst_info:"+(st_info/16)  
  80.                     +"\nst_other:"+(((short)st_other) & 0xF)  
  81.                     +"\nst_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.                     +"\ne_type:"+Utils.bytes2HexString(e_type)  
  147.                     +"\ne_machine:"+Utils.bytes2HexString(e_machine)  
  148.                     +"\ne_version:"+Utils.bytes2HexString(e_version)  
  149.                     +"\ne_entry:"+Utils.bytes2HexString(e_entry)  
  150.                     +"\ne_phoff:"+Utils.bytes2HexString(e_phoff)  
  151.                     +"\ne_shoff:"+Utils.bytes2HexString(e_shoff)  
  152.                     +"\ne_flags:"+Utils.bytes2HexString(e_flags)  
  153.                     +"\ne_ehsize:"+Utils.bytes2HexString(e_ehsize)  
  154.                     +"\ne_phentsize:"+Utils.bytes2HexString(e_phentsize)  
  155.                     +"\ne_phnum:"+Utils.bytes2HexString(e_phnum)  
  156.                     +"\ne_shentsize:"+Utils.bytes2HexString(e_shentsize)  
  157.                     +"\ne_shnum:"+Utils.bytes2HexString(e_shnum)  
  158.                     +"\ne_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.                     +"\np_offset:"+Utils.bytes2HexString(p_offset)  
  188.                     +"\np_vaddr:"+Utils.bytes2HexString(p_vaddr)  
  189.                     +"\np_paddr:"+Utils.bytes2HexString(p_paddr)  
  190.                     +"\np_filesz:"+Utils.bytes2HexString(p_filesz)  
  191.                     +"\np_memsz:"+Utils.bytes2HexString(p_memsz)  
  192.                     +"\np_flags:"+Utils.bytes2HexString(p_flags)  
  193.                     +"\np_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.                     +"\nsh_type:"+Utils.bytes2HexString(sh_type)  
  235.                     +"\nsh_flags:"+Utils.bytes2HexString(sh_flags)  
  236.                     +"\nsh_add:"+Utils.bytes2HexString(sh_addr)  
  237.                     +"\nsh_offset:"+Utils.bytes2HexString(sh_offset)  
  238.                     +"\nsh_size:"+Utils.bytes2HexString(sh_size)  
  239.                     +"\nsh_link:"+Utils.bytes2HexString(sh_link)  
  240.                     +"\nsh_info:"+Utils.bytes2HexString(sh_info)  
  241.                     +"\nsh_addralign:"+Utils.bytes2HexString(sh_addralign)  
  242.                     +"\nsh_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[]中,定義一個數據結構類型

[java] view plain copy
  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段的位置


按照上面的圖我們就可以很容易的解析

[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, 016);//魔數  
  27.     type_32.hdr.e_type = Utils.copyBytes(header, 162);  
  28.     type_32.hdr.e_machine = Utils.copyBytes(header, 182);  
  29.     type_32.hdr.e_version = Utils.copyBytes(header, 204);  
  30.     type_32.hdr.e_entry = Utils.copyBytes(header, 244);  
  31.     type_32.hdr.e_phoff = Utils.copyBytes(header, 284);  
  32.     type_32.hdr.e_shoff = Utils.copyBytes(header, 324);  
  33.     type_32.hdr.e_flags = Utils.copyBytes(header, 364);  
  34.     type_32.hdr.e_ehsize = Utils.copyBytes(header, 402);  
  35.     type_32.hdr.e_phentsize = Utils.copyBytes(header, 422);  
  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, 482);  
  39.     type_32.hdr.e_shstrndx = Utils.copyBytes(header, 502);  
  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.   
  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, 04);  
  29.     shdr.sh_type = Utils.copyBytes(header, 44);  
  30.     shdr.sh_flags = Utils.copyBytes(header, 84);  
  31.     shdr.sh_addr = Utils.copyBytes(header, 124);  
  32.     shdr.sh_offset = Utils.copyBytes(header, 164);  
  33.     shdr.sh_size = Utils.copyBytes(header, 204);  
  34.     shdr.sh_link = Utils.copyBytes(header, 244);  
  35.     shdr.sh_info = Utils.copyBytes(header, 284);  
  36.     shdr.sh_addralign = Utils.copyBytes(header, 324);  
  37.     shdr.sh_entsize = Utils.copyBytes(header, 364);  
  38.     return shdr;  
  39. }  
這裏需要注意的是,我們看到的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.   
  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, 04);  
  28.     phdr.p_offset = Utils.copyBytes(header, 44);  
  29.     phdr.p_vaddr = Utils.copyBytes(header, 84);  
  30.     phdr.p_paddr = Utils.copyBytes(header, 124);  
  31.     phdr.p_filesz = Utils.copyBytes(header, 164);  
  32.     phdr.p_memsz = Utils.copyBytes(header, 204);  
  33.     phdr.p_flags = Utils.copyBytes(header, 244);  
  34.     phdr.p_align = Utils.copyBytes(header, 284);  
  35.     return phdr;  
  36.   
  37. }  


當然還有其他結構的解析工作,這裏就不在一一介紹了,因爲這些結構我們在後面的介紹中不會用到,但是也是需要了解的,詳細參見pdf文檔。


5、驗證解析結果

那麼上面我們的解析工作做完了,爲了驗證我們的解析工作是否正確,我們需要給每個結構定義個打印函數,也就是從寫toString方法即可。


然後我們在使用readelf工具來查看so文件的各個結構內容,對比就可以知道解析的是否成功了。


解析代碼下載地址:http://download.csdn.net/detail/jiangwei0910410003/9204119


上面我們用的是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.     非常重要的一個宏,功能很簡單: 
  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\n", mapSZ);  
  64.   
  65.     base = (char*)malloc(mapSZ * 2 + nNewSecSize);//2*源文件大小+新加的Section size  
  66.     printf("base 0x%x \n", 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 \n", load1, load1->p_offset);  
  91.   
  92.             }  
  93.             else  
  94.             {  
  95.                 load2 = t_phdr;  
  96.                 printf("load2 = %p, offset = 0x%x \n", 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 \n", 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\n",load2->p_paddr);  
  111.     nNewSecAddr = ALIGN(load2->p_paddr + load2->p_memsz - nModuleBase, load2->p_align);  
  112.     printf("new section add:%x \n", nNewSecAddr);  
  113.   
  114.     if(load1->p_filesz < ALIGN(load2->p_paddr + load2->p_memsz, load2->p_align) )  
  115.     {  
  116.         printf("offset:%x\n",(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("無法添加節\n");  
  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 個節\n", (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\n",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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章