-
-
Save Nublo/4078c2c0eb273b0ceae4eca1f3fcda65 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.IOException | |
import java.io.RandomAccessFile | |
import java.nio.charset.StandardCharsets | |
/** | |
* https://android.googlesource.com/platform/dalvik.git/+/master/tools/dexdeps/src/com/android/dexdeps | |
* | |
* Extracts information about methods and fields from .dex file | |
*/ | |
internal class DexParser( | |
private val dexFile: RandomAccessFile | |
) { | |
private val tmpBuf = ByteArray(INT_SIZE_IN_BYTES) | |
private var isBigEndian = false | |
@Throws(IOException::class) | |
fun load(): DexInfo { | |
val headerItem = parseHeaderItem() | |
return DexInfo( | |
methods = headerItem.methodIdsSize, | |
fields = headerItem.fieldIdsCount, | |
) | |
} | |
@Throws(IOException::class) | |
@Suppress("MagicNumber") | |
private fun parseHeaderItem(): HeaderItem { | |
dexFile.seek(0) | |
verifyMagic() | |
val headerItem = HeaderItem() | |
/* | |
* Read the endian tag, so we properly swap things as we read | |
* them from here on. | |
*/ | |
dexFile.seek((DEX_FILE_MAGIC_LENGTH + 4 + 20 + 4 + 4).toLong()) | |
headerItem.endianTag = readInt() | |
isBigEndian = if (headerItem.endianTag == ENDIAN_CONSTANT) { | |
false | |
} else if (headerItem.endianTag == REVERSE_ENDIAN_CONSTANT) { | |
/* file is big-endian (!), reverse future reads */ | |
true | |
} else { | |
throw IOException("Endian constant has unexpected value " + Integer.toHexString(headerItem.endianTag)) | |
} | |
dexFile.seek((DEX_FILE_MAGIC_LENGTH + 4 + 20).toLong()) // magic, checksum, signature | |
/*headerItem.fileSize =*/readInt() | |
/*headerItem.headerSize =*/readInt() | |
/*headerItem.endianTag =*/readInt() | |
/*headerItem.linkSize =*/readInt() | |
/*headerItem.linkOff =*/readInt() | |
/*headerItem.mapOff =*/readInt() | |
/*headerItem.stringIdsSize =*/ readInt() | |
/*headerItem.stringIdsOff =*/readInt() | |
/*headerItem.typeIdsSize =*/ readInt() | |
/*headerItem.typeIdsOff =*/readInt() | |
/*headerItem.protoIdsSize =*/readInt() | |
/*headerItem.protoIdsOff =*/readInt() | |
headerItem.fieldIdsCount = readInt() | |
/*headerItem.fieldIdsOff =*/readInt() | |
headerItem.methodIdsSize = readInt() | |
/*headerItem.methodIdsOff =*/readInt() | |
/*headerItem.classDefsSize =*/ readInt() | |
/*headerItem.classDefsOff =*/readInt() | |
/*headerItem.dataSize =*/readInt() | |
/*headerItem.dataOff =*/readInt() | |
return headerItem | |
} | |
@Throws(IOException::class) | |
private fun verifyMagic() { | |
val magic = ByteArray(DEX_FILE_MAGIC_LENGTH) | |
dexFile.readFully(magic) | |
if (!verifyMagic(magic)) { | |
throw IOException("Magic number is wrong -- are you sure this is a DEX file?") | |
} | |
} | |
/** | |
* Reads a signed 32-bit integer, byte-swapping if necessary. | |
*/ | |
@Throws(IOException::class) | |
@Suppress("MagicNumber") | |
private fun readInt(): Int { | |
dexFile.readFully(tmpBuf, 0, INT_SIZE_IN_BYTES) | |
return if (isBigEndian) { | |
tmpBuf[3].toInt() | |
.and(0xff) | |
.or(tmpBuf[2].toInt().and(0xff).shl(8)) | |
.or(tmpBuf[1].toInt().and(0xff).shl(16)) | |
.or(tmpBuf[0].toInt().and(0xff).shl(24)) | |
} else { | |
tmpBuf[0].toInt() | |
.and(0xff) | |
.or(tmpBuf[1].toInt() and 0xff shl 8) | |
.or(tmpBuf[2].toInt() and 0xff shl 16) | |
.or(tmpBuf[3].toInt() and 0xff shl 24) | |
} | |
} | |
private fun verifyMagic(magic: ByteArray): Boolean { | |
return magic.contentEquals(DEX_FILE_MAGIC_v035) || | |
magic.contentEquals(DEX_FILE_MAGIC_v037) || | |
magic.contentEquals(DEX_FILE_MAGIC_v038) || | |
magic.contentEquals(DEX_FILE_MAGIC_v039) | |
} | |
/** | |
* Holds the contents of a header_item. | |
*/ | |
internal class HeaderItem { | |
var endianTag = 0 | |
var fieldIdsCount = 0 | |
var methodIdsSize = 0 | |
} | |
companion object { | |
private const val DEX_FILE_MAGIC_LENGTH = 8 | |
/* expected magic values */ | |
private val DEX_FILE_MAGIC_v035 = "dex\n035\u0000".toByteArray(StandardCharsets.US_ASCII) | |
// Dex version 036 skipped because of an old dalvik bug on some versions | |
// of android where dex files with that version number would erroneously | |
// be accepted and run. See: art/runtime/dex_file.cc | |
// V037 was introduced in API LEVEL 24 | |
private val DEX_FILE_MAGIC_v037 = "dex\n037\u0000".toByteArray(StandardCharsets.US_ASCII) | |
// V038 was introduced in API LEVEL 26 | |
private val DEX_FILE_MAGIC_v038 = "dex\n038\u0000".toByteArray(StandardCharsets.US_ASCII) | |
// V039 was introduced in API LEVEL 28 | |
private val DEX_FILE_MAGIC_v039 = "dex\n039\u0000".toByteArray(StandardCharsets.US_ASCII) | |
private const val ENDIAN_CONSTANT = 0x12345678 | |
private const val REVERSE_ENDIAN_CONSTANT = 0x78563412 | |
private const val INT_SIZE_IN_BYTES = 4 | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment