元始天尊 发表于 2016-5-19 14:46:44

root android模拟器的最通用方式

这里介绍的是绝对可行的root方式,对于android模拟器,无论x86 x64 ,无论4.0 5.0 6.0都适用
真机的话5.0和6.0,root成功率比较低,因此不适合在上面做root方面的测试,这时只能考虑虚拟机,因为虚拟机adb有root权限,利用adb root权限便可以提升su权限
网上盛传的方法是http://androidsu.com/superuser https://www.0xaa55.com/forum.php?mod=viewthread&tid=1648&extra=
需要superuser.apk,我分析了该网站的su,发现除了传统su的流程(setuid setgid)外,还加入了和superuser.apk通信的这一步
superuser.apk用于单个app权限设置。
遗憾的是该作者并没完成mips架构,和5.0以上的su适配

默认的su,是只允许root权限和shell权限来执行的,因此一般的app并不能使用,因此这也是我们修改的重点,同时还要给su加上必要的环境变量
我这种方式,去除了权限判断部分,因此减少了某些情况下由于这部分带来的root权限未成功获取的bug

这里提供一种方式,可以支持全平台
先构造文件夹:
/jni
/jni/su.cpp
/jni/Android.mk
/jni/Application.mk


//su.cpp
/*
* Copyright (C) 2008 The Android Open Source Project
*
* 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 <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>

#define AID_ROOT             0/* traditional unix root user */
#define AID_SYSTEM      1000/* system server */
#define AID_SHELL         2000/* adb and debug shell user */
#define AID_NOBODY      9999
#define AID_APP          10000/* first app user */
#define AID_USER      100000/* offset for uid ranges for each user */


void pwtoid(const char* tok, uid_t* uid, gid_t* gid) {
    struct passwd* pw = getpwnam(tok);
    if (pw)
    {
      if (uid) *uid = pw->pw_uid;
      if (gid) *gid = pw->pw_gid;
    }
    else
    {
      char* end;
      errno = 0;
      uid_t tmpid = strtoul(tok, &end, 10);
      if (errno != 0 || end == tok)
              printf("invalid uid/gid '%s'", tok);
      if (uid) *uid = tmpid;
      if (gid) *gid = tmpid;
    }
}

void extract_uidgids(const char* uidgids, uid_t* uid, gid_t* gid, gid_t* gids, int* gids_count) {
    char *clobberablegids;
    char *nexttok;
    char *tok;
    int gids_found;

    if (!uidgids || !*uidgids)
    {
      *gid = *uid = 0;
      *gids_count = 0;
      return;
    }

    clobberablegids = strdup(uidgids);
    strcpy(clobberablegids, uidgids);
    nexttok = clobberablegids;
    tok = strsep(&nexttok, ",");
    pwtoid(tok, uid, gid);
    tok = strsep(&nexttok, ",");
    if (!tok)
    {
      /* gid is already set above */
      *gids_count = 0;
      free(clobberablegids);
      return;
    }
    pwtoid(tok, NULL, gid);
    gids_found = 0;
    while ((gids_found < *gids_count) && (tok = strsep(&nexttok, ",")))
    {
      pwtoid(tok, NULL, gids);
      gids_found++;
      gids++;
    }
    if (nexttok && gids_found == *gids_count)
    {
      fprintf(stderr, "too many group ids\n");
    }
    *gids_count = gids_found;
    free(clobberablegids);
}

int main(int argc, char** argv)
{
    uid_t current_uid = getuid();

    // Handle -h and --help.
    ++argv;
    if (*argv && (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0))
    {
      fprintf(stderr,
                "usage: su ...]] ]\n"
                "\n"
                "Switch to WHO (default 'root') and run the given command (default sh).\n"
                "\n"
                "where WHO is a comma-separated list of user, group,\n"
                "and supplementary groups in that order.\n"
                "\n");
      return 0;
    }

    uid_t uid = 0;
    gid_t gid = 0;

    if (*argv)
    {
      gid_t gids;
      int gids_count = sizeof(gids)/sizeof(gids);
      extract_uidgids(*argv, &uid, &gid, gids, &gids_count);
      if (gids_count) {
            if (setgroups(gids_count, gids))
            {
                printf("setgroups failed");
            }
      }
      ++argv;
    }

    if (setgid(gid))
            printf("setgid failed");
    if (setuid(uid))
            printf("setuid failed");

    //设置环境变量
    setenv("PATH", "/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin", 1);
    setenv("LD_LIBRARY_PATH", "/vendor/lib:/system/lib", 1);
    setenv("ANDROID_BOOTLOGO", "1", 1);
    setenv("ANDROID_ROOT", "/system", 1);
    setenv("ANDROID_DATA", "/data", 1);
    setenv("ANDROID_ASSETS", "/system/app", 1);
    setenv("EXTERNAL_STORAGE", "/sdcard", 1);
    setenv("ASEC_MOUNTPOINT", "/mnt/asec", 1);
    setenv("LOOP_MOUNTPOINT", "/mnt/obb", 1);
    char* exec_args;
    size_t i = 0;
    for (; *argv != NULL; ++i)
    {
      exec_args = *argv++;
    }

    if (i == 0) exec_args = "/system/bin/sh";
    exec_args = NULL;

    execvp(exec_args, exec_args);
    printf("failed to exec %s", exec_args);
}

//Android.mk
LOCAL_SRC_FILES := su.cpp
LOCAL_MODULE := su
LOCAL_MODULE_FILENAME := su
LOCAL_LDFLAGS += -fPIE -pie
LOCAL_CFLAGS += -fPIE
include $(BUILD_EXECUTABLE)

//Application.mk   根据自己的需求调整
APP_ABI := armeabi
APP_PLATFORM := android-15



在jni目录下执行ndk-build即可,生成的su文件替换成/system/bin/mysu
模拟器中:
adb remount                              //使/system可写
adb push \??su /system/bin/mysu
adb shell chmod 6777 /system/bin/mysu      //使普通进程可以执行su并获取root权限

我们用简单的代码测试:

    public static boolean canRunRootCommands() {
      boolean retval = false;
      Process suProcess;

      try {
            suProcess = Runtime.getRuntime().exec("mysu");

            DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
            DataInputStream osRes = new DataInputStream(suProcess.getInputStream());

            if (null != os && null != osRes) {
                // Getting the id of the current user to check if this is root
                os.writeBytes("id\n");
                os.flush();

                String currUid = osRes.readLine();
                boolean exitSu = false;
                if (null == currUid) {
                  retval = false;
                  exitSu = false;
                  Log.d("ROOT", "Can't get root access or denied by user");
                } else if (true == currUid.contains("uid=0")) {
                  retval = true;
                  exitSu = true;
                  Log.d("ROOT", "Root access granted");
                } else {
                  retval = false;
                  exitSu = true;
                  Log.d("ROOT", "Root access rejected: " + currUid);
                }

                if (exitSu) {
                  os.writeBytes("exit\n");
                  os.flush();
                }
            }
      } catch (Exception e) {
            // Can't get root !
            // Probably broken pipe exception on trying to write to output
            // stream after su failed, meaning that the device is not rooted

            retval = false;
            Log.d("ROOT",
                  "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
      }

      return retval;
    }


可以发现是成功的
页: [1]
查看完整版本: root android模拟器的最通用方式