diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ApkFile.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ApkFile.kt deleted file mode 100755 index e1cb338d..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ApkFile.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. - * Copyright (C) 2019-2022 HighCapable - * https://github.com/fankes/YukiHookAPI - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is Created by fankes on 2022/5/24. - */ -@file:Suppress("unused") - -package com.highcapable.yukihookapi.hook.core.reflex.parse - -import android.content.pm.ApplicationInfo -import com.highcapable.yukihookapi.hook.utils.parallelForEach -import java.io.Closeable -import java.io.File -import java.nio.ByteBuffer -import java.util.zip.ZipEntry -import java.util.zip.ZipFile - -/** - * 封装对 APK 文件的解析操作 - * - * 参考了 dongliu 的 [apk-parser](https://github.com/hsiafan/apk-parser) 项目 - * - * Contributed from [conan](https://github.com/meowool-catnip/conan) - * @param apkFile APK 文件 - */ -internal class ApkFile private constructor(apkFile: File) : Closeable { - - internal companion object { - - private const val DEX_FILE = "classes.dex" - private const val DEX_ADDITIONAL = "classes%d.dex" - - /** - * 获取 [ApkFile] 实例 - * @param appInfo 当前 APP 的 [ApplicationInfo] - * @return [ApkFile] or null - */ - internal fun from(appInfo: ApplicationInfo?) = runCatching { ApkFile(File(appInfo!!.sourceDir)) }.getOrNull() - } - - override fun close() = zipFile.close() - - /** - * 当前 APK 文件 - * @return [ZipFile] - */ - private val zipFile = ZipFile(apkFile) - - /** - * 读取 Entry - * @param entry 压缩文件 Entry - * @return [ByteArray] - */ - private fun readEntry(entry: ZipEntry) = zipFile.getInputStream(entry).use { it.readBytes() } - - /** - * 读取 DEX 文件路径 - * @param idx 字节序号 - * @return [String] - */ - private fun readDexFilePath(idx: Int) = if (idx == 1) DEX_FILE else String.format(DEX_ADDITIONAL, idx) - - /** - * DEX 文件是否存在 - * @param idx 字节序号 - * @return [Boolean] - */ - private fun isDexFileExist(idx: Int) = zipFile.getEntry(readDexFilePath(idx)) != null - - /** - * 读取 DEX 文件字节流 - * @param idx 字节序号 - * @return [ByteArray] - */ - private fun readDexFile(idx: Int) = readEntry(zipFile.getEntry(readDexFilePath(idx))) - - /** - * 获取当前 DEX 的 package 结构实例 - * @return [ClassTrie] - */ - internal val classTypes by lazy { - var end = 2 - while (isDexFileExist(end)) end++ - val ret = ClassTrie() - (1 until end).parallelForEach { idx -> - val data = readDexFile(idx) - val buffer = ByteBuffer.wrap(data) - val parser = DexParser(buffer) - ret += parser.parseClassTypes() - } - return@lazy ret.apply { mutable = false } - } -} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ClassTrie.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ClassTrie.kt deleted file mode 100755 index 992dce52..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/ClassTrie.kt +++ /dev/null @@ -1,169 +0,0 @@ -/* - * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. - * Copyright (C) 2019-2022 HighCapable - * https://github.com/fankes/YukiHookAPI - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is Created by fankes on 2022/5/24. - */ -@file:Suppress("unused") - -package com.highcapable.yukihookapi.hook.core.reflex.parse - -import java.util.concurrent.ConcurrentHashMap - -/** - * 用来储存一个 APK 的 package 结构 - * - * 出于性能考虑 - 这个类不支持读线程和写线程同时操作 - 但支持同类型的线程同时操作 - * - * Contributed from [conan](https://github.com/meowool-catnip/conan) - */ -internal class ClassTrie internal constructor() { - - private companion object { - - /** - * 用来将 JVM 格式的类型标识符转换为类名 - * - * Example: String 的类型标识符为 "Ljava/lang/String;" - * - * [Refer](https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3) - * @return [String] - */ - private fun convertJVMTypeToClassName(type: String) = type.substring(1, type.length - 1).replace(oldChar = '/', newChar = '.') - } - - /** - * 读写开关 - 用于增强线程间的安全性 - * - * 只有开关设为 true 的时候 - 写操作才会被执行 - * - * 只有开关设为 false 的时候 - 读操作才会返回有效数据 - */ - @Volatile - internal var mutable = true - - /** - * package 结构的根结点 - * @return [TrieNode] - */ - private val root = TrieNode() - - /** - * 插入一个单独的 JVM 格式的类型标识符 - * @param type 类型 - */ - internal operator fun plusAssign(type: String) { - if (mutable) root.add(convertJVMTypeToClassName(type)) - } - - /** - * 插入一组 JVM 格式的类型标识符 - * @param types 类型数组 - */ - internal operator fun plusAssign(types: Array) = types.forEach { this += it } - - /** - * 查找指定包里指定深度的所有类 - * - * 出于性能方面的考虑 - 只有深度相等的类才会被返回 - 比如查找深度为 0 的时候 - 就只返回这个包自己拥有的类 - 不包括它里面其他包拥有的类 - * @param packageName 包名 - 默认为根包名 - * @param depth 深度 - * @return [List] 查找到的类名数组 - */ - internal fun search(packageName: String = "root", depth: Int): List { - if (mutable) return emptyList() - if (packageName == "root") return root.classes - return root.search(packageName, depth) - } - - /** - * 私有的节点结构 - */ - inner class TrieNode { - - /** 当前的 Class 实例数组 */ - internal val classes: MutableList = ArrayList(50) - - /** 当前的 Class 子实例数组 */ - private val children: MutableMap = ConcurrentHashMap() - - /** - * 添加节点 - * @param className 类名 - */ - internal fun add(className: String) = add(className, pos = 0) - - /** - * 获取节点下的类名数组 - * @param depth 深度 - * @return [List] - */ - internal fun get(depth: Int = 0): List { - if (depth == 0) return classes - return children.flatMap { it.value.get(depth - 1) } - } - - /** - * 查找当前包里指定深度的所有类 - * @param packageName 包名 - 默认为根包名 - * @param depth 深度 - * @return [List] 查找到的类名数组 - */ - internal fun search(packageName: String, depth: Int) = search(packageName, depth, pos = 0) - - /** - * 添加节点 - * @param className 类名 - * @param pos 下标 - */ - private fun add(className: String, pos: Int) { - val delimiterAt = className.indexOf(char = '.', pos) - if (delimiterAt == -1) { - synchronized(this) { classes.add(className) } - return - } - val pkg = className.substring(pos, delimiterAt) - if (pkg !in children) children[pkg] = TrieNode() - children[pkg]?.add(className, pos = delimiterAt + 1) - } - - /** - * 查找当前包里指定深度的所有类 - * @param packageName 包名 - 默认为根包名 - * @param depth 深度 - * @param pos 下标 - * @return [List] 查找到的类名数组 - */ - private fun search(packageName: String, depth: Int, pos: Int): List { - val delimiterAt = packageName.indexOf(char = '.', pos) - if (delimiterAt == -1) { - val pkg = packageName.substring(pos) - return children[pkg]?.get(depth) ?: emptyList() - } - val pkg = packageName.substring(pos, delimiterAt) - val next = children[pkg] ?: return emptyList() - return next.search(packageName, depth, pos = delimiterAt + 1) - } - } -} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexHeader.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexHeader.kt deleted file mode 100755 index 90ffb695..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexHeader.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. - * Copyright (C) 2019-2022 HighCapable - * https://github.com/fankes/YukiHookAPI - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is Created by fankes on 2022/5/24. - */ -@file:Suppress("unused") - -package com.highcapable.yukihookapi.hook.core.reflex.parse - -/** - * DEX 格式的文件头 - * - * 参考来源 [Dex Format](https://source.android.com/devices/tech/dalvik/dex-format) - * - * Contributed from [conan](https://github.com/meowool-catnip/conan) - */ -internal class DexHeader internal constructor() { - - internal var version = 0 - internal var checksum = 0u - internal var signature = ByteArray(kSHA1DigestLen) - internal var fileSize = 0u - internal var headerSize = 0u - internal var endianTag = 0u - internal var linkSize = 0u - internal var linkOff = 0u - internal var mapOff = 0u - internal var stringIdsSize = 0 - internal var stringIdsOff = 0u - internal var typeIdsSize = 0 - internal var typeIdsOff = 0u - internal var protoIdsSize = 0 - internal var protoIdsOff = 0u - internal var fieldIdsSize = 0 - internal var fieldIdsOff = 0u - internal var methodIdsSize = 0 - internal var methodIdsOff = 0u - internal var classDefsSize = 0 - internal var classDefsOff = 0u - internal var dataSize = 0 - internal var dataOff = 0u - - internal companion object { - - internal const val kSHA1DigestLen = 20 - internal const val kSHA1DigestOutputLen = kSHA1DigestLen * 2 + 1 - } -} \ No newline at end of file diff --git a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexParser.kt b/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexParser.kt deleted file mode 100644 index eee8e97d..00000000 --- a/yukihookapi/src/api/kotlin/com/highcapable/yukihookapi/hook/core/reflex/parse/DexParser.kt +++ /dev/null @@ -1,226 +0,0 @@ -/* - * YukiHookAPI - An efficient Kotlin version of the Xposed Hook API. - * Copyright (C) 2019-2022 HighCapable - * https://github.com/fankes/YukiHookAPI - * - * MIT License - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * This file is Created by fankes on 2022/5/24. - */ -@file:Suppress("unused") - -package com.highcapable.yukihookapi.hook.core.reflex.parse - -import java.io.UTFDataFormatException -import java.nio.Buffer -import java.nio.ByteBuffer -import java.nio.ByteOrder - -/** - * 封装对 DEX 格式数据的解析操作 - * - * 参考了 dongliu 的 [apk-parser](https://github.com/hsiafan/apk-parser) 项目 - * - * Contributed from [conan](https://github.com/meowool-catnip/conan) - * @param buffer DEX 字节流 - */ -internal class DexParser internal constructor(buffer: ByteBuffer) { - - /** - * 当前字节流 - * @return [ByteBuffer] - */ - private val buffer = buffer.duplicate().apply { order(ByteOrder.LITTLE_ENDIAN) } - - /** - * 读取 Bytes - * @param size 大小 - * @return [ByteArray] - */ - private fun ByteBuffer.readBytes(size: Int) = ByteArray(size).also { get(it) } - - /** - * 获取当前 package 下全部的类名 - * @return [Array] - */ - internal fun parseClassTypes(): Array { - // read magic - val magic = String(buffer.readBytes(8)) - if (magic.startsWith(prefix = "dex\n").not()) return arrayOf() - val version = magic.substring(4, 7).toInt() - // now the version is 035 - if (version < 35) { - // version 009 was used for the M3 releases of the Android platform (November–December 2007), - // and version 013 was used for the M5 releases of the Android platform (February–March 2008) - error("Dex file version: $version is not supported") - } - // read header - val header = readDexHeader() - header.version = version - // read string offsets - val stringOffsets = readStringOffsets(header.stringIdsOff, header.stringIdsSize) - // read type ids - val typeIds = readTypeIds(header.typeIdsOff, header.typeIdsSize) - // read class ids - val classIds = readClassIds(header.classDefsOff, header.classDefsSize) - // read class types - return Array(classIds.size) { i -> - val classId = classIds[i] - val typeId = typeIds[classId] - val offset = stringOffsets[typeId] - readStringAtOffset(offset) - } - } - - /** - * 获取 DEX 文件头 - * @return [DexHeader] - */ - private fun readDexHeader() = DexHeader().apply { - checksum = buffer.int.toUInt() - buffer.get(signature) - fileSize = buffer.int.toUInt() - headerSize = buffer.int.toUInt() - endianTag = buffer.int.toUInt() - linkSize = buffer.int.toUInt() - linkOff = buffer.int.toUInt() - mapOff = buffer.int.toUInt() - stringIdsSize = buffer.int - stringIdsOff = buffer.int.toUInt() - typeIdsSize = buffer.int - typeIdsOff = buffer.int.toUInt() - protoIdsSize = buffer.int - protoIdsOff = buffer.int.toUInt() - fieldIdsSize = buffer.int - fieldIdsOff = buffer.int.toUInt() - methodIdsSize = buffer.int - methodIdsOff = buffer.int.toUInt() - classDefsSize = buffer.int - classDefsOff = buffer.int.toUInt() - dataSize = buffer.int - dataOff = buffer.int.toUInt() - } - - /** - * 读取字符串偏移量 - * @param stringIdsOff 偏移量 - * @param stringIdsSize 偏移大小 - * @return [IntArray] - */ - private fun readStringOffsets(stringIdsOff: UInt, stringIdsSize: Int): IntArray { - (buffer as Buffer).position(stringIdsOff.toInt()) - return IntArray(stringIdsSize) { buffer.int } - } - - /** - * 读取 Ids 类型偏移量 - * @param typeIdsOff 偏移量 - * @param typeIdsSize 偏移大小 - * @return [IntArray] - */ - private fun readTypeIds(typeIdsOff: UInt, typeIdsSize: Int): IntArray { - (buffer as Buffer).position(typeIdsOff.toInt()) - return IntArray(typeIdsSize) { buffer.int } - } - - /** - * 读取 Class Ids 偏移量 - * @param classDefsOff 偏移量 - * @param classDefsSize 偏移大小 - * @return [Array] - */ - private fun readClassIds(classDefsOff: UInt, classDefsSize: Int): Array { - (buffer as Buffer).position(classDefsOff.toInt()) - return Array(classDefsSize) { - val classIdx = buffer.int - // access_flags, skip - buffer.int - // superclass_idx, skip - buffer.int - // interfaces_off, skip - buffer.int - // source_file_idx, skip - buffer.int - // annotations_off, skip - buffer.int - // class_data_off, skip - buffer.int - // static_values_off, skip - buffer.int - classIdx - } - } - - /** - * 读取偏移量的字符串 - * @param offset 偏移量 - * @return [String] - */ - private fun readStringAtOffset(offset: Int): String { - (buffer as Buffer).position(offset) - return readString(readULEB128Int()) - } - - /** - * 读取 ULEB128 整型 - * @return [Int] - */ - private fun readULEB128Int(): Int { - var ret = 0 - var count = 0 - var byte: Int - do { - if (count > 4) error("read varints error.") - byte = buffer.get().toInt() - ret = ret or (byte and 0x7f shl count * 7) - count++ - } while (byte and 0x80 != 0) - return ret - } - - /** - * 读取字符串 - * @param len 长度 - * @return [String] - */ - private fun readString(len: Int): String { - val chars = CharArray(len) - for (i in 0 until len) { - val byte = buffer.get().toInt() - when { - // ascii char - byte and 0x80 == 0 -> chars[i] = byte.toChar() - // read one more - byte and 0xe0 == 0xc0 -> { - val b = buffer.get().toInt() - chars[i] = (byte and 0x1F shl 6 or (b and 0x3F)).toChar() - } - byte and 0xf0 == 0xe0 -> { - val b = buffer.get().toInt() - val c = buffer.get().toInt() - chars[i] = (byte and 0x0F shl 12 or (b and 0x3F shl 6) or (c and 0x3F)).toChar() - } - else -> throw UTFDataFormatException() - } - } - return String(chars) - } -} \ No newline at end of file