使用 root 安装 APK,处理“/data/local/tmp/”文件夹的新限制
Posted
技术标签:
【中文标题】使用 root 安装 APK,处理“/data/local/tmp/”文件夹的新限制【英文标题】:Install APK using root, handling new limitations of "/data/local/tmp/" folder 【发布时间】:2018-11-05 12:15:11 【问题描述】:背景
到目前为止,我可以通过以下代码使用 root(在应用程序内)安装 APK 文件:
pm install -t -f fullPathToApkFile
如果我想(尝试)安装到 sd 卡:
pm install -t -s fullPathToApkFile
问题
最近,不确定来自哪个 android 版本(至少在 Android P beta 上存在问题),上述方法失败,向我显示此消息:
avc: denied read for scontext=u:r:system_server:s0 tcontext=u:object_r:sdcardfs:s0 tclass=file permissive=0
System server has no access to read file context u:object_r:sdcardfs:s0 (from path /storage/emulated/0/Download/FDroid.apk, context u:r:system_server:s0)
Error: Unable to open file: /storage/emulated/0/Download/FDroid.apk
Consider using a file under /data/local/tmp/
Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
Exception occurred while executing:
java.lang.IllegalArgumentException: Error: Can't open file: /storage/emulated/0/Download/FDroid.apk
at com.android.server.pm.PackageManagerShellCommand.setParamsSize(PackageManagerShellCommand.java:306)
at com.android.server.pm.PackageManagerShellCommand.runInstall(PackageManagerShellCommand.java:884)
at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:138)
at android.os.ShellCommand.exec(ShellCommand.java:103)
at com.android.server.pm.PackageManagerService.onShellCommand(PackageManagerService.java:21125)
at android.os.Binder.shellCommand(Binder.java:634)
at android.os.Binder.onTransact(Binder.java:532)
at android.content.pm.IPackageManager$Stub.onTransact(IPackageManager.java:2806)
at com.android.server.pm.PackageManagerService.onTransact(PackageManagerService.java:3841)
at android.os.Binder.execTransact(Binder.java:731)
这似乎也影响了热门应用,例如“Titanium backup (pro)”,无法恢复应用。
我尝试过的
查看所写的内容,它似乎没有安装/data/local/tmp/
中不存在的APK 文件的权限。
所以我尝试了接下来的事情,看看我是否可以克服它:
-
设置对文件的访问权限 (
chmod 777
) - 没有帮助。
向我的应用授予存储权限和REQUEST_INSTALL_PACKAGES(使用ACTION_MANAGE_UNKNOWN_APP_SOURCES Intent)权限 - 没有帮助。
使用官方 API 创建文件的符号链接,使其位于 /data/local/tmp/
内:
Os.symlink(fullPathToApkFile, symLinkFilePath)
这没有做任何事情。
使用此创建符号链接:
ln -sf $fullPathToApkFile $symLinkFilePath
这部分工作。该文件在那里,我可以在 Total Commander 应用程序中看到它,但是当我尝试检查它是否存在时,并且当我尝试从那里安装 APK 时,它失败了。
将文件复制/移动(使用cp
或mv
)到/data/local/tmp/
路径,然后从那里安装。这行得通,但它有缺点:移动是有风险的,因为它会暂时隐藏原始文件,并且会更改原始文件的时间戳。复制很糟糕,因为仅用于安装(即使是临时的)占用了额外的空间,而且这样做会浪费时间。
复制APK文件,告诉它避免实际复制(意味着硬链接),使用这个命令(取自here):
cp -p -r -l $fullPathToApkFile $tempFileParentPath"
这不起作用。它给了我这个错误:
cp: /data/local/tmp/test.apk: Cross-device link
检查在安装应用程序的其他情况下会发生什么。当您通过 IDE 安装时,它实际上会在此特殊路径中创建 APK 文件,但如果您通过 Play Store、简单 APK 安装(通过 Intent)或 adb(通过 PC)安装,则不会。
在这里也写过这个:https://issuetracker.google.com/issues/80270303
问题
有没有办法克服在这个特殊路径上使用 root 安装 APK 的缺点?甚至可以完全避免处理这条路径?
为什么操作系统突然需要使用这个路径?为什么不使用原始路径,就像安装应用程序的其他方法一样?安装应用程序的其他方法有什么作用,以某种方式避免使用空间路径?
【问题讨论】:
【参考方案1】:如果您不介意移动过程,一个解决方案是同时保存和恢复原始文件的时间戳,如下所示:
val tempFileParentPath = "/data/local/tmp/"
val tempFilePath = tempFileParentPath + File(fullPathToApkFile).name
val apkTimestampTempFile = File(context.cacheDir, "apkTimestamp")
apkTimestampTempFile.delete()
apkTimestampTempFile.mkdirs()
apkTimestampTempFile.createNewFile()
root.runCommands("touch -r $fullPathToApkFile $apkTimestampTempFile.absolutePath")
root.runCommands("mv $fullPathToApkFile $tempFileParentPath")
root.runCommands("pm install -t -f $tempFilePath")
root.runCommands("mv $tempFilePath $fullPathToApkFile")
root.runCommands("touch -r $apkTimestampTempFile.absolutePath $fullPathToApkFile")
apkTimestampTempFile.delete()
还是有点危险,不过比复制文件好……
编辑:谷歌向我展示了一个很好的解决方法(here):
我们不支持从设备上的随机目录安装 APK。它们要么需要使用“adb install”直接从主机安装,要么您必须流式传输内容才能安装--
$ cat foo.apk | pm install -S APK_SIZE
虽然我认为他们不支持从随机路径安装 APK 文件(以前一直有效)是不正确的,但解决方法似乎确实有效。我需要在安装 APK 文件的代码中进行更改:
val length = File(fullPathToApkFile ).length()
commands.add("cat $fullPathToApkFile | pm install -S $length")
问题是,现在我还有一些其他问题:
-
此解决方法是否可以避免将 APK 移动/复制到存储中,并且不会影响原始文件? - 好像是的
这是否支持任何 APK 文件,甚至是大文件? - 对于占用 433MB 的 APK,它似乎成功了,所以我认为它可以安全地用于所有大小。
只有 Android P 才需要,对吧? - 到目前为止似乎如此。
为什么需要文件大小作为参数? - 不知道,但如果我删除它,它将无法工作
【讨论】:
我知道这是旧的但是你如何执行这个命令。我尝试将其传递为Runtime.getRuntime().exec(commands.toArray())
。
我的应用是具有系统权限的系统应用,但运行此命令时出现错误提示“无法运行此程序”
如果您可以在这里查看我的问题,那将非常有帮助:- ***.com/questions/59089567/…
@noname 抱歉,我不知道系统应用程序是如何做到的。其实我一直不明白如何在Android Q上转换成系统应用。你是怎么做到的?【参考方案2】:
感谢您的回答!我还在其他地方寻找了完整的 OTA 设置以适用于 Android 10 等等。它 100% 可在运行 Android 10 的三星 Galaxy Tab 10.1 上运行。
这是一篇带有代码的中型文章: https://medium.com/@jnishu1996/over-the-air-ota-updates-for-android-apps-download-apk-silent-apk-installation-auto-launch-8ee6f342197c
神奇之处在于以 root 访问权限运行此命令:
process = Runtime.getRuntime().exec("su");
out = process.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
// Get all file permissions
dataOutputStream.writeBytes("chmod 777 " + file.getPath() + "\n");
// Perform silent installation command, all flags are necessary for some reason, only this works reliably post Android 10
String installCommand = "cat " + file.getAbsolutePath() + "| pm install -d -t -S " + file.length();
// Data to send to the LaunchActivity to the app knows it got updated and performs necessary functions to notify backend
// es stands for extraString
// In LaunchActivity onCreate(), you can get this data by running -> if (getIntent().getStringExtra("OTA").equals("true"))
String launchCommandIntentArguments = "--es OTA true --es messageId " + MyApplication.mLastSQSMessage.receiptHandle();
// Start a background thread to wait for 8 seconds before reopening the app's LaunchActivity, and pass necessary arguments
String launchCommand = "(sleep 8; am start -n co.getpresso.Presso/.activities.LaunchActivity " + launchCommandIntentArguments + ")&";
// The entire command is deployed with a ";" in the middle to launchCommand run after installCommand
String installAndLaunchCommand = installCommand + "; " + launchCommand;
// begins the installation
dataOutputStream.writeBytes(installAndLaunchCommand);
dataOutputStream.flush();
// Close the stream operation
dataOutputStream.close();
out.close();
int value = process.waitFor();
【讨论】:
以上是关于使用 root 安装 APK,处理“/data/local/tmp/”文件夹的新限制的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向获取安装在手机中的应用的 APK 包 ( 进入 adb shell | 获取 root 权限 | 进入 /data/app/ 目录 | 拷贝 base.apk 到外置存储 )(代码
想把E盘的apk文件用adb批处理安装到手机 system/app下怎么实现