本文将深刻揭示提取Android ROOT权限的完整过程。这一过程与网上的方法有很大的差别。不少网上提取ROOT权限的方式都是使用别人作好的程序,有的甚至点击一下按钮就会自动完成全部的工做。这样作尽管能够成功提取ROOT权限,但读者并不能了解其中的原理,并且因为Android设备的千差万别,可能并非每一种Android设备均可以很容易找到提取ROOT权限的工具。因此最通用的方法就是尽量利用现成的工具来完成提取ROOT权限的工做。那么现成的工具备什么呢?其实主要就是Android源代码以及Linux内核源代码。也就是说,大多数工做均可以经过这些源代码来解决。当了解了这一过程的原理后,若是并无找到合适的提取ROOT权限的工具,就能够经过本文介绍的方法很容易获取Android设备的ROOT权限。
本文介绍的提取ROOT权限的方法主要针对Nexus 7,对于其余Android设备也可使用相似的方法。但必须知足以下两个条件。
Android设备容许进入bootloader模式。有少数的Android设备关闭了bootloader模式,因此没法刷机。根据笔者的经验,Google的几个亲儿子(Nexus系列),HTC、三星的Android设备基本都没问题,其余厂商的设备就须要具体问题具体分析了。css
能够找到合适的Recovery。有的Android设备自带的Recovery因为功能有限,在刷机上也有一些问题,因此不得不使用第三方的Recovery。一般大厂商生产的Android设备都能找到合适的第三方Recovery。但刷Recovery一般也必须知足第1个条件。android
资深Android
玩家和喜欢玩“酷”的Android
用户在Android
手机到手后的第一件事就是提取ROOT
权限,由于Android
设备有了ROOT
权限,就彻底在本身的控制之下了,尽管这样作会带来必定的安全隐患,但在他们看来,自由比任何东西都重要。
可能不少人是经过从网上下载的工具提取ROOT
权限的。那么提取ROOT
权限真的很复杂吗?No
,其实提取ROOT
权限远没有编写Android
应用复杂,只须要一些简单的步骤就能够搞定,固然,若是有编程的基础,那就会更以为提取ROOT
权限的过程简直太“酷”了。那么在本文先看一下提取ROOT
权限的基本步骤。
对于一部没有ROOT
权限的Android
设备,将文件复制到系统目录的方法有以下两个。
一般复制完整的系统目录结构会使用第1
种方法,例如,复制整个system
文件系统(system.img
)。而要将少许的文件复制到某些文件系统,通常会使用第2
种方法。由于这种方法能够经过脚本命令复制指定的文件到系统目录(如/system/xbin
),并且不会影响其余无关的数据。
读者可使用官方的Recovery
,也能够下载第三方的Recovery
。通常第三方的Recovery
会更强大一些。在下一节会详细介绍如何使用第三方的Recovery
。
提取ROOT
权限的关键就是执行su
命令。不过Android
系统带的su
命令在默认状况下只能由root
用户调用,因此使用su
命令以前须要先破解su
命令,也就是修改su
源代码,将检测调用权限的代码去掉,若是有必要,再加入知足本身需求的代码。也就是所,提取ROOT
权限实际上使用的是已经破解了的su
命令。在后面的内容
会详细介绍如何修改su
源代码,并从新生成su
命令文件。
第
3
步
:制做Recovery
刷机文件(update.zip
)
要想将破解后的su
命令放到Android
的系统目录(如/system/bin
)目录中,须要制做Recovery
刷机包,也就是一个普通的zip
压缩文件。一般文件名为update.zip
。不过不少高级的Recovery
容许选择其余的zip
文件。因此通常Recovery
刷机包能够起任何文件名。
Recovery
刷机包的内部结构有必定的规则,主要的工做就是编写updater-script
脚本文件,该脚本文件的详细编写过程会在后面的内容
节介绍。
到这一步就水到渠成了,直接执行su
命令,当前的Shell
就拥有root
权限了(Shell
标识符由$
变成了#
)。如今能够在Shell
中浏览只有root
权限才能看到的内容,例如,经过ls /data/data
命令查看/data/data
目录中的文件和目录列表。
其实第4
步已经成功使当前的Shell
拥有了root
权限,不过还有一点缺陷,就是当进入Android
设备的Shell
时每次都须要执行su
命令才能获取root
权限,这样有些麻烦。所以还须要修改配置文件,使得一进入Shell
就能够马上拥有root
权限。在后面的内容
会详细介绍如何修改,以及修改哪些配置文件。
任何一个在Android
设备上成功运行的ROM
都会自带一个Recovery
,经过Recovery
,能够将一个zip
格式刷机包中的内容复制到指定的系统目录。Nexus 7
官方ROM
也带了一个Recovery
。不过这个Recovery
功能有限,并且zip
文件还须要签名才能刷机,很麻烦。固然,读者能够定制本身的Recovery
,不过定制Recovery
的工做量是很是大的,一般不可能在短期内完成,因此本文暂不作深究,在后续的文章中会专门探讨与定制Recovery
相关的问题。既然官方自带的ROM
不给力,而定制Recovery
又很费劲,那么最节省时间的方法就是使用第三方的Recovery
。
如今有不少第三方的Recovery,其中比较著名的是Clockwork Recovery。目前大多数流行的Android设备都有对应的Clockwork Recovery。Clockwork Recovery不只功能强大,并且zip格式的刷机包不须要签名就能够正常使用。
刷机以前首先应执行下面的命令进入bootloader模式。
大概3到5秒时间,Nexus 7就会自动重启,并进入bootloader模式。要注意的是,在bootloader模式下adb命令再也不起做用,取而代之的是fastboot命令,要想知道当前PC有多少Android设备处于bootloader模式,可使用下面的命令。固然,若是Android设备处于正常模式下,可使用adb devices显示当前链接到PC的Android设备列表。
进入到bootloader模式后就能够为所欲为地刷机了。在本文只介绍如何刷Recovery,后续的文章会深刻讲解如何刷各类镜像文件。假设下载的Recovery镜像文件是recovery-clockwork-touch-6.0.2.3-grouper.img。在bootloader模式下刷Recovery镜像文件的命令以下:
fastboot flash recovery recovery-clockwork-touch-6.0.2.3-grouper.img

在bootloader
模式下经过音量上下键切换到“Recovery mode
”选择项(在屏幕右上角显示),而后按电源键,大概等5
秒,Nexus 7
就会进入Clockwork Recovery
。若是想从新启动Nexus 7
,并进入正常的模式,能够选择Recovery
的“reboot system now
”菜单项,而后按电源键便可。若是读者刷的是带触摸功能的Recovery
,只要点击“reboot system now
”菜单项便可从新启动Nexus 7
。若是读者目前处于正常模式下,而且Nexus 7
已经过USB
线与PC
链接,可使用下面的命令进入Recovery
模式。
刷完了Recovery
后,就须要将su
文件放到Android
设备中的/system/bin
或/system/xbin
目录中,而后直接执行su
命令便可使当前的Shell
得到root
权限(Shell
提示符从$
变成了#
),之前不少不能作的事也能够作了,例如,普通用户不能查看/data/data
目录中的内容,使用su
命令提取root
权限后也可使用ls
命令查看/data/data
目录的内容了。
读者能够从网上下载合适的su
文件,或直接从Android
源代码中获取su
文件。若是Android
源代码尚未编译,须要按着1.3.2
节的步骤编译整个Android
源代码。成功编译Android
源代码后,就能够在以下的目录找到编译好的su
文件。
<Android源代码本目录>/out/target/product/generic/system/xbin
实际上这个su
命令彻底能够知足目前的需求,也就是提取Android
设备当前Shell
的root
权限。不过先别忙将su
文件弄到Android
设备上。接下来先看一下su
文件的源代码,了解一下su
文件的运行原理以及为何能在Android
设备上成功执行。
<Android源代码根目录>/system/extras/su
su
是用C
语言编写的普通可执行文件,主文件是su.c
。读者能够打开该文件看一下su
的源代码。
su.c文件中除了引用的一些头文件外,就只有一个main函数,代码以下:
源代码文件:<Android源代码根目录>/system/extras/su/su.c
#define LOG_TAG "su"… …int main(int argc, char **argv){ struct passwd *pw; int uid, gid, myuid; myuid = getuid(); if (myuid != AID_ROOT && myuid != AID_SHELL) { fprintf(stderr,"su: uid %d not allowed to su\n", myuid); return 1; } if(argc < 2) { uid = gid = 0;} else { pw = getpwnam(argv[1]); if(pw == 0) { uid = gid = atoi(argv[1]); } else { uid = pw->pw_uid; gid = pw->pw_gid; } } if(setgid(gid) || setuid(uid)) { fprintf(stderr,"su: permission denied\n"); return 1; } if (argc == 3 ) { if (execlp(argv[2], argv[2], NULL) < 0) { fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); return -errno; } } else if (argc > 3) { char *exec_args[argc - 1]; memset(exec_args, 0, sizeof(exec_args)); memcpy(exec_args, &argv[2], sizeof(exec_args)); if (execvp(argv[2], exec_args) < 0) { fprintf(stderr, "su: exec failed for %s Error:%s\n", argv[2], strerror(errno)); return -errno; } } execlp("/system/bin/sh", "sh", NULL); fprintf(stderr, "su: exec failed\n"); return 1;}
从su.c文件的代码能够看出,su命令支持多个命令行参数。这些命令行参数分为以下两类。
第1类:su的第一个参数,该参数指定了要提高权限的用户ID或用户名,若是不指定,就是当前的用户。
第2类:其他的参数。表示提高权限后要马上执行的命令和该命令的参数。
# su user ls –al /data/data/
经过setgid和setuid函数提高权限,也就是使得任何用户在执行sh命令时都会拥有与sh命令拥有者一样的权限。因为sh命令的拥有者是root用户,因此天然就将新的Shell提高到了root权限。web
经过execlp函数执行sh命令。因为前面已经调用了setgid和setuid函数,因此执行sh命令会进入新的Shell,而且该Shell与sh命令文件的全部者(root用户)拥有一样的权限。执行exit命令会退出拥有root权限的Shell,并从新回到原来没有root权限的Shell。再次执行exit命令后,就会退出Android Shell,回到Ubuntu Linux的终端。shell
4. 制做第一个Recovery刷机包(编写脚本文件)
Recovery使用的刷机包就是zip格式的压缩文件。根据不一样的需求,刷机包中包含的文件不一样,一个完整的刷机包很是复杂,不过本节的目的只是将su文件复制到/system/xbin目录中,因此暂时用不着那么复杂的刷机包。关于Recovery刷机包的详细制做过程将在后面跟的章节深刻探讨。本文只作最基本的刷机包。
其实要作一个Recovery刷机包并不困难。制做Recovery刷机包以前一般要考虑使用的是哪一个Recovery。例如,本书主要使用了Clockwork Recovery,因此能够利用Clockwork Recovery中的一些特性。
本文要制做的刷机包中只有两个目录:system和META-INF。其中system就是编译Android源代码后,在<Android源代码根目录>/out/target/product/generic目录
中的system目录,也是进入Android设备的Shell后看到的“/system”目录,在该目录下包含了Android的系统文件,其中包括不少命令文件以及系统应用程序
。不过本文制做的Recovery刷机包没这么复杂。因为只须要将su文件复制到/system/xbin目录,因此在system目录中只要有一个xbin子目录,而且在该目录中放一个在上一节得到的su文件便可。
可能不少读者会问,将su文件放到/system/xbin目录中,Recovery中刷机时就会将su文件复制到Android系统的/system/xbin目录中吗?答案很简单,Recovery固然不知道本身要作什么,具体要完成什么工做,如何来完成,玄机全在META-INF目录中。
在META-INF/com/google/android目录中有一个updater-script脚本文件(纯文本文件)和一个update-binary可执行文件。别看这两个文件一共不到200KB,它们倒是整个Recovery刷机包中最核心的部分。尤为是update-binary,该文件一般在190KB上下,别看文件尺寸不大,这但是内嵌于Recovery的一种轻量级脚本语言的解析器,而updater-script脚本文件就是使用这种脚本语言写的。这种脚本语言就是edify。该语言除了定义一些简单的语句外,还定义了几十个用于各类操做的函数,例如,复制文件、删除文件、创建连接等。
Edify语言会在后面的章节介绍,在本文只介绍该语言的几个经常使用的函数。这些函数的原型、含义及其用法以下:
原型:ui_print(msg1, ..., msgN);
含义:该函数用于在Recovery界面输出字符串。其中msg一、…、msgN表示N个字符串参数,该函数至少须要指定一个参数。若是指定多个参数,会将这些参数值链接起来输出。
用法:ui_print(" hello world ");
原型:run_program(prog, arg1, .., argN);
含义:该函数用于执行程序,其中prog参数表示要执行的程序文件(要写完整路径),arg1、…、argN表示要执行程序的参数。prog参数是必须的,其余参数都是可选的。
用法:run_program("/sbin/busybox","mount", "/system");
原型:delete(file1, file2, ..., fileN);
含义:该函数用于删除一个或多个文件。其中file1、file2、…、fileN表示要删除文件的路径,至少须要指定一个文件。
用法:delete("/system/xbin/su");
原型:package_extract_dir(package_path, destination_path);
含义:用于提取刷机包中package_path指定目录的全部文件到destination_path指定的目录。其中package_path参数表示刷机包中的目录,destination_path参数表示目标目录。
用法:package_extract_dir("system", "/system");
原型:set_perm(uid, gid, mode, file1, file2, ..., fileN);
含义:用于设置一个或多个文件的权限。其中uid参数表示用户ID,gid参数表示用户组ID。若是想让文件的用户和用户组都是root,uid和gid须要都为0。mode参数表示设置的权限,这个权限与chmod命令设置的权限彻底同样,例如,若是将一个文件设为任何用户均可以读写和执行的权限值是0777。file1、file2、…、fileN表示要设置权限的文件的路径。
用法:set_perm(0, 0, 0777, "/system/xbin/su");
含义:用于解除文件系统的挂载。其中mount_point参数表示文件系统。
本文要编写的updater-script文件只会使用上面的函数。该脚本文件主要实现以下基本功能。
以读写模式挂载/system。编程
删除旧的su文件。安全
复制新的su文件。微信
修改su文件的权限。架构
卸载/system。
其中挂载/system调用了busybox命令,该命令并不属于Android。不过该命令十分强大,常被人称为Android的瑞士军刀
。busybox是一个开源的命令集合,将多达上百个命令集成在了一个大概2MB的文件中。例如,本文要用的mount命令就是其中之一。尽管Android从本质上也属于Linux系统,但较其余Linux系统集成的命令是不多的,因此若是想在Android中执行各类操做,一般就须要将busybox文件复制到Android系统的/system/xbin或其余存储命令文件的系统目录。这样只须要一个busybox命令就能够搞定一切。若是读者想知道busybox到底支持多少命令,只须要直接执行busybox命令便可。
读者能够在http://www.busybox.net下载busybox最新版本的源代码,并按着说明使用交叉编译器编译busybox便可(在ARM架构的设备上运行必需要使用交叉编译器),为了方便读者,在随书光盘中带了一个编译好的busybox文件,路径是/tools/busybox。该文件只能在ARM架构的设备上运行,不能在X86 PC上使用。读者须要将busybox文件上传到Android设备的/system/xbin目录中(须要root权限),并执行 chmod 777 /system/xbin/busybox命令修改其权限后便可执行该命令。
如今看一下updater-script文件的代码。
ui_print("*********************");ui_print("My First Recovery Update");ui_print("*********************"); ui_print("----Mounting /system ----");run_program("/sbin/busybox", "mount","-o", "rw", "/system"); ui_print("----Delete /system/xbin/su ----");delete("/system/xbin/su");ui_print("- Extracting files");package_extract_dir("system", "/system");ui_print("----- Setting permissions");set_perm(0, 0, 0777, "/system/xbin/su");unmount("/system");ui_print("finished");
在updater-script文件中使用run_program函数调用了busybox命令,并经过该命令执行了mount操做,实际上,至关于在Linux终端执行以下的命令。
busybox mount –o rw /system
若是执行mount操做时未指定“-o rw”,默认是只读挂载,也就是说/system目录及其子目录是只读的,为了将/system目录变成读写模式的,须要再次执行以下的命令。
busybox mount –o rw, remount /system
其中“-o rw, remount”中的rw和remount是mount命令的两个选项,表示从新将/system目录mount成可读写的。
若是须要修改默认的挂载点对应的路径,例如,将/system挂载到/my_system目录(该目录必须存在),须要使用下面的命令。
busybox mount –o rw /system /my_system
如今updater-script脚本文件已经编写完了,接下来还须要一个用于解析updater-script脚本文件的update-binary程序。读者能够从网上找一个Recovery刷机包,将其中的update-binary文件放到本身的刷机包中,或到<Android源代码根目录> /out/target/product/generic/system/bin目录寻找一个updater文件,将该文件更名为update-binary便可。其实updater文件也是Android源代码的某个子程序编译生成的,在后面深刻探讨Recovery时再详细分析updater的实现原理。
如今Recovery刷机包的全部文件(su、updater-script和update-binary)都搞定了,接下来完成最后一步,就是将system和META-INF目录用zip格式压缩(压缩文件名任意取)。在下一节会介绍如何将这个zip格式文件中的内容刷到Nexus 7上。为了方便读者,在随书光盘上已经带了这个zip压缩文件(/recovery/su_update.zip),读者能够直接将该文件刷到Android设备上(除了Nexus 7外,其余使用Clockwork Recovery的Android设备也可使用该刷机包)。
可能有的读者会发现,updater-script脚本中执行了/sbin目录中的busybox命令,但成功启动Android后进入Shell,在/sbin目录中并无发现busybox命令,这究竟是真么回事呢?
实际上,busybox命令的确存在,但却不在system文件系统里,而是在recovery文件系统中。在Android正常启动后,实际上挂载的是system文件系统。而进入Recovery模式后,系统会自动挂载recovery文件系统,而挂载system文件系统要在updater-script脚本文件中经过相应的函数来完成(如本文使用了run_program函数调用了busybox命令挂载system文件系统)。system和recovery文件系统都有一个sbin目录,但目录中的文件是不同的。实际上,经过Android源代码编译生成的Recovery镜像
是不包含busybox命令的,而Clockwork Recovery的busybox命令是本身添加的,也就是说,其余的Recovery镜像并不必定包含busybox命令,因此本文编写的updater-script脚本文件只适合Clockwork Recovery。若是要想使用其余的Recovery,或者将busybox命令添加到Recovery镜像中,或者使用edify脚本语言的mount函数挂载相应的文件系统(如/system、/data等)。在后面的章节会详细介绍Recovery镜像文件的生成,以及如何修改现成的Recovery镜像。
到如今为止,一切准备工做都已经完成了,如今只剩下最后一步,就是提取Nexus 7的root权限。不过此次提取root权限彻底是DIY方式的,这也显得更有意义,由于做为程序员来讲,了解其中的过程比结果更重要。
如今须要用USB线链接Nexus 7和PC,而后将上一节生成的zip文件上传到Nexus 7的SD卡中。若是不想上传也没问题。一般在Recovery模式下选择sideloader上传方式,而不是事先上传到Nexus 7上,由于这样Nexus 7不须要在正常模式和Recovery模式之间来回切换,这也更适合须要频繁调试和刷机的程序员。
如今进入Nexus 7的Recovery模式(正常模式下执行adb reboot recovery命令),若是读者已经将zip压缩文件(这里假设为update.zip)上传到Nexus 7的SD卡中,选择“install zip from sdcard”(第2个菜单项),而后继续选择zip文件便可刷机。若是未将zip文件上传,选择“install zip from sideload”菜单项,而后Recovery模式会处于等待状态。如今adb的sideload上传模式能够工做了。在Linux终端输入adb sideload update.zip命令,便可成功刷机。在刷机的过程当中会看到updater-script脚本文件中使用ui_print函数输出的字符串。updater-script脚本文件中的命令执行完后,就会回到Recovery主界面。而后选择“reboot system now”(第1个菜单项)重启Nexus 7。
当Nexus 7进入正常模式后,进入Shell,这时尚未执行su命令,因此当前Shell仍然没有root权限,如今执行su命令,会看到Shell提示符从“$”变为“#”,这说明当前Shell已经拥有了root权限。如今作一些之前作不了的事,例如,使用ls /data/data命令查看系统的data目录,如今能够成功列出该目录中全部的子目录(都是系统应用或普通应用创建的私有目录)。
6. 上传Android应用到/system/app目录
尽管提取root权限的目的不少,有的是为了调用Linux的命令,有的是为了直接访问系统的数据。不过本章提取root权限的目的主要是为了将系统应用的APK文件上传到/system/app目录中,以即可以在真机的环境下测试系统应用。
如今进入Android设备的Shell,并执行su命令提取root权限,而后看看/system/app目录是否可写。有的Android设备在挂载system文件系统时,/system及其子目录是只读的,若是是这种状况,执行以下的命令便可将/system目录及其子目录变成可读写的。
mount –o rw,remount /system
如今退出Android设备的Shell,从新回到Linux终端。而后找一个APK文件。但有一个问题,当执行adb shell命令进入Android设备的Shell时,一开始并无root权限,须要执行su命令才能提权,因此就不能直接使用adb push命令将APK文件上传到/system/app目录中(由于没有root权限,该目录是只读的)。解决的方法也很简单,就是首先使用adb push命令将APK文件上传到Android设备的SD卡上,而后在执行adb shell的同时执行su命令提权。例如,下面的命令能够在Linux终端下删除Android设备中/system/app目录中的Test.apk文件。
adb shell su -c "mount -o remount,rw /system | rm -f /system/app/Test.apk "
下面的命令将SD卡根目录中的Test.apk文件复制到Android设备的/system/app目录中。
adb su -c "mount -o remount,rw /system | cp /sdcard/Test.apk /system/app/Test.apk "
为了方便,读者能够编写一个带参数的Shell脚本文件,将adb push和adb shell命令在一块儿使用。
- EOF -
关注「极客起源」公众号,加星标,不错过精彩技术干货
