第一、前言
從今天開始我們正式開始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
不過在下載的過程中,我擔心小朋友們會遇到挫折,所以很貼心的,放到的雲盤里面:
下載下來之后,需要改一個東西才能用:
改一下這個文件:
這個路徑要改成你本地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 +"\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 }
有了結構定義,下面就來看看如何解析吧。
在解析之前我們需要將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\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
第五、總結
關於Elf文件的格式,就介紹到這里,通過自己寫一個解析類的話,可以很深刻的了解elf文件的格式,所以我們在以后遇到一個文件格式的了解過程中,最好的方式就是手動的寫一個工具類就好了。那么這篇文章是逆向之旅的第一篇,也是以后篇章的基礎,下面一篇文章我們會介紹如何來手動的在elf中添加一個段數據結構,盡情期待~~