阅读 196

编写仿supersu的权限管理工具(aosp11 root、实现aosp系统内置wifi、root管理apk)

一、题目介绍

项目所有内容均需基于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的镜像地址替换为清华的地址mirrors.tuna.tsinghua.edu.cn/git/AOSP/pl… 首先找到一个空目录

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,对应的硬件库下载地址是: developers.google.cn/android/dri… 将两个文件下载到源码根目录,然后执行,会自动生成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


作者:有理想的懒羊羊
链接:https://juejin.cn/post/7028771190407692324


文章分类
代码人生
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐