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