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    /**
46     * The magic numbers for dex files.
47     *
48     * They are: "dex\n035\0" and "dex\n037\0".
49     */
50    public static final byte[][] MAGIC_VALUES= new byte[][] {
51            new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00},
52            new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x37, 0x00}};
53
54    public static final int LITTLE_ENDIAN_TAG = 0x12345678;
55    public static final int BIG_ENDIAN_TAG = 0x78563412;
56
57    public static final int CHECKSUM_OFFSET = 8;
58
59    // this is the start of the checksumed data
60    public static final int CHECKSUM_DATA_START_OFFSET = 12;
61    public static final int SIGNATURE_OFFSET = 12;
62    public static final int SIGNATURE_SIZE = 20;
63
64    // this is the start of the sha-1 hashed data
65    public static final int SIGNATURE_DATA_START_OFFSET = 32;
66    public static final int FILE_SIZE_OFFSET = 32;
67
68    public static final int HEADER_SIZE_OFFSET = 36;
69
70    public static final int ENDIAN_TAG_OFFSET = 40;
71
72    public static final int MAP_OFFSET = 52;
73
74    public static final int STRING_COUNT_OFFSET = 56;
75    public static final int STRING_START_OFFSET = 60;
76
77    public static final int TYPE_COUNT_OFFSET = 64;
78    public static final int TYPE_START_OFFSET = 68;
79
80    public static final int PROTO_COUNT_OFFSET = 72;
81    public static final int PROTO_START_OFFSET = 76;
82
83    public static final int FIELD_COUNT_OFFSET = 80;
84    public static final int FIELD_START_OFFSET = 84;
85
86    public static final int METHOD_COUNT_OFFSET = 88;
87    public static final int METHOD_START_OFFSET = 92;
88
89    public static final int CLASS_COUNT_OFFSET = 96;
90    public static final int CLASS_START_OFFSET = 100;
91
92    @Nonnull private RawDexFile dexFile;
93
94    public HeaderItem(@Nonnull RawDexFile dexFile) {
95        this.dexFile = dexFile;
96    }
97
98    public int getChecksum() {
99        return dexFile.readSmallUint(CHECKSUM_OFFSET);
100    }
101
102    @Nonnull public byte[] getSignature() {
103        return dexFile.readByteRange(SIGNATURE_OFFSET, SIGNATURE_SIZE);
104    }
105
106    public int getMapOffset() {
107        return dexFile.readSmallUint(MAP_OFFSET);
108    }
109
110    public int getHeaderSize() {
111        return dexFile.readSmallUint(HEADER_SIZE_OFFSET);
112    }
113
114    public int getStringCount() {
115        return dexFile.readSmallUint(STRING_COUNT_OFFSET);
116    }
117
118    public int getStringOffset() {
119        return dexFile.readSmallUint(STRING_START_OFFSET);
120    }
121
122    public int getTypeCount() {
123        return dexFile.readSmallUint(TYPE_COUNT_OFFSET);
124    }
125
126    public int getTypeOffset() {
127        return dexFile.readSmallUint(TYPE_START_OFFSET);
128    }
129
130    public int getProtoCount() {
131        return dexFile.readSmallUint(PROTO_COUNT_OFFSET);
132    }
133
134    public int getProtoOffset() {
135        return dexFile.readSmallUint(PROTO_START_OFFSET);
136    }
137
138    public int getFieldCount() {
139        return dexFile.readSmallUint(FIELD_COUNT_OFFSET);
140    }
141
142    public int getFieldOffset() {
143        return dexFile.readSmallUint(FIELD_START_OFFSET);
144    }
145
146    public int getMethodCount() {
147        return dexFile.readSmallUint(METHOD_COUNT_OFFSET);
148    }
149
150    public int getMethodOffset() {
151        return dexFile.readSmallUint(METHOD_START_OFFSET);
152    }
153
154    public int getClassCount() {
155        return dexFile.readSmallUint(CLASS_COUNT_OFFSET);
156    }
157
158    public int getClassOffset() {
159        return dexFile.readSmallUint(CLASS_START_OFFSET);
160    }
161
162    @Nonnull
163    public static SectionAnnotator makeAnnotator(@Nonnull DexAnnotator annotator, @Nonnull MapItem mapItem) {
164        return new SectionAnnotator(annotator, mapItem) {
165            @Nonnull @Override public String getItemName() {
166                return "header_item";
167            }
168
169            @Override
170            protected void annotateItem(@Nonnull AnnotatedBytes out, int itemIndex, @Nullable String itemIdentity) {
171                int startOffset = out.getCursor();
172                int headerSize;
173
174                StringBuilder magicBuilder = new StringBuilder();
175                for (int i=0; i<8; i++) {
176                    magicBuilder.append((char)dexFile.readUbyte(startOffset + i));
177                }
178
179                out.annotate(8, "magic: %s", StringUtils.escapeString(magicBuilder.toString()));
180                out.annotate(4, "checksum");
181                out.annotate(20, "signature");
182                out.annotate(4, "file_size: %d", dexFile.readInt(out.getCursor()));
183
184                headerSize = dexFile.readInt(out.getCursor());
185                out.annotate(4, "header_size: %d", headerSize);
186
187                int endianTag = dexFile.readInt(out.getCursor());
188                out.annotate(4, "endian_tag: 0x%x (%s)", endianTag, getEndianText(endianTag));
189
190                out.annotate(4, "link_size: %d", dexFile.readInt(out.getCursor()));
191                out.annotate(4, "link_offset: 0x%x", dexFile.readInt(out.getCursor()));
192
193                out.annotate(4, "map_off: 0x%x", dexFile.readInt(out.getCursor()));
194
195                out.annotate(4, "string_ids_size: %d", dexFile.readInt(out.getCursor()));
196                out.annotate(4, "string_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
197
198                out.annotate(4, "type_ids_size: %d", dexFile.readInt(out.getCursor()));
199                out.annotate(4, "type_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
200
201                out.annotate(4, "proto_ids_size: %d", dexFile.readInt(out.getCursor()));
202                out.annotate(4, "proto_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
203
204                out.annotate(4, "field_ids_size: %d", dexFile.readInt(out.getCursor()));
205                out.annotate(4, "field_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
206
207                out.annotate(4, "method_ids_size: %d", dexFile.readInt(out.getCursor()));
208                out.annotate(4, "method_ids_off: 0x%x", dexFile.readInt(out.getCursor()));
209
210                out.annotate(4, "class_defs_size: %d", dexFile.readInt(out.getCursor()));
211                out.annotate(4, "class_defs_off: 0x%x", dexFile.readInt(out.getCursor()));
212
213                out.annotate(4, "data_size: %d", dexFile.readInt(out.getCursor()));
214                out.annotate(4, "data_off: 0x%x", dexFile.readInt(out.getCursor()));
215
216                if (headerSize > ITEM_SIZE) {
217                    out.annotateTo(headerSize, "header padding");
218                }
219            }
220        };
221    }
222
223    private static String getEndianText(int endianTag) {
224        if (endianTag == LITTLE_ENDIAN_TAG) {
225            return "Little Endian";
226        }
227        if (endianTag == BIG_ENDIAN_TAG) {
228            return "Big Endian";
229        }
230        return "Invalid";
231    }
232
233
234    /**
235     * Get the higest magic number supported by Android for this api level.
236     * @return The dex file magic number
237     */
238    public static byte[] getMagicForApi(int api) {
239        if (api < 24) {
240            // Prior to Android N we only support dex version 035.
241            return HeaderItem.MAGIC_VALUES[0];
242        } else {
243            // On android N and later we support dex version 037.
244            return HeaderItem.MAGIC_VALUES[1];
245        }
246    }
247
248    private static int getVersion(byte[] buf, int offset) {
249        if (buf.length - offset < 8) {
250            return 0;
251        }
252
253        boolean matches = true;
254        for (int i=0; i<MAGIC_VALUES.length; i++) {
255            byte[] expected = MAGIC_VALUES[i];
256            matches = true;
257            for (int j=0; j<8; j++) {
258                if (buf[offset + j] != expected[j]) {
259                    matches = false;
260                    break;
261                }
262            }
263            if (matches) {
264                return i==0?35:37;
265            }
266        }
267        return 0;
268    }
269
270    public static boolean verifyMagic(byte[] buf, int offset) {
271        // verifies the magic value
272        return getVersion(buf, offset) != 0;
273    }
274
275
276    public static int getEndian(byte[] buf, int offset) {
277        BaseDexBuffer bdb = new BaseDexBuffer(buf);
278        return bdb.readInt(offset + ENDIAN_TAG_OFFSET);
279    }
280}
281