静默安装,就是apk在后台悄悄地安装,用于解决android自动嵌入式智能设备远程自动升级问题,安装过程无需人为操作。很多流氓软件喜欢干。但从现在的情况看,只有root了的手机,或者你能搞到手机厂商的签名,才能静默安装。
关于静默安装,基本上有两种情况。
1.root情况下静默安装
2.非root下面静默安装
root情况静默安装
-
调用pm指令,下面就是调用pm指令(pminstall -r)把/sdcard/haha.apk安装掉。
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
runShellCommand("pminstall -r /sdcard/haha.apk");//执行命令
}
privatevoidrunShellCommand(String command){
Processprocess = null;
BufferedReader bufferedReader = null;
StringBuilder mShellCommandSB =new StringBuilder();
Log.d("wenfeng", "runShellCommand :" + command);
mShellCommandSB.delete(0, mShellCommandSB.length());
String[] cmd = newString[] { "/system/bin/sh", "-c", command }; //调用bin文件
try {
byte b[] = newbyte[1024];
process =Runtime.getRuntime().exec(cmd);
bufferedReader = new BufferedReader(newInputStreamReader(
process.getInputStream()));
Stringline;
while ((line = bufferedReader.readLine()) != null) {
mShellCommandSB.append(line);
}
Log.d("wenfeng", "runShellCommand result : " + mShellCommandSB.toString());
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if(bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
}
}
if (process != null) {
process.destroy();
}
}
}
2. 调用cp指令,把apk拷贝到data/app目录下,把上面的runShellCommand("pminstall -r /sdcard/haha.apk")替换成下面这句。然后用户下次重启时,apk就会自动安装。
runShellCommand("cp /sdcard/haha.apk /data/app")
非root下面静默安装
调用辅助功能,跟抢红包原理差不多。这里也就是使用了AccessibilityService,其实这种方式并不算严格的静默安装,因为用户可以看到安装的界面。很多应用商店都用到这种方式批量安装apk,下载后自动弹出安装界面,然后自动帮你点击安装。就能抢红包一样,自动打开红包页面,自动点击红包的道理一样一样的。
这个辅助功能最开始google设计是用于残疾人的,让他们更加方便的使用手机。一个应用要获取辅助功能,首先要在设置里面授权。
-
定义一个类继承AccessibilityService,重写方法onAccessibilityEvent(AccessibilityEventevent),通过onAccessibilityEvent这个方法我们可以获取屏幕状态变化,屏幕内容变化以及通知栏的变化,
我们可以根据相应的变化做出我们的处理,屏幕滑动,或者点击相应的Button按钮。
2. 反射调用PackageManage
当我们选择手动安装应用时,会跳转到应用安装界面,这个界面就是系统的PackageInstaller 提供,专门用来让用户有感知地安装应用。
Itent intent =
newIntent(Intent.ACTION_VIEW);
ri uri = Uri.fromFile(
newFile(
"/sdcard/news.apk")));
intent.setDataAndType(uri,
"application/vnd.android.package-archive");
startActivity(intent);
分析 PackageInstaller 的源码,我们发现它会通过PackageManager 调用installPackage 方法,这是个隐藏的抽象方法,实现类是ApplicationPackageManager。主要看一下四个参数:packageURI 就是 apk 的路径;observer 是安装的监听器,应用安装完成时会被回调,不能为null;flags 是标志位,指定安装的参数;installersPackageName表示可选的安装来源,比如应用宝之类的。
publicabstractvoidinstallPackage(UripackageURI, PackageInstallObserver observer, int flags, StringinstallerPackageName);
ApplicationPackageManager 里面 mPM 是一个IPackageManager 类型的对象,它会执行具体的安装任务
try {
mPM.installPackage(originPath,observer.getBinder(), flags, installerPackageName, verificationParams,
null);
}
catch(RemoteException ignored) {
}
ContextImpl 的getPackageManager 方法,通过 ActivityThread 获取 IPackageManager 对象用来构造ApplicationPackageManager,然后返回 ApplicationPackageManager。
ActivityThread 的getPackageManager 方法,其实就是获取系统服务的过程。
publicstatic IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default =" + sPackageManager);
return sPackageManager;
}
IBinder b =ServiceManager.getService("package");
//Slog.v("PackageManager","default service binder = " + b);
sPackageManager =IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager","default service = " + sPackageManager);
return sPackageManager;
}
通过以上分析,我们通过 PackageManager 调用 installPackage 方法就行了,下面看代码:
publicstatic boolean silentInstall(PackageManager packageManager, String apkPath) {
Class<?> pmClz =packageManager.getClass();
try {
if (Build.VERSION.SDK_INT >= 21){
Class<?> aClass =Class.forName("android.app.PackageInstallObserver");
Constructor<?>constructor = aClass.getDeclaredConstructor();
constructor.setAccessible(true);
Object installObserver =constructor.newInstance();
Method method =pmClz.getDeclaredMethod("installPackage", Uri.class, aClass,int.class, String.class);
method.setAccessible(true);
method.invoke(packageManager,Uri.fromFile(new File(apkPath)), installObserver, 2, null);
} else {
Method method =pmClz.getDeclaredMethod("installPackage", Uri.class,Class.forName("android.content.pm.IPackageInstallObserver"),int.class, String.class);
method.setAccessible(true);
method.invoke(packageManager,Uri.fromFile(new File(apkPath)), null, 2, null);
}
return true;
} catch (Exception e) {
log.error(e);
}
return false;
}
然而,这个方法介绍了那么多,其实这种方式是不可行的,原因是:
1.installPackageAsUser会远程调用PackageManagerService进行安装,但安装前需要校验权限,只有System权限以上才能通过校验。所以为了编译System权限的app,需要在AndroidManifest增加一句android:sharedUserId="android.uid.system"。
2.但你以为你这个System权限的app可以安装到现实手机中吗?不可能的,系统在安装AndroidManifest包含android:sharedUserId="android.uid.system"的app时,都会首先看下app的签名。app签名不对直接就安装失败的。除非,你能搞到手机厂家的签名!!!!签名这个可是重大机密,泄露可是要出人命的。
2017.07.08测试技术嘉年华火热报名中
国际著名安全机构OWASP、前惠普高级性能工程师大牛就要来讯飞啦
还不快来报名!
报名地址:https://www.sojump.hk/jq/14382135.aspx
嘉年华官网:http://itest.iflytek.com/
爱测未来公众号:itest_forever
CSDN:http://blog.csdn.net/itest_2016
QQ群:274166295(爱测未来2群)、610934609(爱测未来3群)
会场地址:合肥市望江西路666号科大讯飞语音产业基地A1#201会场
时间:2017.07.08