DexFile.java revision 1af01ba10760876505772643778532d8e55c0265
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29package org.jf.dexlib; 30 31import org.jf.dexlib.util.AnnotatedOutput; 32import org.jf.dexlib.util.ByteArrayInput; 33import org.jf.dexlib.util.FileUtils; 34import org.jf.dexlib.util.Input; 35 36import java.io.File; 37import java.security.DigestException; 38import java.security.MessageDigest; 39import java.security.NoSuchAlgorithmException; 40import java.util.HashMap; 41import java.util.zip.Adler32; 42 43public class DexFile 44{ 45 private final HashMap<ItemType, Section> sectionsByType; 46 private final IndexedSection[] indexedSections; 47 private final OffsettedSection[] offsettedSections; 48 private int fileSize; 49 private int dataOffset; 50 private int dataSize; 51 52 private final DexFile dexFile = this; 53 54 private DexFile() { 55 sectionsByType = new HashMap<ItemType, Section>(18); 56 57 sectionsByType.put(ItemType.TYPE_ANNOTATION_ITEM, AnnotationsSection); 58 sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_ITEM, AnnotationSetsSection); 59 sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_REF_LIST, AnnotationSetRefListsSection); 60 sectionsByType.put(ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, AnnotationDirectoriesSection); 61 sectionsByType.put(ItemType.TYPE_CLASS_DATA_ITEM, ClassDataSection); 62 sectionsByType.put(ItemType.TYPE_CLASS_DEF_ITEM, ClassDefsSection); 63 sectionsByType.put(ItemType.TYPE_CODE_ITEM, CodeItemsSection); 64 sectionsByType.put(ItemType.TYPE_DEBUG_INFO_ITEM, DebugInfoItemsSection); 65 sectionsByType.put(ItemType.TYPE_ENCODED_ARRAY_ITEM, EncodedArraysSection); 66 sectionsByType.put(ItemType.TYPE_FIELD_ID_ITEM, FieldIdsSection); 67 sectionsByType.put(ItemType.TYPE_HEADER_ITEM, HeaderItemSection); 68 sectionsByType.put(ItemType.TYPE_MAP_LIST, MapSection); 69 sectionsByType.put(ItemType.TYPE_METHOD_ID_ITEM, MethodIdsSection); 70 sectionsByType.put(ItemType.TYPE_PROTO_ID_ITEM, ProtoIdsSection); 71 sectionsByType.put(ItemType.TYPE_STRING_DATA_ITEM, StringDataSection); 72 sectionsByType.put(ItemType.TYPE_STRING_ID_ITEM, StringIdsSection); 73 sectionsByType.put(ItemType.TYPE_TYPE_ID_ITEM, TypeIdsSection); 74 sectionsByType.put(ItemType.TYPE_TYPE_LIST, TypeListsSection); 75 76 indexedSections = new IndexedSection[] { 77 StringIdsSection, 78 TypeIdsSection, 79 ProtoIdsSection, 80 FieldIdsSection, 81 MethodIdsSection, 82 ClassDefsSection 83 }; 84 85 offsettedSections = new OffsettedSection[] { 86 AnnotationSetRefListsSection, 87 AnnotationSetsSection, 88 CodeItemsSection, 89 AnnotationDirectoriesSection, 90 TypeListsSection, 91 StringDataSection, 92 DebugInfoItemsSection, 93 AnnotationsSection, 94 EncodedArraysSection, 95 ClassDataSection 96 }; 97 } 98 99 public DexFile(File file) { 100 this(); 101 Input in = new ByteArrayInput(FileUtils.readFile(file)); 102 103 HeaderItemSection.readFrom(1, in); 104 HeaderItem headerItem = HeaderItemSection.items.get(0); 105 106 in.setCursor(headerItem.getMapOffset()); 107 108 MapSection.readFrom(1, in); 109 110 MapField[] mapEntries = MapSection.items.get(0).getMapEntries(); 111 HashMap<Integer, MapField> mapMap = new HashMap<Integer, MapField>(); 112 for (MapField mapField: mapEntries) { 113 mapMap.put(mapField.getSectionItemType().getMapValue(), mapField); 114 } 115 116 int[] sectionTypes = new int[] { 117 ItemType.TYPE_HEADER_ITEM.getMapValue(), 118 ItemType.TYPE_STRING_ID_ITEM.getMapValue(), 119 ItemType.TYPE_TYPE_ID_ITEM.getMapValue(), 120 ItemType.TYPE_PROTO_ID_ITEM.getMapValue(), 121 ItemType.TYPE_FIELD_ID_ITEM.getMapValue(), 122 ItemType.TYPE_METHOD_ID_ITEM.getMapValue(), 123 ItemType.TYPE_CLASS_DEF_ITEM.getMapValue(), 124 ItemType.TYPE_STRING_DATA_ITEM.getMapValue(), 125 ItemType.TYPE_ENCODED_ARRAY_ITEM.getMapValue(), 126 ItemType.TYPE_ANNOTATION_ITEM.getMapValue(), 127 ItemType.TYPE_ANNOTATION_SET_ITEM.getMapValue(), 128 ItemType.TYPE_ANNOTATION_SET_REF_LIST.getMapValue(), 129 ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM.getMapValue(), 130 ItemType.TYPE_TYPE_LIST.getMapValue(), 131 ItemType.TYPE_DEBUG_INFO_ITEM.getMapValue(), 132 ItemType.TYPE_CODE_ITEM.getMapValue(), 133 ItemType.TYPE_CLASS_DATA_ITEM.getMapValue(), 134 ItemType.TYPE_MAP_LIST.getMapValue() 135 }; 136 137 for (int sectionType: sectionTypes) { 138 MapField mapField = mapMap.get(sectionType); 139 if (mapField != null) { 140 Section section = sectionsByType.get(mapField.getSectionItemType()); 141 if (section != null) { 142 in.setCursor(mapField.getSectionOffset()); 143 section.readFrom(mapField.getSectionSize(), in); 144 } 145 } 146 } 147 } 148 149 public static DexFile makeBlankDexFile() { 150 DexFile dexFile = new DexFile(); 151 try 152 { 153 dexFile.HeaderItemSection.intern(dexFile, new HeaderItem(dexFile, 0)); 154 } catch (Exception ex) { 155 throw new RuntimeException(ex); 156 } 157 158 dexFile.MapSection.intern(dexFile, MapItem.makeBlankMapItem(dexFile)); 159 return dexFile; 160 } 161 162 163 public <T extends Item> Section<T> getSectionForItem(T item) { 164 return sectionsByType.get(item.getItemType()); 165 } 166 167 public Section getSectionForType(ItemType itemType) { 168 return sectionsByType.get(itemType); 169 } 170 171 public int getFileSize() { 172 return fileSize; 173 } 174 175 public int getDataOffset() { 176 return dataOffset; 177 } 178 179 public int getDataSize() { 180 return dataSize; 181 } 182 183 public void place() { 184 int offset = 0; 185 186 offset = HeaderItemSection.place(offset); 187 for (IndexedSection indexedSection: indexedSections) { 188 indexedSection.unplace(); 189 offset = indexedSection.place(offset); 190 } 191 192 dataOffset = offset; 193 194 for (OffsettedSection offsettedSection: offsettedSections) { 195 offsettedSection.unplace(); 196 197 offset = offsettedSection.place(offset); 198 } 199 200 offset = MapSection.place(offset); 201 202 dataSize = offset - dataOffset; 203 fileSize = offset; 204 } 205 206 public void writeTo(AnnotatedOutput out) { 207 HeaderItemSection.writeTo(out); 208 for (IndexedSection indexedSection: indexedSections) { 209 indexedSection.writeTo(out); 210 } 211 212 for (OffsettedSection offsettedSection: offsettedSections) { 213 offsettedSection.writeTo(out); 214 } 215 216 MapSection.writeTo(out); 217 } 218 219 public final IndexedSection<HeaderItem> HeaderItemSection = new IndexedSection<HeaderItem>() { 220 protected HeaderItem make(int index) { 221 return new HeaderItem(dexFile, index); 222 } 223 }; 224 225 public final IndexedSection<StringIdItem> StringIdsSection = new IndexedSection<StringIdItem>() { 226 protected StringIdItem make(int index) { 227 return new StringIdItem(dexFile, index); 228 } 229 }; 230 231 public final IndexedSection<TypeIdItem> TypeIdsSection = new IndexedSection<TypeIdItem>() { 232 protected TypeIdItem make(int index) { 233 return new TypeIdItem(dexFile, index); 234 } 235 }; 236 237 public final IndexedSection<ProtoIdItem> ProtoIdsSection = new IndexedSection<ProtoIdItem>() { 238 protected ProtoIdItem make(int index) { 239 return new ProtoIdItem(dexFile, index); 240 } 241 }; 242 243 public final IndexedSection<FieldIdItem> FieldIdsSection = new IndexedSection<FieldIdItem>() { 244 protected FieldIdItem make(int index) { 245 return new FieldIdItem(dexFile, index); 246 } 247 }; 248 249 public final IndexedSection<MethodIdItem> MethodIdsSection = new IndexedSection<MethodIdItem>() { 250 protected MethodIdItem make(int index) { 251 return new MethodIdItem(dexFile, index); 252 } 253 }; 254 255 public final IndexedSection<ClassDefItem> ClassDefsSection = new IndexedSection<ClassDefItem>() { 256 protected ClassDefItem make(int index) { 257 return new ClassDefItem(dexFile, index); 258 } 259 260 public int place(int offset) { 261 int ret = ClassDefItem.placeClassDefItems(this, offset); 262 263 this.offset = items.get(0).getOffset(); 264 return ret; 265 } 266 }; 267 268 public final IndexedSection<MapItem> MapSection = new IndexedSection<MapItem>() { 269 protected MapItem make(int index) { 270 return new MapItem(dexFile, index); 271 } 272 273 public MapItem intern(DexFile dexFile, MapItem item) { 274 this.items.add(item); 275 return item; 276 } 277 }; 278 279 public final OffsettedSection<TypeListItem> TypeListsSection = new OffsettedSection<TypeListItem>() { 280 protected TypeListItem make(int offset) { 281 return new TypeListItem(dexFile, offset); 282 } 283 }; 284 285 public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection = 286 new OffsettedSection<AnnotationSetRefList>() { 287 protected AnnotationSetRefList make(int offset) { 288 return new AnnotationSetRefList(dexFile, offset); 289 } 290 }; 291 292 public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection = 293 new OffsettedSection<AnnotationSetItem>() { 294 protected AnnotationSetItem make(int offset) { 295 return new AnnotationSetItem(dexFile, offset); 296 } 297 }; 298 299 public final OffsettedSection<ClassDataItem> ClassDataSection = new OffsettedSection<ClassDataItem>() { 300 protected ClassDataItem make(int offset) { 301 return new ClassDataItem(dexFile, offset); 302 } 303 }; 304 305 public final OffsettedSection<CodeItem> CodeItemsSection = new OffsettedSection<CodeItem>() { 306 protected CodeItem make(int offset) { 307 return new CodeItem(dexFile, offset); 308 } 309 }; 310 311 public final OffsettedSection<StringDataItem> StringDataSection = new OffsettedSection<StringDataItem>() { 312 protected StringDataItem make(int offset) { 313 return new StringDataItem(offset); 314 } 315 }; 316 317 public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection = new OffsettedSection<DebugInfoItem>() { 318 protected DebugInfoItem make(int offset) { 319 return new DebugInfoItem(dexFile, offset); 320 } 321 }; 322 323 public final OffsettedSection<AnnotationItem> AnnotationsSection = new OffsettedSection<AnnotationItem>() { 324 protected AnnotationItem make(int offset) { 325 return new AnnotationItem(dexFile, offset); 326 } 327 }; 328 329 public final OffsettedSection<EncodedArrayItem> EncodedArraysSection = new OffsettedSection<EncodedArrayItem>() { 330 protected EncodedArrayItem make(int offset) { 331 return new EncodedArrayItem(dexFile, offset); 332 } 333 }; 334 335 public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection = 336 new OffsettedSection<AnnotationDirectoryItem>() { 337 protected AnnotationDirectoryItem make(int offset) { 338 return new AnnotationDirectoryItem(dexFile, offset); 339 } 340 }; 341 342 343 /** 344 * Calculates the signature for the <code>.dex</code> file in the 345 * given array, and modify the array to contain it. 346 * 347 * @param bytes non-null; the bytes of the file 348 */ 349 public static void calcSignature(byte[] bytes) { 350 MessageDigest md; 351 352 try { 353 md = MessageDigest.getInstance("SHA-1"); 354 } catch (NoSuchAlgorithmException ex) { 355 throw new RuntimeException(ex); 356 } 357 358 md.update(bytes, 32, bytes.length - 32); 359 360 try { 361 int amt = md.digest(bytes, 12, 20); 362 if (amt != 20) { 363 throw new RuntimeException("unexpected digest write: " + amt + 364 " bytes"); 365 } 366 } catch (DigestException ex) { 367 throw new RuntimeException(ex); 368 } 369 } 370 371 /** 372 * Calculates the checksum for the <code>.dex</code> file in the 373 * given array, and modify the array to contain it. 374 * 375 * @param bytes non-null; the bytes of the file 376 */ 377 public static void calcChecksum(byte[] bytes) { 378 Adler32 a32 = new Adler32(); 379 380 a32.update(bytes, 12, bytes.length - 12); 381 382 int sum = (int) a32.getValue(); 383 384 bytes[8] = (byte) sum; 385 bytes[9] = (byte) (sum >> 8); 386 bytes[10] = (byte) (sum >> 16); 387 bytes[11] = (byte) (sum >> 24); 388 } 389} 390