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