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