像极客同样提取Android的Root权限

本文将深刻揭示提取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

1.  提取ROOT权限的步骤
资深Android 玩家和喜欢玩“酷”的Android 用户在Android 手机到手后的第一件事就是提取ROOT 权限,由于Android 设备有了ROOT 权限,就彻底在本身的控制之下了,尽管这样作会带来必定的安全隐患,但在他们看来,自由比任何东西都重要。
可能不少人是经过从网上下载的工具提取ROOT 权限的。那么提取ROOT 权限真的很复杂吗?No ,其实提取ROOT 权限远没有编写Android 应用复杂,只须要一些简单的步骤就能够搞定,固然,若是有编程的基础,那就会更以为提取ROOT 权限的过程简直太“酷”了。那么在本文先看一下提取ROOT 权限的基本步骤。
1 :刷一个合适的Recovery
对于一部没有ROOT 权限的Android 设备,将文件复制到系统目录的方法有以下两个。
  • 在bootloader模式下复制整个的文件系统。nginx

  • 在recovery模式下将文件复制到Android设备中的指定目录。程序员


一般复制完整的系统目录结构会使用第1 种方法,例如,复制整个system 文件系统(system.img )。而要将少许的文件复制到某些文件系统,通常会使用第2 种方法。由于这种方法能够经过脚本命令复制指定的文件到系统目录(如/system/xbin ),并且不会影响其余无关的数据。
读者可使用官方的Recovery ,也能够下载第三方的Recovery 。通常第三方的Recovery 会更强大一些。在下一节会详细介绍如何使用第三方的Recovery
2 :破解su 命令
提取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 脚本文件,该脚本文件的详细编写过程会在后面的内容 节介绍。
 
4 :执行su 命令提取ROOT 权限
到这一步就水到渠成了,直接执行su 命令,当前的Shell 就拥有root 权限了(Shell 标识符由$ 变成了# )。如今能够在Shell 中浏览只有root 权限才能看到的内容,例如,经过ls /data/data 命令查看/data/data 目录中的文件和目录列表。
 
5 :使ROOT 权限更完美
其实第4 步已经成功使当前的Shell 拥有了root 权限,不过还有一点缺陷,就是当进入Android 设备的Shell 时每次都须要执行su 命令才能获取root 权限,这样有些麻烦。所以还须要修改配置文件,使得一进入Shell 就能够马上拥有root 权限。在后面的内容 会详细介绍如何修改,以及修改哪些配置文件。

2.  须要一个很酷的recovery
任何一个在Android 设备上成功运行的ROM 都会自带一个Recovery ,经过Recovery ,能够将一个zip 格式刷机包中的内容复制到指定的系统目录。Nexus 7 官方ROM 也带了一个Recovery 。不过这个Recovery 功能有限,并且zip 文件还须要签名才能刷机,很麻烦。固然,读者能够定制本身的Recovery ,不过定制Recovery 的工做量是很是大的,一般不可能在短期内完成,因此本文暂不作深究,在后续的文章中会专门探讨与定制Recovery 相关的问题。既然官方自带的ROM 不给力,而定制Recovery 又很费劲,那么最节省时间的方法就是使用第三方的Recovery
如今有不少第三方的Recovery,其中比较著名的是Clockwork Recovery。目前大多数流行的Android设备都有对应的Clockwork RecoveryClockwork Recovery不只功能强大,并且zip格式的刷机包不须要签名就能够正常使用。
刷机以前首先应执行下面的命令进入bootloader模式。
adb reboot-bootloader
大概35秒时间,Nexus 7就会自动重启,并进入bootloader模式。要注意的是,在bootloader模式下adb命令再也不起做用,取而代之的是fastboot命令,要想知道当前PC有多少Android设备处于bootloader模式,可使用下面的命令。固然,若是Android设备处于正常模式下,可使用adb devices显示当前链接到PCAndroid设备列表。
fastboot devices
进入到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
若是刷机成功,会显示如图1所示的信息。

1  成功刷Recovery
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 模式。
adb reboot recovery
3. su命令源代码分析
刷完了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 设备上成功执行。
读者能够从以下的目录找到su 命令的源代码。
<Android源代码根目录>/system/extras/su
su 是用C 语言编写的普通可执行文件,主文件是su.c 。读者能够打开该文件看一下su 的源代码。
su.c文件中除了引用的一些头文件外,就只有一个main函数,代码以下:
源代码文件:<Android源代码根目录>/system/extras/su/su.c
#define LOG_TAG "su"… …/* 此处省略了#include … 语句 */int main(int argc, char **argv){ struct passwd *pw; int uid, gid, myuid; /* 获取用户ID,只有root和当前的Shell能执行su命令 */ 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 { /* 根据参数指定的用户名获取用户属性,若是getpwnam函数返回0,表示参数指定的是用户ID 而不是用户名 */ pw = getpwnam(argv[1]);  if(pw == 0) { uid = gid = atoi(argv[1]); } else { uid = pw->pw_uid; gid = pw->pw_gid; } }/*setgid函数要设置一个用户组ID,使用属于该用户组的用户执行任何文件时都拥有该文件全部者的权限。setuid函数与setgid函数相似,须要设置一个用户ID。使用该用户执行任何可执行文件都会拥有该文件全部者的权限。例如,sh命令的全部者是root用户,而当前登陆用户是user,这时使用setuid函数设置user的ID后,再执行sh命令,就至关于以root用户的身份执行sh命令,因此进入新的Shell后就会拥有root权限*/ 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) { /* Copy the rest of the args from main. */ 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; } } /* 执行sh命令进入新的Shell,若是成功执行,当前程序会马上退出,若是执行失败,会继续 执行下面的语句 */ execlp("/system/bin/sh", "sh", NULL);  fprintf(stderr, "su: exec failed\n"); return 1;}

su.c文件的代码能够看出,su命令支持多个命令行参数。这些命令行参数分为以下两类。
1类:su的第一个参数,该参数指定了要提高权限的用户ID或用户名,若是不指定,就是当前的用户。
2类:其他的参数。表示提高权限后要马上执行的命令和该命令的参数。
下面都是合法的su命令调用形式。
# su
# su user
# su user ls –al /data/data/
su提高权限的核心有以下两个。
  • 经过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中的一些特性。
本文要制做的刷机包中只有两个目录:systemMETA-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
原型:ui_print(msg1, ..., msgN);
含义:该函数用于在Recovery界面输出字符串。其中msg一、…、msgN表示N个字符串参数,该函数至少须要指定一个参数。若是指定多个参数,会将这些参数值链接起来输出。
用法:ui_print(" hello world ");
run_program
原型:run_program(prog, arg1, .., argN);
含义:该函数用于执行程序,其中prog参数表示要执行的程序文件(要写完整路径),arg1argN表示要执行程序的参数。prog参数是必须的,其余参数都是可选的。
用法:run_program("/sbin/busybox","mount", "/system");
delete
    原型:delete(file1, file2, ..., fileN);
含义:该函数用于删除一个或多个文件。其中file1file2fileN表示要删除文件的路径,至少须要指定一个文件。
用法:delete("/system/xbin/su");
 
package_extract_dir
原型:package_extract_dir(package_path, destination_path);
含义:用于提取刷机包中package_path指定目录的全部文件到destination_path指定的目录。其中package_path参数表示刷机包中的目录,destination_path参数表示目标目录。
用法:package_extract_dir("system", "/system");
 
set_perm
原型:set_perm(uid, gid, mode, file1, file2, ..., fileN);
含义:用于设置一个或多个文件的权限。其中uid参数表示用户IDgid参数表示用户组ID。若是想让文件的用户和用户组都是rootuidgid须要都为0mode参数表示设置的权限,这个权限与chmod命令设置的权限彻底同样,例如,若是将一个文件设为任何用户均可以读写和执行的权限值是0777file1file2fileN表示要设置权限的文件的路径。
用法:set_perm(0, 0, 0777, "/system/xbin/su");
 
unmount
原型:unmount(mount_point);
含义:用于解除文件系统的挂载。其中mount_point参数表示文件系统。
用法:unmount("/system");
 
本文要编写的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 ----");# 以读写模式挂载/systemrun_program("/sbin/busybox", "mount","-o", "rw", "/system"); ui_print("----Delete /system/xbin/su ----");# 删除旧的su文件delete("/system/xbin/su");ui_print("- Extracting files");# 将刷机包中system目录的全部文件复制到/system目录中的相应位置package_extract_dir("system", "/system");ui_print("----- Setting permissions");# 设置ui命令为任何用户均可执行set_perm(0, 0, 0777, "/system/xbin/su");# 卸载/systemunmount("/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”中的rwremountmount命令的两个选项,表示从新将/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刷机包的全部文件(suupdater-scriptupdate-binary)都搞定了,接下来完成最后一步,就是将systemMETA-INF目录用zip格式压缩(压缩文件名任意取)。在下一节会介绍如何将这个zip格式文件中的内容刷到Nexus 7上。为了方便读者,在随书光盘上已经带了这个zip压缩文件(/recovery/su_update.zip),读者能够直接将该文件刷到Android设备上(除了Nexus 7外,其余使用Clockwork RecoveryAndroid设备也可使用该刷机包)。
 
可能有的读者会发现,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镜像。

5. 首次经过DIY方式提取ROOT权限
到如今为止,一切准备工做都已经完成了,如今只剩下最后一步,就是提取Nexus 7root权限。不过此次提取root权限彻底是DIY方式的,这也显得更有意义,由于做为程序员来讲,了解其中的过程比结果更重要。
如今须要用USB线链接Nexus 7PC,而后将上一节生成的zip文件上传到Nexus 7SD卡中。若是不想上传也没问题。一般在Recovery模式下选择sideloader上传方式,而不是事先上传到Nexus 7上,由于这样Nexus 7不须要在正常模式和Recovery模式之间来回切换,这也更适合须要频繁调试和刷机的程序员。
如今进入Nexus 7Recovery模式(正常模式下执行adb reboot recovery命令),若是读者已经将zip压缩文件(这里假设为update.zip)上传到Nexus 7SD卡中,选择“install zip from sdcard”(第2个菜单项),而后继续选择zip文件便可刷机。若是未将zip文件上传,选择“install zip from sideload”菜单项,而后Recovery模式会处于等待状态。如今adbsideload上传模式能够工做了。在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 pushadb shell命令在一块儿使用。

- EOF -

推荐阅读   点击标题可跳转

一、连Python产生器(Generator)的原理都解释不了,还敢说Python用了5年?

二、牛掰了!鸿蒙与Android完美融合,将鸿蒙设备当Android设备用

三、【鸿蒙学院】鸿蒙App开发直播学员提问与回答

四、【鸿蒙学院】鸿蒙IDE:下载、安装DevEco Studio

五、 Python高效编程之88条军规(2):你真的会格式化字符串吗?


关注「极客起源」公众号,加星标,不错过精彩技术干货


本文分享自微信公众号 - 极客起源(geekculture)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索