将APK转换为最小APK后如何解析?

问题描述 投票:0回答:1

背景

我希望能够解析来自各种来源或各种来源的APK文件。我只想获得关于它们的非常具体的基本信息:

  • 程序包名称
  • 版本代码
  • 版本名称
  • 标签(应用名称)
  • icon
  • minSdkVersion
  • 是否为分割APK(解析清单时可用)

Android框架具有解析APK文件(PackageManager.getPackageArchiveInfo)的单一功能,但有2个缺点:

  1. 它需要一个文件路径。这并不总是可用,因为您可能需要处理Uri,或者您的APK文件位于某个ZIP文件中。

  2. 它无法处理拆分的APK文件。只有其中之一。如果在拆分的APK文件上尝试使用,则只会得到null。

因此,我试图找到一种可靠的Java&Kotlin解决方案,可以轻松处理它们。

问题

我发现的每个解决方案都有其缺点。有些库我什至找不到使用方法,有些库在没有真实文件路径的情况下无法解析APK文件。其中一些只是创建新文件,而不是用Java / Kotlin返回对象。有些甚至还使用其他编程语言,这使我想知道是否可以使用它们。

到目前为止,我发现唯一足够出色的是"hsiafan" apk-parser,它在APK中仅需要两个文件:清单文件和资源文件。它有一些问题,但通常它可以为您提供我所提到的信息。

[正如我所写,它并不总是有效,所以我想回到基础知识,至少在我注意到它无法解析的情况下,至少在分割APK文件为标准APK的情况下。它不能很好处理的一些情况是:

  1. 有时无法正确获取应用名称(here。]
  2. 某些资源以某种方式隐藏在某些应用程序(here)中。甚至Jadx之类的工具也无法显示它们,但其他工具则可以。
  3. 自适应图标很难解析,包括可绘制的XML文件。我虽然成功解析了VectorDrawable(here),但这是一种解决方法。

和其他一些。

我尝试过的

所以,我想再次尝试Android框架,但是这次有了一个新的主意:除了处理整个原始APK,我还可以根据当前的观察结果仅提取它真正需要的内容。

例如,如果我知道需要处理的资源,则只能将它们(以及一些需要的关键文件)复制到新的APK中,然后放弃其余的。这样可以减少仅解析APK并获取有关信息的复制/下载大量数据的需求。例如,如果APK文件为100MB,则当我们只需要一小部分文件时,就无需全部获取它。

首先,我想看看如何创建可以解析的新APK,所以现在我将原始APK(当前为当前应用程序)的所有条目复制到一个新文件中,然后选择解析它:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState != null)
            return
        thread {
            val originalApplicationInfo = packageManager.getApplicationInfo(packageName, 0)
            val filePath = originalApplicationInfo.publicSourceDir
            val outputFile = File(cacheDir, "test.apk")
            outputFile.parentFile!!.mkdirs()
            outputFile.delete()
            ZipFile(filePath).use { zipFile ->
                ZipOutputStream(FileOutputStream(outputFile)).use { zipOutputStream ->
                    for (entry in zipFile.entries()) {
                        val name = entry.name
                        zipOutputStream.putNextEntry(ZipEntry(name))
                        zipFile.getInputStream(entry).use { it.copyTo(zipOutputStream.buffered()) }
                        zipOutputStream.closeEntry()
                    }
                }
            }
            val originalLabel = originalApplicationInfo.loadLabel(packageManager)
            val originalIcon: Drawable? = originalApplicationInfo.loadIcon(packageManager)
            Log.d("AppLog", "originalPackageInfo: label:$originalLabel appIcon:${originalIcon?.javaClass?.simpleName}")
            //
            val packageArchiveInfo = packageManager.getPackageArchiveInfo(outputFile.absolutePath, 0)
            val label = packageArchiveInfo?.applicationInfo?.loadLabel(packageManager)?.toString()
            val appIcon = packageArchiveInfo?.applicationInfo?.loadIcon(packageManager)
            Log.d("AppLog", "packageArchiveInfo!=null?${packageArchiveInfo != null} label:$label appIcon:${appIcon?.javaClass?.simpleName}")
        }
    }
}

确实生成了文件,但是由于packageArchiveInfo为空,由于某些原因,Android框架无法解析它。

问题

  1. 我制作的样品为什么不起作用?为什么新的APK无法解析?
  2. 仅出于我上面提到的信息,getPackageArchiveInfo函数需要至少多少个文件集才能解析APK?
  3. 如果这种解决方案不起作用,也许有一个库可以处理带有我提到的所有信息的各种APK文件,无论其来源(包括Uri和zip文件中的内容)?
android apk android-package-managers
1个回答
0
投票

更改复制代码自

zipFile.getInputStream(entry).use { it.copyTo(zipOutputStream.buffered()) }

to

zipFile.getInputStream(entry).use {
    val bufStream = zipOutputStream.buffered()
    it.copyTo(bufStream)
    bufStream.flush()
}

或类似的方法,以确保所有数据都被写出。

我以为APK无法通过证书验证,但事实并非如此。前述内容将为您提供标签和图标。

使用原始代码,我看到了以下错误

块大小大于给定数据 无法在APK'/ data / user / 0 / com中加载'resources.arsc'

这向我表明输出出现了问题。通过上述更改,我看到了以下内容:

originalPackageInfo:标签:APK复制并读取appIcon:AdaptiveIconDrawable packageArchiveInfo!= null?true标签:APK复制并读取appIcon:AdaptiveIconDrawable

[如果我从Android Studio中的设备资源管理器中打开创建的APK,它将很好地解析。我确实注意到每个文件的“原始文件大小”反映了“ test.apk”的压缩大小,而“ base.apk”则显示了真正的未压缩大小。

base.apkbase.apk

test.apkenter image description here

© www.soinside.com 2019 - 2024. All rights reserved.