• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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