• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.dexdeps;
18 
19 import java.io.IOException;
20 import java.io.RandomAccessFile;
21 import java.nio.charset.StandardCharsets;
22 import java.util.Arrays;
23 
24 /**
25  * Data extracted from a DEX file.
26  */
27 public class DexData {
28     private RandomAccessFile mDexFile;
29     private HeaderItem mHeaderItem;
30     private String[] mStrings;              // strings from string_data_*
31     private TypeIdItem[] mTypeIds;
32     private ProtoIdItem[] mProtoIds;
33     private FieldIdItem[] mFieldIds;
34     private MethodIdItem[] mMethodIds;
35     private ClassDefItem[] mClassDefs;
36 
37     private byte tmpBuf[] = new byte[4];
38     private boolean isBigEndian = false;
39 
40     /**
41      * Constructs a new DexData for this file.
42      */
DexData(RandomAccessFile raf)43     public DexData(RandomAccessFile raf) {
44         mDexFile = raf;
45     }
46 
47     /**
48      * Loads the contents of the DEX file into our data structures.
49      *
50      * @throws IOException if we encounter a problem while reading
51      * @throws DexDataException if the DEX contents look bad
52      */
load()53     public void load() throws IOException {
54         parseHeaderItem();
55 
56         loadStrings();
57         loadTypeIds();
58         loadProtoIds();
59         loadFieldIds();
60         loadMethodIds();
61         loadClassDefs();
62 
63         markInternalClasses();
64     }
65 
66     /**
67      * Verifies the given magic number.
68      */
verifyMagic(byte[] magic)69     private static boolean verifyMagic(byte[] magic) {
70         return Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v035) ||
71             Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v037) ||
72             Arrays.equals(magic, HeaderItem.DEX_FILE_MAGIC_v038);
73     }
74 
75     /**
76      * Parses the interesting bits out of the header.
77      */
parseHeaderItem()78     void parseHeaderItem() throws IOException {
79         mHeaderItem = new HeaderItem();
80 
81         seek(0);
82 
83         byte[] magic = new byte[8];
84         readBytes(magic);
85         if (!verifyMagic(magic)) {
86             System.err.println("Magic number is wrong -- are you sure " +
87                 "this is a DEX file?");
88             throw new DexDataException();
89         }
90 
91         /*
92          * Read the endian tag, so we properly swap things as we read
93          * them from here on.
94          */
95         seek(8+4+20+4+4);
96         mHeaderItem.endianTag = readInt();
97         if (mHeaderItem.endianTag == HeaderItem.ENDIAN_CONSTANT) {
98             /* do nothing */
99         } else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
100             /* file is big-endian (!), reverse future reads */
101             isBigEndian = true;
102         } else {
103             System.err.println("Endian constant has unexpected value " +
104                 Integer.toHexString(mHeaderItem.endianTag));
105             throw new DexDataException();
106         }
107 
108         seek(8+4+20);  // magic, checksum, signature
109         mHeaderItem.fileSize = readInt();
110         mHeaderItem.headerSize = readInt();
111         /*mHeaderItem.endianTag =*/ readInt();
112         /*mHeaderItem.linkSize =*/ readInt();
113         /*mHeaderItem.linkOff =*/ readInt();
114         /*mHeaderItem.mapOff =*/ readInt();
115         mHeaderItem.stringIdsSize = readInt();
116         mHeaderItem.stringIdsOff = readInt();
117         mHeaderItem.typeIdsSize = readInt();
118         mHeaderItem.typeIdsOff = readInt();
119         mHeaderItem.protoIdsSize = readInt();
120         mHeaderItem.protoIdsOff = readInt();
121         mHeaderItem.fieldIdsSize = readInt();
122         mHeaderItem.fieldIdsOff = readInt();
123         mHeaderItem.methodIdsSize = readInt();
124         mHeaderItem.methodIdsOff = readInt();
125         mHeaderItem.classDefsSize = readInt();
126         mHeaderItem.classDefsOff = readInt();
127         /*mHeaderItem.dataSize =*/ readInt();
128         /*mHeaderItem.dataOff =*/ readInt();
129     }
130 
131     /**
132      * Loads the string table out of the DEX.
133      *
134      * First we read all of the string_id_items, then we read all of the
135      * string_data_item.  Doing it this way should allow us to avoid
136      * seeking around in the file.
137      */
loadStrings()138     void loadStrings() throws IOException {
139         int count = mHeaderItem.stringIdsSize;
140         int stringOffsets[] = new int[count];
141 
142         //System.out.println("reading " + count + " strings");
143 
144         seek(mHeaderItem.stringIdsOff);
145         for (int i = 0; i < count; i++) {
146             stringOffsets[i] = readInt();
147         }
148 
149         mStrings = new String[count];
150 
151         seek(stringOffsets[0]);
152         for (int i = 0; i < count; i++) {
153             seek(stringOffsets[i]);         // should be a no-op
154             mStrings[i] = readString();
155             //System.out.println("STR: " + i + ": " + mStrings[i]);
156         }
157     }
158 
159     /**
160      * Loads the type ID list.
161      */
loadTypeIds()162     void loadTypeIds() throws IOException {
163         int count = mHeaderItem.typeIdsSize;
164         mTypeIds = new TypeIdItem[count];
165 
166         //System.out.println("reading " + count + " typeIds");
167         seek(mHeaderItem.typeIdsOff);
168         for (int i = 0; i < count; i++) {
169             mTypeIds[i] = new TypeIdItem();
170             mTypeIds[i].descriptorIdx = readInt();
171 
172             //System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
173             //    " " + mStrings[mTypeIds[i].descriptorIdx]);
174         }
175     }
176 
177     /**
178      * Loads the proto ID list.
179      */
loadProtoIds()180     void loadProtoIds() throws IOException {
181         int count = mHeaderItem.protoIdsSize;
182         mProtoIds = new ProtoIdItem[count];
183 
184         //System.out.println("reading " + count + " protoIds");
185         seek(mHeaderItem.protoIdsOff);
186 
187         /*
188          * Read the proto ID items.
189          */
190         for (int i = 0; i < count; i++) {
191             mProtoIds[i] = new ProtoIdItem();
192             mProtoIds[i].shortyIdx = readInt();
193             mProtoIds[i].returnTypeIdx = readInt();
194             mProtoIds[i].parametersOff = readInt();
195 
196             //System.out.println(i + ": " + mProtoIds[i].shortyIdx +
197             //    " " + mStrings[mProtoIds[i].shortyIdx]);
198         }
199 
200         /*
201          * Go back through and read the type lists.
202          */
203         for (int i = 0; i < count; i++) {
204             ProtoIdItem protoId = mProtoIds[i];
205 
206             int offset = protoId.parametersOff;
207 
208             if (offset == 0) {
209                 protoId.types = new int[0];
210                 continue;
211             } else {
212                 seek(offset);
213                 int size = readInt();       // #of entries in list
214                 protoId.types = new int[size];
215 
216                 for (int j = 0; j < size; j++) {
217                     protoId.types[j] = readShort() & 0xffff;
218                 }
219             }
220         }
221     }
222 
223     /**
224      * Loads the field ID list.
225      */
loadFieldIds()226     void loadFieldIds() throws IOException {
227         int count = mHeaderItem.fieldIdsSize;
228         mFieldIds = new FieldIdItem[count];
229 
230         //System.out.println("reading " + count + " fieldIds");
231         seek(mHeaderItem.fieldIdsOff);
232         for (int i = 0; i < count; i++) {
233             mFieldIds[i] = new FieldIdItem();
234             mFieldIds[i].classIdx = readShort() & 0xffff;
235             mFieldIds[i].typeIdx = readShort() & 0xffff;
236             mFieldIds[i].nameIdx = readInt();
237 
238             //System.out.println(i + ": " + mFieldIds[i].nameIdx +
239             //    " " + mStrings[mFieldIds[i].nameIdx]);
240         }
241     }
242 
243     /**
244      * Loads the method ID list.
245      */
loadMethodIds()246     void loadMethodIds() throws IOException {
247         int count = mHeaderItem.methodIdsSize;
248         mMethodIds = new MethodIdItem[count];
249 
250         //System.out.println("reading " + count + " methodIds");
251         seek(mHeaderItem.methodIdsOff);
252         for (int i = 0; i < count; i++) {
253             mMethodIds[i] = new MethodIdItem();
254             mMethodIds[i].classIdx = readShort() & 0xffff;
255             mMethodIds[i].protoIdx = readShort() & 0xffff;
256             mMethodIds[i].nameIdx = readInt();
257 
258             //System.out.println(i + ": " + mMethodIds[i].nameIdx +
259             //    " " + mStrings[mMethodIds[i].nameIdx]);
260         }
261     }
262 
263     /**
264      * Loads the class defs list.
265      */
loadClassDefs()266     void loadClassDefs() throws IOException {
267         int count = mHeaderItem.classDefsSize;
268         mClassDefs = new ClassDefItem[count];
269 
270         //System.out.println("reading " + count + " classDefs");
271         seek(mHeaderItem.classDefsOff);
272         for (int i = 0; i < count; i++) {
273             mClassDefs[i] = new ClassDefItem();
274             mClassDefs[i].classIdx = readInt();
275 
276             /* access_flags = */ readInt();
277             /* superclass_idx = */ readInt();
278             /* interfaces_off = */ readInt();
279             /* source_file_idx = */ readInt();
280             /* annotations_off = */ readInt();
281             /* class_data_off = */ readInt();
282             /* static_values_off = */ readInt();
283 
284             //System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
285             //    mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
286         }
287     }
288 
289     /**
290      * Sets the "internal" flag on type IDs which are defined in the
291      * DEX file or within the VM (e.g. primitive classes and arrays).
292      */
markInternalClasses()293     void markInternalClasses() {
294         for (int i = mClassDefs.length -1; i >= 0; i--) {
295             mTypeIds[mClassDefs[i].classIdx].internal = true;
296         }
297 
298         for (int i = 0; i < mTypeIds.length; i++) {
299             String className = mStrings[mTypeIds[i].descriptorIdx];
300 
301             if (className.length() == 1) {
302                 // primitive class
303                 mTypeIds[i].internal = true;
304             } else if (className.charAt(0) == '[') {
305                 mTypeIds[i].internal = true;
306             }
307 
308             //System.out.println(i + " " +
309             //    (mTypeIds[i].internal ? "INTERNAL" : "external") + " - " +
310             //    mStrings[mTypeIds[i].descriptorIdx]);
311         }
312     }
313 
314 
315     /*
316      * =======================================================================
317      *      Queries
318      * =======================================================================
319      */
320 
321     /**
322      * Returns the class name, given an index into the type_ids table.
323      */
classNameFromTypeIndex(int idx)324     private String classNameFromTypeIndex(int idx) {
325         return mStrings[mTypeIds[idx].descriptorIdx];
326     }
327 
328     /**
329      * Returns an array of method argument type strings, given an index
330      * into the proto_ids table.
331      */
argArrayFromProtoIndex(int idx)332     private String[] argArrayFromProtoIndex(int idx) {
333         ProtoIdItem protoId = mProtoIds[idx];
334         String[] result = new String[protoId.types.length];
335 
336         for (int i = 0; i < protoId.types.length; i++) {
337             result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx];
338         }
339 
340         return result;
341     }
342 
343     /**
344      * Returns a string representing the method's return type, given an
345      * index into the proto_ids table.
346      */
returnTypeFromProtoIndex(int idx)347     private String returnTypeFromProtoIndex(int idx) {
348         ProtoIdItem protoId = mProtoIds[idx];
349         return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx];
350     }
351 
352     /**
353      * Returns an array with all of the class references that don't
354      * correspond to classes in the DEX file.  Each class reference has
355      * a list of the referenced fields and methods associated with
356      * that class.
357      */
getExternalReferences()358     public ClassRef[] getExternalReferences() {
359         // create a sparse array of ClassRef that parallels mTypeIds
360         ClassRef[] sparseRefs = new ClassRef[mTypeIds.length];
361 
362         // create entries for all externally-referenced classes
363         int count = 0;
364         for (int i = 0; i < mTypeIds.length; i++) {
365             if (!mTypeIds[i].internal) {
366                 sparseRefs[i] =
367                     new ClassRef(mStrings[mTypeIds[i].descriptorIdx]);
368                 count++;
369             }
370         }
371 
372         // add fields and methods to the appropriate class entry
373         addExternalFieldReferences(sparseRefs);
374         addExternalMethodReferences(sparseRefs);
375 
376         // crunch out the sparseness
377         ClassRef[] classRefs = new ClassRef[count];
378         int idx = 0;
379         for (int i = 0; i < mTypeIds.length; i++) {
380             if (sparseRefs[i] != null)
381                 classRefs[idx++] = sparseRefs[i];
382         }
383 
384         assert idx == count;
385 
386         return classRefs;
387     }
388 
389     /**
390      * Runs through the list of field references, inserting external
391      * references into the appropriate ClassRef.
392      */
addExternalFieldReferences(ClassRef[] sparseRefs)393     private void addExternalFieldReferences(ClassRef[] sparseRefs) {
394         for (int i = 0; i < mFieldIds.length; i++) {
395             if (!mTypeIds[mFieldIds[i].classIdx].internal) {
396                 FieldIdItem fieldId = mFieldIds[i];
397                 FieldRef newFieldRef = new FieldRef(
398                         classNameFromTypeIndex(fieldId.classIdx),
399                         classNameFromTypeIndex(fieldId.typeIdx),
400                         mStrings[fieldId.nameIdx]);
401                 sparseRefs[mFieldIds[i].classIdx].addField(newFieldRef);
402             }
403         }
404     }
405 
406     /**
407      * Runs through the list of method references, inserting external
408      * references into the appropriate ClassRef.
409      */
addExternalMethodReferences(ClassRef[] sparseRefs)410     private void addExternalMethodReferences(ClassRef[] sparseRefs) {
411         for (int i = 0; i < mMethodIds.length; i++) {
412             if (!mTypeIds[mMethodIds[i].classIdx].internal) {
413                 MethodIdItem methodId = mMethodIds[i];
414                 MethodRef newMethodRef = new MethodRef(
415                         classNameFromTypeIndex(methodId.classIdx),
416                         argArrayFromProtoIndex(methodId.protoIdx),
417                         returnTypeFromProtoIndex(methodId.protoIdx),
418                         mStrings[methodId.nameIdx]);
419                 sparseRefs[mMethodIds[i].classIdx].addMethod(newMethodRef);
420             }
421         }
422     }
423 
424 
425     /*
426      * =======================================================================
427      *      Basic I/O functions
428      * =======================================================================
429      */
430 
431     /**
432      * Seeks the DEX file to the specified absolute position.
433      */
seek(int position)434     void seek(int position) throws IOException {
435         mDexFile.seek(position);
436     }
437 
438     /**
439      * Fills the buffer by reading bytes from the DEX file.
440      */
readBytes(byte[] buffer)441     void readBytes(byte[] buffer) throws IOException {
442         mDexFile.readFully(buffer);
443     }
444 
445     /**
446      * Reads a single signed byte value.
447      */
readByte()448     byte readByte() throws IOException {
449         mDexFile.readFully(tmpBuf, 0, 1);
450         return tmpBuf[0];
451     }
452 
453     /**
454      * Reads a signed 16-bit integer, byte-swapping if necessary.
455      */
readShort()456     short readShort() throws IOException {
457         mDexFile.readFully(tmpBuf, 0, 2);
458         if (isBigEndian) {
459             return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
460         } else {
461             return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
462         }
463     }
464 
465     /**
466      * Reads a signed 32-bit integer, byte-swapping if necessary.
467      */
readInt()468     int readInt() throws IOException {
469         mDexFile.readFully(tmpBuf, 0, 4);
470 
471         if (isBigEndian) {
472             return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
473                    ((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
474         } else {
475             return (tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8) |
476                    ((tmpBuf[2] & 0xff) << 16) | ((tmpBuf[3] & 0xff) << 24);
477         }
478     }
479 
480     /**
481      * Reads a variable-length unsigned LEB128 value.  Does not attempt to
482      * verify that the value is valid.
483      *
484      * @throws EOFException if we run off the end of the file
485      */
readUnsignedLeb128()486     int readUnsignedLeb128() throws IOException {
487         int result = 0;
488         byte val;
489 
490         do {
491             val = readByte();
492             result = (result << 7) | (val & 0x7f);
493         } while (val < 0);
494 
495         return result;
496     }
497 
498     /**
499      * Reads a UTF-8 string.
500      *
501      * We don't know how long the UTF-8 string is, so we have to read one
502      * byte at a time.  We could make an educated guess based on the
503      * utf16_size and seek back if we get it wrong, but seeking backward
504      * may cause the underlying implementation to reload I/O buffers.
505      */
readString()506     String readString() throws IOException {
507         int utf16len = readUnsignedLeb128();
508         byte inBuf[] = new byte[utf16len * 3];      // worst case
509         int idx;
510 
511         for (idx = 0; idx < inBuf.length; idx++) {
512             byte val = readByte();
513             if (val == 0)
514                 break;
515             inBuf[idx] = val;
516         }
517 
518         return new String(inBuf, 0, idx, "UTF-8");
519     }
520 
521 
522     /*
523      * =======================================================================
524      *      Internal "structure" declarations
525      * =======================================================================
526      */
527 
528     /**
529      * Holds the contents of a header_item.
530      */
531     static class HeaderItem {
532         public int fileSize;
533         public int headerSize;
534         public int endianTag;
535         public int stringIdsSize, stringIdsOff;
536         public int typeIdsSize, typeIdsOff;
537         public int protoIdsSize, protoIdsOff;
538         public int fieldIdsSize, fieldIdsOff;
539         public int methodIdsSize, methodIdsOff;
540         public int classDefsSize, classDefsOff;
541 
542         /* expected magic values */
543         public static final byte[] DEX_FILE_MAGIC_v035 =
544             "dex\n035\0".getBytes(StandardCharsets.US_ASCII);
545 
546         // Dex version 036 skipped because of an old dalvik bug on some versions
547         // of android where dex files with that version number would erroneously
548         // be accepted and run. See: art/runtime/dex_file.cc
549 
550         // V037 was introduced in API LEVEL 24
551         public static final byte[] DEX_FILE_MAGIC_v037 =
552             "dex\n037\0".getBytes(StandardCharsets.US_ASCII);
553 
554         // V038 was introduced in API LEVEL 26
555         public static final byte[] DEX_FILE_MAGIC_v038 =
556             "dex\n038\0".getBytes(StandardCharsets.US_ASCII);
557 
558         public static final int ENDIAN_CONSTANT = 0x12345678;
559         public static final int REVERSE_ENDIAN_CONSTANT = 0x78563412;
560     }
561 
562     /**
563      * Holds the contents of a type_id_item.
564      *
565      * This is chiefly a list of indices into the string table.  We need
566      * some additional bits of data, such as whether or not the type ID
567      * represents a class defined in this DEX, so we use an object for
568      * each instead of a simple integer.  (Could use a parallel array, but
569      * since this is a desktop app it's not essential.)
570      */
571     static class TypeIdItem {
572         public int descriptorIdx;       // index into string_ids
573 
574         public boolean internal;        // defined within this DEX file?
575     }
576 
577     /**
578      * Holds the contents of a proto_id_item.
579      */
580     static class ProtoIdItem {
581         public int shortyIdx;           // index into string_ids
582         public int returnTypeIdx;       // index into type_ids
583         public int parametersOff;       // file offset to a type_list
584 
585         public int types[];             // contents of type list
586     }
587 
588     /**
589      * Holds the contents of a field_id_item.
590      */
591     static class FieldIdItem {
592         public int classIdx;            // index into type_ids (defining class)
593         public int typeIdx;             // index into type_ids (field type)
594         public int nameIdx;             // index into string_ids
595     }
596 
597     /**
598      * Holds the contents of a method_id_item.
599      */
600     static class MethodIdItem {
601         public int classIdx;            // index into type_ids
602         public int protoIdx;            // index into proto_ids
603         public int nameIdx;             // index into string_ids
604     }
605 
606     /**
607      * Holds the contents of a class_def_item.
608      *
609      * We don't really need a class for this, but there's some stuff in
610      * the class_def_item that we might want later.
611      */
612     static class ClassDefItem {
613         public int classIdx;            // index into type_ids
614     }
615 }
616