• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
verifyDexHeader(@onnull InputStream inputStream)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      */
verifyDexHeader(@onnull byte[] buf, int offset)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      */
verifyOdexHeader(@onnull InputStream inputStream)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      */
verifyOdexHeader(@onnull byte[] buf, int offset)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 {
InvalidFile()157         public InvalidFile() {
158         }
159 
InvalidFile(String message)160         public InvalidFile(String message) {
161             super(message);
162         }
163 
InvalidFile(String message, Throwable cause)164         public InvalidFile(String message, Throwable cause) {
165             super(message, cause);
166         }
167 
InvalidFile(Throwable cause)168         public InvalidFile(Throwable cause) {
169             super(cause);
170         }
171     }
172 
173     public static class UnsupportedFile extends RuntimeException {
UnsupportedFile()174         public UnsupportedFile() {
175         }
176 
UnsupportedFile(String message)177         public UnsupportedFile(String message) {
178             super(message);
179         }
180 
UnsupportedFile(String message, Throwable cause)181         public UnsupportedFile(String message, Throwable cause) {
182             super(message, cause);
183         }
184 
UnsupportedFile(Throwable cause)185         public UnsupportedFile(Throwable cause) {
186             super(cause);
187         }
188     }
189 }
190