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