6.6.3 - Alpha5 - 提升 APK 文件签名信息检测效率

This commit is contained in:
SuperMonster003
2025-05-22 15:49:54 +08:00
parent 3d8e3e28eb
commit e4437fc935
19 changed files with 117 additions and 60 deletions

View File

@@ -44,6 +44,8 @@
* `优化` 已忽略更新记录及客户端模式连接地址记录支持清空操作
* `优化` 版本更新信息支持多语言显示 (与当前显示语言同步)
* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性
* `优化` APK 文件签名信息提升检测效率
* `优化` APK 文件类型信息及媒体文件类型信息优化对话框显示效率及信息展示逻辑
* `优化` Gradle 构建脚本提升版本自适应能力 _[`discussion #369`](http://discussions.autojs6.com/369)_
* `依赖` 本地化 Material Dialogs 版本 0.9.6.0
* `依赖` 本地化 Material Date Time Picker 版本 4.2.3

View File

@@ -44,6 +44,8 @@
* `优化` 已忽略更新记录及客户端模式连接地址记录支持清空操作
* `优化` 版本更新信息支持多语言显示 (与当前显示语言同步)
* `优化` 使用异步加载方式一定程度提升文件管理器列表滑动流畅性
* `优化` APK 文件签名信息提升检测效率
* `优化` APK 文件类型信息及媒体文件类型信息优化对话框显示效率及信息展示逻辑
* `优化` Gradle 构建脚本提升版本自适应能力 _[`discussion #369`](http://discussions.autojs6.com/369)_
* `依赖` 本地化 Material Dialogs 版本 0.9.6.0
* `依赖` 本地化 Material Date Time Picker 版本 4.2.3

View File

@@ -13,7 +13,6 @@ import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.afollestad.materialdialogs.DialogAction
import com.afollestad.materialdialogs.MaterialDialog
import com.android.apksig.ApkVerifier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
@@ -34,8 +33,6 @@ import org.autojs.autojs.util.IntentUtils.ToastExceptionHolder
import org.autojs.autojs6.R
import org.autojs.autojs6.databinding.ApkFileInfoDialogListItemBinding
import java.io.File
import java.util.jar.JarEntry
import java.util.jar.JarFile
object ApkInfoDialogManager {
@@ -229,37 +226,17 @@ object ApkInfoDialogManager {
}
private fun getApkSignatureInfo(apkFile: File): String? = runCatching {
ApkVerifier.Builder(apkFile).build().verify().run {
listOfNotNull(
"V1".takeIf { isVerifiedUsingV1Scheme || hasV1Signature(apkFile) },
"V2".takeIf { isVerifiedUsingV2Scheme },
"V3".takeIf { isVerifiedUsingV3Scheme },
"V4".takeIf { isVerifiedUsingV4Scheme },
).takeUnless { it.isEmpty() }?.joinToString(" + ")
}
// ApkVerifier.Builder(apkFile).build().verify().run {
// listOfNotNull(
// "V1".takeIf { isVerifiedUsingV1Scheme || hasV1Signature(apkFile) },
// "V2".takeIf { isVerifiedUsingV2Scheme },
// "V3".takeIf { isVerifiedUsingV3Scheme },
// "V4".takeIf { isVerifiedUsingV4Scheme },
// ).takeUnless { it.isEmpty() }?.joinToString(" + ")
// }
ApkSignatureDetector.detectSchemes(apkFile)
}.getOrNull()
private fun hasV1Signature(apkFile: File): Boolean {
if (!apkFile.isFile) return false
JarFile(apkFile).use { jar ->
var hasManifest = false
var hasSF = false
var hasRSA = false
for (entry: JarEntry in jar.entries()) {
val name = entry.name.uppercase()
when {
name == "META-INF/MANIFEST.MF" -> hasManifest = true
name.endsWith(".SF") && name.startsWith("META-INF/") -> hasSF = true
name.endsWith(".RSA") && name.startsWith("META-INF/") -> hasRSA = true
}
if (hasManifest && hasSF && hasRSA) {
return true
}
}
return false
}
}
private data class ApkInfo(
val label: String?,
val packageName: String?,

View File

@@ -0,0 +1,69 @@
package org.autojs.autojs.ui.main.scripts
import com.android.apksig.apk.ApkUtils
import com.android.apksig.internal.apk.ApkSigningBlockUtils
import com.android.apksig.util.DataSources
import java.io.File
import java.io.RandomAccessFile
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.jar.JarEntry
import java.util.jar.JarFile
object ApkSignatureDetector {
private const val BLOCK_ID_V2 = 0x7109871a
private const val BLOCK_ID_V3 = 0xF05368C0.toInt()
private const val BLOCK_ID_V4 = 0x2b09189e
fun detectSchemes(apkFile: File): String? {
if (!apkFile.isFile) return null
RandomAccessFile(apkFile, "r").use { raf ->
val dataSource = DataSources.asDataSource(raf, 0, raf.length())
val signingBlock = ApkUtils.findApkSigningBlock(dataSource).contents
val signingBlockBuffer = signingBlock.getByteBuffer(0, signingBlock.size().toInt())
// Duplicate() is called for each detection to prevent position being modified by previous checks.
// zh-CN: 每次检测都 duplicate(), 防止 position 被前一次修改.
val hasV2 = hasScheme(signingBlockBuffer.duplicateAndOrderSignBuffer(), BLOCK_ID_V2)
val hasV3 = hasScheme(signingBlockBuffer.duplicateAndOrderSignBuffer(), BLOCK_ID_V3)
val hasV4 = hasScheme(signingBlockBuffer.duplicateAndOrderSignBuffer(), BLOCK_ID_V4)
val hasV1 = hasV1Signature(apkFile)
return listOfNotNull(
"V1".takeIf { hasV1 },
"V2".takeIf { hasV2 },
"V3".takeIf { hasV3 },
"V4".takeIf { hasV4 },
).takeUnless { it.isEmpty() }?.joinToString(" + ")
}
}
private fun ByteBuffer.duplicateAndOrderSignBuffer(): ByteBuffer {
return duplicate().order(ByteOrder.LITTLE_ENDIAN)
}
private fun hasScheme(buf: ByteBuffer, id: Int): Boolean = runCatching {
ApkSigningBlockUtils.findApkSignatureSchemeBlock(buf, id, null)
}.isSuccess
private fun hasV1Signature(apkFile: File): Boolean {
JarFile(apkFile).use { jar ->
var hasManifest = false
var hasSF = false
var hasRSA = false
for (entry: JarEntry in jar.entries()) {
val n = entry.name.uppercase()
when {
n == "META-INF/MANIFEST.MF" -> hasManifest = true
n.startsWith("META-INF/") && n.endsWith(".SF") -> hasSF = true
n.startsWith("META-INF/") && n.endsWith(".RSA") -> hasRSA = true
}
if (hasManifest && hasSF && hasRSA) return true
}
}
return false
}
}