Android程序的安全系统 apk获得root权限
2016-04-13 17:19
337 查看
最近在移植Android过程中遇到了Android程序(apk)权限的问题。最近也对这方面进行了一些了解,在此和大家分享。
Android框架是基于Linux内核构建,所以Android安全系统也是基于Linux的安全架构建立的。在Linux安全系统中,用户和组起着重要的作用,Linux中所有的资源给不同的用户和用户组设置了不同的访问属性。如果你对Linux下面用户和组的概念不熟悉,请先补习一下Linux基础知识。
在Android系统中,系统为每一个应用程序(apk)创建了一个用户和组。这个用户和组都是受限用户,不能访问系统的数据,只能访问自己的文件和目录,当然它也不能访问其他应用程序的数据。这样设计可以尽可能地保护应用程序的私有数据,增强系统的安全性和健壮性。
但是有一些应用程序是需要访问一些系统资源的。比如Setting程序,他就需要访问wiffi,在系统中创建删除文件等等操作。怎样做到这一点儿呢?Android通过一定途径可以获得system权限。获得system用户权限,需要以下步骤:
1. 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行
3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。
一般情况下system用户权限就已经够用了,system用户可以在系统中创建和删除文件,访问设备等等。但是有些情况下system权限还是不够的。比如:修改系统默认字体,ifconfig命令是需要root权限的。我可以很肯定的说,在Android下面应用程序是没有可能拿到root权限的。但是如果我的应用程序需要root权限怎么办呢?只能想办法绕般过去。就以我的问题为例,修改系统默认的中文字体,root权限下面命令为:
cp -f /data/fonts/test.ttf /system/fonts/DroidSansFallback.ttf
在普通用户或者system用户权限下面这条命令是不起作用的,但是不会返回失败和异常,这个我个人认为是Android的bug。那么怎样实现这个功能呢?我想出了两个办法。
1、系统启动的时候init进程创建一个后台进程,该进程处于root用户权限下面。用来监听系统中应用程序的请求(可以用socket实现),并代其完成。这样应用程序就可以执行root用户权限的任务了。
2、实现一个虚拟的设备,该设备的功能就是在内核态帮应用程序执行相应的命令。Linux内核态没有权限的问题了。肯定可以执行成功。
问题
我遇到的问题是我想在Java应用程序中动态mount一个NFS的系统,但是执行mount命令必须要要root权限才可以。一般情况下,在Android的Java层是不能获得root权限的。
思路
我在博文《Android程序的安全系统》中提到两种思路:
1、实现一个init实现一个Service,来帮助Android应用程序执行root权限的命令。
2、实现一个虚拟设备,这个设备帮助Android应用程序执行root权限的命令。
本文将会选择第一种来解决Android应用程序mount NFS文件系统的问题。
Init.rc Service
在Android系统init.rc中定义很多Service,具体定义格式可以参考《Android
Platform Developer’s Guide》中的“Android Init Language”。Init.rc中定义的Service将会被Init进程创建,这样将可以获得root权限。
现在问题是Android应用程序怎样启动让init进程知道我们想运行那个进程呢?答案是设置系统属性“ctl.start”,把“ctl.start”设置为你要运行的Service,假设为“xxx”,Android系统将会帮你运行“ctl.start”系统属性中指定的Service。那么运行结果init进程将会将会写入命名为“init.svc.+Service名称”的属性中,也就是“init.svc.xxx”属性,应用程序可以参考查阅这个值来确定Service执行的情况。想更深入了解Android
property系统可以参考博文《(翻译)Android属性系统》。
Android property权限
难道Android属性“ctl.start”是所有进程都可以设置的吗?那世界不就乱套了,谁都可以可以执行init.rc中Service了,查看property_service.c中的源码,设置Android系统属性的函数为handle_property_set_fd:
1: void handle_property_set_fd(int fd) 2: { 3: ......
4: switch(msg.cmd) { 5:case PROP_MSG_SETPROP: 6: msg.name[PROP_NAME_MAX-1] = 0;
7: msg.value[PROP_VALUE_MAX-1] = 0; 8: 9:
if(memcmp(msg.name,"ctl.",4) == 0) { 10:if (check_control_perms(msg.value, cr.uid, cr.gid)) {
11: handle_control_message((char*) msg.name + 4, (char*) msg.value); 12: }else {
13: ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n", 14: msg.name + 4, msg.value, cr.uid, cr.pid); 15: }
16: } 17: ...... 18: } 19: }
从源码中我们发现如果设置“ctl.”开头的Android系统property,将会调用check_control_perms函数来检查调用者的权限,其定义如下:
1: staticint check_control_perms(constchar *name,int
uid,int gid) { 2:int i; 3:if (uid == AID_SYSTEM || uid == AID_ROOT)
4:return 1; 5: 6:/* Search the ACL */ 7:for
(i = 0; control_perms[i].service; i++) { 8:if (strcmp(control_perms[i].service, name) == 0) { 9:if
((uid && control_perms[i].uid == uid) || 10: (gid && control_perms[i].gid == gid)) { 11:return 1;
12: } 13: } 14: } 15:return 0; 16:
}
我们发现root权限和system权限的应用程序将会授权修改“ctl.”开头的Android系统属性。否则将会检查control_perms全局变量中的定义权限和Service。
如果想更深入的了解Android Init进程和Android Property的权限控制,请参考《Android
Permission》。
实例
通过上面的介绍我们基本已经有思路了,下面以上面提出的mount nfs文件系统为例说明:
1、首先定义一个执行mount的脚本,我把它位于/system/etc/mount_nfs.sh,定义如下:
1: #!/system/bin/sh 2: 3: /system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt
不要忘了把它加上可执行权限。
2、在init.rc中加入一个Service定义,定义如下:
1: service mount_nfs /system/etc/mount_nfs.sh 2: oneshot 3: disabled
3、让自己的应用程序获得system权限,博文《Android程序的安全系统》中提到了怎样获得system权限,请参考,这里就不赘述了。
4、在自己应用程序中设置System系统属性“ctl.start”为“mount_nfs”,这样Android系统将会帮我们运行mount_nfs系统属性了。这里需要强调的是不能够调用System.getProperty,这个函数只是修改JVM中的系统属性。而不能修改Android的系统属性。可以调用android.os.SystemProperties(Android
2.1 Eclair系统可以调用这个API),如果你的Android版本不能调用这个类,只能通过JNI,调用C/C++层的API property_get和property_set函数了。如果想详细了解请参考《(翻译)Android属性系统》。代码如下:
1: SystemProperties.set("ctl.start","mount_nfs");
5、最后在自己应用程序中,读取“init.svc.mount_nfs”Android系统Property,检查执行结果。代码如下:
1: while(true) 2: { 3: mount_rt = SystemProperties.get("init.svc.mount_nfs","");
4:if(mount_rt != null && mount_rt.equals("stopped")) 5: { 6:return
true; 7: } 8:9:try 10: {
11: Thread.sleep(1000); 12: }catch(Exception ex){ 13: Log.e(TAG,"Exception: " + ex.getMessage());
14: } 15: }
init进程维护一个service的队列,所以我们需要轮训来查询service的执行结果。
通过上面的这些步骤,Android应用程序就能够调用init.rc中定义的Service了。这样你的Android应用程序也就获得了root权限。
总结
通过上文可以看出,在Android获得root权限还是需要一些前提的,比如:
1、必须是Android系统开发人员,否则你无法修改init.rc等文件。 2、你的应用程序必须要获得system权限。
这样可以防止root权限被应用程序无限制的使用,最终危及Android系统安全。
忘了,原作者及其链接了,我是按照上面的思路实现了在系统没有root的情况下,换掉系统默认字体的。
Android框架是基于Linux内核构建,所以Android安全系统也是基于Linux的安全架构建立的。在Linux安全系统中,用户和组起着重要的作用,Linux中所有的资源给不同的用户和用户组设置了不同的访问属性。如果你对Linux下面用户和组的概念不熟悉,请先补习一下Linux基础知识。
在Android系统中,系统为每一个应用程序(apk)创建了一个用户和组。这个用户和组都是受限用户,不能访问系统的数据,只能访问自己的文件和目录,当然它也不能访问其他应用程序的数据。这样设计可以尽可能地保护应用程序的私有数据,增强系统的安全性和健壮性。
但是有一些应用程序是需要访问一些系统资源的。比如Setting程序,他就需要访问wiffi,在系统中创建删除文件等等操作。怎样做到这一点儿呢?Android通过一定途径可以获得system权限。获得system用户权限,需要以下步骤:
1. 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行
3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。
一般情况下system用户权限就已经够用了,system用户可以在系统中创建和删除文件,访问设备等等。但是有些情况下system权限还是不够的。比如:修改系统默认字体,ifconfig命令是需要root权限的。我可以很肯定的说,在Android下面应用程序是没有可能拿到root权限的。但是如果我的应用程序需要root权限怎么办呢?只能想办法绕般过去。就以我的问题为例,修改系统默认的中文字体,root权限下面命令为:
cp -f /data/fonts/test.ttf /system/fonts/DroidSansFallback.ttf
在普通用户或者system用户权限下面这条命令是不起作用的,但是不会返回失败和异常,这个我个人认为是Android的bug。那么怎样实现这个功能呢?我想出了两个办法。
1、系统启动的时候init进程创建一个后台进程,该进程处于root用户权限下面。用来监听系统中应用程序的请求(可以用socket实现),并代其完成。这样应用程序就可以执行root用户权限的任务了。
2、实现一个虚拟的设备,该设备的功能就是在内核态帮应用程序执行相应的命令。Linux内核态没有权限的问题了。肯定可以执行成功。
问题
我遇到的问题是我想在Java应用程序中动态mount一个NFS的系统,但是执行mount命令必须要要root权限才可以。一般情况下,在Android的Java层是不能获得root权限的。
思路
我在博文《Android程序的安全系统》中提到两种思路:
1、实现一个init实现一个Service,来帮助Android应用程序执行root权限的命令。
2、实现一个虚拟设备,这个设备帮助Android应用程序执行root权限的命令。
本文将会选择第一种来解决Android应用程序mount NFS文件系统的问题。
Init.rc Service
在Android系统init.rc中定义很多Service,具体定义格式可以参考《Android
Platform Developer’s Guide》中的“Android Init Language”。Init.rc中定义的Service将会被Init进程创建,这样将可以获得root权限。
现在问题是Android应用程序怎样启动让init进程知道我们想运行那个进程呢?答案是设置系统属性“ctl.start”,把“ctl.start”设置为你要运行的Service,假设为“xxx”,Android系统将会帮你运行“ctl.start”系统属性中指定的Service。那么运行结果init进程将会将会写入命名为“init.svc.+Service名称”的属性中,也就是“init.svc.xxx”属性,应用程序可以参考查阅这个值来确定Service执行的情况。想更深入了解Android
property系统可以参考博文《(翻译)Android属性系统》。
Android property权限
难道Android属性“ctl.start”是所有进程都可以设置的吗?那世界不就乱套了,谁都可以可以执行init.rc中Service了,查看property_service.c中的源码,设置Android系统属性的函数为handle_property_set_fd:
1: void handle_property_set_fd(int fd) 2: { 3: ......
4: switch(msg.cmd) { 5:case PROP_MSG_SETPROP: 6: msg.name[PROP_NAME_MAX-1] = 0;
7: msg.value[PROP_VALUE_MAX-1] = 0; 8: 9:
if(memcmp(msg.name,"ctl.",4) == 0) { 10:if (check_control_perms(msg.value, cr.uid, cr.gid)) {
11: handle_control_message((char*) msg.name + 4, (char*) msg.value); 12: }else {
13: ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n", 14: msg.name + 4, msg.value, cr.uid, cr.pid); 15: }
16: } 17: ...... 18: } 19: }
从源码中我们发现如果设置“ctl.”开头的Android系统property,将会调用check_control_perms函数来检查调用者的权限,其定义如下:
1: staticint check_control_perms(constchar *name,int
uid,int gid) { 2:int i; 3:if (uid == AID_SYSTEM || uid == AID_ROOT)
4:return 1; 5: 6:/* Search the ACL */ 7:for
(i = 0; control_perms[i].service; i++) { 8:if (strcmp(control_perms[i].service, name) == 0) { 9:if
((uid && control_perms[i].uid == uid) || 10: (gid && control_perms[i].gid == gid)) { 11:return 1;
12: } 13: } 14: } 15:return 0; 16:
}
我们发现root权限和system权限的应用程序将会授权修改“ctl.”开头的Android系统属性。否则将会检查control_perms全局变量中的定义权限和Service。
如果想更深入的了解Android Init进程和Android Property的权限控制,请参考《Android
Permission》。
实例
通过上面的介绍我们基本已经有思路了,下面以上面提出的mount nfs文件系统为例说明:
1、首先定义一个执行mount的脚本,我把它位于/system/etc/mount_nfs.sh,定义如下:
1: #!/system/bin/sh 2: 3: /system/bin/busybox mount -o rw,nolock -t nfs 192.168.1.6:/nfs_srv /data/mnt
不要忘了把它加上可执行权限。
2、在init.rc中加入一个Service定义,定义如下:
1: service mount_nfs /system/etc/mount_nfs.sh 2: oneshot 3: disabled
3、让自己的应用程序获得system权限,博文《Android程序的安全系统》中提到了怎样获得system权限,请参考,这里就不赘述了。
4、在自己应用程序中设置System系统属性“ctl.start”为“mount_nfs”,这样Android系统将会帮我们运行mount_nfs系统属性了。这里需要强调的是不能够调用System.getProperty,这个函数只是修改JVM中的系统属性。而不能修改Android的系统属性。可以调用android.os.SystemProperties(Android
2.1 Eclair系统可以调用这个API),如果你的Android版本不能调用这个类,只能通过JNI,调用C/C++层的API property_get和property_set函数了。如果想详细了解请参考《(翻译)Android属性系统》。代码如下:
1: SystemProperties.set("ctl.start","mount_nfs");
5、最后在自己应用程序中,读取“init.svc.mount_nfs”Android系统Property,检查执行结果。代码如下:
1: while(true) 2: { 3: mount_rt = SystemProperties.get("init.svc.mount_nfs","");
4:if(mount_rt != null && mount_rt.equals("stopped")) 5: { 6:return
true; 7: } 8:9:try 10: {
11: Thread.sleep(1000); 12: }catch(Exception ex){ 13: Log.e(TAG,"Exception: " + ex.getMessage());
14: } 15: }
init进程维护一个service的队列,所以我们需要轮训来查询service的执行结果。
通过上面的这些步骤,Android应用程序就能够调用init.rc中定义的Service了。这样你的Android应用程序也就获得了root权限。
总结
通过上文可以看出,在Android获得root权限还是需要一些前提的,比如:
1、必须是Android系统开发人员,否则你无法修改init.rc等文件。 2、你的应用程序必须要获得system权限。
这样可以防止root权限被应用程序无限制的使用,最终危及Android系统安全。
忘了,原作者及其链接了,我是按照上面的思路实现了在系统没有root的情况下,换掉系统默认字体的。
相关文章推荐
- Android动画之旅一Android动画基本介绍
- Android mediaPlayer属性详解
- Android 5.0以后版本打开“有权查看使用情况的应用”
- android xml之Drawable 篇 --------shape和selector和layer-list的
- Android开发笔记(八十五)手机数据库Realm
- android 5.0以上获取前台应用的包名
- 初试Android的MVP框架
- Android群英传--绘图机制和处理技巧(二)
- Android - Fragments
- Android APK反编译就这么简单 详解(附图)
- Android:让EditText不自动获取焦点
- Android——SQLite数据库(二)升级数据库、增、删、改、查、事务
- Android 更改系统DataPicker分割线颜色和距离
- Android Studio 使用zxing二维码处理
- ubuntu编译android源码
- Android Studio rename module Can't rename root module
- 【转载】第一次使用Android Studio时你应该知道的一切配置(三):gradle项目构建
- Android海量字体设置
- 启程Android之路
- XPlane android 2D手游开发实战