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 32package org.jf.dexlib2.dexbacked.raw; 33 34import org.jf.dexlib2.dexbacked.BaseDexBuffer; 35import org.jf.dexlib2.dexbacked.raw.util.DexAnnotator; 36import org.jf.dexlib2.util.AnnotatedBytes; 37import org.jf.util.StringUtils; 38 39import javax.annotation.Nonnull; 40import javax.annotation.Nullable; 41 42public class HeaderItem { 43 public static final int ITEM_SIZE = 0x70; 44 45 public static final byte[][] MAGIC_VALUES= new byte[][] { 46 new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00}, 47 new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x36, 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 @Nonnull private RawDexFile dexFile; 88 89 public HeaderItem(@Nonnull RawDexFile dexFile) { 90 this.dexFile = dexFile; 91 } 92 93 public int getChecksum() { 94 return dexFile.readSmallUint(CHECKSUM_OFFSET); 95 } 96 97 @Nonnull public byte[] getSignature() { 98 return dexFile.readByteRange(SIGNATURE_OFFSET, SIGNATURE_SIZE); 99 } 100 101 public int getMapOffset() { 102 return dexFile.readSmallUint(MAP_OFFSET); 103 } 104 105 public int getHeaderSize() { 106 return dexFile.readSmallUint(HEADER_SIZE_OFFSET); 107 } 108 109 public int getStringCount() { 110 return dexFile.readSmallUint(STRING_COUNT_OFFSET); 111 } 112 113 public int getStringOffset() { 114 return dexFile.readSmallUint(STRING_START_OFFSET); 115 } 116 117 public int getTypeCount() { 118 return dexFile.readSmallUint(TYPE_COUNT_OFFSET); 119 } 120 121 public int getTypeOffset() { 122 return dexFile.readSmallUint(TYPE_START_OFFSET); 123 } 124 125 public int getProtoCount() { 126 return dexFile.readSmallUint(PROTO_COUNT_OFFSET); 127 } 128 129 public int getProtoOffset() { 130 return dexFile.readSmallUint(PROTO_START_OFFSET); 131 } 132 133 public int getFieldCount() { 134 return dexFile.readSmallUint(FIELD_COUNT_OFFSET); 135 } 136 137 public int getFieldOffset() { 138 return dexFile.readSmallUint(FIELD_START_OFFSET); 139 } 140 141 public int getMethodCount() { 142 return dexFile.readSmallUint(METHOD_COUNT_OFFSET); 143 } 144 145 public int getMethodOffset() { 146 return dexFile.readSmallUint(METHOD_START_OFFSET); 147 } 148 149 public int getClassCount() { 150 return dexFile.readSmallUint(CLASS_COUNT_OFFSET); 151 } 152 153 public int getClassOffset() { 154 return dexFile.readSmallUint(CLASS_START_OFFSET); 155 } 156 157 @Nonnull 158 public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) { 159 return new SectionAnnotator(annotator, mapItem) { 160 @Nonnull @Override public String getItemName() { 161 return "header_item"; 162 } 163 164 @Override 165 protected void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) { 166 int startOffset = out.getCursor(); 167 int headerSize; 168 169 StringBuilder magicBuilder = new StringBuilder(); 170 for (int i=0; i<8; i++) { 171 magicBuilder.append((char)dexFile.readUbyte(startOffset + i)); 172 } 173 174 out.annotate(8, "magic: %s", StringUtils.escapeString(magicBuilder.toString())); 175 out.annotate(4, "checksum"); 176 out.annotate(20, "signature"); 177 out.annotate(4, "file_size: %d", dexFile.readInt(out.getCursor())); 178 179 headerSize = dexFile.readInt(out.getCursor()); 180 out.annotate(4, "header_size: %d", headerSize); 181 182 int endianTag = dexFile.readInt(out.getCursor()); 183 out.annotate(4, "endian_tag: 0x%x (%s)", endianTag, getEndianText(endianTag)); 184 185 out.annotate(4, "link_size: %d", dexFile.readInt(out.getCursor())); 186 out.annotate(4, "link_offset: 0x%x", dexFile.readInt(out.getCursor())); 187 188 out.annotate(4, "map_off: 0x%x", dexFile.readInt(out.getCursor())); 189 190 out.annotate(4, "string_ids_size: %d", dexFile.readInt(out.getCursor())); 191 out.annotate(4, "string_ids_off: 0x%x", dexFile.readInt(out.getCursor())); 192 193 out.annotate(4, "type_ids_size: %d", dexFile.readInt(out.getCursor())); 194 out.annotate(4, "type_ids_off: 0x%x", dexFile.readInt(out.getCursor())); 195 196 out.annotate(4, "proto_ids_size: %d", dexFile.readInt(out.getCursor())); 197 out.annotate(4, "proto_ids_off: 0x%x", dexFile.readInt(out.getCursor())); 198 199 out.annotate(4, "field_ids_size: %d", dexFile.readInt(out.getCursor())); 200 out.annotate(4, "field_ids_off: 0x%x", dexFile.readInt(out.getCursor())); 201 202 out.annotate(4, "method_ids_size: %d", dexFile.readInt(out.getCursor())); 203 out.annotate(4, "method_ids_off: 0x%x", dexFile.readInt(out.getCursor())); 204 205 out.annotate(4, "class_defs_size: %d", dexFile.readInt(out.getCursor())); 206 out.annotate(4, "class_defs_off: 0x%x", dexFile.readInt(out.getCursor())); 207 208 out.annotate(4, "data_size: %d", dexFile.readInt(out.getCursor())); 209 out.annotate(4, "data_off: 0x%x", dexFile.readInt(out.getCursor())); 210 211 if (headerSize > ITEM_SIZE) { 212 out.annotateTo(headerSize, "header padding"); 213 } 214 } 215 }; 216 } 217 218 private static String getEndianText(int endianTag) { 219 if (endianTag == LITTLE_ENDIAN_TAG) { 220 return "Little Endian"; 221 } 222 if (endianTag == BIG_ENDIAN_TAG) { 223 return "Big Endian"; 224 } 225 return "Invalid"; 226 } 227 228 private static int getVersion(byte[] buf, int offset) { 229 if (buf.length - offset < 8) { 230 return 0; 231 } 232 233 boolean matches = true; 234 for (int i=0; i<MAGIC_VALUES.length; i++) { 235 byte[] expected = MAGIC_VALUES[i]; 236 matches = true; 237 for (int j=0; j<8; j++) { 238 if (buf[offset + j] != expected[j]) { 239 matches = false; 240 break; 241 } 242 } 243 if (matches) { 244 return i==0?35:36; 245 } 246 } 247 return 0; 248 } 249 250 public static boolean verifyMagic(byte[] buf, int offset) { 251 // verifies the magic value 252 return getVersion(buf, offset) != 0; 253 } 254 255 256 public static int getEndian(byte[] buf, int offset) { 257 BaseDexBuffer bdb = new BaseDexBuffer(buf); 258 return bdb.readInt(offset + ENDIAN_TAG_OFFSET); 259 } 260} 261