• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
HeaderItem(@onnull RawDexFile dexFile)88     public HeaderItem(@Nonnull RawDexFile dexFile) {
89         this.dexFile = dexFile;
90     }
91 
getChecksum()92     public int getChecksum() {
93         return dexFile.readSmallUint(CHECKSUM_OFFSET);
94     }
95 
getSignature()96     @Nonnull public byte[] getSignature() {
97         return dexFile.readByteRange(SIGNATURE_OFFSET, SIGNATURE_SIZE);
98     }
99 
getMapOffset()100     public int getMapOffset() {
101         return dexFile.readSmallUint(MAP_OFFSET);
102     }
103 
getHeaderSize()104     public int getHeaderSize() {
105         return dexFile.readSmallUint(HEADER_SIZE_OFFSET);
106     }
107 
getStringCount()108     public int getStringCount() {
109         return dexFile.readSmallUint(STRING_COUNT_OFFSET);
110     }
111 
getStringOffset()112     public int getStringOffset() {
113         return dexFile.readSmallUint(STRING_START_OFFSET);
114     }
115 
getTypeCount()116     public int getTypeCount() {
117         return dexFile.readSmallUint(TYPE_COUNT_OFFSET);
118     }
119 
getTypeOffset()120     public int getTypeOffset() {
121         return dexFile.readSmallUint(TYPE_START_OFFSET);
122     }
123 
getProtoCount()124     public int getProtoCount() {
125         return dexFile.readSmallUint(PROTO_COUNT_OFFSET);
126     }
127 
getProtoOffset()128     public int getProtoOffset() {
129         return dexFile.readSmallUint(PROTO_START_OFFSET);
130     }
131 
getFieldCount()132     public int getFieldCount() {
133         return dexFile.readSmallUint(FIELD_COUNT_OFFSET);
134     }
135 
getFieldOffset()136     public int getFieldOffset() {
137         return dexFile.readSmallUint(FIELD_START_OFFSET);
138     }
139 
getMethodCount()140     public int getMethodCount() {
141         return dexFile.readSmallUint(METHOD_COUNT_OFFSET);
142     }
143 
getMethodOffset()144     public int getMethodOffset() {
145         return dexFile.readSmallUint(METHOD_START_OFFSET);
146     }
147 
getClassCount()148     public int getClassCount() {
149         return dexFile.readSmallUint(CLASS_COUNT_OFFSET);
150     }
151 
getClassOffset()152     public int getClassOffset() {
153         return dexFile.readSmallUint(CLASS_START_OFFSET);
154     }
155 
156     @Nonnull
makeAnnotator(@onnull DexAnnotator annotator, @Nonnull MapItem mapItem)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