DexFile.java revision 1af01ba10760876505772643778532d8e55c0265
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.util.AnnotatedOutput;
32import org.jf.dexlib.util.ByteArrayInput;
33import org.jf.dexlib.util.FileUtils;
34import org.jf.dexlib.util.Input;
35
36import java.io.File;
37import java.security.DigestException;
38import java.security.MessageDigest;
39import java.security.NoSuchAlgorithmException;
40import java.util.HashMap;
41import java.util.zip.Adler32;
42
43public class DexFile
44{
45    private final HashMap<ItemType, Section> sectionsByType;
46    private final IndexedSection[] indexedSections;
47    private final OffsettedSection[] offsettedSections;
48    private int fileSize;
49    private int dataOffset;
50    private int dataSize;
51
52    private final DexFile dexFile = this;
53
54    private DexFile() {
55        sectionsByType = new HashMap<ItemType, Section>(18);
56
57        sectionsByType.put(ItemType.TYPE_ANNOTATION_ITEM, AnnotationsSection);
58        sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_ITEM, AnnotationSetsSection);
59        sectionsByType.put(ItemType.TYPE_ANNOTATION_SET_REF_LIST, AnnotationSetRefListsSection);
60        sectionsByType.put(ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM, AnnotationDirectoriesSection);
61        sectionsByType.put(ItemType.TYPE_CLASS_DATA_ITEM, ClassDataSection);
62        sectionsByType.put(ItemType.TYPE_CLASS_DEF_ITEM, ClassDefsSection);
63        sectionsByType.put(ItemType.TYPE_CODE_ITEM, CodeItemsSection);
64        sectionsByType.put(ItemType.TYPE_DEBUG_INFO_ITEM, DebugInfoItemsSection);
65        sectionsByType.put(ItemType.TYPE_ENCODED_ARRAY_ITEM, EncodedArraysSection);
66        sectionsByType.put(ItemType.TYPE_FIELD_ID_ITEM, FieldIdsSection);
67        sectionsByType.put(ItemType.TYPE_HEADER_ITEM, HeaderItemSection);
68        sectionsByType.put(ItemType.TYPE_MAP_LIST, MapSection);
69        sectionsByType.put(ItemType.TYPE_METHOD_ID_ITEM, MethodIdsSection);
70        sectionsByType.put(ItemType.TYPE_PROTO_ID_ITEM, ProtoIdsSection);
71        sectionsByType.put(ItemType.TYPE_STRING_DATA_ITEM, StringDataSection);
72        sectionsByType.put(ItemType.TYPE_STRING_ID_ITEM, StringIdsSection);
73        sectionsByType.put(ItemType.TYPE_TYPE_ID_ITEM, TypeIdsSection);
74        sectionsByType.put(ItemType.TYPE_TYPE_LIST, TypeListsSection);
75
76        indexedSections = new IndexedSection[] {
77                StringIdsSection,
78                TypeIdsSection,
79                ProtoIdsSection,
80                FieldIdsSection,
81                MethodIdsSection,
82                ClassDefsSection
83        };
84
85        offsettedSections = new OffsettedSection[] {
86                AnnotationSetRefListsSection,
87                AnnotationSetsSection,
88                CodeItemsSection,
89                AnnotationDirectoriesSection,
90                TypeListsSection,
91                StringDataSection,
92                DebugInfoItemsSection,
93                AnnotationsSection,
94                EncodedArraysSection,
95                ClassDataSection
96        };
97    }
98
99    public DexFile(File file) {
100        this();
101        Input in = new ByteArrayInput(FileUtils.readFile(file));
102
103        HeaderItemSection.readFrom(1, in);
104        HeaderItem headerItem = HeaderItemSection.items.get(0);
105
106        in.setCursor(headerItem.getMapOffset());
107
108        MapSection.readFrom(1, in);
109
110        MapField[] mapEntries = MapSection.items.get(0).getMapEntries();
111        HashMap<Integer, MapField> mapMap = new HashMap<Integer, MapField>();
112        for (MapField mapField: mapEntries) {
113            mapMap.put(mapField.getSectionItemType().getMapValue(), mapField);
114        }
115
116        int[] sectionTypes = new int[] {
117            ItemType.TYPE_HEADER_ITEM.getMapValue(),
118            ItemType.TYPE_STRING_ID_ITEM.getMapValue(),
119            ItemType.TYPE_TYPE_ID_ITEM.getMapValue(),
120            ItemType.TYPE_PROTO_ID_ITEM.getMapValue(),
121            ItemType.TYPE_FIELD_ID_ITEM.getMapValue(),
122            ItemType.TYPE_METHOD_ID_ITEM.getMapValue(),
123            ItemType.TYPE_CLASS_DEF_ITEM.getMapValue(),
124            ItemType.TYPE_STRING_DATA_ITEM.getMapValue(),
125            ItemType.TYPE_ENCODED_ARRAY_ITEM.getMapValue(),
126            ItemType.TYPE_ANNOTATION_ITEM.getMapValue(),
127            ItemType.TYPE_ANNOTATION_SET_ITEM.getMapValue(),
128            ItemType.TYPE_ANNOTATION_SET_REF_LIST.getMapValue(),
129            ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM.getMapValue(),
130            ItemType.TYPE_TYPE_LIST.getMapValue(),
131            ItemType.TYPE_DEBUG_INFO_ITEM.getMapValue(),
132            ItemType.TYPE_CODE_ITEM.getMapValue(),
133            ItemType.TYPE_CLASS_DATA_ITEM.getMapValue(),
134            ItemType.TYPE_MAP_LIST.getMapValue()
135        };
136
137        for (int sectionType: sectionTypes) {
138            MapField mapField = mapMap.get(sectionType);
139            if (mapField != null) {
140                Section section = sectionsByType.get(mapField.getSectionItemType());
141                if (section != null) {
142                    in.setCursor(mapField.getSectionOffset());
143                    section.readFrom(mapField.getSectionSize(), in);
144                }
145            }
146        }
147    }
148
149    public static DexFile makeBlankDexFile() {
150        DexFile dexFile = new DexFile();
151        try
152        {
153            dexFile.HeaderItemSection.intern(dexFile, new HeaderItem(dexFile, 0));
154        } catch (Exception ex) {
155            throw new RuntimeException(ex);
156        }
157
158        dexFile.MapSection.intern(dexFile, MapItem.makeBlankMapItem(dexFile));
159        return dexFile;
160    }
161
162
163    public <T extends Item> Section<T> getSectionForItem(T item) {
164        return sectionsByType.get(item.getItemType());
165    }
166
167    public Section getSectionForType(ItemType itemType) {
168        return sectionsByType.get(itemType);
169    }
170
171    public int getFileSize() {
172        return fileSize;
173    }
174
175    public int getDataOffset() {
176        return dataOffset;
177    }
178
179    public int getDataSize() {
180        return dataSize;
181    }
182
183    public void place() {
184        int offset = 0;
185
186        offset = HeaderItemSection.place(offset);
187        for (IndexedSection indexedSection: indexedSections) {
188            indexedSection.unplace();
189            offset = indexedSection.place(offset);
190        }
191
192        dataOffset = offset;
193
194        for (OffsettedSection offsettedSection: offsettedSections) {
195            offsettedSection.unplace();
196
197            offset = offsettedSection.place(offset);
198        }
199
200        offset = MapSection.place(offset);
201
202        dataSize = offset - dataOffset;
203        fileSize = offset;
204    }
205
206    public void writeTo(AnnotatedOutput out) {
207        HeaderItemSection.writeTo(out);
208        for (IndexedSection indexedSection: indexedSections) {
209            indexedSection.writeTo(out);
210        }
211
212        for (OffsettedSection offsettedSection: offsettedSections) {
213            offsettedSection.writeTo(out);
214        }
215
216        MapSection.writeTo(out);
217    }
218
219    public final IndexedSection<HeaderItem> HeaderItemSection = new IndexedSection<HeaderItem>() {
220        protected HeaderItem make(int index) {
221            return new HeaderItem(dexFile, index);
222        }
223    };
224
225    public final IndexedSection<StringIdItem> StringIdsSection = new IndexedSection<StringIdItem>() {
226        protected StringIdItem make(int index) {
227            return new StringIdItem(dexFile, index);
228        }
229    };
230
231    public final IndexedSection<TypeIdItem> TypeIdsSection = new IndexedSection<TypeIdItem>() {
232        protected TypeIdItem make(int index) {
233            return new TypeIdItem(dexFile, index);
234        }
235    };
236
237    public final IndexedSection<ProtoIdItem> ProtoIdsSection = new IndexedSection<ProtoIdItem>() {
238        protected ProtoIdItem make(int index) {
239            return new ProtoIdItem(dexFile, index);
240        }
241    };
242
243    public final IndexedSection<FieldIdItem> FieldIdsSection = new IndexedSection<FieldIdItem>() {
244        protected FieldIdItem make(int index) {
245            return new FieldIdItem(dexFile, index);
246        }
247    };
248
249    public final IndexedSection<MethodIdItem> MethodIdsSection = new IndexedSection<MethodIdItem>() {
250        protected MethodIdItem make(int index) {
251            return new MethodIdItem(dexFile, index);
252        }
253    };
254
255    public final IndexedSection<ClassDefItem> ClassDefsSection = new IndexedSection<ClassDefItem>() {
256        protected ClassDefItem make(int index) {
257            return new ClassDefItem(dexFile, index);
258        }
259
260        public int place(int offset) {
261            int ret = ClassDefItem.placeClassDefItems(this, offset);
262
263            this.offset = items.get(0).getOffset();
264            return ret;
265        }
266    };
267
268    public final IndexedSection<MapItem> MapSection = new IndexedSection<MapItem>() {
269        protected MapItem make(int index) {
270            return new MapItem(dexFile, index);
271        }
272
273        public MapItem intern(DexFile dexFile, MapItem item) {
274            this.items.add(item);
275            return item;
276        }
277    };
278
279    public final OffsettedSection<TypeListItem> TypeListsSection = new OffsettedSection<TypeListItem>() {
280        protected TypeListItem make(int offset) {
281            return new TypeListItem(dexFile, offset);
282        }
283    };
284
285    public final OffsettedSection<AnnotationSetRefList> AnnotationSetRefListsSection =
286            new OffsettedSection<AnnotationSetRefList>() {
287                protected AnnotationSetRefList make(int offset) {
288                    return new AnnotationSetRefList(dexFile, offset);
289                }
290            };
291
292    public final OffsettedSection<AnnotationSetItem> AnnotationSetsSection =
293            new OffsettedSection<AnnotationSetItem>() {
294                protected AnnotationSetItem make(int offset) {
295                    return new AnnotationSetItem(dexFile, offset);
296                }
297            };
298
299    public final OffsettedSection<ClassDataItem> ClassDataSection = new OffsettedSection<ClassDataItem>() {
300        protected ClassDataItem make(int offset) {
301            return new ClassDataItem(dexFile, offset);
302        }
303    };
304
305    public final OffsettedSection<CodeItem> CodeItemsSection = new OffsettedSection<CodeItem>() {
306        protected CodeItem make(int offset) {
307            return new CodeItem(dexFile, offset);
308        }
309    };
310
311    public final OffsettedSection<StringDataItem> StringDataSection = new OffsettedSection<StringDataItem>() {
312        protected StringDataItem make(int offset) {
313            return new StringDataItem(offset);
314        }
315    };
316
317    public final OffsettedSection<DebugInfoItem> DebugInfoItemsSection = new OffsettedSection<DebugInfoItem>() {
318        protected DebugInfoItem make(int offset) {
319            return new DebugInfoItem(dexFile, offset);
320        }
321    };
322
323    public final OffsettedSection<AnnotationItem> AnnotationsSection = new OffsettedSection<AnnotationItem>() {
324        protected AnnotationItem make(int offset) {
325            return new AnnotationItem(dexFile, offset);
326        }
327    };
328
329    public final OffsettedSection<EncodedArrayItem> EncodedArraysSection = new OffsettedSection<EncodedArrayItem>() {
330        protected EncodedArrayItem make(int offset) {
331            return new EncodedArrayItem(dexFile, offset);
332        }
333    };
334
335    public final OffsettedSection<AnnotationDirectoryItem> AnnotationDirectoriesSection =
336            new OffsettedSection<AnnotationDirectoryItem>() {
337                protected AnnotationDirectoryItem make(int offset) {
338                    return new AnnotationDirectoryItem(dexFile, offset);
339                }
340            };
341
342
343    /**
344     * Calculates the signature for the <code>.dex</code> file in the
345     * given array, and modify the array to contain it.
346     *
347     * @param bytes non-null; the bytes of the file
348     */
349    public static void calcSignature(byte[] bytes) {
350        MessageDigest md;
351
352        try {
353            md = MessageDigest.getInstance("SHA-1");
354        } catch (NoSuchAlgorithmException ex) {
355            throw new RuntimeException(ex);
356        }
357
358        md.update(bytes, 32, bytes.length - 32);
359
360        try {
361            int amt = md.digest(bytes, 12, 20);
362            if (amt != 20) {
363                throw new RuntimeException("unexpected digest write: " + amt +
364                                           " bytes");
365            }
366        } catch (DigestException ex) {
367            throw new RuntimeException(ex);
368        }
369    }
370
371    /**
372     * Calculates the checksum for the <code>.dex</code> file in the
373     * given array, and modify the array to contain it.
374     *
375     * @param bytes non-null; the bytes of the file
376     */
377    public static void calcChecksum(byte[] bytes) {
378        Adler32 a32 = new Adler32();
379
380        a32.update(bytes, 12, bytes.length - 12);
381
382        int sum = (int) a32.getValue();
383
384        bytes[8]  = (byte) sum;
385        bytes[9]  = (byte) (sum >> 8);
386        bytes[10] = (byte) (sum >> 16);
387        bytes[11] = (byte) (sum >> 24);
388    }
389}
390