DexFile.java revision fdef6422d2c5c87c0a6599bd568943d493436820
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 43 /** 44 * <h3>Main use cases</h3> 45 * 46 * <p>These are the main use cases that drove the design of this library</p> 47 * 48 * <ol> 49 * <li><p><b>Annotate an existing dex file</b> - In this case, the intent is to document the structure of 50 * an existing dex file. We want to be able to read in the dex file, and then write out a dex file 51 * that is exactly the same (while adding annotation information to an AnnotatedOutput object)</p></li> 52 * 53 * <li><p><b>Canonicalize an existing dex file</b> - In this case, the intent is to rewrite an existing dex file 54 * so that it is in a canonical form. There is a certain amount of leeway in how various types of 55 * tems in a dex file are ordered or represented. It is sometimes useful to be able to easily 56 * compare a disassebled and reassembled dex file with the original dex file. If both dex-files are 57 * written canonically, they "should" match exactly, barring any explicit changes to the reassembled 58 * file.</p> 59 * 60 * <p>Currently, there are a couple of pieces of information that probably won't match exactly 61 * <ul> 62 * <li>the order of exception handlers in the <code>EncodedCatchHandlerList</code> for a method</li> 63 * <li>the ordering of some of the debug info in the <code>{@link DebugInfoItem}</code> for a method</li> 64 * </ul></p> 65 * 66 * 67 * <p>Note that the above discrepancies should typically only be "intra-item" differences. They 68 * shouldn't change the size of the item, or affect how anything else is placed or laid out</p></li> 69 * 70 * <li><p><b>Creating a dex file from scratch</b> - In this case, a blank dex file is created and then classes 71 * are added to it incrementally by calling the {@link Section#intern intern} method of 72 * {@link DexFile#ClassDefsSection}, which will add all the information necessary to represent the given 73 * class. For example, when assembling a dex file from a set of assembly text files.</p> 74 * 75 * <p>In this case, we can choose to write the dex file in a canonical form or not. It is somewhat 76 * slower to write it in a canonical format, due to the extra sorting and calculations that are 77 * required.</p></li> 78 * 79 * 80 * <li><p><b>Reading in the dex file</b> - In this case, the intent is to read in a dex file and expose all the 81 * data to the calling application. For example, when disassembling a dex file into a text based 82 * assembly format, or doing other misc processing of the dex file.</p></li> 83 * 84 * 85 * <h3>Other use cases</h3> 86 * 87 * <p>These are other use cases that are possible, but did not drive the design of the library. 88 * No effort was made to test these use cases or ensure that they work. Some of these could 89 * probably be better achieved with a disassemble - modify - reassemble type process, using 90 * smali/baksmali or another assembler/disassembler pair that are compatible with each other</p> 91 * 92 * <ul> 93 * <li>deleting classes/methods/etc. from a dex file</li> 94 * <li>merging 2 dex files</li> 95 * <li>splitting a dex file</li> 96 * <li>moving classes from 1 dex file to another</li> 97 * <li>removing the debug information from a dex file</li> 98 * <li>obfustication of a dex file</li> 99 * </ul> 100 */ 101public class DexFile 102{ 103 /** 104 * A mapping from ItemType to the section that contains items of the given type 105 */ 106 private final HashMap<ItemType, Section> sectionsByType; 107 108 /** 109 * Ordered lists of the indexed and offsetted sections. The order of these lists specifies the order 110 * that the sections will be written in 111 */ 112 private final IndexedSection[] indexedSections; 113 private final OffsettedSection[] offsettedSections; 114 115 /** 116 * dalvik had a bug where it wrote the registers for certain types of debug info in a signed leb 117 * format, instead of an unsigned leb format. There are no negative registers of course, but 118 * certain positive values have a different encoding depending on whether they are encoded as 119 * an unsigned leb128 or a signed leb128. Specifically, the signed leb128 is 1 byte longer in some cases. 120 * 121 * This determine whether we should keep any signed registers as signed, or force all register to 122 * unsigned. By default we don't keep track of whether they were signed or not, and write them back 123 * out as unsigned. This option only has an effect when reading an existing dex file. It has no 124 * effect when a dex file is created from scratch 125 * 126 * The 2 main use-cases in play are 127 * 1. Annotate an existing dex file - In this case, preserveSignedRegisters should be false, so that we keep 128 * track of any signed registers and write them back out as signed Leb128 values. 129 * 130 * 2. Canonicalize an existing dex file - In this case, fixRegisters should be true, so that all 131 * registers in the debug info are written as unsigned Leb128 values regardless of how they were 132 * originally encoded 133 */ 134 private final boolean preserveSignedRegisters; 135 136 /** 137 * When true, this prevents any sorting of the items during placement of the dex file. This 138 * should *only* be set to true when this dex file was read in from an existing (valid) dex file, 139 * and no modifications were made (i.e. no items added or deleted). Otherwise it is likely that 140 * an invalid dex file will be generated. 141 * 142 * This is useful for the first use case (annotating an existing dex file). This ensures the items 143 * retain the same order as in the original dex file. 144 */ 145 private boolean inplace = false; 146 147 /** 148 * When true, this imposes an full ordering on all the items, to force them into a (possibly 149 * arbitrary) canonical order. When false, only the items that the dex format specifies 150 * an order for are sorted. The rest of the items are not ordered. 151 * 152 * This is useful for the second use case (canonicalizing an existing dex file) or possibly for 153 * the third use case (creating a dex file from scratch), if there is a need to write the new 154 * dex file in a canonical form. 155 */ 156 private boolean sortAllItems = false; 157 158 159 /** 160 * this is used to access the dex file from within inner classes, when they declare fields or 161 * variable that hide fields on this object 162 */ 163 private final DexFile dexFile = this; 164 165 166 /** 167 * A private constructor containing common code to initialize the section maps and lists 168 * @param preserveSignedRegisters If true, keep track of any registers in the debug information 169 * that are signed, so they will be written in the same format. See 170 * <code>getPreserveSignedRegisters()</code> 171 */ 172 private DexFile(boolean preserveSignedRegisters) { 173 this.preserveSignedRegisters = preserveSignedRegisters; 174 175 sectionsByType = new HashMap<ItemType, Section>(18); 176 177 sectionsByType.put(ItemType.TYPE_ANNOTATION_ITEM, AnnotationsSection); 178 sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_ITEM, AnnotationSetsSection); 179 sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_REF_LIST, AnnotationSetRefListsSection); 180 sectionsByType.put(ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, AnnotationDirectoriesSection); 181 sectionsByType.put(ItemType.TYPE_CLASS_DATA_ITEM, ClassDataSection); 182 sectionsByType.put(ItemType.TYPE_CLASS_DEF_ITEM, ClassDefsSection); 183 sectionsByType.put(ItemType.TYPE_CODE_ITEM, CodeItemsSection); 184 sectionsByType.put(ItemType.TYPE_DEBUG_INFO_ITEM, DebugInfoItemsSection); 185 sectionsByType.put(ItemType.TYPE_ENCODED_ARRAY_ITEM, EncodedArraysSection); 186 sectionsByType.put(ItemType.TYPE_FIELD_ID_ITEM, FieldIdsSection); 187 sectionsByType.put(ItemType.TYPE_HEADER_ITEM, HeaderItemSection); 188 sectionsByType.put(ItemType.TYPE_MAP_LIST, MapSection); 189 sectionsByType.put(ItemType.TYPE_METHOD_ID_ITEM, MethodIdsSection); 190 sectionsByType.put(ItemType.TYPE_PROTO_ID_ITEM, ProtoIdsSection); 191 sectionsByType.put(ItemType.TYPE_STRING_DATA_ITEM, StringDataSection); 192 sectionsByType.put(ItemType.TYPE_STRING_ID_ITEM, StringIdsSection); 193 sectionsByType.put(ItemType.TYPE_TYPE_ID_ITEM, TypeIdsSection); 194 sectionsByType.put(ItemType.TYPE_TYPE_LIST, TypeListsSection); 195 196 indexedSections = new IndexedSection[] { 197 StringIdsSection, 198 TypeIdsSection, 199 ProtoIdsSection, 200 FieldIdsSection, 201 MethodIdsSection, 202 ClassDefsSection 203 }; 204 205 offsettedSections = new OffsettedSection[] { 206 AnnotationSetRefListsSection, 207 AnnotationSetsSection, 208 CodeItemsSection, 209 AnnotationDirectoriesSection, 210 TypeListsSection, 211 StringDataSection, 212 AnnotationsSection, 213 EncodedArraysSection, 214 ClassDataSection, 215 DebugInfoItemsSection 216 }; 217 } 218 219 220 /** 221 * Construct a new DexFile instance by reading in the given dex file. 222 * @param file The dex file to read in 223 */ 224 public DexFile(String file) { 225 this(new File(file), true); 226 } 227 228 /** 229 * Construct a new DexFile instance by reading in the given dex file, 230 * and optionally keep track of any registers in the debug information that are signed, 231 * so they will be written in the same format. 232 * @param file The dex file to read in 233 * @param preserveSignedRegisters If true, keep track of any registers in the debug information 234 * that are signed, so they will be written in the same format. See 235 * <code>getPreserveSignedRegisters()</code> 236 */ 237 public DexFile(String file, boolean preserveSignedRegisters) { 238 this(new File(file), preserveSignedRegisters); 239 } 240 241 /** 242 * Construct a new DexFile instead by reading in the given dex file. 243 * @param file The dex file to read in 244 */ 245 public DexFile(File file) { 246 this(file, true); 247 } 248 249 /** 250 * Construct a new DexFile instance by reading in the given dex file, 251 * and optionally keep track of any registers in the debug information that are signed, 252 * so they will be written in the same format. 253 * @param file The dex file to read in 254 * @param preserveSignedRegisters If true, keep track of any registers in the debug information 255 * that are signed, so they will be written in the same format. 256 * @see #getPreserveSignedRegisters 257 */ 258 public DexFile(File file, boolean preserveSignedRegisters) { 259 this(preserveSignedRegisters); 260 261 Input in = new ByteArrayInput(FileUtils.readFile(file)); 262 263 HeaderItemSection.readFrom(1, in); 264 HeaderItem headerItem = HeaderItemSection.items.get(0); 265 266 in.setCursor(headerItem.getMapOffset()); 267 MapSection.readFrom(1, in); 268 269 MapField[] mapEntries = MapSection.items.get(0).getMapEntries(); 270 HashMap<Integer, MapField> mapMap = new HashMap<Integer, MapField>(); 271 for (MapField mapField: mapEntries) { 272 mapMap.put(mapField.getSectionItemType().getMapValue(), mapField); 273 } 274 275 /** 276 * This defines the order in which the sections are read in. This is not 277 * necessarily the order in which the appear in the file. 278 */ 279 //TODO: do we *need* to read them in a specific order, rather than the order that is in the file? 280 int[] sectionTypes = new int[] { 281 ItemType.TYPE_HEADER_ITEM.getMapValue(), 282 ItemType.TYPE_STRING_ID_ITEM.getMapValue(), 283 ItemType.TYPE_TYPE_ID_ITEM.getMapValue(), 284 ItemType.TYPE_PROTO_ID_ITEM.getMapValue(), 285 ItemType.TYPE_FIELD_ID_ITEM.getMapValue(), 286 ItemType.TYPE_METHOD_ID_ITEM.getMapValue(), 287 ItemType.TYPE_CLASS_DEF_ITEM.getMapValue(), 288 ItemType.TYPE_STRING_DATA_ITEM.getMapValue(), 289 ItemType.TYPE_ENCODED_ARRAY_ITEM.getMapValue(), 290 ItemType.TYPE_ANNOTATION_ITEM.getMapValue(), 291 ItemType.TYPE_ANNOTATION_SET_ITEM.getMapValue(), 292 ItemType.TYPE_ANNOTATION_SET_REF_LIST.getMapValue(), 293 ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM.getMapValue(), 294 ItemType.TYPE_TYPE_LIST.getMapValue(), 295 ItemType.TYPE_CODE_ITEM.getMapValue(), 296 ItemType.TYPE_CLASS_DATA_ITEM.getMapValue(), 297 ItemType.TYPE_DEBUG_INFO_ITEM.getMapValue(), 298 ItemType.TYPE_MAP_LIST.getMapValue() 299 }; 300 301 for (int sectionType: sectionTypes) { 302 MapField mapField = mapMap.get(sectionType); 303 if (mapField != null) { 304 Section section = sectionsByType.get(mapField.getSectionItemType()); 305 if (section != null) { 306 in.setCursor(mapField.getCachedSectionOffset()); 307 section.readFrom(mapField.getCachedSectionSize(), in); 308 } 309 } 310 } 311 } 312 313 /** 314 * Constructs a new, blank dex file. Classes can be added to this dex file by calling 315 * the <code>Section.intern()</code> method of <code>ClassDefsSection</code> 316 */ 317 public DexFile() { 318 this(true); 319 320 HeaderItemSection.intern(new HeaderItem(dexFile, 0)); 321 MapSection.intern(new MapItem(dexFile)); 322 } 323 324 /** 325 * Convenience method to retrieve the header item 326 * @return the header item 327 */ 328 public HeaderItem getHeaderItem() { 329 return HeaderItemSection.getItems().get(0); 330 } 331 332 /** 333 * Convenience method to retrieve the map item 334 * @return the map item 335 */ 336 public MapItem getMapItem() { 337 return MapSection.getItems().get(0); 338 } 339 340 /** 341 * Get the <code>Section</code> containing items of the same type as the given item 342 * @param item Get the <code>Section</code> that contains items of this type 343 * @param <T> The specific item subclass - inferred from the passed item 344 * @return the <code>Section</code> containing items of the same type as the given item 345 */ 346 public <T extends Item> Section<T> getSectionForItem(T item) { 347 return sectionsByType.get(item.getItemType()); 348 } 349 350 /** 351 * Get the <code>Section</code> containing items of the given type 352 * @param itemType the type of item 353 * @return the <code>Section</code> containing items of the given type 354 */ 355 public Section getSectionForType(ItemType itemType) { 356 return sectionsByType.get(itemType); 357 } 358 359 /** 360 * Get a boolean value indicating whether this dex file preserved any signed 361 * registers in the debug info as it read the dex file in. By default, the dex file 362 * doesn't check whether the registers are encoded as unsigned or signed values. 363 * 364 * This does *not* affect the actual register value that is read in. The value is 365 * read correctly regardless 366 * 367 * This does affect whether any signed registers will retain the same encoding or be 368 * forced to the (correct) unsigned encoding when the dex file is written back out. 369 * 370 * See the discussion about signed register values in the documentation for 371 * <code>DexFile</code> 372 * @return a boolean indicating whether this dex file preserved any signed registers 373 * as it was read in 374 */ 375 public boolean getPreserveSignedRegisters() { 376 return preserveSignedRegisters; 377 } 378 379 /** 380 * Get a boolean value indicating whether all items should be placed into a 381 * (possibly arbitrary) "canonical" ordering. If false, then only the items 382 * that must be ordered per the dex specification are sorted. 383 * 384 * When true, writing the dex file involves somewhat more overhead 385 * 386 * If both SortAllItems and Inplace are true, Inplace takes precedence 387 * @return a boolean value indicating whether all items should be sorted 388 */ 389 public boolean getSortAllItems() { 390 return this.sortAllItems; 391 } 392 393 /** 394 * Set a boolean value indicating whether all items should be placed into a 395 * (possibly arbitrary) "canonical" ordering. If false, then only the items 396 * that must be ordered per the dex specification are sorted. 397 * 398 * When true, writing the dex file involves somewhat more overhead 399 * 400 * If both SortAllItems and Inplace are true, Inplace takes precedence 401 * @param value a boolean value indicating whether all items should be sorted 402 */ 403 public void setSortAllItems(boolean value) { 404 this.sortAllItems = value; 405 } 406 407 /** 408 * Get a boolean value indicating whether items in this dex file should be 409 * written back out "in-place", or whether the normal layout logic should be 410 * applied. 411 * 412 * This should only be used for a dex file that has been read from an existing 413 * dex file, and no modifications have been made to the dex file. Otherwise, 414 * there is a good chance that the resulting dex file will be invalid due to 415 * items that aren't placed correctly 416 * 417 * If both SortAllItems and Inplace are true, Inplace takes precedence 418 * @return a boolean value indicating whether items in this dex file should be 419 * written back out in-place. 420 */ 421 public boolean getInplace() { 422 return this.inplace; 423 } 424 425 /** 426 * Set a boolean value indicating whether items in this dex file should be 427 * written back out "in-place", or whether the normal layout logic should be 428 * applied. 429 * 430 * This should only be used for a dex file that has been read from an existing 431 * dex file, and no modifications have been made to the dex file. Otherwise, 432 * there is a good chance that the resulting dex file will be invalid due to 433 * items that aren't placed correctly 434 * 435 * If both SortAllItems and Inplace are true, Inplace takes precedence 436 * @param value a boolean value indicating whether items in this dex file should be 437 * written back out in-place. 438 */ 439 public void setInplace(boolean value) { 440 this.inplace = value; 441 } 442 443 /** 444 * This method should be called before writing a dex file. It sorts the sections 445 * as needed or as indicated by <code>getSortAllItems()</code> and <code>getInplace()</code>, 446 * and then performs a pass through all of the items, finalizing the position (i.e. 447 * index and/or offset) of each item in the dex file. 448 * 449 * This step is needed primarily so that the indexes and offsets of all indexed and 450 * offsetted items are available when writing references to those items elsewhere. 451 */ 452 public void place() { 453 HeaderItem headerItem = getHeaderItem(); 454 455 int offset = HeaderItemSection.place(0); 456 457 int dataOffset; 458 459 for (IndexedSection indexedSection: indexedSections) { 460 offset = indexedSection.place(offset); 461 } 462 463 dataOffset = offset; 464 headerItem.setDataOffset(dataOffset); 465 466 //TODO: if inplace is true, we need to use the order of the sections as they were in the original file 467 for (OffsettedSection offsettedSection: offsettedSections) { 468 if (this.sortAllItems && !this.inplace) { 469 offsettedSection.sortSection(); 470 } 471 offset = offsettedSection.place(offset); 472 } 473 474 offset = MapSection.place(offset); 475 476 477 headerItem.setFileSize(offset); 478 headerItem.setDataSize(offset - dataOffset); 479 } 480 481 /** 482 * Writes the dex file to the give <code>AnnotatedOutput</code> object. If 483 * <code>out.Annotates()</code> is true, then annotations that document the format 484 * of the dex file are written. 485 * 486 * You must call <code>place()</code> on this dex file, before calling this method 487 * @param out the AnnotatedOutput object to write the dex file and annotations to 488 * 489 * After calling this method, you should call <code>calcSignature()</code> and 490 * then <code>calcChecksum()</code> on the resulting byte array, to calculate the 491 * signature and checksum in the header 492 */ 493 public void writeTo(AnnotatedOutput out) { 494 HeaderItemSection.writeTo(out); 495 496 for (IndexedSection indexedSection: indexedSections) { 497 indexedSection.writeTo(out); 498 } 499 500 for (OffsettedSection offsettedSection: offsettedSections) { 501 offsettedSection.writeTo(out); 502 } 503 504 MapSection.writeTo(out); 505 } 506 507 /** 508 * The <code>IndexedSection</code> containing the sole <code>HeaderItem</code> item. Use 509 * <code>getHeaderItem()</code> instead. 510 */ 511 public final IndexedSection<HeaderItem> HeaderItemSection = new IndexedSection<HeaderItem>(this) { 512 protected HeaderItem make(int index) { 513 return new HeaderItem(dexFile, index); 514 } 515 }; 516 517 /** 518 * The <code>IndexedSection</code> containing <code>StringIdItem</code> items 519 */ 520 public final IndexedSection<StringIdItem> StringIdsSection = new IndexedSection<StringIdItem>(this) { 521 protected StringIdItem make(int index) { 522 return new StringIdItem(dexFile, index); 523 } 524 }; 525 526 /** 527 * The <code>IndexedSection</code> containing <code>TypeIdItem</code> items 528 */ 529 public final IndexedSection<TypeIdItem> TypeIdsSection = new IndexedSection<TypeIdItem>(this) { 530 protected TypeIdItem make(int index) { 531 return new TypeIdItem(dexFile, index); 532 } 533 }; 534 535 /** 536 * The <code>IndexedSection</code> containing <code>ProtoIdItem</code> items 537 */ 538 public final IndexedSection<ProtoIdItem> ProtoIdsSection = new IndexedSection<ProtoIdItem>(this) { 539 protected ProtoIdItem make(int index) { 540 return new ProtoIdItem(dexFile, index); 541 } 542 }; 543 544 /** 545 * The <code>IndexedSection</code> containing <code>FieldIdItem</code> items 546 */ 547 public final IndexedSection<FieldIdItem> FieldIdsSection = new IndexedSection<FieldIdItem>(this) { 548 protected FieldIdItem make(int index) { 549 return new FieldIdItem(dexFile, index); 550 } 551 }; 552 553 /** 554 * The <code>IndexedSection</code> containing <code>MethodIdItem</code> items 555 */ 556 public final IndexedSection<MethodIdItem> MethodIdsSection = new IndexedSection<MethodIdItem>(this) { 557 protected MethodIdItem make(int index) { 558 return new MethodIdItem(dexFile, index); 559 } 560 }; 561 562 /** 563 * The <code>IndexedSection</code> containing <code>ClassDefItem</code> items 564 */ 565 public final IndexedSection<ClassDefItem> ClassDefsSection = new IndexedSection<ClassDefItem>(this) { 566 protected ClassDefItem make(int index) { 567 return new ClassDefItem(dexFile, index); 568 } 569 570 public int place(int offset) { 571 if (dexFile.getInplace()) { 572 return super.place(offset); 573 } 574 575 int ret = ClassDefItem.placeClassDefItems(this, offset); 576 577 this.offset = items.get(0).getOffset(); 578 return ret; 579 } 580 }; 581 582 /** 583 * The <code>IndexedSection</code> containing the sole <code>MapItem</code>. Use 584 * <code>getMapItem()</code> instead 585 */ 586 public final IndexedSection<MapItem> MapSection = new IndexedSection<MapItem>(this) { 587 protected MapItem make(int index) { 588 return new MapItem(dexFile, index); 589 } 590 591 public MapItem intern(MapItem item) { 592 this.items.add(item); 593 return item; 594 } 595 }; 596 597 /** 598 * The <code>OffsettedSection</code> containing <code>TypeListItem</code> items 599 */ 600 public final OffsettedSection<TypeListItem> TypeListsSection = new OffsettedSection<TypeListItem>(this) { 601 protected TypeListItem make(int offset) { 602 return new TypeListItem(dexFile, offset); 603 } 604 }; 605 606 /** 607 * The <code>OffsettedSection</code> containing <code>AnnotationSetRefList</code> items 608 */ 609 public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection = 610 new OffsettedSection<AnnotationSetRefList>(this) { 611 protected AnnotationSetRefList make(int offset) { 612 return new AnnotationSetRefList(dexFile, offset); 613 } 614 }; 615 616 /** 617 * The <code>OffsettedSection</code> containing <code>AnnotationSetItem</code> items 618 */ 619 public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection = 620 new OffsettedSection<AnnotationSetItem>(this) { 621 protected AnnotationSetItem make(int offset) { 622 return new AnnotationSetItem(dexFile, offset); 623 } 624 }; 625 626 /** 627 * The <code>OffsettedSection</code> containing <code>ClassDataItem</code> items 628 */ 629 public final OffsettedSection<ClassDataItem> ClassDataSection = new OffsettedSection<ClassDataItem>(this) { 630 protected ClassDataItem make(int offset) { 631 return new ClassDataItem(dexFile, offset); 632 } 633 }; 634 635 /** 636 * The <code>OffsettedSection</code> containing <code>CodeItem</code> items 637 */ 638 public final OffsettedSection<CodeItem> CodeItemsSection = new OffsettedSection<CodeItem>(this) { 639 protected CodeItem make(int offset) { 640 return new CodeItem(dexFile, offset); 641 } 642 }; 643 644 /** 645 * The <code>OffsettedSection</code> containing <code>StringDataItem</code> items 646 */ 647 public final OffsettedSection<StringDataItem> StringDataSection = new OffsettedSection<StringDataItem>(this) { 648 protected StringDataItem make(int offset) { 649 return new StringDataItem(dexFile, offset); 650 } 651 }; 652 653 /** 654 * The <code>OffsettedSection</code> containing <code>DebugInfoItem</code> items 655 */ 656 public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection = new OffsettedSection<DebugInfoItem>(this) { 657 protected DebugInfoItem make(int offset) { 658 return new DebugInfoItem(dexFile, offset); 659 } 660 }; 661 662 /** 663 * The <code>OffsettedSection</code> containing <code>AnnotationItem</code> items 664 */ 665 public final OffsettedSection<AnnotationItem> AnnotationsSection = new OffsettedSection<AnnotationItem>(this) { 666 protected AnnotationItem make(int offset) { 667 return new AnnotationItem(dexFile, offset); 668 } 669 }; 670 671 /** 672 * The <code>OffsettedSection</code> containing <code>EncodedArrayItem</code> items 673 */ 674 public final OffsettedSection<EncodedArrayItem> EncodedArraysSection = new OffsettedSection<EncodedArrayItem>(this) { 675 protected EncodedArrayItem make(int offset) { 676 return new EncodedArrayItem(dexFile, offset); 677 } 678 }; 679 680 /** 681 * The <code>OffsettedSection</code> containing <code>AnnotationDirectoryItem</code> items 682 */ 683 public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection = 684 new OffsettedSection<AnnotationDirectoryItem>(this) { 685 protected AnnotationDirectoryItem make(int offset) { 686 return new AnnotationDirectoryItem(dexFile, offset); 687 } 688 }; 689 690 691 /** 692 * Calculates the signature for the dex file in the given byte array, 693 * and then writes the signature to the appropriate location in the header 694 * containing in the array 695 * 696 * @param bytes non-null; the bytes of the file 697 */ 698 public static void calcSignature(byte[] bytes) { 699 MessageDigest md; 700 701 try { 702 md = MessageDigest.getInstance("SHA-1"); 703 } catch (NoSuchAlgorithmException ex) { 704 throw new RuntimeException(ex); 705 } 706 707 md.update(bytes, 32, bytes.length - 32); 708 709 try { 710 int amt = md.digest(bytes, 12, 20); 711 if (amt != 20) { 712 throw new RuntimeException("unexpected digest write: " + amt + 713 " bytes"); 714 } 715 } catch (DigestException ex) { 716 throw new RuntimeException(ex); 717 } 718 } 719 720 /** 721 * Calculates the checksum for the <code>.dex</code> file in the 722 * given array, and modify the array to contain it. 723 * 724 * @param bytes non-null; the bytes of the file 725 */ 726 public static void calcChecksum(byte[] bytes) { 727 Adler32 a32 = new Adler32(); 728 729 a32.update(bytes, 12, bytes.length - 12); 730 731 int sum = (int) a32.getValue(); 732 733 bytes[8] = (byte) sum; 734 bytes[9] = (byte) (sum >> 8); 735 bytes[10] = (byte) (sum >> 16); 736 bytes[11] = (byte) (sum >> 24); 737 } 738} 739