diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFile.java index 3a8d58f8..7d697f1f 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFile.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFile.java @@ -31,14 +31,43 @@ package org.jf.dexlib2.dexbacked; +import org.jf.util.ExceptionWithContext; + import javax.annotation.Nullable; public class DexFile { + // TODO: consider using a direct ByteBuffer instead + protected final byte[] buf; + + public DexFile(byte[] buf) { + this.buf = buf; + } + + public String getString(int stringIndex) { + return null; + } + + public int getFieldIdItemOffset(int fieldIndex) { + return 0; + } + + public int getMethodIdItemOffset(int methodIndex) { + return 0; + } + + public int getProtoIdItemOffset(int methodIndex) { + return 0; + } + public String getType(int typeIndex) { return null; } - public String getString(int stringIndex) { + public String getField(int fieldIndex) { + return null; + } + + public String getMethod(int methodIndex) { return null; } @@ -47,20 +76,61 @@ public class DexFile { return null; } - public int getFieldIdItemOffset(int fieldIndex) { - return 0; - } - - public int readSmallUleb128(int offset) { - return 0; + public String getReference(int referenceType, int referenceIndex) { + return null; } public int readSmallUint(int offset) { - return 0; + byte[] buf = this.buf; + int result = (buf[offset] & 0xff) | + ((buf[offset+1] & 0xff) << 8) | + ((buf[offset+2] & 0xff) << 16) | + ((buf[offset+3] & 0xff) << 24); // TODO: can get rid of last & 0xff? + if (result < 0) { + throw new ExceptionWithContext("Encountered uint that is out of range at offset 0x%x", offset); + } + return result; } public int readUshort(int offset) { - return 0; + byte[] buf = this.buf; + return (buf[offset] & 0xff) | + ((buf[offset+1] & 0xff) << 8); + } + + public int readUbyte(int offset) { + return buf[offset] & 0xff; + } + + public long readLong(int offset) { + // TODO: use | or +? + byte[] buf = this.buf; + return (buf[offset] & 0xffL) | + ((buf[offset+1] & 0xff) << 8) | + ((buf[offset+2] & 0xff) << 16) | + ((buf[offset+3] & 0xff) << 24) | + ((buf[offset+4] & 0xffL) << 32) | + ((buf[offset+5] & 0xffL) << 40) | + ((buf[offset+6] & 0xffL) << 48) | + (((long)buf[offset+7]) << 56); + } + + public int readInt(int offset) { + byte[] buf = this.buf; + return (buf[offset] & 0xff) | + ((buf[offset+1] & 0xff) << 8) | + ((buf[offset+2] & 0xff) << 16) | + (buf[offset+3] << 24); + } + + public int readShort(int offset) { + byte[] buf = this.buf; + return (buf[offset] & 0xff) | + (buf[offset+1] << 8); + } + + public int readByte(int offset) { + return buf[offset]; } public DexFileReader readerAt(int offset) { diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFileReader.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFileReader.java index ca44eb77..b8198141 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFileReader.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexFileReader.java @@ -31,150 +31,418 @@ package org.jf.dexlib2.dexbacked; +import org.jf.util.ExceptionWithContext; + import javax.annotation.Nonnull; public class DexFileReader { - public DexFileReader(DexFile dexFile, int offset) { + @Nonnull private final DexFile dexFile; + private int offset; + + public DexFileReader(@Nonnull DexFile dexFile, int offset) { + this.dexFile = dexFile; + this.offset = offset; } - @Nonnull - public DexFile getDexFile() { - return null; - } + @Nonnull public DexFile getDexFile() { return dexFile; } + public int getOffset() { return offset; } - public int getOffset() { - return 0; - } - - public String getString(int stringIndex) { - return null; - } - - public int getFieldIdItemOffset(int fieldIndex) { - return 0; - } - - public int getMethodIdItemOffset(int methodIndex) { - return 0; - } - - public int getProtoIdItemOffset(int methodIndex) { - return 0; - } - - public String getType(int typeIndex) { - return null; - } - - public String getField(int fieldIndex) { - return null; - } - - public String getMethod(int fieldIndex) { - return null; - } - - public String getReference(int referenceType, int referenceIndex) { - return null; - } + public String getString(int stringIndex) { return dexFile.getString(stringIndex); } + public int getFieldIdItemOffset(int fieldIndex) { return dexFile.getFieldIdItemOffset(fieldIndex); } + public int getMethodIdItemOffset(int methodIndex) { return dexFile.getMethodIdItemOffset(methodIndex); } + public int getProtoIdItemOffset(int methodIndex) { return dexFile.getProtoIdItemOffset(methodIndex); } + public String getType(int typeIndex) { return dexFile.getType(typeIndex); } + public String getField(int fieldIndex) { return dexFile.getField(fieldIndex); } + public String getMethod(int methodIndex) { return dexFile.getMethod(methodIndex); } + public String getReference(int type, int index) { return dexFile.getReference(type, index); } + /** {@inheritDoc} */ public int readSleb128() { - return 0; + int end = offset; + int currentByteValue; + int result; + byte[] buf = dexFile.buf; + + result = buf[end++] & 0xff; + if (result <= 0x7f) { + result = (result << 25) >> 25; + } else { + currentByteValue = buf[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue <= 0x7f) { + result = (result << 18) >> 18; + } else { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue <= 0x7f) { + result = (result << 11) >> 11; + } else { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue <= 0x7f) { + result = (result << 4) >> 4; + } else { + currentByteValue = buf[end++] & 0xff; + if (currentByteValue > 0x0f) { + throw new ExceptionWithContext( + "Invalid sleb128 integer encountered at offset 0x%x", offset); + } + result |= currentByteValue << 28; + } + } + } + } + + offset = end; + return result; } public int readSmallUleb128() { - return 0; + int end = offset; + int currentByteValue; + int result; + byte[] buf = dexFile.buf; + + result = buf[end++] & 0xff; + if (result > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result = (result & 0x7f) | ((currentByteValue & 0x7f) << 7); + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 14; + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++] & 0xff; + result |= (currentByteValue & 0x7f) << 21; + if (currentByteValue > 0x7f) { + currentByteValue = buf[end++] & 0xff; + // we assume the most significant bit will never be set + if (currentByteValue > 0x07) { + if (currentByteValue <= 0x0f) { + throw new ExceptionWithContext( + "Encountered valid uleb128 that is out of range at offset 0x%x", offset); + } + throw new ExceptionWithContext( + "Invalid uleb128 integer encountered at offset 0x%x", offset); + } + result |= currentByteValue << 28; + } + } + } + } + + offset = end; + return result; } public void skipUleb128() { + int end = offset; + byte currentByteValue; + byte[] buf = dexFile.buf; + + currentByteValue = buf[end++]; + if (currentByteValue < 0) { // if the MSB is set + currentByteValue = buf[end++]; + if (currentByteValue < 0) { // if the MSB is set + currentByteValue = buf[end++]; + if (currentByteValue < 0) { // if the MSB is set + currentByteValue = buf[end++]; + if (currentByteValue < 0) { // if the MSB is set + currentByteValue = buf[end++]; + if (currentByteValue < 0 || currentByteValue > 0x0f) { + throw new ExceptionWithContext( + "Invalid uleb128 integer encountered at offset 0x%x", offset); + } + } + } + } + } + + offset = end; } public int readSmallUint() { - return 0; + int o = offset; + int result = dexFile.readSmallUint(o); + offset = o + 4; + return result; } public int readUshort() { - return 0; + int o = offset; + int result = dexFile.readUshort(offset); + offset = o + 2; + return result; } public int readUbyte() { - // returns an int between 0 and 255 - return 0; - } - - public int readByte() { - // returns an int between -128 and 127 - return 0; - } - - public int readUshort(int offset) { - return 0; - } - - public int readShort() { - return 0; - } - - public int readSizedInt(int bytes) { - // bytes must be from 1 to 4. reads and interprets that many bytes as a little-endian sign-extended integer - return 0; - } - - public int readSizedSmallUint(int bytes) { - // bytes must be from 1 to 4. reads and interprets that many bytes as a little-endian zero-extended integer - return 0; - } - - public int readSizedRightExtendedUint(int bytes) { - // bytes must be from 1 to 4. reads and interprets that many bytes as a little-endian zero-right-extended - // integer - return 0; - } - - public int readSizedRightExtendedUlong(int bytes) { - // bytes must be from 1 to 8. reads and interprets that many bytes as a little-endian zero-right-extended - // long - return 0; - } - - public long readSizedLong(int bytes) { - // bytes must be from 1 to 8. reads and interprets that many bytes as a little-endian sign-extended long - return 0; - } - - public long readSizedUlong(int bytes) { - // bytes must be from 1 to 8. reads and interprets that many bytes as a little-endian zero-extended long - return 0; - } - - public int readSmallUint(int offset) { - return 0; - } - - public int readInt() { - return 0; + int o = offset; + int result = dexFile.readUbyte(offset); + offset = o + 1; + return result; } public long readLong() { - return 0; + int o = offset; + long result = dexFile.readLong(offset); + offset = o + 2; + return result; } - - - @Nonnull - public DexFileReader atAbsolute(int offset) { - return null; + public int readInt() { + int o = offset; + int result = dexFile.readInt(offset); + offset = o + 4; + return result; } - // returns copy of this DexFileReader - @Nonnull - public DexFileReader copy() { - return null; + public int readShort() { + int o = offset; + int result = dexFile.readShort(offset); + offset = o + 2; + return result; } - public void skipByte() { + public int readByte() { + int o = offset; + int result = dexFile.readByte(offset); + offset = o + 1; + return result; } - public void skipBytes(int i) { + public void skipByte() { offset++; } + public void skipBytes(int i) { offset += i; } + + public int readSmallUint(int offset) { return dexFile.readSmallUint(offset); } + public int readUshort(int offset) { return dexFile.readUshort(offset); } + public int readUbyte(int offset) { return dexFile.readUbyte(offset); } + public long readLong(int offset) { return dexFile.readLong(offset); } + public int readInt(int offset) { return dexFile.readInt(offset); } + public int readShort(int offset) { return dexFile.readShort(offset); } + public int readByte(int offset) { return dexFile.readByte(offset); } + + + public int readSizedInt(int bytes) { + int o = offset; + byte[] buf = dexFile.buf; + + int result; + switch (bytes) { + case 4: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + (buf[o+3] << 24); + break; + case 3: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2]) << 16); + break; + case 2: + result = (buf[o] & 0xff) | + ((buf[o+1]) << 8); + break; + case 1: + result = buf[o]; + break; + default: + throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset); + } + offset = o + bytes; + return result; + } + + public int readSizedSmallUint(int bytes) { + int o = offset; + byte[] buf = dexFile.buf; + + int result = 0; + switch (bytes) { + case 4: + int b = buf[o+3]; + if (b < 0) { + throw new ExceptionWithContext( + "Encountered valid sized uint that is out of range at offset 0x%x", offset); + } + result = (b & 0xff) << 24; + // fall-through + case 3: + result |= (buf[o+2] & 0xff) << 16; + // fall-through + case 2: + result |= (buf[o+1] & 0xff) << 8; + // fall-through + case 1: + result |= (buf[o] & 0xff); + break; + default: + throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset); + } + offset = o + bytes; + return result; + } + + public int readSizedRightExtendedInt(int bytes) { + int o = offset; + byte[] buf = dexFile.buf; + + int result = 0; + switch (bytes) { + case 4: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + (buf[o+3] << 24); + break; + case 3: + result = (buf[o] & 0xff) << 8 | + ((buf[o+1] & 0xff) << 16) | + (buf[o+2] << 24); + break; + case 2: + result = (buf[o] & 0xff) << 16 | + (buf[o+1] << 24); + break; + case 1: + result = buf[o] << 24; + break; + default: + throw new ExceptionWithContext( + "Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset); + } + offset = o + bytes; + return result; + } + + public long readSizedRightExtendedLong(int bytes) { + int o = offset; + byte[] buf = dexFile.buf; + + long result = 0; + switch (bytes) { + case 8: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + ((buf[o+3] & 0xff) << 24) | + ((buf[o+4] & 0xffL) << 32) | + ((buf[o+5] & 0xffL) << 40) | + ((buf[o+6] & 0xffL) << 48) | + (((long)buf[o+7]) << 56); + break; + case 7: + result = ((buf[o] & 0xff)) << 8 | + ((buf[o+1] & 0xff) << 16) | + ((buf[o+2] & 0xff) << 24) | + ((buf[o+3] & 0xffL) << 32) | + ((buf[o+4] & 0xffL) << 40) | + ((buf[o+5] & 0xffL) << 48) | + (((long)buf[o+6]) << 56); + break; + case 6: + result = ((buf[o] & 0xff)) << 16 | + ((buf[o+1] & 0xff) << 24) | + ((buf[o+2] & 0xffL) << 32) | + ((buf[o+3] & 0xffL) << 40) | + ((buf[o+4] & 0xffL) << 48) | + (((long)buf[o+5]) << 56); + break; + case 5: + result = ((buf[o] & 0xff)) << 24 | + ((buf[o+1] & 0xffL) << 32) | + ((buf[o+2] & 0xffL) << 40) | + ((buf[o+3] & 0xffL) << 48) | + (((long)buf[o+4]) << 56); + break; + case 4: + result = ((buf[o] & 0xffL)) << 32 | + ((buf[o+1] & 0xffL) << 40) | + ((buf[o+2] & 0xffL) << 48) | + (((long)buf[o+3]) << 56); + break; + case 3: + result = ((buf[o] & 0xffL)) << 40 | + ((buf[o+1] & 0xffL) << 48) | + (((long)buf[o+2]) << 56); + break; + case 2: + result = ((buf[o] & 0xffL)) << 48 | + (((long)buf[o+1]) << 56); + break; + case 1: + result = ((long)buf[o]) << 56; + break; + default: + throw new ExceptionWithContext( + "Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset); + } + offset = o + bytes; + return result; + } + + public long readSizedLong(int bytes) { + int o = offset; + byte[] buf = dexFile.buf; + + long result = 0; + switch (bytes) { + case 8: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + ((buf[o+3] & 0xff) << 24) | + ((buf[o+4] & 0xffL) << 32) | + ((buf[o+5] & 0xffL) << 40) | + ((buf[o+6] & 0xffL) << 48) | + (((long)buf[o+7]) << 56); + break; + case 7: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + ((buf[o+3] & 0xff) << 24) | + ((buf[o+4] & 0xffL) << 32) | + ((buf[o+5] & 0xffL) << 40) | + ((long)(buf[o+6]) << 48); + break; + case 6: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + ((buf[o+3] & 0xff) << 24) | + ((buf[o+4] & 0xffL) << 32) | + ((long)(buf[o+5]) << 40); + break; + case 5: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + ((buf[o+3] & 0xff) << 24) | + ((long)(buf[o+4]) << 32); + break; + case 4: + result = (buf[o] & 0xff) | + ((buf[o+1] & 0xff) << 8) | + ((buf[o+2] & 0xff) << 16) | + (buf[o+3] << 24); + break; + case 3: + result = (buf[o] & 0xff) << 8 | + ((buf[o+1] & 0xff) << 16) | + (buf[o+2] << 24); + break; + case 2: + result = (buf[o] & 0xff) << 16 | + (buf[o+1] << 24); + break; + case 1: + result = buf[o] << 24; + break; + default: + throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset); + } + + o += bytes; + return result; } } \ No newline at end of file diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/value/DexBackedEncodedValue.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/value/DexBackedEncodedValue.java index dc08ee92..dbe6178a 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/value/DexBackedEncodedValue.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/value/DexBackedEncodedValue.java @@ -66,11 +66,11 @@ public abstract class DexBackedEncodedValue { case ValueType.FLOAT: Preconditions.checkValueArg(valueArg, 3); return new ImmutableFloatEncodedValue(Float.intBitsToFloat( - reader.readSizedRightExtendedUint(valueArg + 1))); + reader.readSizedRightExtendedInt(valueArg + 1))); case ValueType.DOUBLE: Preconditions.checkValueArg(valueArg, 7); return new ImmutableDoubleEncodedValue(Double.longBitsToDouble( - reader.readSizedRightExtendedUlong(valueArg + 1))); + reader.readSizedRightExtendedLong(valueArg + 1))); case ValueType.STRING: Preconditions.checkValueArg(valueArg, 3); return new ImmutableStringEncodedValue(reader.getString(reader.readSizedSmallUint(valueArg + 1)));