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