1 /* 2 * Copyright (C) 2014 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 dexfuzz.rawdex; 18 19 import dexfuzz.Log; 20 21 import java.io.FileNotFoundException; 22 import java.io.IOException; 23 import java.io.RandomAccessFile; 24 25 /** 26 * An extension to RandomAccessFile that allows reading/writing 27 * DEX files in little-endian form, the variable-length LEB format 28 * and also provides word-alignment functions. 29 */ 30 public class DexRandomAccessFile extends RandomAccessFile { 31 private OffsetTracker offsetTracker; 32 getOffsetTracker()33 public OffsetTracker getOffsetTracker() { 34 return offsetTracker; 35 } 36 setOffsetTracker(OffsetTracker offsetTracker)37 public void setOffsetTracker(OffsetTracker offsetTracker) { 38 this.offsetTracker = offsetTracker; 39 } 40 41 /** 42 * Constructor, passes straight on to RandomAccessFile currently. 43 * @param filename The file to open. 44 * @param mode Strings "r" or "rw" work best. 45 */ DexRandomAccessFile(String filename, String mode)46 public DexRandomAccessFile(String filename, String mode) 47 throws FileNotFoundException { 48 super(filename, mode); 49 } 50 51 /** 52 * @return A 16-bit number, read from the file as little-endian. 53 */ readUShort()54 public short readUShort() throws IOException { 55 int b1 = readUnsignedByte(); 56 int b2 = readUnsignedByte(); 57 return (short) ((b2 << 8) | b1); 58 } 59 60 /** 61 * @param value A 16-bit number to be written to the file in little-endian. 62 */ writeUShort(short value)63 public void writeUShort(short value) throws IOException { 64 int b1 = value & 0xff; 65 int b2 = (value & 0xff00) >> 8; 66 writeByte(b1); 67 writeByte(b2); 68 } 69 70 /** 71 * @return A 32-bit number, read from the file as little-endian. 72 */ readUInt()73 public int readUInt() throws IOException { 74 int b1 = readUnsignedByte(); 75 int b2 = readUnsignedByte(); 76 int b3 = readUnsignedByte(); 77 int b4 = readUnsignedByte(); 78 return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; 79 } 80 81 /** 82 * @param value A 32-bit number to be written to the file in little-endian. 83 */ writeUInt(int value)84 public void writeUInt(int value) throws IOException { 85 int b1 = value & 0xff; 86 writeByte(b1); 87 int b2 = (value & 0xff00) >> 8; 88 writeByte(b2); 89 int b3 = (value & 0xff0000) >> 16; 90 writeByte(b3); 91 int b4 = (value & 0xff000000) >> 24; 92 writeByte(b4); 93 } 94 95 /** 96 * @return An up to 32-bit number, read from the file in ULEB128 form. 97 */ readUleb128()98 public int readUleb128() throws IOException { 99 int shift = 0; 100 int value = 0; 101 int rawByte = readUnsignedByte(); 102 boolean done = false; 103 while (!done) { 104 // Get the lower seven bits. 105 // 0x7f = 0111 1111 106 value |= ((rawByte & 0x7f) << shift); 107 shift += 7; 108 // Check the 8th bit - if it's 0, we're done. 109 // 0x80 = 1000 0000 110 if ((rawByte & 0x80) == 0) { 111 done = true; 112 } else { 113 rawByte = readUnsignedByte(); 114 } 115 } 116 return value; 117 } 118 119 /** 120 * @param value A 32-bit number to be written to the file in ULEB128 form. 121 */ writeUleb128(int value)122 public void writeUleb128(int value) throws IOException { 123 if (value == 0) { 124 writeByte(0); 125 return; 126 } 127 128 while (value != 0) { 129 int marker = 1; 130 // If we're down to the last 7 bits, the marker will be 0. 131 if ((value & 0xffffff80) == 0) { 132 marker = 0; 133 } 134 // Get the lowest 7 bits, add on the marker in the high bit. 135 int nextByte = value & 0x7f | (marker << 7); 136 writeByte(nextByte); 137 value >>>= 7; 138 } 139 } 140 141 /** 142 * Write out ULEB128 value always using 5 bytes. 143 * A version of ULEB128 that will always write out 5 bytes, because this 144 * value will be patched later, and if we used a smaller encoding, the new value 145 * may overflow the previously selected encoding size. 146 * The largest encoding for 0 in ULEB128 would be: 147 * 0x80 0x80 0x80 0x80 0x00 148 * and for 1 would be: 149 * 0x81 0x80 0x80 0x80 0x00 150 */ writeLargestUleb128(int value)151 public void writeLargestUleb128(int value) throws IOException { 152 Log.debug("Writing " + value + " using the largest possible ULEB128 encoding."); 153 if (value == 0) { 154 writeByte(0x80); 155 writeByte(0x80); 156 writeByte(0x80); 157 writeByte(0x80); 158 writeByte(0x0); 159 return; 160 } 161 162 for (int i = 0; i < 5; i++) { 163 int marker = 1; 164 // If we're writing the 5th byte, the marker is 0. 165 if (i == 4) { 166 marker = 0; 167 } 168 // Get the lowest 7 bits, add on the marker in the high bit. 169 int nextByte = value & 0x7f | (marker << 7); 170 writeByte(nextByte); 171 value >>>= 7; 172 } 173 } 174 175 /** 176 * @return An up to 32-bit number, read from the file in SLEB128 form. 177 */ readSleb128()178 public int readSleb128() throws IOException { 179 int shift = 0; 180 int value = 0; 181 int rawByte = readUnsignedByte(); 182 boolean done = false; 183 boolean mustSignExtend = false; 184 while (!done) { 185 // Get the lower seven bits. 186 // 0x7f = 0111 1111 187 value |= ((rawByte & 0x7f) << shift); 188 shift += 7; 189 // Check the 8th bit - if it's 0, we're done. 190 // 0x80 = 1000 0000 191 if ((rawByte & 0x80) == 0) { 192 // Check the 7th bit - if it's a 1, we need to sign extend. 193 if ((rawByte & 0x60) != 0) { 194 mustSignExtend = true; 195 } 196 done = true; 197 } else { 198 rawByte = readUnsignedByte(); 199 } 200 } 201 if (mustSignExtend) { 202 // Example: 203 // say we shifted 7 bits, we need 204 // to make all the upper 25 bits 1s. 205 // load a 1... 206 // 00000000 00000000 00000000 00000001 207 // << 7 208 // 00000000 00000000 00000000 10000000 209 // - 1 210 // 00000000 00000000 00000000 01111111 211 // ~ 212 // 11111111 11111111 11111111 10000000 213 int upperOnes = ~((1 << shift) - 1); 214 value |= (upperOnes); 215 } 216 return value; 217 } 218 219 /** 220 * @param value A 32-bit number to be written to the file in SLEB128 form. 221 */ writeSleb128(int value)222 public void writeSleb128(int value) throws IOException { 223 if (value == 0) { 224 writeByte(0); 225 return; 226 } 227 if (value > 0) { 228 writeUleb128(value); 229 return; 230 } 231 if (value == -1) { 232 writeByte(0x7f); 233 } 234 235 // When it's all 1s (0xffffffff), we're done! 236 while (value != 0xffffffff) { 237 int marker = 1; 238 // If we're down to the last 7 bits (i.e., shifting a further 7 is all 1s), 239 // the marker will be 0. 240 if ((value >> 7) == 0xffffffff) { 241 marker = 0; 242 } 243 // Get the lowest 7 bits, add on the marker in the high bit. 244 int nextByte = value & 0x7f | (marker << 7); 245 writeByte(nextByte); 246 value >>= 7; 247 } 248 } 249 250 /** 251 * In DEX format, strings are in MUTF-8 format, the first ULEB128 value is the decoded size 252 * (i.e., string.length), and then follows a null-terminated series of characters. 253 * @param decodedSize The ULEB128 value that should have been read just before this. 254 * @return The raw bytes of the string, not including the null character. 255 */ readDexUtf(int decodedSize)256 public byte[] readDexUtf(int decodedSize) throws IOException { 257 // In the dex MUTF-8, the encoded size can never be larger than 3 times 258 // the actual string's length (which is the ULEB128 value just before this 259 // string, the "decoded size") 260 261 // Therefore, allocate as much space as we might need. 262 byte[] str = new byte[decodedSize * 3]; 263 264 // Get our first byte. 265 int encodedSize = 0; 266 byte rawByte = readByte(); 267 268 // Keep reading until we find the end marker. 269 while (rawByte != 0) { 270 str[encodedSize++] = rawByte; 271 rawByte = readByte(); 272 } 273 274 // Copy everything we read into str into the correctly-sized actual string. 275 byte[] actualString = new byte[encodedSize]; 276 for (int i = 0; i < encodedSize; i++) { 277 actualString[i] = str[i]; 278 } 279 280 return actualString; 281 } 282 283 /** 284 * Writes out raw bytes that would have been read by readDexUTF(). 285 * Will automatically write out the null-byte at the end. 286 * @param data Bytes to be written out. 287 */ writeDexUtf(byte[] data)288 public void writeDexUtf(byte[] data) throws IOException { 289 write(data); 290 // Remember to add the end marker. 291 writeByte(0); 292 } 293 294 /** 295 * Align the file handle's seek pointer to the next N bytes. 296 * @param alignment N to align to. 297 */ alignForwards(int alignment)298 public void alignForwards(int alignment) throws IOException { 299 long offset = getFilePointer(); 300 long mask = alignment - 1; 301 if ((offset & mask) != 0) { 302 int extra = alignment - (int) (offset & mask); 303 seek(offset + extra); 304 } 305 } 306 307 /** 308 * Align the file handle's seek pointer backwards to the previous N bytes. 309 * @param alignment N to align to. 310 */ alignBackwards(int alignment)311 public void alignBackwards(int alignment) throws IOException { 312 long offset = getFilePointer(); 313 long mask = alignment - 1; 314 if ((offset & mask) != 0) { 315 offset &= (~mask); 316 seek(offset); 317 } 318 } 319 } 320