Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright 2016, 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 
     32 package org.jf.dexlib2.util;
     33 
     34 import com.google.common.io.ByteStreams;
     35 import org.jf.dexlib2.dexbacked.DexBackedDexFile.NotADexFile;
     36 import org.jf.dexlib2.dexbacked.DexBackedOdexFile.NotAnOdexFile;
     37 import org.jf.dexlib2.dexbacked.raw.HeaderItem;
     38 import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem;
     39 
     40 import javax.annotation.Nonnull;
     41 import java.io.EOFException;
     42 import java.io.IOException;
     43 import java.io.InputStream;
     44 
     45 public class DexUtil {
     46 
     47     /**
     48      * Reads in the dex header from the given input stream and verifies that it is valid and a supported version
     49      *
     50      * The inputStream must support mark(), and will be reset to initial position upon exiting the method
     51      *
     52      * @param inputStream An input stream that is positioned at a dex header
     53      * @throws NotADexFile If the file is not a dex file
     54      * @throws InvalidFile If the header appears to be a dex file, but is not valid for some reason
     55      * @throws UnsupportedFile If the dex header is valid, but uses unsupported functionality
     56      */
     57     public static void verifyDexHeader(@Nonnull InputStream inputStream) throws IOException {
     58         if (!inputStream.markSupported()) {
     59             throw new IllegalArgumentException("InputStream must support mark");
     60         }
     61         inputStream.mark(44);
     62         byte[] partialHeader = new byte[44];
     63         try {
     64             ByteStreams.readFully(inputStream, partialHeader);
     65         } catch (EOFException ex) {
     66             throw new NotADexFile("File is too short");
     67         } finally {
     68             inputStream.reset();
     69         }
     70 
     71         verifyDexHeader(partialHeader, 0);
     72     }
     73 
     74     /**
     75      * Verifies that the dex header is valid and a supported version
     76      *
     77      * @param buf A byte array containing at least the first 44 bytes of a dex file
     78      * @param offset The offset within the array to the dex header
     79      * @throws NotADexFile If the file is not a dex file
     80      * @throws InvalidFile If the header appears to be a dex file, but is not valid for some reason
     81      * @throws UnsupportedFile If the dex header is valid, but uses unsupported functionality
     82      */
     83     public static void verifyDexHeader(@Nonnull byte[] buf, int offset) {
     84         int dexVersion = HeaderItem.getVersion(buf, offset);
     85         if (dexVersion == -1) {
     86             StringBuilder sb = new StringBuilder("Not a valid dex magic value:");
     87             for (int i=0; i<8; i++) {
     88                 sb.append(String.format(" %02x", buf[i]));
     89             }
     90             throw new NotADexFile(sb.toString());
     91         }
     92 
     93         if (!HeaderItem.isSupportedDexVersion(dexVersion)) {
     94             throw new UnsupportedFile(String.format("Dex version %03d is not supported", dexVersion));
     95         }
     96 
     97         int endian = HeaderItem.getEndian(buf, offset);
     98         if (endian == HeaderItem.BIG_ENDIAN_TAG) {
     99             throw new UnsupportedFile("Big endian dex files are not supported");
    100         }
    101 
    102         if (endian != HeaderItem.LITTLE_ENDIAN_TAG) {
    103             throw new InvalidFile(String.format("Invalid endian tag: 0x%x", endian));
    104         }
    105     }
    106 
    107     /**
    108      * Reads in the odex header from the given input stream and verifies that it is valid and a supported version
    109      *
    110      * The inputStream must support mark(), and will be reset to initial position upon exiting the method
    111      *
    112      * @param inputStream An input stream that is positioned at an odex header
    113      * @throws NotAnOdexFile If the file is not an odex file
    114      * @throws UnsupportedFile If the odex header is valid, but is an unsupported version
    115      */
    116     public static void verifyOdexHeader(@Nonnull InputStream inputStream) throws IOException {
    117         if (!inputStream.markSupported()) {
    118             throw new IllegalArgumentException("InputStream must support mark");
    119         }
    120         inputStream.mark(8);
    121         byte[] partialHeader = new byte[8];
    122         try {
    123             ByteStreams.readFully(inputStream, partialHeader);
    124         } catch (EOFException ex) {
    125             throw new NotAnOdexFile("File is too short");
    126         } finally {
    127             inputStream.reset();
    128         }
    129 
    130         verifyOdexHeader(partialHeader, 0);
    131     }
    132 
    133     /**
    134      * Verifies that the odex header is valid and a supported version
    135      *
    136      * @param buf A byte array containing at least the first 8 bytes of an odex file
    137      * @param offset The offset within the array to the odex header
    138      * @throws NotAnOdexFile If the file is not an odex file
    139      * @throws UnsupportedFile If the odex header is valid, but uses unsupported functionality
    140      */
    141     public static void verifyOdexHeader(@Nonnull byte[] buf, int offset) {
    142         int odexVersion = OdexHeaderItem.getVersion(buf, offset);
    143         if (odexVersion == -1) {
    144             StringBuilder sb = new StringBuilder("Not a valid odex magic value:");
    145             for (int i=0; i<8; i++) {
    146                 sb.append(String.format(" %02x", buf[i]));
    147             }
    148             throw new NotAnOdexFile(sb.toString());
    149         }
    150 
    151         if (!OdexHeaderItem.isSupportedOdexVersion(odexVersion)) {
    152             throw new UnsupportedFile(String.format("Odex version %03d is not supported", odexVersion));
    153         }
    154     }
    155 
    156     public static class InvalidFile extends RuntimeException {
    157         public InvalidFile() {
    158         }
    159 
    160         public InvalidFile(String message) {
    161             super(message);
    162         }
    163 
    164         public InvalidFile(String message, Throwable cause) {
    165             super(message, cause);
    166         }
    167 
    168         public InvalidFile(Throwable cause) {
    169             super(cause);
    170         }
    171     }
    172 
    173     public static class UnsupportedFile extends RuntimeException {
    174         public UnsupportedFile() {
    175         }
    176 
    177         public UnsupportedFile(String message) {
    178             super(message);
    179         }
    180 
    181         public UnsupportedFile(String message, Throwable cause) {
    182             super(message, cause);
    183         }
    184 
    185         public UnsupportedFile(Throwable cause) {
    186             super(cause);
    187         }
    188     }
    189 }
    190