IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 编写仿supersu的权限管理工具 -> 正文阅读

[移动开发]编写仿supersu的权限管理工具

一、题目介绍

项目所有内容均需基于AOSP原版代码实现,版本可选择10或者11,测试设备建议使用Pixel3.
1、修改su代码,并实现root管理APK,功能至少包括APP申请root权限管理、root权限请求记录;
2、实现USB调试功能一键开关,WiFi一键开关;
3、实现AOSP系统内置指定WiFi名称与密码,刷机后可自动连接指定WiFi;
4、(可选)为AOSP11版本系统添加OpenSSH Server;

二、环境介绍

pixel3测试机一台
aosp11源代码(android-11.0.0_r1)
Android studio

三、操作步骤与程序设计

aosp源码下载

我们将Google的镜像地址替换为清华的地址https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest之后,下载的速度会更快一些。
首先找到一个空目录

mkdir android11
cd android11
repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest #初始化repo仓库
repo init --depth 1 -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-11.0.1_r1  #这里加入--depth 1 控制git的深度可以节省磁盘空间
repo sync -j20 #这一步执行时开始下载源码

接着我们就进入漫长的下载等待时间,如果速度能达到10m/s的话,估计要等待3-5个小时。

pixel3 硬件库下载

Android11刷机不能缺乏硬件库,否则刷机后会不断重启,首先我们要查看对应的BUILD_ID和pixel3的代号,查看源码中的build/core/build_id.mk文件,其中BUILD_ID=RP1A.200720.009,pixel3的代号是blueline,对应的硬件库下载地址是:
https://developers.google.cn/android/drivers?hl=zh-cn#bluelinerp1a.200720.009
将两个文件下载到源码根目录,然后执行,会自动生成vendor文件

~/code/android11$ tar zxvf google_devices-blueline-rp1a.200720.009-6cd41940.tgz
~/code/android11$ tar zxvf qcom-blueline-rp1a.200720.009-f772c38c.tgz
~/code/android11$ ./extract-google_devices-blueline.sh
~/code/android11$ ./extract-qcom-blueline.sh

这里补充一下,Google的每一代手机都有一个对应的代号,具体如下图:
在这里插入图片描述

编译aosp11源码

首先我们需要安装openjdk8

sudo apt-get update
sudo apt-get install openjdk-8-jdk

之后开始编译

~/code/android11$ source build/envsetup.sh
~/code/android11$ lunch aosp_blueline-userdebug
~/code/android11$ make -j16

编译的过程时间也是非常的长,大约三个小时,不过第一次编译之后,下一次再进行编译就不用整体编译一遍了,只需要编译修改的源码部分。

为pixel3烧录aosp11

首先我们解锁OEM,开发者选项里面解开就行
之后bootloader解锁,手机连接上电脑后,打开命令行

adb reboot bootloader
fastboot deivces #查看机器
fastboot flashing unlock
fastboot reboot

烧录,由于我使用的是linux服务器,本地机子是Windows,所以每次编译完成后给将编译好的系统进行打包下载到本地再烧录。
打包命令

tar -zcvf blueline.tar.gz blueline/  #此时要切换到/out/target/product/blueline路径下
#直接用linux烧录的话
~/code/android11$ export ANDROID_PRODUCT_OUT=./out/target/product/blueline
set ANDROID_PRODUCT_OUT=这里是blueline的路径  #如果是windows系统,也就是我的机器,则就用set命令
~/code/android11$ fastboot flashall -w

aosp11 root过程

在源码里面总共修改11个文件

system/core/init/selinux.cpp
frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
build/make/target/product/base_system.mk
/system/core/libcutils/fs_config.cpp
/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
system/core/adb/Android.bp
system/core/adb/daemon/main.cpp
system/core/fs_mgr/Android.bp
system/sepolicy/Android.mk
systen/sepolicy/definitions.mk
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java
1、修改SELinux权限为Permissive

system/core/init/selinux.cpp

bool IsEnforcing() {
    //改了这里
+    return false;
    //改了这里
    if (ALLOW_PERMISSIVE_SELINUX) {
        return StatusFromCmdline() == SELINUX_ENFORCING;
    }
    return true;
}

frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

                if (!Build.isBuildConsistent()) {
                    Slog.e(TAG, "Build fingerprint is not consistent, warning user");
                    mUiHandler.post(() -> {
                        if (mShowDialogs) {
                            AlertDialog d = new BaseErrorDialog(mUiContext);
                            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
                            d.setCancelable(false);
                            d.setTitle(mUiContext.getText(R.string.android_system_label));
                            d.setMessage(mUiContext.getText(R.string.system_error_manufacturer));
                            d.setButton(DialogInterface.BUTTON_POSITIVE,
                                    mUiContext.getText(R.string.ok),
                                    mUiHandler.obtainMessage(DISMISS_DIALOG_UI_MSG, d));
                            //改了这里
-                            d.show();
                            //改了这里
                        }
                    });
                }
            }
        }
2、增加su相关,确保apk root权限

需要编译su.cpp到system/bin目录下,为adb添加remount命令
build/make/target/product/base_system.mk

PRODUCT_PACKAGES += \
    remout \
    su \

注册用户组权限检测
system/extras/su/su.cpp

int main(int argc, char** argv) {
    // uid_t current_uid = getuid();
    // if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");

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

给su文件默认授予root权限
/system/core/libcutils/fs_config.cpp

    // the following two files are INTENTIONALLY set-uid, but they
    // are NOT included on user builds.
    { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
    { 06755, AID_ROOT,      AID_SHELL,     0, "system/bin/su" },
    { 06755, AID_ROOT,      AID_SHELL,     0, "system/xbin/su" },

/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

static void DropCapabilitiesBoundingSet(fail_fn_t fail_fn) {
  // for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {;
  //   if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
  //     if (errno == EINVAL) {
  //       ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
  //             "your kernel is compiled with file capabilities support");
  //     } else {
  //       fail_fn(CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)));
  //     }
  //   }
  // }
}
5、解锁fastboot,并关闭verity按需操作

system/core/adb/Android.bp

cc_defaults {
    name: "adbd_defaults",
    defaults: ["adb_defaults"],
//改了这里
-   cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+    //cflags: ["-UADB_HOST", "-DADB_HOST=0"],
+    cflags: [
+        "-UADB_HOST",
+        "-DADB_HOST=0",
+        "-UALLOW_ADBD_ROOT",
+        "-DALLOW_ADBD_ROOT=1",
+        "-DALLOW_ADBD_DISABLE_VERITY",
+        "-DALLOW_ADBD_NO_AUTH",
    ],
}


cc_library {
    name: "libadbd_services",
    defaults: ["adbd_defaults", "host_adbd_supported"],
    recovery_available: true,
    compile_multilib: "both",

	....

//改了这里
+	required: [ "remount",],
    
    target: {
        android: {
            srcs: [
                "daemon/abb_service.cpp",

system/core/adb/daemon/main.cpp

static void drop_privileges(int server_port) {
    ScopedMinijail jail(minijail_new());

    // Add extra groups:
    // AID_ADB to access the USB driver
    // AID_LOG to read system logs (adb logcat)
    // AID_INPUT to diagnose input issues (getevent)
    // AID_INET to diagnose network issues (ping)
    // AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
    // AID_SDCARD_R to allow reading from the SD card
    // AID_SDCARD_RW to allow writing to the SD card
    // AID_NET_BW_STATS to read out qtaguid statistics
    // AID_READPROC for reading /proc entries across UID boundaries
    // AID_UHID for using 'hid' command to read/write to /dev/uhid
    gid_t groups[] = {AID_ADB,          AID_LOG,          AID_INPUT,    AID_INET,
                      AID_NET_BT,       AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
                      AID_NET_BW_STATS, AID_READPROC,     AID_UHID};
    minijail_set_supplementary_gids(jail.get(), arraysize(groups), groups);

    // Don't listen on a port (default 5037) if running in secure mode.
    // Don't run as root if running in secure mode.
    if (should_drop_privileges()) {
        //改了这里
-       //const bool should_drop_caps = !__android_log_is_debuggable();
+       const bool should_drop_caps = false;
        if (should_drop_caps) {
            minijail_use_caps(jail.get(), CAP_TO_MASK(CAP_SETUID) | CAP_TO_MASK(CAP_SETGID));
        }

system/core/fs_mgr/Android.bp

    whole_static_libs: [
        "liblogwrap",
        "libdm",
        "libext2_uuid",
        "libfscrypt",
        "libfstab",
    ],
    cppflags: [
-       "-DALLOW_ADBD_DISABLE_VERITY=0"
+       "-UALLOW_ADBD_DISABLE_VERITY",
+       "-DALLOW_ADBD_DISABLE_VERITY=1",
    ],
    product_variables: {
        debuggable: {
            cppflags: [
                "-UALLOW_ADBD_DISABLE_VERITY",
                "-DALLOW_ADBD_DISABLE_VERITY=1",
            ],
        },
    },



    srcs: [
        "fs_mgr_remount.cpp",
    ],
    cppflags: [
-       "-DALLOW_ADBD_DISABLE_VERITY=0",
+       "-UALLOW_ADBD_DISABLE_VERITY",
+       "-DALLOW_ADBD_DISABLE_VERITY=1",
    ],
    product_variables: {
        debuggable: {
            cppflags: [
                "-UALLOW_ADBD_DISABLE_VERITY",
                "-DALLOW_ADBD_DISABLE_VERITY=1",
            ],
        },
    },

user版本启动overlayfs来装载remount对应分区
system/sepolicy/Android.mk

 ifneq ($(filter address,$(SANITIZE_TARGET)),)
   local_fc_files += $(wildcard $(addsuffix /file_contexts_asan, $(PLAT_PRIVATE_POLICY)))
 endif
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
+ifneq (,$(filter user userdebug eng,$(TARGET_BUILD_VARIANT)))
   local_fc_files += $(wildcard $(addsuffix /file_contexts_overlayfs, $(PLAT_PRIVATE_POLICY)))
 endif
 ifeq ($(TARGET_FLATTEN_APEX),true)

file_contexts.device.tmp :=
file_contexts.local.tmp :=

systen/sepolicy/definitions.mk

# Command to turn collection of policy files into a policy.conf file to be
# processed by checkpolicy
define transform-policy-to-conf
@mkdir -p $(dir $@)
$(hide) $(M4) --fatal-warnings $(PRIVATE_ADDITIONAL_M4DEFS) \
	-D mls_num_sens=$(PRIVATE_MLS_SENS) -D mls_num_cats=$(PRIVATE_MLS_CATS) \
-   -D target_build_variant=$(PRIVATE_TARGET_BUILD_VARIANT)
+   -D target_build_variant=eng \
	-D target_with_dexpreopt=$(WITH_DEXPREOPT) \
	-D target_arch=$(PRIVATE_TGT_ARCH) \
	-D target_with_asan=$(PRIVATE_TGT_WITH_ASAN) \
	-D target_with_native_coverage=$(PRIVATE_TGT_WITH_NATIVE_COVERAGE) \
	-D target_full_treble=$(PRIVATE_SEPOLICY_SPLIT) \
	-D target_compatible_property=$(PRIVATE_COMPATIBLE_PROPERTY) \
	-D target_treble_sysprop_neverallow=$(PRIVATE_TREBLE_SYSPROP_NEVERALLOW) \
	-D target_exclude_build_test=$(PRIVATE_EXCLUDE_BUILD_TEST) \
	-D target_requires_insecure_execmem_for_swiftshader=$(PRODUCT_REQUIRES_INSECURE_EXECMEM_FOR_SWIFTSHADER) \
	$(PRIVATE_TGT_RECOVERY) \
	-s $(PRIVATE_POLICY_FILES) > $@
endef
.KATI_READONLY := transform-policy-to-conf
6、默认开启OEM和去除OEM解锁警告提示

默认开启OEM解锁选项
frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

         protected void finishBoot() {
+            android.service.oemlock.OemLockManager mOemLockManager 
+            = (android.service.oemlock.OemLockManager) mContext.getSystemService(Context.OEM_LOCK_SERVICE);
+            mOemLockManager.setOemUnlockAllowedByUser(true);
+
             if (mBootCompleted && mCurrentUsbFunctionsReceived && mSystemReady) {
                 if (mPendingBootBroadcast) {
                     updateUsbStateBroadcastIfNeeded(getAppliedFunctions(mCurrentFunctions));

su代码修改,实现root权限管理APK,功能包括APP申请root权限管理,root权限请求记录。

首先我们来分析一下app应用如何执行root权限的命令,可以看出,应用先要执行su命令,然后再执行想要执行的命令,那我们在管理应用root权限的时候,首先我们需要知道是哪个应用需要执行root命令,还需要获取本机所有的应用,并控制这些应用app执行root命令的权限。

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class RootCommand {
    public static String runCommand(String command){
        Process process = null;
        String result = "";
        DataOutputStream os = null;
        DataInputStream is = null;
        try{
        //执行su命令
            process = Runtime.getRuntime().exec("su");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {

            }
            //获取命令行输入输出
            os = new DataOutputStream((process.getOutputStream()));
            is = new DataInputStream(process.getInputStream());
            //执行命令
            os.writeBytes(command + "\n");
            //退出命令行
            os.writeBytes("exit\n");
            os.flush();
            //获取执行命令后的输出结果
            String line = null;
            while((line = is.readLine())!= null){
                result+=line;
                result += "\n";
            }
            process.waitFor();
        }catch (IOException | InterruptedException e){
            e.printStackTrace();
        }
        finally {
            if (os != null){
                try{
                    os.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (is != null){
                try{
                    is.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (process != null){
                process.destroy();
            }
        }
        return result;
    }
}

root权限管理思路
1.我们让应用在执行su命令的时候获取这个应用的包名,具体实现方式是:在su源代码中实现localsocket连接,和应用层的supersu进行通信,然后通过getppid()获取调用su命令的应用的pid,之后再通过cat /proc/"+ pid +"/cmdline -r 命令获取进程名称(应用包名),之后将包名通过localsocket发送给supersu,在supersu里接收到包名后,检测此应用是否获得了root权限授权,决定是否切换为root权限。
2.利用List allApps = getPackageManager().getInstalledApplications(0);获取本机安装的所有app应用信息,其中包括包名,然后使用sqlite数据库将所有包名和是否获得root授权的信息建表,对权限进行管理。

su.cpp修改

//localsocket名称
#define PATH "supersu.localsocket"

int main(int argc, char **argv)
{
    int socketID, ret; //socketID,接收字符数
    char sendStr[10]; //socket发送缓冲区
    //获取调用su的应用pid
    pid_t fpid = getppid();
    snprintf(sendStr, 10, "%d", (int)fpid);
    int count = 0;
    //将int类型的pid转化为字符串
    for (int i = 0; i < 10; i++)
    {
        if (sendStr[i] != 0)
        {
            count = count*10 + (sendStr[i] - '0'); 
        }else{
            break;
        }
        
    }
    std::stringstream ss;
    std::string str;
    ss<<count;
    ss>>str;
    FILE *fp = NULL;
	char data[100] = {'0'};
    std::string cmd = "cat /proc/"+ str +"/cmdline";
	char buf[40];
    strcpy(buf, cmd.c_str());
    //执行cat 命令
    fp = popen(buf, "r");
	if (fp == NULL)
	{
		printf("popen error!\n");
		return 1;
	}
	while (fgets(data, sizeof(data), fp) != NULL)
	{
		printf("shell result is : %s\n", data);
	}
	pclose(fp);
    //建立localsocket连接
    socketID = socket_local_client(PATH, 0, SOCK_STREAM);
    if (socketID < 0)
    {
        return socketID;
    }
    //发送包名
    ret = write(socketID, data, strlen(data));
    if (ret < 0)
    {
        return ret;
    }
    char recvStr[10];
    //接收supersu的通知
    ret = read(socketID, recvStr, sizeof(recvStr));
    if (ret < 0)
    {
        return ret;
    }
    //如果supersu发送命令为‘1’,则证明此应用获得了root权限授权,否则不予执行root命令
    if (recvStr[0] == '1')
    {
        //uid_t current_uid = getuid();
        //if (current_uid != AID_ROOT && current_uid != AID_SHELL) error(1, 0, "not allowed");

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

        // The default user is root.
        uid_t uid = 0;
        gid_t gid = 0;

        // If there are any arguments, the first argument is the uid/gid/supplementary groups.
        if (*argv)
        {
            gid_t gids[10];
            int gids_count = sizeof(gids) / sizeof(gids[0]);
            extract_uidgids(*argv, &uid, &gid, gids, &gids_count);
            if (gids_count)
            {
                if (setgroups(gids_count, gids))
                {
                    error(1, errno, "setgroups failed");
                }
            }
            ++argv;
        }

        if (setgid(gid))
            error(1, errno, "setgid failed");
        if (setuid(uid))
            error(1, errno, "setuid failed");

        // Reset parts of the environment.
        setenv("PATH", _PATH_DEFPATH, 1);
        unsetenv("IFS");
        struct passwd *pw = getpwuid(uid);
        if (pw)
        {
            setenv("LOGNAME", pw->pw_name, 1);
            setenv("USER", pw->pw_name, 1);
        }
        else
        {
            unsetenv("LOGNAME");
            unsetenv("USER");
        }

        // Set up the arguments for exec.
        char *exec_args[argc + 1]; // Having too much space is fine.
        size_t i = 0;
        for (; *argv != NULL; ++i)
        {
            exec_args[i] = *argv++;
        }
        // Default to the standard shell.
        if (i == 0)
            exec_args[i++] = const_cast<char *>("/system/bin/sh");
        exec_args[i] = NULL;

        execvp(exec_args[0], exec_args);
        error(1, errno, "failed to exec %s", exec_args[0]);
        return 0;
    }

}

supersu关键代码:
1.应用初始化创建记录应用授权记录的数据库

//创建数据库
        dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
        //将Android设备中所有应用程序建表
        List<ApplicationInfo> allApps = getPackageManager().getInstalledApplications(0);
        for (ApplicationInfo ai : allApps) {
            //Log.d("packageName", ai.packageName);
            Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + ai.packageName + "'", null);
            //如果数据库中没有此项app记录,则插入数据
            if (cursor.getCount() == 0){
                if (ai.packageName.equals("edu.scse.supersu")){//先给supersu以root权限
                    dbHelper.getWritableDatabase().execSQL("insert into AppRoot values(null,?,1)", new String[]{ai.packageName});
                }else{
                    dbHelper.getWritableDatabase().execSQL("insert into AppRoot values(null,?,0)", new String[]{ai.packageName});
                }
            }
        }

2.
(1)注册服务,监听来自su的localsocket的连接并进行通信授权,如果执行root命令时没有被授予root权限,则弹出授权框,询问用户是否进行授权。

RootManagementService.java

package edu.scse.supersu.service;

import android.Manifest;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManager;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Objects;

import edu.scse.supersu.Sql.MyDatabaseHelper;
import edu.scse.supersu.common.RootCommand;

public class RootManagementService extends Service {

    private static final String localSocketName = "supersu.localsocket";
    private ServerThread mThread = null;
    private MyDatabaseHelper dbHelper;

    public RootManagementService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //打开sqlite数据库,查询授权情况
        dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
        System.out.println("service is created\n");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("service 启动\n");
        startServer();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //stopServer();
        System.out.println("serivce 退出\n");
    }

    //开启服务端
    private void startServer() {
        stopServer();
        mThread = new ServerThread();
        mThread.start();
    }

    //关闭服务端
    private void stopServer() {
        if (mThread != null) {
            mThread.exit();
            mThread = null;
        }
    }

    private class ServerThread extends Thread {
        private boolean exit = false;

        public void run() {
            LocalServerSocket server = null;
            try {
                //创建localsocket
                server = new LocalServerSocket(localSocketName);
                //监听localsocket连接
                while (!exit) {
                    LocalSocket connect = server.accept();
                    Credentials cre = connect.getPeerCredentials();
                    Log.i("RootManagement", "accept socket uid:" + cre.getUid());
                    new ConnectThread(connect).start();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    server.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        public void exit() {
            exit = true;
        }
    }

    class ConnectThread extends Thread {
        LocalSocket socket = null;
        BufferedReader mBufferedReader = null;
        InputStream input = null;
        PrintWriter send = null;
        String readString = null;

        public ConnectThread(LocalSocket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                input = socket.getInputStream();
                byte[] buffer = new byte[100];
                int len = input.read(buffer);
                send = new PrintWriter(socket.getOutputStream());
                String packageName = new String(buffer, "utf-8");
                packageName = packageName.substring(0, len);
                Log.d("RootManagement", "packageName is :" + packageName);
                //select此app记录
                Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + packageName + "'", null);
                if (cursor.getCount() == 0) {
                    send.println("B\0");
                }
                cursor.moveToNext();
                //查看是否授予root权限
                String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
                if (isRoot.charAt(0) == '1') {
                    //授予则给正确指令
                    send.println("1\0");
                } else {
                    //如果没有授权root权限,则弹出授权框
                    Looper.prepare();
                    AlertDialog.Builder builder = new AlertDialog.Builder(getApplicationContext())
                            .setIcon(android.R.drawable.ic_dialog_info)
                            .setTitle("root授权通知")
                            .setMessage("是否要授予此应用root权限?")
                            .setPositiveButton("授予",
                                    new DialogInterface.OnClickListener() {
                                        public void onClick(DialogInterface dialog,
                                                            int whichButton) {
                                            send.println("A\0");
                                            //由于Looper.loop 后面的代码不会执行,所以这里就要结束socket连接,否则会卡死。
                                            //断开连接
                                            send.flush();
                                            send.close();
                                            try {
                                                socket.close();
                                            } catch (IOException e) {
                                                e.printStackTrace();
                                            }
                                        }
                                    });
                    builder.setNegativeButton("拒绝", null);
                    AlertDialog dialog = builder.create();
                    //设置点击其他地方不可取消此 Dialog
                    dialog.setCancelable(false);
                    dialog.setCanceledOnTouchOutside(false);
                    //8.0系统加强后台管理,禁止在其他应用和窗口弹提醒弹窗,如果要弹,必须使用TYPE_APPLICATION_OVERLAY,否则弹不出
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        Objects.requireNonNull(dialog.getWindow()).setType((WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY));
                    } else {
                        Objects.requireNonNull(dialog.getWindow()).setType((WindowManager.LayoutParams.TYPE_SYSTEM_ALERT));
                    }
                    dialog.show();
                    Looper.loop();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                //断开连接
                send.flush();
                send.close();
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

(2)root权限管理页面

activity_root_manager.xml 应用信息列表

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".RootManager">

    <androidx.appcompat.widget.SearchView
        android:id="@+id/search"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:iconifiedByDefault="false"></androidx.appcompat.widget.SearchView>

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/RootManage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></androidx.recyclerview.widget.RecyclerView>
    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</LinearLayout>

array_item.xml 列表子项

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginRight="10dp"
    android:descendantFocusability="beforeDescendants"
    android:minHeight="80dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center|start"
        android:orientation="horizontal"
        android:padding="10dp">

        <ImageView
            android:id="@+id/appIcon"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginRight="10dp"
            android:scaleType="fitXY" />

        <LinearLayout
            android:layout_width="218dp"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center|start"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/appName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:gravity="start"
                    android:textColor="#000"
                    android:textSize="18sp"
                    android:textStyle="bold" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="15dp"
                android:gravity="center|start"
                android:orientation="horizontal">

                <TextView
                    android:id="@+id/packageName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="start"
                    android:padding="3dp"
                    android:textSize="12sp" />
            </LinearLayout>
        </LinearLayout>

        <Button
            android:id="@+id/grant"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:text="授予"
            android:textSize="19dp">

        </Button>
    </LinearLayout>
</LinearLayout>

RootManager.java root权限管理

package edu.scse.supersu;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SearchView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;

import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.scse.supersu.Sql.MyDatabaseHelper;
import edu.scse.supersu.adapter.RootAdapter;

public class RootManager extends AppCompatActivity {

    RecyclerView rootManagerList;
    List<Map<String, Object>> appList = new ArrayList<>();
    SearchView searchView;
    SwipeRefreshLayout swipeRefreshLayout;
    MyDatabaseHelper dbHelper;
    RootAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_root_manager);

        rootManagerList = findViewById(R.id.RootManage);
        swipeRefreshLayout = findViewById(R.id.refresh);
        searchView = findViewById(R.id.search);
        rootManagerList.setHasFixedSize(true);
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
        //设置纵向滚动
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        rootManagerList.setLayoutManager(linearLayoutManager);
        //设置Adapter
        adapter = new RootAdapter(this, appList);
        rootManagerList.setAdapter(adapter);
        getAllAppInfo();

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refreshList();

            }
        });

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String query) {
                searchAppInfo(query);
                return true;
            }

            @Override
            public boolean onQueryTextChange(String newText) {
                if (newText.equals("")) {
                    getAllAppInfo();
                }
                return true;
            }
        });

    }

    private void searchAppInfo(String query) {
        List<Map<String, Object>> tempList = new ArrayList<>();
        appList.clear();
        dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
        Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName LIKE '%" + query + "%'", null);
        if (cursor.getCount() != 0) {
            while (cursor.moveToNext()) {
                Map<String, Object> item = new HashMap<String, Object>();
                String packageName = cursor.getString(cursor.getColumnIndex("appPackageName"));
                String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
                try {
                    Drawable appIcon = getPackageManager().getApplicationIcon(packageName);
                    item.put("appIcon", appIcon);
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                try {
                    ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(packageName, 0);
                    String name = (String) getPackageManager().getApplicationLabel(applicationInfo);
                    item.put("appName", name);
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
                item.put("appPackageName", packageName);
                item.put("isRoot", isRoot);
                tempList.add(item);
            }
        }
        appList.addAll(tempList);
        adapter.notifyDataSetChanged();
    }

    private void getAllAppInfo() {
        List<Map<String, Object>> tempList = new ArrayList<>();
        appList.clear();
        List<ApplicationInfo> allApps = getPackageManager().getInstalledApplications(0);
        for (ApplicationInfo info : allApps) {
            Map<String, Object> item = new HashMap<String, Object>();
            String name = (String) getPackageManager().getApplicationLabel(info);
            item.put("appName", name);
            try {
                Drawable appIcon = getPackageManager().getApplicationIcon(info.packageName);
                item.put("appIcon", appIcon);
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            item.put("appPackageName", info.packageName);
            dbHelper = new MyDatabaseHelper(this, "/data/data/edu.scse.supersu/databases/rootManager.db", 1);
            Cursor cursor = dbHelper.getReadableDatabase().rawQuery("select * from AppRoot where appPackageName = '" + info.packageName + "'", null);
            if (cursor.getCount() != 0) {
                cursor.moveToNext();
                String isRoot = cursor.getString(cursor.getColumnIndex("isRoot"));
                item.put("isRoot", isRoot);
            }
            tempList.add(item);
        }
        appList.addAll(tempList);
        adapter.notifyDataSetChanged();
    }

    private void refreshList() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        getAllAppInfo();//重新生成数据
                        adapter.notifyDataSetChanged();
                        swipeRefreshLayout.setRefreshing(false);
                    }
                });
            }
        }).start();
    }

    private void searchRefreshList() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        adapter.notifyDataSetChanged();
                        swipeRefreshLayout.setRefreshing(false);
                    }
                });
            }
        }).start();
    }
}

一键开启USB调试和WIFI一键开关

思路:应用获取root权限之后,就可以通过执行adb命令来打开usb调试和wifi

实现USB调试功能一键开关,WiFi一键开关

  • USB调试

    /system/build.prop
    persist.service.adb.enable=1
    persist.service.debuggable=1
    persist.sys.usb.config=mtp,adb
    
    settings put global adb_enabled 0
    
  • WiFi

    su -c 'svc wifi enable'
    
//打开USB调试
        openUSB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String result = RootCommand.runCommand("settings put global adb_enabled 1");
                System.out.println(result);
            }
        });
        //关闭USB调试
        closeUSB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String result = RootCommand.runCommand("settings put global adb_enabled 0");
                System.out.println(result);
            }
        });
        //打开WiFi
        openWiFi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String result = RootCommand.runCommand("svc wifi enable");
                System.out.println(result);
            }
        });
        //关闭WiFi
        closeWiFi.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String result = RootCommand.runCommand("svc wifi disable");
                System.out.println(result);
            }
        });

实现AOSP系统内置制定WiFi名称与密码,刷机后可自动链接制定WiFi

需要修改的文件

out/target/product/generic/vendor/etc/wifi/wpa_supplicant.conf
external/wpa_supplicant_8/wpa_supplicant/wpa_supplicant.conf

修改wpa_supplicant.conf

network={
 ssid="WiFi名称"
 psk="WiFi密码"
 key_mgmt=WPA-PSK
}

为AOSP11版本系统添加OpenSSH

Android11源代码中已经有了openssh包,位于external/openssh;Android系统编译的时候默认没有添加openssh,所以需要在Android.mk中配置openssh编译。

openssh包含以下模块:
scp, sftp, ssh, sshd, sshd_config, ssh-keygen, start-ssh

device/google/crosshatch/aosp_blueline.mk中进行以下修改

PRODUCT_PACKAGES += \
   com.android.vndk.current.on_vendor \
                scp \
                sftp \
                ssh \
                sshd \
                sshd_config \
               ssh-keygen \
               start-ssh \
	           wpa_supplicant

系统编译完之后,能够看到如下文件

/system/bin/ssh
/system/bin/ssh-keygen
/system/bin/sshd
/system/bin/start-ssh
/system/bin/scp
/system/bin/sftp
/system/etc/ssh/sshd_config

配置ssh
(1)创建目录结构

mkdir -p /data/ssh/empty
chmod 700 /data/ssh
chmod 700 /data/ssh/empty

(2)生成配置文件

cat /system/etc/ssh/sshd_config | \
    sed 's/#PermitRootLogin yes$/PermitRootLogin without-password/' | \
    sed 's/#RSAAuthentication yes/RSAAuthentication yes/' | \
    sed 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' | \
    sed 's/PasswordAuthentication no/#PasswordAuthentication no/' | \
    sed 's/#PermitEmptyPasswords no/PermitEmptyPasswords yes/' | \
    sed 's/#ChallengeResponseAuthentication yes/ChallengeResponseAuthentication yes/' | \
    sed 's/#UsePrivilegeSeparation yes/UsePrivilegeSeparation no/' | \
    sed 's;/usr/libexec/sftp-server;internal-sftp;' > \
    /data/ssh/sshd_config
chmod 600 /data/ssh/sshd_config

(3)生成密钥
在Windows/Linux上通过下面的命令来生成密钥

ssh-keygen -t rsa -C "your_email_address"

上面的命令会在主目录下生成.ssh目录, 目录包含id_rsa(私钥)和id_rsa.pub(公钥)两个文件,然后通过adb等命令将id_rsa.pub上传至Android中

adb push id_rsa.pub /data/ssh/authorized_keys
chmod 600 /data/ssh/authorized_keys
chown root:root /data/ssh/authorized_keys

(4)生成启动脚本

mkdir -p /data/local/userinit.d
cat /system/bin/start-ssh | \
    sed 's;/system/etc/ssh/sshd_config;/data/ssh/sshd_config;' > \
    /data/local/userinit.d/99sshd
chmod 755 /data/local/userinit.d/99sshd

通过上面的命令单独生成一个启动脚本
然后就可以通过执行下面的脚本来启动sshd

/data/local/userinit.d/99sshd

(5)连接sshd
使用命令即可连接sshd

ssh root@ip
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-27 11:58:42  更:2021-08-27 11:59:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:36:47-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码