Android系统Root与静默安装
Android系統(tǒng)Root與靜默安裝
靜默安裝,指的是安裝時無需任何用戶干預(yù),直接按默認(rèn)設(shè)置安裝應(yīng)用。因?yàn)?#xff0c;它的無需用戶干預(yù),很多情況下變成了用戶壓根不知道,應(yīng)用不知不覺就安裝上了。是在推廣上極為流氓的手段,很類似PC上的捆綁安裝。正因?yàn)殪o默安裝時極為流氓的推廣行為,所以,其推廣價格也極其高。
Android應(yīng)用安裝有如下四種方式
| 系統(tǒng)應(yīng)用安裝 | 開機(jī)時完成,需要加入開機(jī)執(zhí)行的腳本,沒有安裝界面 |
| 網(wǎng)絡(luò)下載應(yīng)用安裝 | 通過系統(tǒng)market應(yīng)用完成,沒有安裝界面 |
| ADB工具中進(jìn)行安裝 | 使用pm install命令,沒有安裝界面。 |
| 第三方應(yīng)用安裝 | 通過SD卡里的APK文件安裝,有安裝界面,由PackageInstaller.apk應(yīng)用處理安裝及卸載過程的界面。 |
應(yīng)用安裝的流程及路徑
| /system/app | 系統(tǒng)自帶的應(yīng)用程序存放,Root權(quán)限才可更改 |
| /data/app | 用戶程序安裝的目錄,有刪除權(quán)限。安裝時把a(bǔ)pk文件復(fù)制到此目錄 |
| /data/data | 存放應(yīng)用程序的數(shù)據(jù) |
| Data/dalvik-cache | 將apk中的dex文件安裝到dalvik-cache目錄下 |
安裝過程
復(fù)制APK安裝包到data/app目錄下,解壓并掃描安裝包,把dex文件(Dalvik字節(jié)碼)保存到dalvik-cache目錄,并data/data目錄下創(chuàng)建對應(yīng)的應(yīng)用數(shù)據(jù)目錄。
卸載過程
刪除安裝過程中在上述三個目錄下創(chuàng)建的文件及目錄。
權(quán)限聲明
Google的安全策略要求任何應(yīng)用在安裝確認(rèn)的時候應(yīng)該提示APK安裝包的權(quán)限,即確認(rèn)開發(fā)者在AndroidManafest.xml中聲明的權(quán)限。當(dāng)然,Google在Android上也做了一些操作,允許一些系統(tǒng)內(nèi)部的應(yīng)用不經(jīng)過授權(quán)界面直接進(jìn)行安裝。而系統(tǒng)進(jìn)入安裝界面其實(shí)也是根據(jù)此intent跳轉(zhuǎn)到了PackageInstaller應(yīng)用來完成權(quán)限的提示與安裝的。
這里寫代碼片`我們在應(yīng)用程序中控制安裝應(yīng)用APP,其實(shí)就是發(fā)送一個如下的intent。去調(diào)用packageinstaller進(jìn)行安裝,具體的操作代碼如下:
/* 安裝apk */ Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setDataAndType(Uri.parse("file://"+ fileName),"application/vnd.android.package-archive"); context.startActivity(intent);對比應(yīng)用正常安裝的流程,靜默安裝的本質(zhì)就是去掉如下圖所示的用戶授權(quán)同意安裝的過程,直接進(jìn)行應(yīng)用安裝。
源碼分析
閱讀過源碼后我們知道,系統(tǒng)的安裝過程其實(shí)是調(diào)用了系統(tǒng)中的PackageInstaller來完成的。希望做到靜默安裝,就是找到一個方法,繞過PackageInstaller中的權(quán)限授予提示,繼續(xù)完成安裝的步驟。
所以,思路很簡單,我們可以從兩方面去操作:
- 找到PackageInstaller源碼,跳過權(quán)限授予提醒,直接調(diào)用后面的安裝API即可完成安裝。(這樣能夠良好的兼容正常安裝,不易出錯)
- 使用pm install 命令進(jìn)行安裝。
調(diào)用PackageInstaller中隱藏的API
查看PackageInstaller源碼我們能夠發(fā)現(xiàn),其實(shí)PackageInstaller也是通過使用PackageManager進(jìn)行安裝的。調(diào)用的是其installPackage方法,但是此方法是一個abstract,且是對外不可見的(hide),
定義如下所示:
public abstractclass PackageManager { ……… /*** 安裝應(yīng)用APK文件* @param packageURI 待安裝的APK文件位置,可以是'file:'或'content:' URI.* @param observer 一個APK文件安裝狀態(tài)的觀察器* @param flags 安裝形式 INSTALL_FORWARD_LOCK, INSTALL_REPLACE_EXISTING, INSTALL_ALLOW_TEST.* @paraminstallerPackageName APK安裝包的PackageName*/ // @SystemApi public abstract void installPackage(UripackageURI, PackageInstallObserverobserver,int flags,StringinstallerPackageName); }且PackageManager與installPackage兩者皆為abstract抽象的。其具體實(shí)現(xiàn)都在ApplicationPackageManager中,其installPackage中的實(shí)現(xiàn)為:
final classApplicationPackageManager extends PackageManager {......ApplicationPackageManager(ContextImpl context, IPackageManager pm) {mContext = context;mPM = pm;}@Overridepublic void installPackage(Uri packageURI, IPackageInstallObserver observer,intflags, String installerPackageName){try {mPM.installPackage(packageURI, observer, flags, installerPackageName);} catch (RemoteException e) {// Should never happen!}} }可見調(diào)用的installPackage方法為IPackageManager中的installPackage方法。在ContextImpl中通過調(diào)用
ActivityThread.getPackageManager()獲得IPackageManager實(shí)例對象。而在在ActivityThread.getPackageManager()方法中,是調(diào)用SystemService中的名為package的Service來實(shí)例化的。代碼如下:
因?yàn)?#xff0c;installPackage是系統(tǒng)的API,為了使用PackageManagerService.installPackage(),考慮通過反射機(jī)制可以調(diào)用installPackage()。
但其中難以得到的是其參數(shù)中的IPackageInstallObserver類型,我們看來一下IPackageInstallObserver,發(fā)現(xiàn)IPackageInstallObserver是由aidl文件定義的。這個也難不倒我們,通過aidl文件的特性,將IPackageInstallObserver.aidl文件拷到本地程序中,可以得到類IPackageInstallObserver.calss,通過它反射出installPackage()方法。
但在invoke調(diào)用該方法時,卻無法得到IPackageInstallObserver的實(shí)例對象,IPackageInstallObserver的實(shí)例對象必須通過IPackageInstallObserver.Stub.asInterface(Binder binder)方式得到,無法得到與其綁定的Binder對象,因而無法執(zhí)行反射出來的方法。
其次,應(yīng)為是系統(tǒng)API,需要聲明安裝應(yīng)用的權(quán)限:android.permission.INSTALL_PACKAGES。當(dāng)時這類比較敏感的權(quán)限不是說聲明系統(tǒng)就會給予的,還需要我們的安裝包APK文件擁有與系統(tǒng)相同的簽名,才能完成靜默安裝操作。這個方式的靜默安裝,對于廣泛的推廣應(yīng)用是不現(xiàn)實(shí)的。
使用pm命令安裝
pm 命令是Android里面PackageManage的命令行,用于安裝包的操作。而系統(tǒng)也主要是提供我們在adb
shell中進(jìn)行使用pm命令,因此pm命令也存在與“/system”目錄下,當(dāng)然,擁有了Root權(quán)限后的應(yīng)用程序就能夠使用它進(jìn)行靜默安裝了。
具體的操作代碼如下所示:
// xxx.apk放置在內(nèi)置儲存的根目錄下 execCommand("system/bin/pminstall -r " + "sdcard/xxx.apk"); // 執(zhí)行command public booleanexecCommand(String cmd) {Process process = null;try {process = Runtime.getRuntime().exec(cmd);process.waitFor();} catch (Exception e) {return false;} finally {try {process.destroy();} catch (Exception e) {}}return true; }pm命令源碼目錄:
/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java,
我們查看其源碼,如下:
public finalclass Pm {IPackageManager mPm;IUserManager mUm;private WeakHashMap<String, Resources> mResourceCache= new WeakHashMap<String, Resources>();private String[] mArgs;private int mNextArg;private String mCurArgData;private static final String PM_NOT_RUNNING_ERR ="Error: Could not access thePackage Manager. Is the systemrunning?";public static void main(String[] args) {new Pm().run(args);}/*** 解析命令參數(shù)* @param args 參數(shù)*/public void run(String[] args) {boolean validCommand = false;if (args.length < 1) {showUsage();return;}mUm =IUserManager.Stub.asInterface(ServiceManager.getService("user"));mPm =IPackageManager.Stub.asInterface(ServiceManager.getService("package"));if (mPm == null) {System.err.println(PM_NOT_RUNNING_ERR);return;}......if("install".equals(op)) {runInstall();return;}......}/*** 開始安裝*/private void runInstall() {......// 安裝邏輯的具體調(diào)用PackageInstallObserver obs = newPackageInstallObserver();try {VerificationParamsverificationParams = new VerificationParams(verificationURI,originatingURI, referrerURI, VerificationParams.NO_UID,null);mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,installerPackageName, verificationParams, encryptionParams);synchronized (obs) {while (!obs.finished) {try {obs.wait();} catch(InterruptedException e) {}}if (obs.result ==PackageManager.INSTALL_SUCCEEDED) {System.out.println("Success");} else {System.err.println("Failure["+installFailureToString(obs.result)+ "]");}}} catch (RemoteException e) {System.err.println(e.toString());System.err.println(PM_NOT_RUNNING_ERR);}}...... }發(fā)現(xiàn)其實(shí)pm命令也是調(diào)用了PackageManager中的安裝方法,只不過是一個驗(yàn)證和加密的方法installPackageWithVerificationAndEncryption進(jìn)行安裝的。即,它的安裝過程與PackageInstaller是一樣的。
而我們安裝應(yīng)用APP的時候,可以是自己的APK安裝包文件存儲在兩個地方“data/app”與“system/app”下,靜默安裝的時候一般情況都是選擇將自己的APK文件push到“system/app”目錄下, 因?yàn)榇四夸浭窍到y(tǒng)應(yīng)用的目錄,在此目錄下的惡意應(yīng)用,進(jìn)行偷發(fā)短信、竊取郵件等操作,用戶是很難察覺的。
刪除預(yù)裝
大部分的普通用戶Root手機(jī)的主要目的就是刪除系統(tǒng)預(yù)先安裝的應(yīng)用程序,要刪除它們,我們首先要知道什么是預(yù)裝應(yīng)用,它們存放在哪里。或者我們換一個思路來看看,系統(tǒng)制造商將應(yīng)用程序的APK文件存放在哪里才能變?yōu)橄到y(tǒng)的應(yīng)用。
1. 系統(tǒng)默認(rèn)的常規(guī)應(yīng)用存放處
Android系統(tǒng)的捆綁應(yīng)用軟件基本安裝在“/system/app”文件夾下,刪除下面的對應(yīng)的了第三方軟件APK文件即可完美卸載。我們知道“/system”是系統(tǒng)的目錄,對此目錄進(jìn)行操作需要Root權(quán)限,所以我們刪除預(yù)裝應(yīng)用需要Root手機(jī)。每個系統(tǒng)程序基本上都是成對的,對應(yīng)的刪除掉后綴分別是.apk 和.odex(優(yōu)化過的dex文件)文件即可刪除預(yù)裝應(yīng)用。
如下圖,使用了Root Explorer查看“/system/app”目錄。則能夠看到了系統(tǒng)中的所有的系統(tǒng)內(nèi)置應(yīng)用程序。
2. 修改系統(tǒng)引導(dǎo)的預(yù)裝
對于存放Apk文件到”/system/app“目錄下已經(jīng)是很普通的預(yù)裝方式了,這就導(dǎo)致了,預(yù)裝應(yīng)用很容易就被卸載掉。惡意的手機(jī)ROM就會想著更加惡心的方法來留住預(yù)裝應(yīng)用,比如修改系統(tǒng)ROM的邏輯,讓系統(tǒng)在開機(jī)的時候檢測一下自己的預(yù)裝是否完整然后重新安裝。那么,當(dāng)然系統(tǒng)預(yù)裝應(yīng)用的安裝文件也會在另一個保存一份。
這類的預(yù)裝應(yīng)用,又稱為“開機(jī)靜默安裝”,常用的方式就是修改init.rc,添加一個開機(jī)執(zhí)行的腳本,在腳本中調(diào)用一個Service使用pm install命令批量安裝應(yīng)用。
如,自頂一個一個init.local.rc內(nèi)容如下:
在系統(tǒng)的init.rc腳本中調(diào)用init.local.rc如下:
#在sysinit前面加 # Include extrainit fileimport /system/etc/init.local.rc而具體的預(yù)裝腳本存在/system/bin/ loadpreinstalls.sh# do preinstall job if [ ! -e /data/.notfirstrun ] then echo "dopreinstall sys" >> /system/log.txt #安裝/system/preinstall下的所有apk文件APKLIST=`ls /system/preinstall/*.apk`for INFILES in $APKLISTdoecho setup package:$INFILESpm install -r $INFILESdoneecho "dopreinstall sd" >> /system/log.txt #安裝/sdcard/preinstall下的所有apk文件APKLIST=`ls /sdcard/preinstall/*.apk`for INFILES in $APKLISTdoecho setup package:$INFILESpm install -r $INFILESdoneecho "do preinstall ok" >> /system/log.txtbusybox touch /data/.notfirstrun fiecho "============================================">> /system/log.txt exit此方式做預(yù)裝用戶在使用Root后刪除掉/system/app下的已安裝應(yīng)用后,系統(tǒng)重啟后又會執(zhí)行啟動腳本自動重裝預(yù)裝應(yīng)用回來,且預(yù)裝apk文件的存放目錄根據(jù)不同的系統(tǒng)ROM還不一樣,是極為流氓的推廣策略。當(dāng)然,我們通過分析已經(jīng)看到了,如果要刪除此類的預(yù)裝應(yīng)用,只需要全盤的掃描apk文件再進(jìn)行刪除即可。
/*
* @author zhoushengtao(周圣韜)
* @since 2015年1月27日 上午14:02:22
* @weixin stchou_zst
* @blog http://blog.csdn.net/yzzst
* @交流學(xué)習(xí)QQ群:341989536
* @私人QQ:445914891
/
總結(jié)
以上是生活随笔為你收集整理的Android系统Root与静默安装的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Informix创建/修改主键
- 下一篇: MyEclipse 2014配置Andr