Home | History | Annotate | Download | only in raw
      1 /*
      2  * Copyright 2013, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.dexbacked.raw;
     33 
     34 import org.jf.dexlib2.dexbacked.BaseDexBuffer;
     35 import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator;
     36 import org.jf.dexlib2.util.AnnotatedBytes;
     37 import org.jf.util.StringUtils;
     38 
     39 import javax.annotation.Nonnull;
     40 import javax.annotation.Nullable;
     41 
     42 public class HeaderItem {
     43     public static final int ITEM_SIZE = 0x70;
     44 
     45     private static final byte[] MAGIC_VALUE = new byte[] { 0x64, 0x65, 0x78, 0x0a, 0x00, 0x00, 0x00, 0x00 };
     46     private static final int[] SUPPORTED_DEX_VERSIONS = new int[] { 35, 37, 38 };
     47 
     48     public static final int LITTLE_ENDIAN_TAG = 0x12345678;
     49     public static final int BIG_ENDIAN_TAG = 0x78563412;
     50 
     51     public static final int CHECKSUM_OFFSET = 8;
     52 
     53     // this is the start of the checksumed data
     54     public static final int CHECKSUM_DATA_START_OFFSET = 12;
     55     public static final int SIGNATURE_OFFSET = 12;
     56     public static final int SIGNATURE_SIZE = 20;
     57 
     58     // this is the start of the sha-1 hashed data
     59     public static final int SIGNATURE_DATA_START_OFFSET = 32;
     60     public static final int FILE_SIZE_OFFSET = 32;
     61 
     62     public static final int HEADER_SIZE_OFFSET = 36;
     63 
     64     public static final int ENDIAN_TAG_OFFSET = 40;
     65 
     66     public static final int MAP_OFFSET = 52;
     67 
     68     public static final int STRING_COUNT_OFFSET = 56;
     69     public static final int STRING_START_OFFSET = 60;
     70 
     71     public static final int TYPE_COUNT_OFFSET = 64;
     72     public static final int TYPE_START_OFFSET = 68;
     73 
     74     public static final int PROTO_COUNT_OFFSET = 72;
     75     public static final int PROTO_START_OFFSET = 76;
     76 
     77     public static final int FIELD_COUNT_OFFSET = 80;
     78     public static final int FIELD_START_OFFSET = 84;
     79 
     80     public static final int METHOD_COUNT_OFFSET = 88;
     81     public static final int METHOD_START_OFFSET = 92;
     82 
     83     public static final int CLASS_COUNT_OFFSET = 96;
     84     public static final int CLASS_START_OFFSET = 100;
     85 
     86     @Nonnull private RawDexFile dexFile;
     87 
     88     public HeaderItem(@Nonnull RawDexFile dexFile) {
     89         this.dexFile = dexFile;
     90     }
     91 
     92     public int getChecksum() {
     93         return dexFile.readSmallUint(CHECKSUM_OFFSET);
     94     }
     95 
     96     @Nonnull public byte[] getSignature() {
     97         return dexFile.readByteRange(SIGNATURE_OFFSET, SIGNATURE_SIZE);
     98     }
     99 
    100     public int getMapOffset() {
    101         return dexFile.readSmallUint(MAP_OFFSET);
    102     }
    103 
    104     public int getHeaderSize() {
    105         return dexFile.readSmallUint(HEADER_SIZE_OFFSET);
    106     }
    107 
    108     public int getStringCount() {
    109         return dexFile.readSmallUint(STRING_COUNT_OFFSET);
    110     }
    111 
    112     public int getStringOffset() {
    113         return dexFile.readSmallUint(STRING_START_OFFSET);
    114     }
    115 
    116     public int getTypeCount() {
    117         return dexFile.readSmallUint(TYPE_COUNT_OFFSET);
    118     }
    119 
    120     public int getTypeOffset() {
    121         return dexFile.readSmallUint(TYPE_START_OFFSET);
    122     }
    123 
    124     public int getProtoCount() {
    125         return dexFile.readSmallUint(PROTO_COUNT_OFFSET);
    126     }
    127 
    128     public int getProtoOffset() {
    129         return dexFile.readSmallUint(PROTO_START_OFFSET);
    130     }
    131 
    132     public int getFieldCount() {
    133         return dexFile.readSmallUint(FIELD_COUNT_OFFSET);
    134     }
    135 
    136     public int getFieldOffset() {
    137         return dexFile.readSmallUint(FIELD_START_OFFSET);
    138     }
    139 
    140     public int getMethodCount() {
    141         return dexFile.readSmallUint(METHOD_COUNT_OFFSET);
    142     }
    143 
    144     public int getMethodOffset() {
    145         return dexFile.readSmallUint(METHOD_START_OFFSET);
    146     }
    147 
    148     public int getClassCount() {
    149         return dexFile.readSmallUint(CLASS_COUNT_OFFSET);
    150     }
    151 
    152     public int getClassOffset() {
    153         return dexFile.readSmallUint(CLASS_START_OFFSET);
    154     }
    155 
    156     @Nonnull
    157     public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) {
    158         return new SectionAnnotator(annotator, mapItem) {
    159             @Nonnull @Override public String getItemName() {
    160                 return "header_item";
    161             }
    162 
    163             @Override
    164             protected void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
    165                 int startOffset = out.getCursor();
    166                 int headerSize;
    167 
    168                 StringBuilder magicBuilder = new StringBuilder();
    169                 for (int i=0; i<8; i++) {
    170                     magicBuilder.append((char)dexFile.readUbyte(startOffset + i));
    171                 }
    172 
    173                 out.annotate(8, "magic: %s", StringUtils.escapeString(magicBuilder.toString()));
    174                 out.annotate(4, "checksum");
    175                 out.annotate(20, "signature");
    176                 out.annotate(4, "file_size: %d", dexFile.readInt(out.getCursor()));
    177 
    178                 headerSize = dexFile.readInt(out.getCursor());
    179                 out.annotate(4, "header_size: %d", headerSize);
    180 
    181                 int endianTag = dexFile.readInt(out.getCursor());
    182                 out.annotate(4, "endian_tag: 0x%x (%s)", endianTag, getEndianText(endianTag));
    183 
    184                 out.annotate(4, "link_size: %d", dexFile.readInt(out.getCursor()));
    185                 out.annotate(4, "link_offset: 0x%x", dexFile.readInt(out.getCursor()));
    186 
    187                 out.annotate(4, "map_off: 0x%x", dexFile.readInt(out.getCursor()));
    188 
    189                 out.annotate(4, "string_ids_size: %d", dexFile.readInt(out.getCursor()));
    190                 out.annotate(4, "string_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
    191 
    192                 out.annotate(4, "type_ids_size: %d", dexFile.readInt(out.getCursor()));
    193                 out.annotate(4, "type_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
    194 
    195                 out.annotate(4, "proto_ids_size: %d", dexFile.readInt(out.getCursor()));
    196                 out.annotate(4, "proto_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
    197 
    198                 out.annotate(4, "field_ids_size: %d", dexFile.readInt(out.getCursor()));
    199                 out.annotate(4, "field_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
    200 
    201                 out.annotate(4, "method_ids_size: %d", dexFile.readInt(out.getCursor()));
    202                 out.annotate(4, "method_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
    203 
    204                 out.annotate(4, "class_defs_size: %d", dexFile.readInt(out.getCursor()));
    205                 out.annotate(4, "class_defs_off: 0x%x", dexFile.readInt(out.getCursor()));
    206 
    207                 out.annotate(4, "data_size: %d", dexFile.readInt(out.getCursor()));
    208                 out.annotate(4, "data_off: 0x%x", dexFile.readInt(out.getCursor()));
    209 
    210                 if (headerSize > ITEM_SIZE) {
    211                     out.annotateTo(headerSize, "header padding");
    212                 }
    213             }
    214         };
    215     }
    216 
    217     private static String getEndianText(int endianTag) {
    218         if (endianTag == LITTLE_ENDIAN_TAG) {
    219             return "Little Endian";
    220         }
    221         if (endianTag == BIG_ENDIAN_TAG) {
    222             return "Big Endian";
    223         }
    224         return "Invalid";
    225     }
    226 
    227     /**
    228      * Get the highest magic number supported by Android for this api level.
    229      * @return The dex file magic number
    230      */
    231     public static byte[] getMagicForApi(int api) {
    232         if (api < 24) {
    233             // Prior to Android N we only support dex version 035.
    234             return getMagicForDexVersion(35);
    235         } else if (api < 26) {
    236             // On android N and later we support dex version 037.
    237             return getMagicForDexVersion(37);
    238         } else {
    239             // On android O and later we support dex version 038.
    240             return getMagicForDexVersion(38);
    241         }
    242     }
    243 
    244     public static byte[] getMagicForDexVersion(int dexVersion) {
    245         byte[] magic = MAGIC_VALUE.clone();
    246 
    247         if (dexVersion < 0 || dexVersion > 999) {
    248             throw new IllegalArgumentException("dexVersion must be within [0, 999]");
    249         }
    250 
    251         for (int i=6; i>=4; i--) {
    252             int digit = dexVersion % 10;
    253             magic[i] = (byte)('0' + digit);
    254             dexVersion /= 10;
    255         }
    256 
    257         return magic;
    258     }
    259 
    260     /**
    261      * Verifies the magic value at the beginning of a dex file
    262      *
    263      * @param buf A byte array containing at least the first 8 bytes of a dex file
    264      * @param offset The offset within the buffer to the beginning of the dex header
    265      * @return True if the magic value is valid
    266      */
    267     public static boolean verifyMagic(byte[] buf, int offset) {
    268         if (buf.length - offset < 8) {
    269             return false;
    270         }
    271 
    272         for (int i=0; i<4; i++) {
    273             if (buf[offset + i] != MAGIC_VALUE[i]) {
    274                 return false;
    275                 }
    276             }
    277         for (int i=4; i<7; i++) {
    278             if (buf[offset + i] < '0' ||
    279                     buf[offset + i] > '9') {
    280                 return false;
    281             }
    282         }
    283         if (buf[offset + 7] != MAGIC_VALUE[7]) {
    284             return false;
    285     }
    286 
    287         return true;
    288     }
    289 
    290     /**
    291      * Gets the dex version from a dex header
    292      *
    293      * @param buf A byte array containing at least the first 7 bytes of a dex file
    294      * @param offset The offset within the buffer to the beginning of the dex header
    295      * @return The dex version if the header is valid or -1 if the header is invalid
    296      */
    297     public static int getVersion(byte[] buf, int offset) {
    298         if (!verifyMagic(buf, offset)) {
    299             return -1;
    300         }
    301 
    302         return getVersionUnchecked(buf, offset);
    303     }
    304 
    305     private static int getVersionUnchecked(byte[] buf, int offset) {
    306         int version = (buf[offset + 4] - '0') * 100;
    307         version += (buf[offset + 5] - '0') * 10;
    308         version += buf[offset + 6] - '0';
    309 
    310         return version;
    311     }
    312 
    313     public static boolean isSupportedDexVersion(int version) {
    314         for (int i=0; i<SUPPORTED_DEX_VERSIONS.length; i++) {
    315             if (SUPPORTED_DEX_VERSIONS[i] == version) {
    316                 return true;
    317             }
    318         }
    319         return false;
    320     }
    321 
    322     public static int getEndian(byte[] buf, int offset) {
    323         BaseDexBuffer bdb = new BaseDexBuffer(buf);
    324         return bdb.readInt(offset + ENDIAN_TAG_OFFSET);
    325     }
    326 }
    327