diff --git a/PermissionManager/.gitignore b/PermissionManager/.gitignore
new file mode 100644
index 00000000..643d213c
--- /dev/null
+++ b/PermissionManager/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/PermissionManager/app/.gitignore b/PermissionManager/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/PermissionManager/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/PermissionManager/app/build.gradle b/PermissionManager/app/build.gradle
new file mode 100644
index 00000000..4a6c8874
--- /dev/null
+++ b/PermissionManager/app/build.gradle
@@ -0,0 +1,51 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "com.linux.permissionmanager"
+ minSdkVersion 26
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ externalNativeBuild {
+ cmake {
+ cppFlags "-std=c++17"
+ abiFilters "arm64-v8a" //需要什么构架的so,就在这边添加即
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ externalNativeBuild {
+ cmake {
+ path "src/main/cpp/CMakeLists.txt"
+ version "3.10.2"
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.2.0'
+ implementation 'com.google.android.material:material:1.3.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.2'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
+}
\ No newline at end of file
diff --git a/PermissionManager/app/proguard-rules.pro b/PermissionManager/app/proguard-rules.pro
new file mode 100644
index 00000000..64b4a059
--- /dev/null
+++ b/PermissionManager/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java b/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..d7eca3a4
--- /dev/null
+++ b/PermissionManager/app/src/androidTest/java/com/linux/permissionmanager/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.linux.permissionmanager;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.linux.permissionmanager", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/AndroidManifest.xml b/PermissionManager/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..37686dd9
--- /dev/null
+++ b/PermissionManager/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/cpp/CMakeLists.txt b/PermissionManager/app/src/main/cpp/CMakeLists.txt
new file mode 100644
index 00000000..42368b42
--- /dev/null
+++ b/PermissionManager/app/src/main/cpp/CMakeLists.txt
@@ -0,0 +1,53 @@
+# For more information about using CMake with Android Studio, read the
+# documentation: https://d.android.com/studio/projects/add-native-code.html
+
+# Sets the minimum version of CMake required to build the native library.
+
+cmake_minimum_required(VERSION 3.10.2)
+
+# Declares and names the project.
+
+project("permissionmanager")
+
+# Creates and names a library, sets it as either STATIC
+# or SHARED, and provides the relative paths to its source code.
+# You can define multiple libraries, and CMake builds them for you.
+# Gradle automatically packages shared libraries with your APK.
+
+add_library( # Sets the name of the library.
+ root
+
+ # Sets the library as a shared library.
+ SHARED
+
+ # Provides a relative path to your source file(s).
+ ../../../../../testRoot/process64_inject.cpp
+ ../../../../../testRoot/adb64_helper.cpp
+ ../../../../../testRoot/ptrace_arm64_utils.cpp
+ ../../../../../testRoot/su_install_helper.cpp
+ ../../../../../testRoot/base64.cpp
+ root.cpp)
+
+# Searches for a specified prebuilt library and stores the path as a
+# variable. Because CMake includes system libraries in the search path by
+# default, you only need to specify the name of the public NDK library
+# you want to add. CMake verifies that the library exists before
+# completing its build.
+
+find_library( # Sets the name of the path variable.
+ log-lib
+
+ # Specifies the name of the NDK library that
+ # you want CMake to locate.
+ log )
+
+# Specifies libraries CMake should link to your target library. You
+# can link multiple libraries, such as libraries you define in this
+# build script, prebuilt third-party libraries, or system libraries.
+
+target_link_libraries( # Specifies the target library.
+ root
+
+ # Links the target library to the log library
+ # included in the NDK.
+ ${log-lib} )
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/cpp/root.cpp b/PermissionManager/app/src/main/cpp/root.cpp
new file mode 100644
index 00000000..178e5bd0
--- /dev/null
+++ b/PermissionManager/app/src/main/cpp/root.cpp
@@ -0,0 +1,259 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "../../../../../testRoot/testRoot.h"
+#include "../../../../../testRoot/kernel_root_helper.h"
+#include "../../../../../testRoot/process64_inject.h"
+#include "../../../../../testRoot/adb64_helper.h"
+#include "../../../../../testRoot/su_install_helper.h"
+
+using namespace std;
+string getCapabilityInfo()
+{
+ __uid_t now_uid, now_euid, now_suid;
+ if (getresuid(&now_uid, &now_euid, &now_suid)) {
+ return "FAILED getresuid()";
+ }
+
+
+ __gid_t now_gid, now_egid, now_sgid;
+ if (getresgid(&now_gid, &now_egid, &now_sgid)) {
+ return "FAILED getresgid()";
+ }
+
+ stringstream sstrCapInfo;
+ sstrCapInfo<< "now_uid="<pid = getpid();
+ cap_header->version = _LINUX_CAPABILITY_VERSION_3; //_1、_2、_3
+
+ if (capget(cap_header, cap_data) < 0) {
+ return "FAILED capget()";
+ // perror("FAILED capget()");
+ //exit(1);
+ }
+ sstrCapInfo << "Cap data effective:"<<(uint64_t *) cap_data->effective<<", permitted:"<<(uint64_t *) cap_data->permitted<<", inheritable:"<<(uint64_t *) cap_data->inheritable<<"\n";
+
+ FILE * fp = popen("getenforce", "r");
+ if (fp)
+ {
+ char cmd[512] = { 0 };
+ fread(cmd, 1, sizeof(cmd), fp);
+ pclose(fp);
+
+ sstrCapInfo<< "SELinux status: "<< cmd;
+ }
+
+ return sstrCapInfo.str();
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_getCapabilityInfo(
+ JNIEnv* env,
+ jobject /* this */) {
+
+ return env->NewStringUTF(getCapabilityInfo().c_str());
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_linux_permissionmanager_MainActivity_getRoot(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey) {
+ return get_root(rootKey);
+}
+extern "C" JNIEXPORT jint JNICALL
+Java_com_linux_permissionmanager_MainActivity_disableSElinux(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey) {
+ return safe_disable_selinux(rootKey);
+}
+
+extern "C" JNIEXPORT jint JNICALL
+Java_com_linux_permissionmanager_MainActivity_enableSElinux(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey) {
+ return safe_enable_selinux(rootKey);
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_runNormalCmd(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey,
+ jstring cmd) {
+
+ const char *str1 = env->GetStringUTFChars(cmd, 0);
+ string strCmd= str1;
+ env->ReleaseStringUTFChars(cmd, str1);
+
+ char szResult[0x1000] = {0};
+ ssize_t ret = safe_run_normal_cmd(rootKey, strCmd.c_str(), szResult, sizeof(szResult));
+ stringstream sstr;
+ sstr << "runRootCmd ret val:" << ret << ", result:" << szResult;
+ return env->NewStringUTF(sstr.str().c_str());
+}
+
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_runRootCmd(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey,
+ jstring cmd) {
+
+ const char *str1 = env->GetStringUTFChars(cmd, 0);
+ string strCmd= str1;
+ env->ReleaseStringUTFChars(cmd, str1);
+
+ stringstream sstr;
+ char szResult[0x1000] = {0};
+ ssize_t inject = safe_inject_adbd64_run_cmd_wrapper(rootKey, strCmd.c_str(), szResult, sizeof(szResult));
+ //ssize_t inject = safe_inject_adbd64_run_cmd_wrapper(rootKey, strCmd.c_str(),NULL, 0);
+ sstr << "runRootCmd ret val:" << inject << ", result:" << szResult;
+ return env->NewStringUTF(sstr.str().c_str());
+}
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_adbRoot(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey) {
+
+ stringstream sstr;
+ char szResult[0x1000] = {0};
+ ssize_t inject = safe_inject_adbd64_run_cmd_wrapper(rootKey, "id", NULL, 0, false ,true ,false, false);
+ sstr << "adbRoot ret val:" << inject << ", result:" << szResult;
+ return env->NewStringUTF(sstr.str().c_str());
+}
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_installSuTools(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey,
+ jstring basePath,
+ jstring suToolsFilePath) {
+
+ const char *str1 = env->GetStringUTFChars(basePath, 0);
+ string strBasePath= str1;
+ env->ReleaseStringUTFChars(basePath, str1);
+
+ str1 = env->GetStringUTFChars(suToolsFilePath, 0);
+ string strSuFilePath= str1;
+ env->ReleaseStringUTFChars(suToolsFilePath, str1);
+
+ stringstream sstr;
+
+ //安装su工具套件
+ std::string su_hidden_path;
+ int install_su_tools_ret = safe_install_su_tools(rootKey, strBasePath.c_str(), su_hidden_path);
+ sstr << "install_su_tools ret val:" << install_su_tools_ret<<", su_hidden_path:" << su_hidden_path << std::endl;
+
+ if (install_su_tools_ret == -504) {
+ //需要补一下su文件的释放
+ std::string tmpCmd = "cp "+strSuFilePath + " " + su_hidden_path+"/su";
+ int cp_ret = safe_run_normal_cmd(rootKey, tmpCmd.c_str(), NULL, 0);
+ if(cp_ret != 0) {
+ sstr << "safe_run_normal_cmd cp_ret val:" <NewStringUTF(sstr.str().c_str());
+ }
+ install_su_tools_ret = safe_install_su_tools(rootKey, strBasePath.c_str(), su_hidden_path);
+ sstr << "install_su_tools ret val:" << install_su_tools_ret<<", su_hidden_path:" << su_hidden_path << std::endl;
+ }
+ if (install_su_tools_ret != 0) {
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ sstr << "installSuTools done.";
+ return env->NewStringUTF(sstr.str().c_str());
+}
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_uninstallSuTools(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey,
+ jstring basePath) {
+
+ const char *str1 = env->GetStringUTFChars(basePath, 0);
+ string strBasePath= str1;
+ env->ReleaseStringUTFChars(basePath, str1);
+
+ stringstream sstr;
+
+ int uninstall_su_tools_ret = safe_uninstall_su_tools(rootKey, strBasePath.c_str());
+ sstr << "uninstallSuTools ret val:" << uninstall_su_tools_ret << std::endl;
+
+ if (uninstall_su_tools_ret != 0) {
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ sstr << "uninstallSuTools done.";
+ return env->NewStringUTF(sstr.str().c_str());
+}
+extern "C" JNIEXPORT jstring JNICALL
+Java_com_linux_permissionmanager_MainActivity_autoSuEnvInject(
+ JNIEnv* env,
+ jobject /* this */,
+ jlong rootKey,
+ jstring targetProcessCmdline,
+ jstring basePath) {
+
+ const char *str1 = env->GetStringUTFChars(targetProcessCmdline, 0);
+ string strTargetProcessCmdline= str1;
+ env->ReleaseStringUTFChars(targetProcessCmdline, str1);
+
+ str1 = env->GetStringUTFChars(basePath, 0);
+ string strBasePath= str1;
+ env->ReleaseStringUTFChars(basePath, str1);
+
+ stringstream sstr;
+
+ //杀光所有历史进程
+ std::vector vOut;
+ int find_all_cmdline_process_ret = safe_find_all_cmdline_process(rootKey, strTargetProcessCmdline.c_str(), vOut);
+ sstr << "find_all_cmdline_process ret val:"<< find_all_cmdline_process_ret<<", cnt:"<NewStringUTF(sstr.str().c_str());
+ }
+ std::string kill_cmd;
+ for (pid_t t : vOut) {
+ kill_cmd += "kill -9 ";
+ kill_cmd += std::to_string(t);
+ kill_cmd += ";";
+ }
+ int kill_ret = run_normal_cmd(rootKey, kill_cmd.c_str());
+ sstr << "kill_ret ret val:"<< kill_ret << std::endl;
+ if (kill_ret != 0) {
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+
+ //注入su环境变量到指定进程
+ std::string su_hidden_path;
+ int install_su_tools_ret = safe_install_su_tools(rootKey, strBasePath.c_str(), su_hidden_path);
+ sstr << "install_su_tools ret val:" << install_su_tools_ret<<", su_hidden_path:" << su_hidden_path << std::endl;
+ if (install_su_tools_ret != 0) {
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+
+ int pid = safe_wait_and_find_cmdline_process(rootKey, strTargetProcessCmdline.c_str());
+
+ sstr << "autoSuEnvInject("<< pid<<", " << su_hidden_path<<")" << std::endl;
+ ssize_t ret = safe_inject_process_env64_PATH_wrapper(rootKey, pid, su_hidden_path.c_str());
+ sstr << "autoSuEnvInject ret val:" << ret << std::endl;
+
+ if (ret != 0) {
+ return env->NewStringUTF(sstr.str().c_str());
+ }
+ sstr << "autoSuEnvInject done.";
+ return env->NewStringUTF(sstr.str().c_str());
+}
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java
new file mode 100644
index 00000000..c2a8ec6b
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Adapter/SelectAppRecyclerAdapter.java
@@ -0,0 +1,110 @@
+package com.linux.permissionmanager.Adapter;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.Model.SelectAppRecyclerItem;
+import com.linux.permissionmanager.R;
+
+import java.util.List;
+
+public class SelectAppRecyclerAdapter extends RecyclerView.Adapter {
+
+
+
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public View v;
+ public ImageView select_app_icon;
+ public TextView select_app_text;
+ public TextView select_package_name;
+ // TODO Auto-generated method stub
+ public ViewHolder(View v) {
+ super(v);
+ this.v = v;
+ }
+
+ }
+
+ private int resourceId;
+ private List objects;
+ private PopupWindow popupWindow;
+ private Handler selectAppItemCallback;
+ public SelectAppRecyclerAdapter(Context context, int textViewResourceId, List objects, PopupWindow popupWindow, Handler selectAppItemCallback) {
+ this.resourceId = textViewResourceId;
+ this.objects = objects;
+ this. popupWindow = popupWindow;
+ this. selectAppItemCallback = selectAppItemCallback;
+ }
+
+
+
+ @NonNull
+ @Override
+ public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ View v =LayoutInflater.from(parent.getContext()).inflate(resourceId,parent,false);
+ SelectAppRecyclerAdapter.ViewHolder holder = new SelectAppRecyclerAdapter.ViewHolder(v);
+
+ holder.select_app_icon = v.findViewById(R.id.select_app_icon);
+ holder.select_app_text=v.findViewById(R.id.select_app_text) ;
+ holder.select_package_name=v.findViewById(R.id.select_package_name) ;
+ return holder;
+ }
+
+
+ @Override
+ public void onBindViewHolder(@NonNull SelectAppRecyclerAdapter.ViewHolder holder, int position) {
+ SelectAppRecyclerItem appItem=objects.get(position); //获取当前项的实例
+
+ //图标+进程PID+名字+内存
+ holder.select_app_icon.setImageDrawable(appItem.getIcon());
+ String showText=""+appItem.getShowName() +" "
+ +" "+" ("+appItem.getPackageName()+")"+"";
+
+ holder.select_app_text.setText(Html.fromHtml(showText));
+ holder.select_package_name.setText(appItem.getPackageName());
+
+ //item被点击
+ holder.v.setOnClickListener(new ClickRecyclerItemListener(appItem));
+ }
+
+
+ @Override
+ public int getItemCount() {
+ return objects.size();
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return position;
+ }
+
+
+ class ClickRecyclerItemListener implements View.OnClickListener {
+ SelectAppRecyclerItem appItem;
+ public ClickRecyclerItemListener( SelectAppRecyclerItem appItem){
+ this.appItem =appItem;
+ }
+ @Override
+ public void onClick(View v) {
+ popupWindow.dismiss();
+ Message msg = new Message();
+ msg.obj = (SelectAppRecyclerItem)appItem;
+ selectAppItemCallback.sendMessage(msg);
+ }
+ }
+
+
+
+
+}
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java
new file mode 100644
index 00000000..dcddce02
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/MainActivity.java
@@ -0,0 +1,540 @@
+package com.linux.permissionmanager;
+
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.PopupWindow;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.linux.permissionmanager.Adapter.SelectAppRecyclerAdapter;
+import com.linux.permissionmanager.Model.PopupWindowOnTouchClose;
+import com.linux.permissionmanager.Model.SelectAppRecyclerItem;
+import com.linux.permissionmanager.Utils.ScreenInfoUtils;
+import com.linux.permissionmanager.Utils.UsbDebugSwitchHelper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainActivity extends AppCompatActivity {
+
+ private long rootKey = 0x7F6766F8;
+
+ private String suBasePath = "/data/local/tmp";
+
+ //保存的本地配置信息
+ private SharedPreferences m_shareSave;
+ private ProgressDialog m_loadingDlg = null;
+
+ // Used to load the 'native-lib' library on application startup.
+ static {
+ System.loadLibrary("root");
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+
+ m_shareSave = getSharedPreferences("zhcs", Context.MODE_PRIVATE);
+ try {
+ rootKey = m_shareSave.getLong("rootKey", rootKey);
+ } catch (Exception e) {
+ }
+
+ //验证用户的KEY
+ final EditText inputKey = new EditText(MainActivity.this);
+ inputKey.setText(Long.toHexString(rootKey));
+ inputKey.setSelection(inputKey.length(), 0);
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setCancelable(false);
+ builder.setTitle("请输入ROOT权限的KEY").setIcon(android.R.drawable.ic_dialog_info).setView(inputKey)
+ .setPositiveButton("确定", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ rootKey = Long.valueOf(inputKey.getText().toString(), 16);
+ //数值保存到本地
+ SharedPreferences.Editor mEdit = m_shareSave.edit();
+ mEdit.putLong("rootKey", rootKey);
+ mEdit.commit();
+ }
+
+ ;
+ });
+ builder.show();
+
+
+ Button show_myself_info_btn = findViewById(R.id.show_myself_info_btn);
+ show_myself_info_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showConsoleMsg(getCapabilityInfo());
+ }
+ });
+
+ Button get_root_btn = findViewById(R.id.get_root_btn);
+ get_root_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int ret = getRoot(rootKey);
+ showConsoleMsg("getRoot: " + ret);
+ }
+ });
+
+ Button disable_selinux_btn = findViewById(R.id.disable_selinux_btn);
+ disable_selinux_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int ret = disableSElinux(rootKey);
+ showConsoleMsg("disableSElinux: " + ret);
+ }
+ });
+
+ Button enable_selinux_btn = findViewById(R.id.enable_selinux_btn);
+ enable_selinux_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ int ret = enableSElinux(rootKey);
+ showConsoleMsg("enableSElinux: " + ret);
+ }
+ });
+
+ Button run_normal_cmd_btn = findViewById(R.id.run_normal_cmd_btn);
+ run_normal_cmd_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ final EditText inputCMD = new EditText(MainActivity.this);
+ inputCMD.setText("id");
+ inputCMD.setSelection(inputCMD.length(), 0);
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle("输入普通命令").setIcon(android.R.drawable.ic_dialog_info).setView(inputCMD)
+ .setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ String text = inputCMD.getText().toString();
+ showConsoleMsg(text + "\n" + runNormalCmd(rootKey, text));
+ }
+
+ ;
+ });
+ builder.show();
+
+ }
+ });
+ Button run_root_cmd_btn = findViewById(R.id.run_root_cmd_btn);
+ run_root_cmd_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ if (!guideOpenUsbDebugSwitch()) {
+ return;
+ }
+ final EditText inputCMD = new EditText(MainActivity.this);
+ inputCMD.setText("id");
+ inputCMD.setSelection(inputCMD.length(), 0);
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
+ builder.setTitle("输入ROOT命令").setIcon(android.R.drawable.ic_dialog_info).setView(inputCMD)
+ .setNegativeButton("取消", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ String text = inputCMD.getText().toString();
+ showConsoleMsg(text + "\n" + runRootCmd(rootKey, text));
+ }
+ });
+ builder.show();
+
+ }
+ });
+ Button adb_root_btn = findViewById(R.id.adb_root_btn);
+ adb_root_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ if (!guideOpenUsbDebugSwitch()) {
+ return;
+ }
+ showConsoleMsg(adbRoot(rootKey));
+ }
+ });
+
+ Button su_env_inject_btn = findViewById(R.id.su_env_inject_btn);
+ su_env_inject_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (!guideOpenUsbDebugSwitch()) {
+ return;
+ }
+ //1.获取su工具文件路径
+ String suToolsFilePath = WirteSuToolsFilePath(MainActivity.this);
+ showConsoleMsg(suToolsFilePath);
+
+ //2.安装su工具
+ String insRet = installSuTools(rootKey, suBasePath, suToolsFilePath);
+ showConsoleMsg(insRet);
+ if(insRet.indexOf("installSuTools done.") == -1) {
+ return;
+ }
+
+ //3.选择APP进程
+ showSelectAppWindow();
+
+ }
+ });
+
+ Button clean_su_btn = findViewById(R.id.clean_su_btn);
+ clean_su_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ showConsoleMsg(uninstallSuTools(rootKey,suBasePath));
+ }
+ });
+
+ Button copy_info_btn = findViewById(R.id.copy_info_btn);
+ copy_info_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ copyConsoleMsg();
+ Toast.makeText(v.getContext(), "复制成功", Toast.LENGTH_SHORT).show();
+
+ }
+ });
+ Button clean_info_btn = findViewById(R.id.clean_info_btn);
+ clean_info_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ cleanConsoleMsg();
+ }
+ });
+ }
+
+ public void showConsoleMsg(String msg) {
+ EditText console_edit = findViewById(R.id.console_edit);
+ StringBuffer txt = new StringBuffer();
+ txt.append(console_edit.getText().toString());
+ if (txt.length() != 0) {
+ txt.append("\n");
+ }
+ txt.append(msg);
+ txt.append("\n");
+ console_edit.setText(txt.toString());
+ console_edit.setSelection(txt.length());
+ }
+
+ public void cleanConsoleMsg() {
+ EditText console_edit = findViewById(R.id.console_edit);
+ console_edit.setText("");
+ }
+
+ public void copyConsoleMsg() {
+ EditText console_edit = findViewById(R.id.console_edit);
+ //获取剪贴板管理器:
+ ClipboardManager cm = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ // 创建普通字符型ClipData
+ ClipData mClipData = ClipData.newPlainText("Label", console_edit.getText());
+ // 将ClipData内容放到系统剪贴板里。
+ cm.setPrimaryClip(mClipData);
+
+ }
+
+ public boolean guideOpenUsbDebugSwitch() {
+ //检查USB调试开关是否打开
+ if (!UsbDebugSwitchHelper.checkUsbDebugSwitch(MainActivity.this)) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
+ .setTitle("提示")
+ .setCancelable(false)
+ .setMessage("请先到开发者选项页面里打开【USB调试】开关(提示:在手机关于页面里连续点击系统版本号可启用开发者选项页面)")
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ dialog.dismiss();
+ UsbDebugSwitchHelper.startDevelopmentActivity(MainActivity.this); //转到开发者页面
+ }
+ })
+ .setNegativeButton("确定", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ UsbDebugSwitchHelper.startDevelopmentActivity(MainActivity.this); //转到开发者页面
+
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.BLACK);
+
+ return false;
+ }
+ return true;
+ }
+
+ public boolean guideCloseUsbDebugSwitch() {
+ //检查USB调试开关是否打开
+ if (UsbDebugSwitchHelper.checkUsbDebugSwitch(MainActivity.this)) {
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
+ .setTitle("提示")
+ .setCancelable(false)
+ .setMessage("请先到开发者选项页面里关闭【USB调试】开关")
+ .setOnDismissListener(new DialogInterface.OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ dialog.dismiss();
+ UsbDebugSwitchHelper.startDevelopmentActivity(MainActivity.this); //转到开发者页面
+ }
+ })
+ .setNegativeButton("确定", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ UsbDebugSwitchHelper.startDevelopmentActivity(MainActivity.this); //转到开发者页面
+
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.BLACK);
+ return false;
+ }
+ return true;
+ }
+
+
+ Handler selectAppItemCallback = new Handler() {
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+
+ SelectAppRecyclerItem appItem = (SelectAppRecyclerItem) msg.obj;
+
+ if (m_loadingDlg == null) {
+ m_loadingDlg = new ProgressDialog(MainActivity.this);
+ m_loadingDlg.setCancelable(false);
+ }
+ m_loadingDlg.setTitle("");
+ m_loadingDlg.setMessage("请现在手动启动APP [" + appItem.getShowName() + "]");
+ m_loadingDlg.show();
+
+ new Thread() {
+ public void run() {
+ String ret = autoSuEnvInject(rootKey, appItem.getPackageName(), suBasePath);
+ runOnUiThread(new Runnable() {
+ public void run() {
+ showConsoleMsg(ret);
+ m_loadingDlg.cancel();
+
+ if(ret.indexOf("autoSuEnvInject done.")!= -1) {
+ //弹个提示,通知一下成功了
+ AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
+ .setCancelable(false)
+ .setTitle("提示")
+ .setIcon(appItem.getIcon())
+ .setMessage("已经授予ROOT权限到APP [" + appItem.getShowName() + "]")
+ .setNegativeButton("确定", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ }
+ });
+ AlertDialog dialog = builder.create();
+ dialog.show();
+ dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(Color.BLACK);
+ }
+
+
+
+
+ }
+ });
+ }
+ }.start();
+ super.handleMessage(msg);
+ }
+ };
+
+ //显示选择应用程序窗口
+ public void showSelectAppWindow() {
+ final PopupWindow popupWindow = new PopupWindow(this);
+
+ View view = View.inflate(this, R.layout.select_app_wnd, null);
+ popupWindow.setContentView(view);
+
+ popupWindow.setHeight(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
+ popupWindow.setBackgroundDrawable(new ColorDrawable(0x9B000000)); //阴影半透明
+ popupWindow.setOutsideTouchable(true);
+ popupWindow.setFocusable(true);
+ popupWindow.setTouchable(true);
+
+ //全屏
+ View parent = View.inflate(MainActivity.this, R.layout.activity_main, null);
+ popupWindow.showAtLocation(parent, Gravity.NO_GRAVITY, 0, 0);
+ popupWindow.showAsDropDown(parent, 0, 0);
+
+ popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
+ @Override
+ public void onDismiss() { //窗口即将关闭
+
+ }
+ });
+
+ //设置中心布局大小
+ final int screenWidth = ScreenInfoUtils.getRealWidth(this);
+ final int screenHeight = ScreenInfoUtils.getRealHeight(this);
+
+ final double centerWidth = ((double) screenWidth) * 0.80;
+ final double centerHeight = ((double) screenHeight) * 0.90;
+
+ LinearLayout center_layout = (LinearLayout) view.findViewById(R.id.center_layout);
+ android.view.ViewGroup.LayoutParams lp = center_layout.getLayoutParams();
+ lp.width = (int) centerWidth;
+ lp.height = (int) centerHeight;
+
+ //点击阴影部分可关闭窗口
+ popupWindow.setTouchInterceptor(new PopupWindowOnTouchClose(popupWindow,
+ screenWidth, screenHeight, (int) centerWidth, (int) centerHeight));
+
+ //显示APP列表
+ List appList = new ArrayList<>();
+
+ //获取已安装的APK列表
+ List packages = getPackageManager().getInstalledPackages(0);
+
+ //先判断cmdline与包名是否完全相同
+ for (int i = 0; i < packages.size(); i++) {
+ PackageInfo packageInfo = packages.get(i);
+
+ if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ //系统应用
+ continue;
+ }
+ String showName = packageInfo.applicationInfo.loadLabel(getPackageManager()).toString();
+
+ Drawable icon = packageInfo.applicationInfo.loadIcon(getPackageManager());
+
+ String packageName = packageInfo.applicationInfo.packageName;
+ if(packageName.equals(getPackageName())){
+ //不显示自己
+ continue;
+ }
+ //加入到显示APP列表
+ appList.add(new SelectAppRecyclerItem(
+ icon,
+ showName,
+ packageName));
+ }
+ for (int i = 0; i < packages.size(); i++) {
+ PackageInfo packageInfo = packages.get(i);
+
+ if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ //非系统应用
+ continue;
+ }
+
+ String showName = packageInfo.applicationInfo.loadLabel(getPackageManager()).toString();
+
+ Drawable icon = packageInfo.applicationInfo.loadIcon(getPackageManager());
+
+ String packageName = packageInfo.applicationInfo.packageName;
+
+ //加入到显示APP列表
+ appList.add(new SelectAppRecyclerItem(
+ icon,
+ showName,
+ packageName));
+ }
+
+ SelectAppRecyclerAdapter adapter = new SelectAppRecyclerAdapter(
+ MainActivity.this, R.layout.select_app_recycler_item, appList, popupWindow, selectAppItemCallback);
+
+ RecyclerView select_app_recycler_view = (RecyclerView) view.findViewById(R.id.select_app_recycler_view);
+ // 设置布局管理器
+ LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
+ linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
+ select_app_recycler_view.setLayoutManager(linearLayoutManager);
+ select_app_recycler_view.setAdapter(adapter);
+
+ }
+
+ public static String WirteSuToolsFilePath(Context context) {
+ String suFileName = "su";
+ String suFilePath = "";
+ try {
+ suFilePath = context.getFilesDir().getPath() + "/" + suFileName;
+ File file = new File(suFilePath);
+ if (!file.exists()) {
+ if (!file.getParentFile().exists()) file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ if (file.exists()) {
+ InputStream inputStream = context.getAssets().open(suFileName);
+ FileOutputStream outputStream = new FileOutputStream(file);
+ byte[] content = new byte[1024];
+ while (inputStream.read(content) > 0) {
+ outputStream.write(content);
+ }
+ inputStream.close();
+ outputStream.flush();
+ outputStream.close();
+ }
+ } catch (Exception e) {
+ }
+ return suFilePath;
+ }
+
+
+ public native String getCapabilityInfo();
+
+ public native int getRoot(long rootKey);
+
+ public native int disableSElinux(long rootKey);
+
+ public native int enableSElinux(long rootKey);
+
+ public native String runNormalCmd(long rootKey, String cmd);
+
+ public native String runRootCmd(long rootKey, String cmd);
+
+ public native String adbRoot(long rootKey);
+
+ public native String installSuTools(long rootKey, String basePath, String suToolsFilePath);
+
+ public native String uninstallSuTools(long rootKey, String basePath);
+
+ public native String autoSuEnvInject(long rootKey, String targetProcessCmdline, String basePath);
+}
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java
new file mode 100644
index 00000000..3856843a
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/PopupWindowOnTouchClose.java
@@ -0,0 +1,48 @@
+package com.linux.permissionmanager.Model;
+
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.PopupWindow;
+
+public class PopupWindowOnTouchClose implements View.OnTouchListener {
+ private boolean lastVailedDown = true;
+ private int screenWidth, screenHeight, centerWidth, centerHeight;
+ private PopupWindow popupWindow;
+
+ public PopupWindowOnTouchClose(PopupWindow popupWindow, int screenWidth, int screenHeight, int centerWidth, int centerHeight) {
+ this.popupWindow = popupWindow;
+ this.screenWidth = screenWidth;
+ this.screenHeight = screenHeight;
+ this.centerWidth = centerWidth;
+ this.centerHeight = centerHeight;
+ }
+
+ private boolean isValiedRegion(View v, MotionEvent event) {
+ int x = (int) event.getX();
+ int y = (int) event.getY();
+ double wndLeft = (screenWidth - centerWidth) / 2;
+ double wndTop = (screenHeight - centerHeight) / 2;
+ if (x < wndLeft || x > wndLeft + centerWidth || y < wndTop || y > wndTop + centerHeight) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ lastVailedDown = isValiedRegion(v, event);
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (!lastVailedDown) {
+ if (!isValiedRegion(v, event)) {
+ popupWindow.dismiss();
+ }
+ }
+ }
+ return false;
+ }
+}
+
+
+
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java
new file mode 100644
index 00000000..f754bbd6
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Model/SelectAppRecyclerItem.java
@@ -0,0 +1,40 @@
+package com.linux.permissionmanager.Model;
+
+import android.graphics.drawable.Drawable;
+
+public class SelectAppRecyclerItem {
+ private Drawable icon=null;
+ private String showName;
+ private String packageName;
+
+ public SelectAppRecyclerItem(Drawable icon, String showName, String packageName){
+ this.icon=icon;
+ this.showName = showName;
+ this.packageName = packageName;
+ }
+
+ public Drawable getIcon() {
+ return icon;
+ }
+
+ public void setIcon(Drawable icon) {
+ this.icon = icon;
+ }
+
+ public String getShowName() {
+ return showName;
+ }
+
+ public void setShowName(String showName) {
+ this.showName = showName;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+ }
+
+}
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/UsbDebugSwitchHelper.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/UsbDebugSwitchHelper.java
new file mode 100644
index 00000000..53e1f489
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/UsbDebugSwitchHelper.java
@@ -0,0 +1,41 @@
+package com.linux.permissionmanager;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+
+public class UsbDebugSwitchHelper {
+ public static boolean checkUsbDebugSwitch(Context ctx) {
+ //检查USB调试是否被打开
+ boolean enableAdb = (Settings.Secure.getInt(ctx.getContentResolver(), Settings.Secure.ADB_ENABLED, 0) > 0);//判断adb调试模式是否打开
+ return enableAdb;
+ }
+
+ /**
+ * 打开开发者模式界面
+ */
+ public static void startDevelopmentActivity(Context ctx) {
+ try {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ ctx.startActivity(intent);
+ } catch (Exception e) {
+ try {
+ ComponentName componentName = new ComponentName("com.android.settings", "com.android.settings.DevelopmentSettings");
+ Intent intent = new Intent();
+ intent.setComponent(componentName);
+ intent.setAction("android.intent.action.View");
+ ctx.startActivity(intent);
+ } catch (Exception e1) {
+ try {
+ Intent intent = new Intent("com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS");//部分小米手机采用这种方式跳转
+ ctx.startActivity(intent);
+ } catch (Exception e2) {
+
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java
new file mode 100644
index 00000000..a4b29642
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/ScreenInfoUtils.java
@@ -0,0 +1,173 @@
+package com.linux.permissionmanager.Utils;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.Display;
+import android.view.WindowManager;
+
+/**
+ * Get Screen Information Utils
+ *
+ * @author yh
+ * @date 2018/12/18.
+ */
+public class ScreenInfoUtils {
+
+ private static final String TAG = "ScreenInfoUtils";
+
+ /**
+ * Get Screen Width
+ */
+ public static int getScreenWidth(Context context) {
+ return getDisplayMetrics(context).widthPixels;
+ }
+
+ /**
+ * Get Screen Height
+ */
+ public static int getScreenHeight(Context context) {
+ return getDisplayMetrics(context).heightPixels;
+ }
+
+
+ /**
+ * Get Screen Real Height
+ *
+ * @param context Context
+ * @return Real Height
+ */
+ public static int getRealHeight(Context context) {
+ Display display = getDisplay(context);
+ if (display == null) {
+ return 0;
+ }
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getRealMetrics(dm);
+ return dm.heightPixels;
+ }
+
+ /**
+ * Get Screen Real Width
+ *
+ * @param context Context
+ * @return Real Width
+ */
+ public static int getRealWidth(Context context) {
+ Display display = getDisplay(context);
+ if (display == null) {
+ return 0;
+ }
+ DisplayMetrics dm = new DisplayMetrics();
+ display.getRealMetrics(dm);
+ return dm.widthPixels;
+ }
+
+ /**
+ * Get StatusBar Height
+ */
+ public static int getStatusBarHeight(Context mContext) {
+ int resourceId = mContext.getResources().getIdentifier("status_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ return mContext.getResources().getDimensionPixelSize(resourceId);
+ }
+ return 0;
+ }
+
+ /**
+ * Get ActionBar Height
+ */
+ public static int getActionBarHeight(Context mContext) {
+ TypedValue tv = new TypedValue();
+ if (mContext.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
+ return TypedValue.complexToDimensionPixelSize(tv.data, mContext.getResources().getDisplayMetrics());
+ }
+ return 0;
+ }
+
+ /**
+ * Get NavigationBar Height
+ */
+ public static int getNavigationBarHeight(Context mContext) {
+ Resources resources = mContext.getResources();
+ int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+ if (resourceId > 0) {
+ return resources.getDimensionPixelSize(resourceId);
+ }
+ return 0;
+ }
+
+ /**
+ * Get Density
+ */
+ private static float getDensity(Context context) {
+ return getDisplayMetrics(context).density;
+ }
+
+ /**
+ * Get Dpi
+ */
+ private static int getDpi(Context context) {
+ return getDisplayMetrics(context).densityDpi;
+ }
+
+ /**
+ * Get Display
+ *
+ * @param context Context for get WindowManager
+ * @return Display
+ */
+ private static Display getDisplay(Context context) {
+ WindowManager wm;
+ if (context instanceof Activity) {
+ Activity activity = (Activity) context;
+ wm = activity.getWindowManager();
+ } else {
+ wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ }
+ if (wm != null) {
+ return wm.getDefaultDisplay();
+ }
+ return null;
+ }
+
+ /**
+ * Get DisplayMetrics
+ *
+ * @param context Context for get Resources
+ * @return DisplayMetrics
+ */
+ private static DisplayMetrics getDisplayMetrics(Context context) {
+ return context.getResources().getDisplayMetrics();
+ }
+
+
+ /**
+ * Get ScreenInfo
+ */
+ private static String getScreenInfo(Context context) {
+ return " \n" +
+ "--------ScreenInfo--------" + "\n" +
+ "Screen Width : " + getScreenWidth(context) + "px\n" +
+ "Screen RealWidth :" + getRealWidth(context) + "px\n" +
+ "Screen Height: " + getScreenHeight(context) + "px\n" +
+ "Screen RealHeight: " + getRealHeight(context) + "px\n" +
+ "Screen StatusBar Height: " + getStatusBarHeight(context)+ "px\n" +
+ "Screen ActionBar Height: " + getActionBarHeight(context)+ "px\n" +
+ "Screen NavigationBar Height: " + getNavigationBarHeight(context)+ "px\n" +
+ "Screen Dpi: " + getDpi(context) + "\n" +
+ "Screen Density: " + getDensity(context) + "\n" +
+ "--------------------------";
+ }
+
+
+ /**
+ * Print screenInfo to logcat
+ */
+ public static void printScreenInfo(Context context) {
+ Log.d(TAG, getScreenInfo(context));
+ }
+}
diff --git a/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/UsbDebugSwitchHelper.java b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/UsbDebugSwitchHelper.java
new file mode 100644
index 00000000..3d9192bb
--- /dev/null
+++ b/PermissionManager/app/src/main/java/com/linux/permissionmanager/Utils/UsbDebugSwitchHelper.java
@@ -0,0 +1,41 @@
+package com.linux.permissionmanager.Utils;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+
+public class UsbDebugSwitchHelper {
+ public static boolean checkUsbDebugSwitch(Context ctx) {
+ //检查USB调试是否被打开
+ boolean enableAdb = (Settings.Secure.getInt(ctx.getContentResolver(), Settings.Secure.ADB_ENABLED, 0) > 0);//判断adb调试模式是否打开
+ return enableAdb;
+ }
+
+ /**
+ * 打开开发者模式界面
+ */
+ public static void startDevelopmentActivity(Context ctx) {
+ try {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ ctx.startActivity(intent);
+ } catch (Exception e) {
+ try {
+ ComponentName componentName = new ComponentName("com.android.settings", "com.android.settings.DevelopmentSettings");
+ Intent intent = new Intent();
+ intent.setComponent(componentName);
+ intent.setAction("android.intent.action.View");
+ ctx.startActivity(intent);
+ } catch (Exception e1) {
+ try {
+ Intent intent = new Intent("com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS");//部分小米手机采用这种方式跳转
+ ctx.startActivity(intent);
+ } catch (Exception e2) {
+
+ }
+
+ }
+ }
+ }
+
+}
diff --git a/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..cc14f035
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml b/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..a4f78de5
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PermissionManager/app/src/main/res/drawable/line.xml b/PermissionManager/app/src/main/res/drawable/line.xml
new file mode 100644
index 00000000..edc8c384
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/line.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/line_drawable.xml b/PermissionManager/app/src/main/res/drawable/line_drawable.xml
new file mode 100644
index 00000000..963faca1
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/line_drawable.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/ripple_grey.xml b/PermissionManager/app/src/main/res/drawable/ripple_grey.xml
new file mode 100644
index 00000000..9fe6ea1b
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/ripple_grey.xml
@@ -0,0 +1,6 @@
+
+//点击时波纹的颜色
+ //未点击时控件的背景(可以是图片,可以是颜色,也可以是drawable里的xml背景(比如圆角))
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml b/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml
new file mode 100644
index 00000000..1e51d694
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/shape_wnd_grey_corner.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/thumb.xml b/PermissionManager/app/src/main/res/drawable/thumb.xml
new file mode 100644
index 00000000..347480a8
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/thumb.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml b/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml
new file mode 100644
index 00000000..f32eb927
--- /dev/null
+++ b/PermissionManager/app/src/main/res/drawable/thumb_drawable.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/layout/activity_main.xml b/PermissionManager/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..8a71afb0
--- /dev/null
+++ b/PermissionManager/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml b/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml
new file mode 100644
index 00000000..4a34d32d
--- /dev/null
+++ b/PermissionManager/app/src/main/res/layout/select_app_recycler_item.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PermissionManager/app/src/main/res/layout/select_app_wnd.xml b/PermissionManager/app/src/main/res/layout/select_app_wnd.xml
new file mode 100644
index 00000000..05eb7e24
--- /dev/null
+++ b/PermissionManager/app/src/main/res/layout/select_app_wnd.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..a26f6fbc
--- /dev/null
+++ b/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..a26f6fbc
--- /dev/null
+++ b/PermissionManager/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png b/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..a571e600
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..61da551c
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png b/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c41dd285
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..db5080a7
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..6dba46da
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..da31a871
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..15ac6817
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..b216f2d3
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..f25a4197
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e96783cc
Binary files /dev/null and b/PermissionManager/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/PermissionManager/app/src/main/res/values-night/themes.xml b/PermissionManager/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..511adaf2
--- /dev/null
+++ b/PermissionManager/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/values/colors.xml b/PermissionManager/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..c2158b73
--- /dev/null
+++ b/PermissionManager/app/src/main/res/values/colors.xml
@@ -0,0 +1,13 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #424242
+ #FF767676
+ #00000000
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/values/strings.xml b/PermissionManager/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..af306add
--- /dev/null
+++ b/PermissionManager/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ PermissionManager 1.0
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/main/res/values/themes.xml b/PermissionManager/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..7b75c0a5
--- /dev/null
+++ b/PermissionManager/app/src/main/res/values/themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java b/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java
new file mode 100644
index 00000000..e69221a1
--- /dev/null
+++ b/PermissionManager/app/src/test/java/com/linux/permissionmanager/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.linux.permissionmanager;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/PermissionManager/appKey.jks b/PermissionManager/appKey.jks
new file mode 100644
index 00000000..81b8ee7e
Binary files /dev/null and b/PermissionManager/appKey.jks differ
diff --git a/PermissionManager/build.gradle b/PermissionManager/build.gradle
new file mode 100644
index 00000000..6ad88d9d
--- /dev/null
+++ b/PermissionManager/build.gradle
@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/PermissionManager/gradle.properties b/PermissionManager/gradle.properties
new file mode 100644
index 00000000..829d0737
--- /dev/null
+++ b/PermissionManager/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/PermissionManager/gradle/wrapper/gradle-wrapper.jar b/PermissionManager/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/PermissionManager/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/PermissionManager/gradle/wrapper/gradle-wrapper.properties b/PermissionManager/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..28a1e17d
--- /dev/null
+++ b/PermissionManager/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Aug 25 01:04:56 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/PermissionManager/gradlew b/PermissionManager/gradlew
new file mode 100644
index 00000000..cccdd3d5
--- /dev/null
+++ b/PermissionManager/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/PermissionManager/gradlew.bat b/PermissionManager/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/PermissionManager/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/PermissionManager/settings.gradle b/PermissionManager/settings.gradle
new file mode 100644
index 00000000..e0ef0a54
--- /dev/null
+++ b/PermissionManager/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "PermissionManager"
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..4398b524
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+ printf(
+ "======================================================\n"
+ "本工具名称: Linux ARM64 完美隐藏ROOT演示\n"
+ "本工具功能列表:\n"
+ "\t1.显示自身权限信息\n"
+ "\t2.获取ROOT权限\n"
+ "\t3.绕过SELinux\n"
+ "\t4.还原SELinux\n"
+ "\t5.执行普通命令\n"
+ "\t6.执行ROOT命令\n"
+ "\t7.授予ADB最高级别权限\n"
+ "\t8.授权ROOT到其他进程\n"
+ "\t9.清理ROOT授权痕迹\n"
+ "\t新一代root,跟面具完全不同思路,摆脱面具被检测的弱点,完美隐藏root功能(免root级别),兼容安卓APP直接JNI稳定调用。\n"
+ "======================================================\n"
+ );
diff --git a/find_kernel_func/empty b/find_kernel_func/empty
new file mode 100644
index 00000000..e69de29b
diff --git a/ida_patch_cmd_creator/3rdparty/aarch64-linux-android-as.exe b/ida_patch_cmd_creator/3rdparty/aarch64-linux-android-as.exe
new file mode 100644
index 00000000..81d86a4a
Binary files /dev/null and b/ida_patch_cmd_creator/3rdparty/aarch64-linux-android-as.exe differ
diff --git a/ida_patch_cmd_creator/3rdparty/arm-linux-androideabi-as.exe b/ida_patch_cmd_creator/3rdparty/arm-linux-androideabi-as.exe
new file mode 100644
index 00000000..e7509498
Binary files /dev/null and b/ida_patch_cmd_creator/3rdparty/arm-linux-androideabi-as.exe differ
diff --git a/ida_patch_cmd_creator/ArmAsmHelper.h b/ida_patch_cmd_creator/ArmAsmHelper.h
new file mode 100644
index 00000000..feacdc7e
--- /dev/null
+++ b/ida_patch_cmd_creator/ArmAsmHelper.h
@@ -0,0 +1,96 @@
+#ifndef ARM_ASM_HELPER_H_
+#define ARM_ASM_HELPER_H_
+#include
+#include
+#include
+#include
+
+std::string AsmToBytes(const std::string & strArm64Asm)
+{
+ //获取汇编文本
+
+ //获取自身运行目录
+ char szFileName[MAX_PATH] = { 0 };
+ ::GetModuleFileNameA(NULL, szFileName, MAX_PATH);
+ std::string strMyPath = szFileName;
+ strMyPath = strMyPath.substr(0, strMyPath.find_last_of('\\') + 1);
+
+ //写出input.txt
+ std::ofstream inputFile;
+ inputFile.open(strMyPath + "input.txt", std::ios_base::out | std::ios_base::trunc);
+ inputFile << strArm64Asm;
+ inputFile.close();
+
+ //ARM64
+ DeleteFileA(std::string(strMyPath + "output.txt").c_str());
+
+ std::string cmd = strMyPath + "aarch64-linux-android-as.exe -ahlm " + strMyPath + "input.txt >> " + strMyPath + "output.txt";
+ system(cmd.c_str());
+
+ //未开发的
+ //ARM:arm-linux-as.exe -ahlm -k -mthumb-interwork -march=armv7-a %s >> %s
+ //Thumb:arm-linux-as.exe -ahlm -k -mthumb-interwork -march=armv7 %s >> %s
+
+ //读取output.txt
+ std::ifstream in(strMyPath + "output.txt");
+ std::stringstream ssOutput;
+ std::string line;
+ bool bIsFirstLine = true;
+ if (in) // 有该文件
+ {
+ while (getline(in, line)) // line中不包括每行的换行符
+ {
+ if(bIsFirstLine)
+ {
+ bIsFirstLine = false;
+ continue;
+ }
+ if(!line.length()) { continue; }
+ if(line.length() == 1 && line == "\n") { continue; }
+
+ std::stringstream ssGetMidBuf;
+ std::string word;
+ ssGetMidBuf << line;
+ int n = 0;
+ while (ssGetMidBuf >> word)
+ {
+ n++;
+ if (n == 3)
+ {
+ ssOutput << word;
+ }
+ word.empty();
+ }
+
+
+ }
+ in.close();
+ }
+
+ return ssOutput.str();
+
+}
+
+const char HEX[16] = {
+'0', '1', '2', '3',
+'4', '5', '6', '7',
+'8', '9', 'a', 'b',
+'c', 'd', 'e', 'f'
+};
+
+/* Convert byte array to hex string. */
+std::string bytesToHexString(const byte* input, size_t length) {
+
+ std::string str;
+ str.reserve(length << 1);
+ for (size_t i = 0; i < length; ++i) {
+ int t = input[i];
+ int a = t / 16;
+ int b = t % 16;
+ str.append(1, HEX[a]);
+ str.append(1, HEX[b]);
+ }
+ return str;
+}
+
+#endif /* ARM_ASM_HELPER_H_ */
diff --git a/ida_patch_cmd_creator/IDAPythonScripHelper.h b/ida_patch_cmd_creator/IDAPythonScripHelper.h
new file mode 100644
index 00000000..fabc5d69
--- /dev/null
+++ b/ida_patch_cmd_creator/IDAPythonScripHelper.h
@@ -0,0 +1,60 @@
+#ifndef IDA_PYTHON_SCRIPT_HELPER_H_
+#define IDA_PYTHON_SCRIPT_HELPER_H_
+#include
+#include
+#include
+#include
+
+
+inline std::string& replace_all_distinct(std::string& str, const std::string& old_value, const std::string& new_value)
+{
+ for (std::string::size_type pos(0); pos != std::string::npos; pos += new_value.length())
+ {
+ if ((pos = str.find(old_value, pos)) != std::string::npos)
+ {
+ str.replace(pos, old_value.length(), new_value);
+ }
+ else
+ {
+ break;
+ }
+ }
+ return str;
+}
+
+
+std::string GetFormatAsmBytes(const std::string & strAsmBytesLine)
+{
+ if (strAsmBytesLine.length() % 2)
+ {
+ return std::string();
+ }
+
+ std::stringstream ssFormatBytesTxt;
+ int n = 0;
+ for (size_t i = 0; i < strAsmBytesLine.length(); i++)
+ {
+ ssFormatBytesTxt << strAsmBytesLine[i];
+ n++;
+ if (n == 2 && i != strAsmBytesLine.length() - 1)
+ {
+ ssFormatBytesTxt << " ";
+ n = 0;
+ }
+ }
+ return ssFormatBytesTxt.str();
+}
+
+
+std::string GetIdaAsmScript(const std::string & strBytes, size_t writeAddr)
+{
+ std::string formatBytes = GetFormatAsmBytes(strBytes);
+ std::stringstream ssScript;
+ ssScript
+ << "buf = [0x" << replace_all_distinct(formatBytes, " ", ", 0x") << "]" << std::endl
+ << "for index in range(len(buf)) :" << std::endl
+ << "\t" << "patch_byte(0x" << std::hex << writeAddr << " + index, buf[index]);";
+ return ssScript.str();
+}
+
+#endif /* IDA_PYTHON_SCRIPT_HELPER_H_ */
diff --git a/su/base64.cpp b/su/base64.cpp
new file mode 100644
index 00000000..56e166d7
--- /dev/null
+++ b/su/base64.cpp
@@ -0,0 +1,96 @@
+#include "base64.h"
+#include
+#include
+static const std::string base64_chars =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (i = 0; (i < 4); i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for (j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while ((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+ int in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i == 4) {
+ for (i = 0; i < 4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j < 4; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
diff --git a/su/base64.h b/su/base64.h
new file mode 100644
index 00000000..145e06fe
--- /dev/null
+++ b/su/base64.h
@@ -0,0 +1,6 @@
+#ifndef BASE_64_H
+#define BASE_64_H
+#include
+std::string base64_encode(unsigned char const*, unsigned int len);
+std::string base64_decode(std::string const& s);
+#endif
diff --git a/su/jni/Android.mk b/su/jni/Android.mk
new file mode 100644
index 00000000..3c73d3e3
--- /dev/null
+++ b/su/jni/Android.mk
@@ -0,0 +1,11 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_CPPFLAGS += -std=c++17
+LOCAL_CFLAGS += -fPIE
+LOCAL_CFLAGS += -fvisibility=hidden
+LOCAL_LDFLAGS += -fPIE -pie
+LOCAL_DISABLE_FATAL_LINKER_WARNINGS := true
+LOCAL_MODULE := su
+LOCAL_SRC_FILES := ../su.cpp ../base64.cpp ../../testRoot/adb64_helper.cpp ../../testRoot/process64_inject.cpp ../../testRoot/ptrace_arm64_utils.cpp
+include $(BUILD_EXECUTABLE)
diff --git a/su/jni/Application.mk b/su/jni/Application.mk
new file mode 100644
index 00000000..d67ad626
--- /dev/null
+++ b/su/jni/Application.mk
@@ -0,0 +1,2 @@
+APP_ABI := arm64-v8a
+APP_STL := c++_static
\ No newline at end of file
diff --git a/su/root_key_helper.h b/su/root_key_helper.h
new file mode 100644
index 00000000..2784ec32
--- /dev/null
+++ b/su/root_key_helper.h
@@ -0,0 +1,48 @@
+#ifndef _SOCKET_ID_HELPER_H_
+#define _SOCKET_ID_HELPER_H_
+#include
+#include
+#include
+#include "base64.h"
+static inline unsigned int get_tmp_root_key(const char* myself_path) {
+
+ //1.ȡ·β
+ unsigned int key = 0;
+ const char* head_flag = "/su_";
+ size_t len = strlen(head_flag);
+ char *pstart = strstr((char*)myself_path, head_flag);
+ if (!pstart) {
+ return 0;
+ }
+ pstart += len;
+
+ size_t copy_len;
+ char *pend = strstr(pstart, "/");
+ if (pend) {
+ copy_len = pend - pstart;
+ }
+ else {
+ copy_len = strlen(pstart);
+ }
+ char buf[256] = { 0 };
+ memcpy(buf, pstart, copy_len);
+ buf[sizeof(buf) - 1] = '\0';
+
+ //2.base64
+ std::string base64 = base64_decode(buf);
+
+ //3.ֹAĸ
+ size_t n = base64.find("A");
+ if (n == -1) {
+ return 0;
+ }
+ base64 = base64.substr(0, n);
+
+ //4.ȡKEY
+ std::stringstream sstrConvert;
+ sstrConvert << base64;
+ sstrConvert >> key;
+ return key;
+}
+
+#endif /* _SOCKET_ID_HELPER_H_ */
diff --git a/su/simple_su.cpp b/su/simple_su.cpp
new file mode 100644
index 00000000..e26728e6
--- /dev/null
+++ b/su/simple_su.cpp
@@ -0,0 +1,42 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "su.h"
+#include "../testRoot/super_root.h"
+
+
+#define ROOT_KEY 0x7F6766F8
+
+// su --> argv[0] argv[1] == NULL
+// su --> argv[0] argv[1] == "stu"
+int main(int argc, char *argv[])
+{
+ const char *user = "root";
+ if (argv[1] != NULL) {
+ user = argv[1];
+ }
+
+ pid_t n = fork();
+ assert(-1 != n);
+ if (0 == n) {
+ struct passwd *pw = getpwnam(user); // passwd结构体指针指向的是新用户的信息
+ assert(pw != NULL);
+
+ //setuid(pw->pw_uid); // 切换到新用户
+ get_root(ROOT_KEY);
+
+ setenv("HOME", pw->pw_dir, 1); // 在程序中修改环境变量
+
+ execl(pw->pw_shell, pw->pw_shell, (char*)0); // main函数的参数至少有一个(执行进程的命令)
+ perror("execl error: ");
+ }
+ else {
+ wait(NULL); // 等创建的子进程(新启动bash)退出
+ }
+ exit(0);
+}
diff --git a/su/su.cpp b/su/su.cpp
new file mode 100644
index 00000000..7c22a147
--- /dev/null
+++ b/su/su.cpp
@@ -0,0 +1,280 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include "su.h"
+#include "root_key_helper.h"
+#include "../testRoot/kernel_root_helper.h"
+#include "../testRoot/adb64_helper.h"
+
+#ifndef PATH_MAX
+# define PATH_MAX 256
+#endif
+
+
+int quit_signals[] = { SIGALRM, SIGABRT, SIGHUP, SIGPIPE, SIGQUIT, SIGTERM, SIGINT, 0 };
+
+static void usage(int status) {
+ FILE *stream = (status == EXIT_SUCCESS) ? stdout : stderr;
+
+ fprintf(stream,
+ "Usage: su [options] [-] [user [argument...]]\n\n"
+ "Options:\n"
+ " -c, --command COMMAND pass COMMAND to the invoked shell\n"
+ " -h, --help display this help message and exit\n"
+ " -s, --shell SHELL use SHELL instead of the default " DEFAULT_SHELL "\n");
+ exit(status);
+}
+
+/*
+ * Bionic's atoi runs through strtol().
+ * Use our own implementation for faster conversion.
+ */
+int parse_int(const char *s) {
+ int val = 0;
+ char c;
+ while ((c = *(s++))) {
+ if (c > '9' || c < '0')
+ return -1;
+ val = val * 10 + c - '0';
+ }
+ return val;
+}
+
+
+static void sighandler(int sig) {
+ // Put back all the default handlers
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = SIG_DFL;
+ for (int i = 0; quit_signals[i]; ++i) {
+ sigaction(quit_signals[i], &act, nullptr);
+ }
+}
+
+static void setup_sighandlers(void(*handler)(int)) {
+ struct sigaction act;
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = handler;
+ for (int i = 0; quit_signals[i]; ++i) {
+ sigaction(quit_signals[i], &act, nullptr);
+ }
+}
+int su_client_main(int argc, char *argv[]) {
+ std::string shell = DEFAULT_SHELL;
+ std::string command;
+ int uid = UID_ROOT;
+
+ int opt;
+ while ((opt = getopt(argc, argv, "c:")) != -1)
+ {
+ switch (opt)
+ {
+ case 'c':
+ for (int i = optind - 1; i < argc; ++i) {
+ if (!command.empty())
+ command += ' ';
+ command += argv[i];
+ }
+ optind = argc;
+ break;
+ case 'h':
+ usage(EXIT_SUCCESS);
+ break;
+ case 's':
+ shell = optarg;
+ default:
+ printf("Unknown command.\n");
+ return 0;
+ break;
+ }
+ }
+
+ TRACE("input shell:%s\n", shell.c_str());
+ TRACE("input command:%s\n", command.c_str());
+
+ /* username or uid */
+ if (optind < argc) {
+ struct passwd *pw;
+ pw = getpwnam(argv[optind]);
+ if (pw)
+ uid = pw->pw_uid;
+ else
+ uid = parse_int(argv[optind]);
+ optind++;
+ }
+
+ setup_sighandlers(sighandler);
+
+ // Setup environment
+ umask(022);
+ char chdir_path[32];
+ snprintf(chdir_path, sizeof(chdir_path), "/proc/%d/cwd", getpid());
+ //chdir(chdir_path); //在下面将会以我的方式重写它
+
+ char path[32];
+ snprintf(path, sizeof(path), "/proc/%d/environ", getpid());
+ char buf[4096] = { 0 };
+ int fd = open(path, O_RDONLY);
+ read(fd, buf, sizeof(buf));
+ close(fd);
+
+ //clearenv(); //在下面将会以我的方式重写它
+
+ std::vector v_process64_env;
+ for (size_t pos = 0; buf[pos];) {
+ char head[0x1000] = { 0 };
+ char val[0x1000] = { 0 };
+ char * find = strrchr(buf + pos, '=');
+ if (!find) {
+ TRACE("unknow env:%s\n", buf + pos);
+ continue;
+ }
+ auto head_size = find - (buf + pos);
+ if (head_size > sizeof(head) - 1) {
+ TRACE("env head size too large:%s\n", buf + pos);
+ continue;
+ }
+ memcpy(head, buf + pos, head_size);
+ find++;
+
+ auto val_size = strlen(find);
+ if (val_size > sizeof(head) - 1) {
+ TRACE("env val size too large:%s\n", buf + pos);
+ continue;
+ }
+ memcpy(val, find, val_size);
+
+ TRACE("new env head:%s\n", head);
+ TRACE("new env val:%s\n", val);
+
+ process64_env new_env;
+ strcpy(new_env.key, head);
+ strcpy(new_env.value, val);
+ v_process64_env.push_back(new_env);
+ //putenv(buf + pos); //在下面将会以我的方式重写它
+ pos += strlen(buf + pos) + 1;
+ }
+
+ struct passwd *pw;
+ pw = getpwuid(uid);
+ if (pw) {
+ process64_env new_env_HOME;
+ strcpy(new_env_HOME.key, "HOME");
+ strcpy(new_env_HOME.value, pw->pw_dir);
+ v_process64_env.push_back(new_env_HOME);
+
+
+ process64_env new_env_USER;
+ strcpy(new_env_USER.key, "USER");
+ strcpy(new_env_USER.value, pw->pw_name);
+ v_process64_env.push_back(new_env_USER);
+
+
+
+ process64_env new_env_LOGNAME;
+ strcpy(new_env_LOGNAME.key, "LOGNAME");
+ strcpy(new_env_LOGNAME.value, pw->pw_name);
+ v_process64_env.push_back(new_env_LOGNAME);
+
+
+
+ process64_env new_env_SHELL;
+ strcpy(new_env_SHELL.key, "SHELL");
+ strcpy(new_env_SHELL.value, shell.data());
+ v_process64_env.push_back(new_env_SHELL);
+ }
+
+ //提权
+ char myself_path[1024] = { 0 };
+ char processname[1024];
+ get_executable_path(myself_path, processname, sizeof(myself_path));
+ TRACE("my directory:%s\nprocessname:%s\n", myself_path, processname);
+ unsigned int root_key = get_tmp_root_key(myself_path);
+ if (root_key == 0) {
+ TRACE("root_key empty\n");
+ return EXIT_FAILURE;
+ }
+
+ //TODO:命令解析简陋版,暂时不支持EXPORT、多行等命令
+ while (1)
+ {
+
+ //获取用户输入的命令
+ printf("root# ");
+
+ std::string input_command;
+ getline(std::cin, input_command);
+ TRACE("[+] su new len: %zu, input_command: %s\n", input_command.length(), input_command.c_str());
+ if (input_command.empty()) {
+ continue;
+ }
+ else if (input_command == "exit") {
+ break;
+ }
+ else if (input_command == "setenforce 0") {
+ int err = safe_disable_selinux(root_key);
+ if (err) {
+ std::cout << "safe_disable_selinux ret:" << err << std::endl;
+ }
+ continue;
+ }
+ else if (input_command == "setenforce 1") {
+ int err = safe_enable_selinux(root_key);
+ if (err) {
+ std::cout << "safe_enable_selinux ret:" << err << std::endl;
+ }
+ continue;
+ }
+
+ //保存旧的SELinux状态
+ bool old_selinux_disable = is_disable_selinux_status();
+
+ //执行命令
+ char out_buf[0x1000] = { 0 }; //TODO:暂时用一页内存装,后面再改进
+ ssize_t inject_ret = safe_inject_adbd64_run_cmd_wrapper(root_key, input_command.c_str(), out_buf, sizeof(out_buf), false, true, true, true, chdir_path, true, &v_process64_env);
+ if (inject_ret < 0) {
+ std::cout << "inject_adbd64_run_cmd_wrapper ret:"<< inject_ret << std::endl;
+ return EXIT_FAILURE;
+ }
+ std::cout << out_buf;
+
+
+ //恢复SELinux状态
+ if (is_disable_selinux_status() != old_selinux_disable) {
+ if (old_selinux_disable) {
+ safe_disable_selinux(root_key);
+ }
+ else {
+ safe_enable_selinux(root_key);
+ }
+
+ }
+ }
+ TRACE("exit code: EXIT_SUCCESS\n");
+ return EXIT_SUCCESS;
+}
+
+
+
+int main(int argc, char *argv[])
+{
+ pid_t n = fork();
+ if (0 == n) {
+ su_client_main(argc, argv);
+ exit(0);
+ }
+ else {
+ wait(NULL);
+ }
+ return 0;
+}
diff --git a/su/su.h b/su/su.h
new file mode 100644
index 00000000..34388988
--- /dev/null
+++ b/su/su.h
@@ -0,0 +1,24 @@
+#ifndef _SU_H_
+#define _SU_H_
+#include
+
+//安静输出模式
+#define QUIET_PRINTF
+
+#ifdef QUIET_PRINTF
+#define TRACE(fmt, ...)
+#else
+#define TRACE(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#endif
+
+#define DEFAULT_SHELL "/system/bin/sh"
+
+// Constants for atty
+#define ATTY_IN (1 << 0)
+#define ATTY_OUT (1 << 1)
+#define ATTY_ERR (1 << 2)
+
+#define UID_ROOT 0
+#define UID_SHELL 2000
+
+#endif /* _SU_H_ */
diff --git a/su/su.vcxproj b/su/su.vcxproj
new file mode 100644
index 00000000..b40d276f
--- /dev/null
+++ b/su/su.vcxproj
@@ -0,0 +1,94 @@
+
+
+
+
+ Debug
+ ARM
+
+
+ Release
+ ARM
+
+
+ Debug
+ ARM64
+
+
+ Release
+ ARM64
+
+
+ Debug
+ x86
+
+
+ Release
+ x86
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ {0ae121c5-8222-45b9-b543-86318a1c88c3}
+ Linux
+ su
+ 15.0
+ Linux
+ 1.0
+ Generic
+ {D51BCBC9-82E9-4017-911E-C93873C4EA2B}
+
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/su/su.vcxproj.filters b/su/su.vcxproj.filters
new file mode 100644
index 00000000..b9e1b314
--- /dev/null
+++ b/su/su.vcxproj.filters
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+ {457d7200-d49a-432e-817b-81eb3f5f2924}
+
+
+
+
+ jni
+
+
+ jni
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/su/su.vcxproj.user b/su/su.vcxproj.user
new file mode 100644
index 00000000..6e2aec7a
--- /dev/null
+++ b/su/su.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/suTest/.gitignore b/suTest/.gitignore
new file mode 100644
index 00000000..643d213c
--- /dev/null
+++ b/suTest/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/suTest/app/.gitignore b/suTest/app/.gitignore
new file mode 100644
index 00000000..42afabfd
--- /dev/null
+++ b/suTest/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/suTest/app/build.gradle b/suTest/app/build.gradle
new file mode 100644
index 00000000..acde441b
--- /dev/null
+++ b/suTest/app/build.gradle
@@ -0,0 +1,41 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "com.linux.sutest"
+ minSdkVersion 26
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.3.1'
+ implementation 'com.google.android.material:material:1.4.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
+ implementation 'androidx.navigation:navigation-fragment:2.2.2'
+ implementation 'androidx.navigation:navigation-ui:2.2.2'
+ testImplementation 'junit:junit:4.+'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+}
\ No newline at end of file
diff --git a/suTest/app/proguard-rules.pro b/suTest/app/proguard-rules.pro
new file mode 100644
index 00000000..64b4a059
--- /dev/null
+++ b/suTest/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/suTest/app/src/androidTest/java/com/linux/sutest/ExampleInstrumentedTest.java b/suTest/app/src/androidTest/java/com/linux/sutest/ExampleInstrumentedTest.java
new file mode 100644
index 00000000..a463075e
--- /dev/null
+++ b/suTest/app/src/androidTest/java/com/linux/sutest/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.linux.sutest;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.linux.sutest", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/suTest/app/src/main/AndroidManifest.xml b/suTest/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..b537ed4e
--- /dev/null
+++ b/suTest/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/java/com/linux/sutest/MainActivity.java b/suTest/app/src/main/java/com/linux/sutest/MainActivity.java
new file mode 100644
index 00000000..d55bd1e4
--- /dev/null
+++ b/suTest/app/src/main/java/com/linux/sutest/MainActivity.java
@@ -0,0 +1,117 @@
+package com.linux.sutest;
+
+import android.graphics.Color;
+import android.os.Bundle;
+
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import com.google.android.material.snackbar.Snackbar;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import android.view.View;
+
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Button;
+import android.widget.TextView;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class MainActivity extends AppCompatActivity {
+
+ //执行root命令
+ public static final String runRootCmd(String cmd, boolean bWait) {
+ String retval = "";
+ try {
+ Process process = Runtime.getRuntime().exec("su");
+ DataOutputStream os = new DataOutputStream(process.getOutputStream());
+ os.writeBytes(cmd + "\n");
+ os.flush();
+ os.writeBytes("exit\n");
+ os.flush();
+ if (bWait) {
+ process.waitFor();
+ } else {
+ Thread.sleep(300);
+ }
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ process.getInputStream()));
+ int read;
+ char[] buffer = new char[4096];
+ StringBuffer output = new StringBuffer();
+ while ((read = reader.read(buffer)) > 0) {
+ output.append(buffer, 0, read);
+ }
+ reader.close();
+ reader = new BufferedReader(new InputStreamReader(
+ process.getErrorStream()));
+ while ((read = reader.read(buffer)) > 0) {
+ output.append(buffer, 0, read);
+ }
+ reader.close();
+
+ try {
+ /*
+ int suProcessRetval = process.waitFor();
+ if (255 != suProcessRetval) {
+ retval = true;
+ } else {
+ retval = false;
+ }
+ */
+ retval = output.toString();
+ } catch (Exception ex) {
+ //Log.e("Error executing root action", ex);
+ }
+ } catch (IOException ex) {
+ //Log.w("ROOT", "Can't get root access", ex);
+ } catch (SecurityException ex) {
+ //Log.w("ROOT", "Can't get root access", ex);
+ } catch (Exception ex) {
+ //Log.w("ROOT", "Error executing internal operation", ex);
+ }
+ return retval;
+ }
+
+ //检查是否具有Root权限
+ public boolean canRunRootCommands() {
+ String suTest = runRootCmd("id", true);
+ if (suTest.contains("uid=0")) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ updateRootStatus();
+ Button test_root_btn = findViewById(R.id.test_root_btn);
+ test_root_btn.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ updateRootStatus();
+ }
+ });
+
+ }
+
+ private void updateRootStatus(){
+ //获取ROOT权限检查
+ TextView root_status_txt = findViewById(R.id.root_status_txt);
+ if (canRunRootCommands()) {
+ root_status_txt.setText("获取成功");
+ root_status_txt.setTextColor(Color.rgb(0,255,0));
+ } else{
+ root_status_txt.setText("获取失败");
+ root_status_txt.setTextColor(Color.rgb(255,0,0));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/suTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/suTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..cc14f035
--- /dev/null
+++ b/suTest/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/drawable/ic_launcher_background.xml b/suTest/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..a4f78de5
--- /dev/null
+++ b/suTest/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/suTest/app/src/main/res/layout/activity_main.xml b/suTest/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..fd241461
--- /dev/null
+++ b/suTest/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/menu/menu_main.xml b/suTest/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..054a6c92
--- /dev/null
+++ b/suTest/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..a26f6fbc
--- /dev/null
+++ b/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..a26f6fbc
--- /dev/null
+++ b/suTest/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/mipmap-hdpi/ic_launcher.png b/suTest/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..a571e600
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/suTest/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/suTest/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..61da551c
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/suTest/app/src/main/res/mipmap-mdpi/ic_launcher.png b/suTest/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c41dd285
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/suTest/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/suTest/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..db5080a7
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..6dba46da
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..da31a871
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..15ac6817
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..b216f2d3
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..f25a4197
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e96783cc
Binary files /dev/null and b/suTest/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/suTest/app/src/main/res/navigation/nav_graph.xml b/suTest/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 00000000..4b714929
--- /dev/null
+++ b/suTest/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/values-night/themes.xml b/suTest/app/src/main/res/values-night/themes.xml
new file mode 100644
index 00000000..6ac0063e
--- /dev/null
+++ b/suTest/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/values/colors.xml b/suTest/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..823492c9
--- /dev/null
+++ b/suTest/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/values/dimens.xml b/suTest/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..6a18878a
--- /dev/null
+++ b/suTest/app/src/main/res/values/dimens.xml
@@ -0,0 +1,3 @@
+
+ 16dp
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/values/strings.xml b/suTest/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..399bda5f
--- /dev/null
+++ b/suTest/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+
+ suTest
+ Settings
+
+ First Fragment
+ Second Fragment
+ Next
+ Previous
+
+ Hello first fragment
+ Hello second fragment. Arg: %1$s
+
\ No newline at end of file
diff --git a/suTest/app/src/main/res/values/themes.xml b/suTest/app/src/main/res/values/themes.xml
new file mode 100644
index 00000000..2aef1410
--- /dev/null
+++ b/suTest/app/src/main/res/values/themes.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/suTest/app/src/test/java/com/linux/sutest/ExampleUnitTest.java b/suTest/app/src/test/java/com/linux/sutest/ExampleUnitTest.java
new file mode 100644
index 00000000..eb3ee144
--- /dev/null
+++ b/suTest/app/src/test/java/com/linux/sutest/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.linux.sutest;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/suTest/build.gradle b/suTest/build.gradle
new file mode 100644
index 00000000..6ad88d9d
--- /dev/null
+++ b/suTest/build.gradle
@@ -0,0 +1,24 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:4.1.1"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
\ No newline at end of file
diff --git a/suTest/gradle.properties b/suTest/gradle.properties
new file mode 100644
index 00000000..829d0737
--- /dev/null
+++ b/suTest/gradle.properties
@@ -0,0 +1,19 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app"s APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
\ No newline at end of file
diff --git a/suTest/gradle/wrapper/gradle-wrapper.jar b/suTest/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/suTest/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/suTest/gradle/wrapper/gradle-wrapper.properties b/suTest/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..5b44fc6f
--- /dev/null
+++ b/suTest/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Sep 22 22:41:16 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
diff --git a/suTest/gradlew b/suTest/gradlew
new file mode 100644
index 00000000..cccdd3d5
--- /dev/null
+++ b/suTest/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/suTest/gradlew.bat b/suTest/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/suTest/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/suTest/settings.gradle b/suTest/settings.gradle
new file mode 100644
index 00000000..84764abb
--- /dev/null
+++ b/suTest/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+rootProject.name = "suTest"
\ No newline at end of file
diff --git a/testRoot/README.md b/testRoot/README.md
index 117e997d..4398b524 100644
--- a/testRoot/README.md
+++ b/testRoot/README.md
@@ -6,8 +6,11 @@
"\t2.获取ROOT权限\n"
"\t3.绕过SELinux\n"
"\t4.还原SELinux\n"
- "\t5.执行ROOT权限级别的Shell命令\n"
- "\t6.赋予ADB最高级别权限\n"
- "\t新一代root,跟面具完全不同思路,摆脱面具被检测的弱点,完美隐藏root功能,挑战全网root检测手段,兼容安卓APP直接JNI调用,稳定、流畅、不闪退。\n"
+ "\t5.执行普通命令\n"
+ "\t6.执行ROOT命令\n"
+ "\t7.授予ADB最高级别权限\n"
+ "\t8.授权ROOT到其他进程\n"
+ "\t9.清理ROOT授权痕迹\n"
+ "\t新一代root,跟面具完全不同思路,摆脱面具被检测的弱点,完美隐藏root功能(免root级别),兼容安卓APP直接JNI稳定调用。\n"
"======================================================\n"
);
diff --git a/testRoot/adb64_helper.cpp b/testRoot/adb64_helper.cpp
new file mode 100644
index 00000000..07610222
--- /dev/null
+++ b/testRoot/adb64_helper.cpp
@@ -0,0 +1,177 @@
+#include "adb64_helper.h"
+#include "kernel_root_helper.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define ADB_TCP_PORT_MIN 50000
+#define ADB_TCP_PORT_MAX 60000
+
+pid_t _find_adbd_pid()
+{
+ int id;
+ pid_t pid = -1;
+ DIR* dir;
+ FILE *fp;
+ char filename[32];
+ char cmdline[256];
+
+ struct dirent * entry;
+ dir = opendir("/proc");
+ if (dir == NULL)
+ return -1;
+
+ while ((entry = readdir(dir)) != NULL) {
+ // 如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
+ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ else if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ else if (strspn(entry->d_name, "1234567890") != strlen(entry->d_name)) {
+ continue;
+ }
+
+ id = atoi(entry->d_name);
+ if (id != 0) {
+ sprintf(filename, "/proc/%d/cmdline", id);
+ fp = fopen(filename, "r");
+ if (fp) {
+ fgets(cmdline, sizeof(cmdline), fp);
+ fclose(fp);
+ //TRACE("[+] find %d process cmdline: %s\n", id, cmdline);
+ if ((strcmp("/system/bin/adbd", cmdline) == 0)
+ || (strstr(cmdline, "/bin/adbd"))) {
+ /* process found */
+ pid = id;
+ break;
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+ return pid;
+}
+
+
+pid_t find_adb_pid(unsigned int root_key)
+{
+ if (get_root(root_key) != 0) {
+ return -401;
+ }
+
+ if (!is_disable_selinux_status()) { //要关掉SELinux才能找到adb进程PID
+ if (disable_selinux(root_key) != 0) {
+ return -402;
+ }
+ }
+
+ return _find_adbd_pid();
+}
+
+pid_t safe_find_adbd_pid(unsigned int root_key)
+{
+ int fd[2];
+ if (pipe(fd))
+ {
+ return -411;
+ }
+
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ //fork error
+ return -412;
+
+ }
+ else if (pid == 0) { /* 子进程 */
+ close(fd[0]); //close read pipe
+ pid_t ret = find_adb_pid(root_key);
+ write(fd[1], &ret, sizeof(ret));
+ close(fd[1]); //close write pipe
+ exit(0);
+ }
+ else { /*父进程*/
+
+ close(fd[1]); //close write pipe
+
+ int status;
+ /* 等待目标进程停止或终止. WUNTRACED - 解释见参考手册 */
+ if (waitpid(pid, &status, WNOHANG | WUNTRACED) < 0) { return -6; }
+
+ pid_t ret = -413;
+ read(fd[0], (void*)&ret, sizeof(ret));
+ close(fd[0]); //close read pipe
+ return ret;
+ }
+ return -414;
+}
+
+void kill_process(unsigned int root_key, pid_t pid) {
+ char kill_shell[256] = { 0 };
+ snprintf(kill_shell, sizeof(kill_shell), "kill -9 %d", pid);
+ int ret = run_normal_cmd(root_key, kill_shell);
+}
+void safe_kill_process(unsigned int root_key, pid_t pid) {
+ char kill_shell[256] = { 0 };
+ snprintf(kill_shell, sizeof(kill_shell), "kill -9 %d", pid);
+ int ret = safe_run_normal_cmd(root_key, kill_shell);
+}
+
+
+//注入adb64进程远程执行命令,备注:此命令会自动提权到ROOT、并且关闭SELinux。结束运行后可根据自己的需要决定是否手动重新打开SELinux
+ssize_t inject_adbd64_run_cmd_wrapper(unsigned int root_key,
+ const char *cmd,
+ const char* p_out_result_buf/* = NULL*/,
+ size_t out_result_buf_size/* = 0*/,
+ bool afert_kill_adb/* = true*/,
+ bool user_root_auth/* = true*/,
+ bool after_recovery_last_uid/* = true*/,
+ bool after_recovery_last_gid/* = true*/,
+ const char * chdir_path/* = NULL*/,
+ bool clear_env/* = false*/,
+ std::vector *set_env/* = NULL*/) {
+ pid_t adb_pid = find_adb_pid(root_key);
+ if (adb_pid < 0) {
+ TRACE("Could not found the ADB daemon process.Please open ADB.\n");
+ return adb_pid;
+ }
+ ssize_t ret = inject_process64_run_cmd_wrapper(root_key, adb_pid, cmd, p_out_result_buf, out_result_buf_size, user_root_auth, after_recovery_last_uid, after_recovery_last_gid, chdir_path, clear_env, set_env);
+
+ if (afert_kill_adb) {
+ kill_process(root_key, adb_pid);
+ }
+ return ret;
+}
+//fork安全版本(可用于安卓APP直接调用)
+ssize_t safe_inject_adbd64_run_cmd_wrapper(unsigned int root_key,
+ const char *cmd,
+ const char* p_out_result_buf/* = NULL*/,
+ size_t out_result_buf_size/* = 0*/,
+ bool afert_kill_adb/* = true*/,
+ bool user_root_auth/* = true*/,
+ bool after_recovery_last_uid/* = true*/,
+ bool after_recovery_last_gid/* = true*/,
+ const char * chdir_path/* = NULL*/,
+ bool clear_env/* = false*/,
+ std::vector *set_env/* = NULL*/) {
+ pid_t adb_pid = safe_find_adbd_pid(root_key);
+ if (adb_pid < 0) {
+ TRACE("Could not found the ADB daemon process.Please open ADB.\n");
+ return adb_pid;
+ }
+ ssize_t ret = safe_inject_process64_run_cmd_wrapper(root_key, adb_pid, cmd, p_out_result_buf, out_result_buf_size, user_root_auth, after_recovery_last_uid, after_recovery_last_gid, chdir_path, clear_env, set_env);
+
+ if (afert_kill_adb) {
+ safe_kill_process(root_key, adb_pid);
+ }
+ return ret;
+}
diff --git a/testRoot/adb64_helper.h b/testRoot/adb64_helper.h
new file mode 100644
index 00000000..594a51e8
--- /dev/null
+++ b/testRoot/adb64_helper.h
@@ -0,0 +1,34 @@
+#ifndef ADB_HELPER_H_
+#define ADB_HELPER_H_
+#include
+#include "process64_inject.h"
+
+//עadbd64ԶִעԶȨROOTҹرSELinuxкɸԼҪǷֶ´SELinux
+ssize_t inject_adbd64_run_cmd_wrapper(
+ unsigned int root_key,
+ const char *cmd,
+ const char* p_out_result_buf = NULL,
+ size_t out_result_buf_size = 0,
+ bool afert_kill_adb = true,
+ bool user_root_auth = true,
+ bool after_recovery_last_uid = true,
+ bool after_recovery_last_gid = true,
+ const char * chdir_path = NULL,
+ bool clear_env = false,
+ std::vector *set_env = NULL);
+
+//forkȫ汾ڰAPPֱӵã
+ssize_t safe_inject_adbd64_run_cmd_wrapper(
+ unsigned int root_key,
+ const char *cmd,
+ const char* p_out_result_buf = NULL,
+ size_t out_result_buf_size = 0,
+ bool afert_kill_adb = true,
+ bool user_root_auth = true,
+ bool after_recovery_last_uid = true,
+ bool after_recovery_last_gid = true,
+ const char * chdir_path = NULL,
+ bool clear_env = false,
+ std::vector *set_env = NULL);
+
+#endif /* ADB_HELPER_H_ */
diff --git a/testRoot/base64.cpp b/testRoot/base64.cpp
new file mode 100644
index 00000000..56e166d7
--- /dev/null
+++ b/testRoot/base64.cpp
@@ -0,0 +1,96 @@
+#include "base64.h"
+#include
+#include
+static const std::string base64_chars =
+"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+"abcdefghijklmnopqrstuvwxyz"
+"0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+ return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+ std::string ret;
+ int i = 0;
+ int j = 0;
+ unsigned char char_array_3[3];
+ unsigned char char_array_4[4];
+
+ while (in_len--) {
+ char_array_3[i++] = *(bytes_to_encode++);
+ if (i == 3) {
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (i = 0; (i < 4); i++)
+ ret += base64_chars[char_array_4[i]];
+ i = 0;
+ }
+ }
+
+ if (i)
+ {
+ for (j = i; j < 3; j++)
+ char_array_3[j] = '\0';
+
+ char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+ char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+ char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+ char_array_4[3] = char_array_3[2] & 0x3f;
+
+ for (j = 0; (j < i + 1); j++)
+ ret += base64_chars[char_array_4[j]];
+
+ while ((i++ < 3))
+ ret += '=';
+
+ }
+
+ return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+ int in_len = encoded_string.size();
+ int i = 0;
+ int j = 0;
+ int in_ = 0;
+ unsigned char char_array_4[4], char_array_3[3];
+ std::string ret;
+
+ while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+ char_array_4[i++] = encoded_string[in_]; in_++;
+ if (i == 4) {
+ for (i = 0; i < 4; i++)
+ char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (i = 0; (i < 3); i++)
+ ret += char_array_3[i];
+ i = 0;
+ }
+ }
+
+ if (i) {
+ for (j = i; j < 4; j++)
+ char_array_4[j] = 0;
+
+ for (j = 0; j < 4; j++)
+ char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+ char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+ char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+ char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+ for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+ }
+
+ return ret;
+}
diff --git a/testRoot/base64.h b/testRoot/base64.h
new file mode 100644
index 00000000..145e06fe
--- /dev/null
+++ b/testRoot/base64.h
@@ -0,0 +1,6 @@
+#ifndef BASE_64_H
+#define BASE_64_H
+#include
+std::string base64_encode(unsigned char const*, unsigned int len);
+std::string base64_decode(std::string const& s);
+#endif
diff --git a/testRoot/jni/Android.mk b/testRoot/jni/Android.mk
index f1a70c67..92c4b0f6 100644
--- a/testRoot/jni/Android.mk
+++ b/testRoot/jni/Android.mk
@@ -1,11 +1,11 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_CPPFLAGS += -std=c++1y
+LOCAL_CPPFLAGS += -std=c++17
LOCAL_CFLAGS += -fPIE
LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_LDFLAGS += -fPIE -pie
LOCAL_DISABLE_FATAL_LINKER_WARNINGS := true
LOCAL_MODULE := testRoot.out
-LOCAL_SRC_FILES := ../main.cpp ../adb_inject.cpp ../ptrace_arm64_utils.cpp
+LOCAL_SRC_FILES := ../testRoot.cpp ../process64_inject.cpp ../adb64_helper.cpp ../ptrace_arm64_utils.cpp ../su_install_helper.cpp ../base64.cpp
include $(BUILD_EXECUTABLE)
diff --git a/testRoot/main.cpp b/testRoot/main.cpp
deleted file mode 100644
index ca905fee..00000000
--- a/testRoot/main.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-#include
-#include
-
-#include "super_root.h"
-#include "adb_inject.h"
-#define ROOT_KEY 0x7F6766F8
-
-void show_capability_info()
-{
- struct __user_cap_header_struct cap_header_data;
- cap_user_header_t cap_header = &cap_header_data;
-
- struct __user_cap_data_struct cap_data_data;
- cap_user_data_t cap_data = &cap_data_data;
-
- cap_header->pid = getpid();
- cap_header->version = _LINUX_CAPABILITY_VERSION_3; //_1、_2、_3
-
- if (capget(cap_header, cap_data) < 0) {
- perror("FAILED capget()");
- exit(1);
- }
-
- printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective, cap_data->permitted, cap_data->inheritable);
- printf("now getuid()=%d,geteuid()=%d,getgid()=%d,getegid()=%d\n", getuid(), geteuid(), getgid(), getegid());
-
- FILE * fp = popen("getenforce", "r");
- if (fp)
- {
- char cmd[512] = { 0 };
- fread(cmd, 1, sizeof(cmd), fp);
- pclose(fp);
-
- printf("SELinux status: %s\n", cmd);
- }
-}
-void test_root()
-{
- show_capability_info();
-
- printf("get_root ret:%d\n", get_root(ROOT_KEY));
-
- show_capability_info();
-
- //system("id");
- //system("/data/local/tmp/getmyinfo");
- //system("insmod /sdcard/rwProcMem37.ko ; echo $?");
- //system("cat /proc/1/maps");
- //system("ls /proc");
- //system("screencap -p /sdcard/temp.png");
- return;
-}
-
-void test_disable_selinux()
-{
- int ret = disable_selinux(ROOT_KEY);
- printf("disable_selinux ret:%d\n", ret);
- printf("done.\n");
- return;
-}
-
-void test_enable_selinux()
-{
- int ret = enable_selinux(ROOT_KEY);
- printf("enable_selinux ret:%d\n", ret);
- printf("done.\n");
- return;
-}
-
-
-void test_run_cmd(char * cmd, bool bKeepAdbRoot = false) {
- printf("inject_cmd_remote_process(%s)\n", cmd);
- char szResult[0x1000] = { 0 };
- ssize_t ret = safe_inject_adb_process_run_cmd_wrapper(ROOT_KEY, cmd, bKeepAdbRoot, szResult, sizeof(szResult));
- printf("inject_cmd_remote_process ret val:%zd\n", ret);
- printf("inject_cmd_remote_process result:%s\n", szResult);
-}
-
-int main(int argc, char *argv[])
-{
- printf(
- "======================================================\n"
- "本工具名称: Linux ARM64 完美隐藏ROOT演示\n"
- "本工具功能列表:\n"
- "\t1.显示自身权限信息\n"
- "\t2.获取ROOT权限\n"
- "\t3.绕过SELinux\n"
- "\t4.还原SELinux\n"
- "\t5.执行ROOT权限级别的Shell命令\n"
- "\t6.赋予ADB最高级别权限\n"
- "\t新一代root,跟面具完全不同思路,摆脱面具被检测的弱点,完美隐藏root功能,挑战全网root检测手段,兼容安卓APP直接JNI调用,稳定、流畅、不闪退。\n"
- "======================================================\n"
- );
-
-
- ++argv;
- --argc;
-
-
- int cmdc;
- char *cmdv[6];
-
- while (argc) {
- // Clean up
- cmdc = 0;
- memset(cmdv, 0, sizeof(cmdv));
-
- // Split the commands
- for (char *tok = strtok(argv[0], " "); tok; tok = strtok(nullptr, " "))
- {
- cmdv[cmdc++] = tok;
- if (cmdc == 0)
- {
- continue;
- }
- }
-
-
- if (strcmp(cmdv[0], "show") == 0) {
- show_capability_info();
- }
- else if (strcmp(cmdv[0], "root") == 0) {
- test_root();
- }
- else if (strcmp(cmdv[0], "disable") == 0) {
- test_disable_selinux();
- }
- else if (strcmp(cmdv[0], "enable") == 0) {
- test_enable_selinux();
- }
- else if (strcmp(cmdv[0], "cmd") == 0) {
- test_run_cmd("id");
- //test_run_cmd("id > /sdcard/run.txt");
- //test_run_cmd("insmod rwProcMem37.ko > /sdcard/run.txt");
- }
- else if (strcmp(cmdv[0], "adb") == 0) {
- test_run_cmd("id", true);
- }
- else {
- return 1;
- }
-
- --argc;
- ++argv;
- }
- return 0;
-}
\ No newline at end of file
diff --git a/testRoot/maps_helper.h b/testRoot/maps_helper.h
new file mode 100644
index 00000000..dc01302e
--- /dev/null
+++ b/testRoot/maps_helper.h
@@ -0,0 +1,114 @@
+#ifndef MAPS_HELPER_H_
+#define MAPS_HELPER_H_
+#include "testRoot.h"
+#include
+#include
+#include
+#include
+#include
+
+
+static std::string find_process_libc_so_path(pid_t pid) {
+ char line[1024] = { 0 };
+ std::string so_path;
+ char filename[32];
+ if (pid < 0) {
+ /* self process */
+ snprintf(filename, sizeof(filename), "/proc/self/maps");
+ }
+ else {
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+ }
+ FILE * fp = fopen(filename, "r");
+ if (fp != NULL) {
+ while (fgets(line, sizeof(line), fp)) {
+ if (strstr(line, "libc.so")) {
+
+ char * start = strstr(line, "/");
+ if (start)
+ {
+ start[strlen(start) - 1] = '\0';
+ so_path = start;
+ }
+ break;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return so_path;
+}
+
+
+//显然,这里面核心的就是get_module_base函数:
+/*
+此函数的功能就是通过遍历/proc/pid/maps文件,来找到目的module_name的内存映射起始地址。
+由于内存地址的表达方式是startAddrxxxxxxx-endAddrxxxxxxx的,所以会在后面使用strtok(line,"-")来分割字符串
+如果pid = -1,表示获取本地进程的某个模块的地址,
+否则就是pid进程的某个模块的地址。
+*/
+
+static void* get_module_base(pid_t pid, const char* module_name)
+{
+ FILE *fp;
+ long addr = 0;
+ char *pch;
+ char filename[32];
+ char line[1024];
+
+ if (pid < 0) {
+ /* self process */
+ snprintf(filename, sizeof(filename), "/proc/self/maps");
+ }
+ else {
+ snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
+ }
+
+ fp = fopen(filename, "r");
+
+ if (fp != NULL) {
+ while (fgets(line, sizeof(line), fp)) {
+ if (strstr(line, module_name)) {
+ //分解字符串为一组字符串。line为要分解的字符串,"-"为分隔符字符串。
+ pch = strtok(line, "-");
+ //将参数pch字符串根据参数base(表示进制)来转换成无符号的长整型数
+ addr = strtoull(pch, NULL, 16);
+
+ if (addr == 0x8000)
+ addr = 0;
+
+ break;
+ }
+ }
+
+ fclose(fp);
+ }
+
+ return (void *)addr;
+}
+
+/*
+该函数为一个封装函数,通过调用get_module_base函数来获取目的进程的某个模块的起始地址,然后通过公式计算出指定函数在目的进程的起始地址。
+*/
+static void* get_remote_addr(pid_t target_pid, const char* module_name, void* local_addr)
+{
+ void* local_handle, *remote_handle;
+
+ //获取本地某个模块的起始地址
+ local_handle = get_module_base(-1, module_name);
+ if (local_handle == NULL) {
+ return local_handle;
+ }
+
+ //获取远程pid的某个模块的起始地址
+ remote_handle = get_module_base(target_pid, module_name);
+
+ TRACE("[+] get_remote_addr: local[%p], remote[%p]\n", local_handle, remote_handle);
+ /*这需要我们好好理解:local_addr - local_handle的值为指定函数(如mmap)在该模块中的偏移量,然后再加上rempte_handle,结果就为指定函数在目的进程的虚拟地址*/
+ void * ret_addr = (void *)((uintptr_t)local_addr + (uintptr_t)remote_handle - (uintptr_t)local_handle);
+
+ return ret_addr;
+}
+
+#endif /* MAPS_HELPER_H_ */
diff --git a/testRoot/process64_inject.h b/testRoot/process64_inject.h
new file mode 100644
index 00000000..4eaf1486
--- /dev/null
+++ b/testRoot/process64_inject.h
@@ -0,0 +1,49 @@
+#ifndef _PROCESS64_INJECT_H_
+#define _PROCESS64_INJECT_H_
+#include "testRoot.h"
+#include
+#include
+
+
+//ע64λԶִעԶȨROOTҹرSELinuxкɸԼҪǷֶ´SELinux
+struct process64_env
+{
+ char key[0x1000]; //keynameֵܴpagesize
+ char value[0x1000];
+};
+ssize_t inject_process64_run_cmd_wrapper(
+ unsigned int root_key,
+ pid_t target_pid,
+ const char *cmd,
+ const char* p_out_result_buf = NULL,
+ size_t out_result_buf_size = 0,
+ bool user_root_auth = true,
+ bool after_recovery_last_uid = false,
+ bool after_recovery_last_gid = false,
+ const char * chdir_path = NULL,
+ bool clear_env = false,
+ std::vector *set_env = NULL);
+//forkȫ汾ڰAPPֱӵã
+ssize_t safe_inject_process64_run_cmd_wrapper(
+ unsigned int root_key,
+ pid_t target_pid,
+ const char *cmd,
+ const char* p_out_result_buf = NULL,
+ size_t out_result_buf_size = 0,
+ bool user_root_auth = true,
+ bool after_recovery_last_uid = false,
+ bool after_recovery_last_gid = false,
+ const char * chdir_path = NULL,
+ bool clear_env = false,
+ std::vector *set_env = NULL);
+
+//עԶ̽PATH·עԶȨROOTҹرSELinuxкɸԼҪǷֶ´SELinux
+ssize_t inject_process_env64_PATH_wrapper(unsigned int root_key, int target_pid, const char *add_path);
+//forkȫ汾ڰAPPֱӵã
+ssize_t safe_inject_process_env64_PATH_wrapper(unsigned int root_key, int target_pid, const char *add_path);
+
+//ע64λ̶̬ӿsoעԶȨROOTҹرSELinuxкɸԼҪǷֶ´SELinux
+ssize_t inject_process64_so_wrapper(unsigned int root_key, pid_t target_pid, const char *p_target_so_path, const char* p_target_so_func_name);
+//forkȫ汾ڰAPPֱӵã
+ssize_t safe_inject_process64_so_wrapper(unsigned int root_key, pid_t target_pid, const char *p_target_so_path, const char* p_target_so_func_name);
+#endif /* _PROCESS64_INJECT_H_ */
diff --git a/testRoot/ptrace_arm64_utils.cpp b/testRoot/ptrace_arm64_utils.cpp
index 3cccfeb4..ce8d674a 100644
--- a/testRoot/ptrace_arm64_utils.cpp
+++ b/testRoot/ptrace_arm64_utils.cpp
@@ -220,28 +220,56 @@ int ptrace_call(pid_t pid, uintptr_t addr, unsigned long *params, int num_params
/*
*Ptrace_setregs就是将修改后的regs写入寄存器中,然后调用ptrace_continue来执行我们指定的代码
*/
- if (ptrace_setregs(pid, regs) == -1
- || ptrace_continue(pid) == -1) {
- TRACE("error\n");
+ if (ptrace_setregs(pid, regs) == -1 || ptrace_continue(pid) == -1) {
return -1;
}
- int stat = 0;
- waitpid(pid, &stat, WUNTRACED);
- /* WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。
- 对于使用ptrace_cont运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。这里的0xb7f就表示子进程进入了暂停状态,且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。那么什么时候会发生这种错误呢?显然,当子进程执行完注入的函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV了!*/
+/*
- //这个循环是否必须我还不确定。因为目前每次ptrace_call调用必定会返回0xb7f,不过在这也算是增加容错性吧~
+WUNTRACED告诉waitpid,如果子进程进入暂停状态,那么就立即返回。如果是被ptrace的子进程,那么即使不提供WUNTRACED参数,也会在子进程进入暂停状态的时候立即返回。对于使用PTRACE_CONT运行的子进程,它会在3种情况下进入暂停状态:①下一次系统调用;②子进程退出;③子进程的执行发生错误。这里的0xb7f就表示子进程进入了暂停状态,且发送的错误信号为11(SIGSEGV),它表示试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。那么什么时候会发生这种错误呢?显然,当子进程执行完注入的函数后,由于我们在前面设置了regs->ARM_lr = 0,它就会返回到0地址处继续执行,这样就会产生SIGSEGV。
+这里还需要了解下arm架构的相关知识。首先是函数参数传递,在arm中,函数的前4个参数分别保存在r0-r3中,当参数大于4个,就依次压入栈中。此外,arm处理器实际上支持两套指令集,即arm和thumb。thumb为16位,arm为32位。这里通过判断pc的最后一位是否是1来确定指令集,这是因为编译器在用thmub指令集编译一个函数时,会将函数的符号地址设置成真正的映射地址+1,实现arm和thumb混编。此外,在切换arm和thumb指令时,还会修改CPSR处理器。在arm中,出了r0-r15这16个处理器,还有状态寄存器CPSR。关于CPSR的其他位这里先不讨论,我们只要知道CPSR寄存器的第低5位T标识了当前的指令集(T=0表示执行arm指令,T=1表示执行Thumb指令),所以在切换指令集时需要修改这一位。
- //通过看ndk的源码sys/wait.h以及man waitpid可以知道这个0xb7f的具体作用。首先说一下stat的值:高2字节用于表示导致子进程的退出或暂停状态信号值,低2字节表示子进程是退出(0x0)还是暂停(0x7f)状态。0xb7f就表示子进程为暂停状态,导致它暂停的信号量为11即sigsegv错误。
- while (stat != 0xb7f) {
- if (ptrace_continue(pid) == -1) {
- TRACE("error\n");
- return -1;
- }
- waitpid(pid, &stat, WUNTRACED);
+
+Arm与Thumb之间的状态切换是通过专用的转移交换指令BX来实现。BX指令以通用寄存器(R0~R15)为操作数,通过拷贝Rn到PC实现绝对跳转。BX利用Rn寄存器中目的地址值的最后一位判断跳转后的状态,如果为“1”表示跳转到Thumb指令集的函数中,如果为“0”表示跳转到Arm指令集的函数中。而Arm指令集的每条指令是32位,即4个字节,也就是说Arm指令的地址肯定是4的倍数,最后两位必定为“00”。所以,直接就可以将从符号表中获得的调用地址模4,看是否为0来判断要修改的函数是用Arm指令集还是Thumb指令集。
+
+ */
+
+
+
+ // waitpid(pid, NULL, WUNTRACED);
+
+ int status = 0;
+ // waitpid(pid,&stat,WUNTRACED);
+ pid_t res;
+ waitpid(pid, NULL, WUNTRACED);
+ /*
+ * Restarts the stopped child as for PTRACE_CONT, but arranges for
+ * the child to be stopped at the next entry to or exit from a sys‐
+ * tem call, or after execution of a single instruction, respec‐
+ * tively.
+ */
+ if (ptrace(PTRACE_SYSCALL, pid, NULL, 0) < 0) {
+ TRACE("ptrace_syscall");
+ return -1;
}
- return 0;
-}
+ waitpid(pid, NULL, WUNTRACED);
+
+ if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL) < 0) {
+ TRACE("ptrace_syscall");
+ return -1;
+ }
+
+ res = waitpid(pid, NULL, WUNTRACED);
+ TRACE("[+] status is %x", status);
+ if (res != pid || !WIFSTOPPED(status))//WIFSTOPPED(status) 若为当前暂停子进程返回的状态,则为真
+ return 0;
+ TRACE("[+]done %d\n", (WSTOPSIG(status) == SIGSEGV) ? 1 : 0);
+ //设置siginal 11信号处理函数
+/* if(signal(SIGSEGV,handler) == SIG_ERR){
+ LOGE("[-]can not set handler for SIGSEGV");
+ }*/
+
+ return 0;
+}
\ No newline at end of file
diff --git a/testRoot/ptrace_arm64_utils.h b/testRoot/ptrace_arm64_utils.h
index d63bb6d7..d3ffe1f7 100644
--- a/testRoot/ptrace_arm64_utils.h
+++ b/testRoot/ptrace_arm64_utils.h
@@ -1,15 +1,8 @@
-#ifndef PTRACE_UTILS_H_
-#define PTRACE_UTILS_H_
-#include
-
-//安静输出模式
-//#define QUIET_PRINTF
+#ifndef PTRACE_ARM64_UTILS_H_
+#define PTRACE_ARM64_UTILS_H_
+#include "testRoot.h"
-#ifdef QUIET_PRINTF
-#define TRACE(fmt, ...)
-#else
-#define TRACE(fmt, ...) printf(fmt, ##__VA_ARGS__)
-#endif
+#include
#ifndef __aarch64__
#error "Not supported"
@@ -59,4 +52,4 @@ int ptrace_call_wrapper(pid_t target_pid, const char * func_name, void * func_ad
*/
int ptrace_call(pid_t pid, uintptr_t addr, unsigned long *params, int num_params, struct pt_regs* regs);
-#endif /* PTRACE_UTILS_H_ */
+#endif /* PTRACE_ARM64_UTILS_H_ */
diff --git a/testRoot/su_install_helper.cpp b/testRoot/su_install_helper.cpp
new file mode 100644
index 00000000..88015850
--- /dev/null
+++ b/testRoot/su_install_helper.cpp
@@ -0,0 +1,350 @@
+#include "su_install_helper.h"
+#include "kernel_root_helper.h"
+#include "testRoot.h"
+#include "../su/root_key_helper.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * xattr name for SELinux attributes.
+ * This may have been exported via Kernel uapi header.
+ */
+#ifndef XATTR_NAME_SELINUX
+#define XATTR_NAME_SELINUX "security.selinux"
+#endif
+
+const char* check_file_list[] = {
+ "su",
+};
+std::string get_child_su_hidden_path(const char* myself_path) {
+
+ std::string id;
+ DIR* dir;
+ FILE *fp;
+ struct dirent * entry;
+ const char* su_head = "su_";
+
+ dir = opendir(myself_path);
+ if (dir == NULL)
+ return id;
+
+ while ((entry = readdir(dir)) != NULL) {
+ // 如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
+ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ else if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ else if (strlen(entry->d_name) <= strlen(su_head)) {
+ continue;
+ }
+ char * p_id = strstr(entry->d_name, su_head);
+ if (!p_id) {
+ continue;
+ }
+ p_id += strlen(su_head);
+ id = myself_path;
+ id += "/";
+ id += entry->d_name;
+ break;
+ }
+ closedir(dir);
+ return id;
+
+}
+/*生成一个长度为n的包含字符和数字的随机字符串*/
+void rand_str(char* dest, int n)
+{
+ int i, randno;
+ char stardstring[63] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ srand((unsigned)time(NULL));
+ for (i = 0; i < n; i++)
+ {
+ randno = rand() % 62;
+ *dest = stardstring[randno];
+ dest++;
+ }
+ *dest = '\0';
+}
+
+std::string create_su_hidden_path(const char* myself_path, unsigned int root_key) {
+
+ //1.生成一个guid
+ char guid[16 + 1] = { 0 };
+ rand_str(guid, sizeof(guid) - 1);
+
+ //2.将root_key密码写前面,加A字幕,加guid
+ std::stringstream sstrBuf;
+ sstrBuf << root_key << "A" << guid;
+
+ //3.base64加密
+ std::string base64 = base64_encode((const unsigned char*)sstrBuf.str().c_str(), sstrBuf.str().length());
+
+ //4.拼接进路径
+ std::string file_path = myself_path;
+ file_path += "/su_";
+ file_path += base64;
+ if (mkdir(file_path.c_str(), 0755)) {
+ TRACE("create_su_hidden_path error:%s\n", file_path.c_str());
+ return {};
+ }
+ if (chmod(file_path.c_str(), 0777)) {
+ TRACE("chmod error:%s\n", file_path.c_str());
+ return {};
+ }
+ return file_path;
+
+}
+bool check_su_file_exist(const char* path) {
+
+ std::string str_path = path;
+ for (size_t i = 0; i < sizeof(check_file_list) / sizeof(check_file_list[0]); i++) {
+ if (access(std::string(str_path + "/" + check_file_list[i]).c_str(), F_OK)) {
+ TRACE("check_su_file_exist could not found %s.\n", check_file_list[i]);
+ return false;
+ }
+ }
+ return true;
+}
+bool set_su_file_access_mode(const char* path) {
+
+ std::string str_path = path;
+ for (size_t i = 0; i < sizeof(check_file_list) / sizeof(check_file_list[0]); i++) {
+ std::string str_target_file_path = std::string(str_path + "/" + check_file_list[i]);
+ if (chmod(str_target_file_path.c_str(), 0777)) {
+ TRACE("set_su_file_access_mode could not found %s.\n", check_file_list[i]);
+ return false;
+ }
+ const char* selinux_file_flag = "u:object_r:system_file:s0";
+ if (setxattr(str_target_file_path.c_str(), XATTR_NAME_SELINUX, selinux_file_flag, strlen(selinux_file_flag) + 1, 0)) {
+ TRACE("setxattr error %s.\n", str_target_file_path.c_str());
+ return false;
+ }
+
+ }
+ return true;
+}
+bool move_su_file_to_su_hidden_path(const char* source_path, const char* target_path) {
+
+ std::string str_source_path = source_path;
+ std::string str_target_path = target_path;
+ for (size_t i = 0; i < sizeof(check_file_list) / sizeof(check_file_list[0]); i++) {
+
+ std::string old_file_path = std::string(str_source_path + "/" + check_file_list[i]);
+ std::string new_file_path = std::string(str_target_path + "/" + check_file_list[i]);
+ if (access(old_file_path.c_str(), F_OK)) {
+ TRACE("move_su_file_to_su_hidden_path could not found %s.\n", old_file_path.c_str());
+ return false;
+ }
+ std::fstream file1;
+ file1.open(old_file_path.c_str(), std::ios::binary | std::ios::in | std::ios::ate); //打开时指针在文件尾
+ if (!file1.is_open()) {
+ TRACE("Could not open file %s.\n", old_file_path.c_str());
+ return false;
+ }
+ size_t length = file1.tellg();
+ std::unique_ptr up_new_file_data = std::make_unique(length);
+ file1.seekg(0);
+ file1.read(up_new_file_data.get(), length); //二进制只能用这个读
+ file1.close();
+
+
+ std::fstream file2;
+ file2.open(new_file_path.c_str(), std::ios::binary | std::ios::out);
+ if (!file2.is_open()) {
+ TRACE("Could not open file %s.\n", new_file_path.c_str());
+ return false;
+ }
+ file2.write(up_new_file_data.get(), length); //二进制只能用这个写
+ file2.close();
+
+ if (chmod(new_file_path.c_str(), 0777)) {
+ TRACE("Could not chmod file %s.\n", new_file_path.c_str());
+ return false;
+ }
+ }
+ return true;
+}
+bool del_su_file(const char* path) {
+
+ std::string str_path = path;
+ for (size_t i = 0; i < sizeof(check_file_list) / sizeof(check_file_list[0]); i++) {
+ std::string file_path = std::string(str_path + "/" + check_file_list[i]);
+ TRACE("del_su_file:%s\n", file_path.c_str());
+ remove(file_path.c_str());
+ }
+ return true;
+}
+int install_su_tools(unsigned int root_key, const char* base_path, std::string & su_hidden_path) {
+
+ if (get_root(root_key) != 0) {
+ return -501;
+ }
+
+ if (!is_disable_selinux_status()) {
+ if (disable_selinux(root_key) != 0) {
+ return -502;
+ }
+ }
+
+ //1.获取su_xxx隐藏目录
+ std::string _su_hidden_path = base_path;
+ unsigned int tmp_root_key = get_tmp_root_key(base_path); //看看自身路径有没有
+ if (tmp_root_key == 0) {
+ _su_hidden_path = get_child_su_hidden_path(base_path); //没有再看看子目录
+
+ if (_su_hidden_path.empty()) {
+ //2.取不到,那就创建一个
+ _su_hidden_path = create_su_hidden_path(base_path, root_key);
+ }
+ if (_su_hidden_path.empty()) {
+ TRACE("su_hidden_path empty error\n");
+ return -503;
+ }
+ su_hidden_path = _su_hidden_path + "/";
+
+ //3.检查su_xxx目录下的文件是否齐全
+ if (!check_su_file_exist(_su_hidden_path.c_str())) {
+ //4.不齐全则开始补齐
+ if (!check_su_file_exist(base_path)) {
+ //自身目录都没有,怎么补过去
+ TRACE("myself path su file not exist:%s\n", base_path);
+ return -504;
+ }
+ //5.开始移动文件补齐到su_xxx目录
+ if (!move_su_file_to_su_hidden_path(base_path, _su_hidden_path.c_str())) {
+ TRACE("move_su_file_to_su_hidden_path error:%s -> %s\n", base_path, _su_hidden_path.c_str());
+ return -505;
+ }
+ }
+ //6.赋值文件运行权限
+ if(!set_su_file_access_mode(_su_hidden_path.c_str())) {
+ TRACE("set_su_file_access_mode error:%s\n", _su_hidden_path.c_str());
+ return -506;
+ }
+ //7.从自身路径中删除文件,移除痕迹,防止被检测
+ del_su_file(base_path);
+ }
+ su_hidden_path = _su_hidden_path + "/";
+ return 0;
+}
+
+int safe_install_su_tools(unsigned int root_key, const char* base_path, std::string & su_hidden_path) {
+ int fd[2];
+ if (pipe(fd)) {
+ return -431;
+ }
+
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ //fork error
+ return -432;
+
+ }
+ else if (pid == 0) { /* 子进程 */
+ close(fd[0]); //close read pipe
+ pid_t ret = install_su_tools(root_key, base_path, su_hidden_path);
+ write(fd[1], &ret, sizeof(ret));
+ char buf[4096] = { 0 };
+ strcpy(buf, su_hidden_path.c_str());
+ write(fd[1], &buf, sizeof(buf));
+ close(fd[1]); //close write pipe
+ exit(0);
+ }
+ else { /*父进程*/
+
+ close(fd[1]); //close write pipe
+
+ int status;
+ /* 等待目标进程停止或终止. WUNTRACED - 解释见参考手册 */
+ if (waitpid(pid, &status, WNOHANG | WUNTRACED) < 0) { return -6; }
+
+ pid_t ret = -433;
+ read(fd[0], (void*)&ret, sizeof(ret));
+ char buf[4096] = { 0 };
+ read(fd[0], (void*)&buf, sizeof(buf));
+ su_hidden_path = buf;
+ close(fd[0]); //close read pipe
+ return ret;
+ }
+ return -434;
+}
+
+int uninstall_su_tools(unsigned int root_key, const char* base_path) {
+
+ if (get_root(root_key) != 0) {
+ return -511;
+ }
+
+ if (!is_disable_selinux_status()) {
+ if (disable_selinux(root_key) != 0) {
+ return -512;
+ }
+ }
+ //从自身路径中删除文件,移除痕迹,防止被检测
+ del_su_file(base_path);
+
+ do
+ {
+ //获取su_xxx隐藏目录
+ std::string _su_hidden_path = get_child_su_hidden_path(base_path); //没有再看看子目录
+ if (_su_hidden_path.empty()) {
+ break;
+ }
+ //取到了,再删
+ del_su_file(_su_hidden_path.c_str());
+
+ //文件夹也删掉
+ std::string del_dir_cmd = "rm -rf ";
+ del_dir_cmd += _su_hidden_path;
+ int err = run_normal_cmd(root_key, del_dir_cmd.c_str());
+ if (err) {
+ return err;
+ }
+
+ } while (1);
+ safe_enable_selinux(root_key);
+ return 0;
+}
+int safe_uninstall_su_tools(unsigned int root_key, const char* base_path) {
+ int fd[2];
+ if (pipe(fd)) {
+ return -520;
+ }
+
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ //fork error
+ return -521;
+
+ }
+ else if (pid == 0) { /* 子进程 */
+ close(fd[0]); //close read pipe
+ int ret = uninstall_su_tools(root_key, base_path);
+ write(fd[1], &ret, sizeof(ret));
+ close(fd[1]); //close write pipe
+ exit(0);
+ }
+ else { /*父进程*/
+
+ close(fd[1]); //close write pipe
+
+ int status;
+ /* 等待目标进程停止或终止. WUNTRACED - 解释见参考手册 */
+ if (waitpid(pid, &status, WNOHANG | WUNTRACED) < 0) { return -6; }
+
+ int ret = -522;
+ read(fd[0], (void*)&ret, sizeof(ret));
+ close(fd[0]); //close read pipe
+ return ret;
+ }
+ return -523;
+}
\ No newline at end of file
diff --git a/testRoot/su_install_helper.h b/testRoot/su_install_helper.h
new file mode 100644
index 00000000..c0e4423d
--- /dev/null
+++ b/testRoot/su_install_helper.h
@@ -0,0 +1,16 @@
+#ifndef _SU_INSTALL_HELPER_H_
+#define _SU_INSTALL_HELPER_H_
+#include
+
+std::string get_child_su_hidden_path(const char* myself_path);
+
+int install_su_tools(unsigned int root_key, const char* base_path, std::string & su_hidden_path);
+//forkȫ汾ڰAPPֱӵã
+int safe_install_su_tools(unsigned int root_key, const char* base_path, std::string & su_hidden_path);
+
+
+int uninstall_su_tools(unsigned int root_key, const char* base_path);
+//forkȫ汾ڰAPPֱӵã
+int safe_uninstall_su_tools(unsigned int root_key, const char* base_path);
+
+#endif /* _SU_INSTALL_HELPER_H_ */
diff --git a/testRoot/testRoot.cpp b/testRoot/testRoot.cpp
new file mode 100644
index 00000000..7b4f1464
--- /dev/null
+++ b/testRoot/testRoot.cpp
@@ -0,0 +1,239 @@
+#include "testRoot.h"
+#include
+#include
+#include
+#include "process64_inject.h"
+#include "adb64_helper.h"
+#include "su_install_helper.h"
+#define ROOT_KEY 0x7F6766F8
+
+
+void show_capability_info()
+{
+ __uid_t now_uid, now_euid, now_suid;
+ if (getresuid(&now_uid, &now_euid, &now_suid)) {
+ perror("FAILED getresuid()");
+ return;
+ }
+
+
+ __gid_t now_gid, now_egid, now_sgid;
+ if (getresgid(&now_gid, &now_egid, &now_sgid)) {
+ perror("FAILED getresgid()");
+ return;
+ }
+
+ printf("now_uid=%d, now_euid=%d, now_suid=%d, now_gid=%d, now_egid=%d, now_sgid=%d\n",
+ now_uid, now_euid, now_suid,
+ now_gid, now_egid, now_sgid);
+
+
+ struct __user_cap_header_struct cap_header_data;
+ cap_user_header_t cap_header = &cap_header_data;
+
+ struct __user_cap_data_struct cap_data_data;
+ cap_user_data_t cap_data = &cap_data_data;
+
+ cap_header->pid = getpid();
+ cap_header->version = _LINUX_CAPABILITY_VERSION_3; //_1、_2、_3
+
+ if (capget(cap_header, cap_data) < 0) {
+ perror("FAILED capget()");
+ return;
+ }
+
+ printf("Cap data 0x%x, 0x%x, 0x%x\n", cap_data->effective, cap_data->permitted, cap_data->inheritable);
+
+
+ FILE * fp = popen("getenforce", "r");
+ if (fp)
+ {
+ char shell[512] = { 0 };
+ fread(shell, 1, sizeof(shell), fp);
+ pclose(fp);
+
+ printf("SELinux status: %s\n", shell);
+ }
+}
+void test_root()
+{
+ show_capability_info();
+
+ printf("get_root ret:%d\n", get_root(ROOT_KEY));
+
+ show_capability_info();
+
+ //system("id");
+ //system("/data/local/tmp/getmyinfo");
+ //system("insmod /sdcard/rwProcMem37.ko ; echo $?");
+ //system("cat /proc/1/maps");
+ //system("ls /proc");
+ //system("screencap -p /sdcard/temp.png");
+ return;
+}
+
+void test_disable_selinux()
+{
+ int ret = disable_selinux(ROOT_KEY);
+ printf("disable_selinux ret:%d\n", ret);
+ printf("done.\n");
+ return;
+}
+
+void test_enable_selinux()
+{
+ int ret = enable_selinux(ROOT_KEY);
+ printf("enable_selinux ret:%d\n", ret);
+ printf("done.\n");
+ return;
+}
+
+void test_run_normal_cmd(const char * shell) {
+ printf("test_run_normal_cmd(%s)\n", shell);
+ char result[0x1000] = { 0 };
+ ssize_t ret = run_normal_cmd(ROOT_KEY, shell, result, sizeof(result));
+ printf("test_run_normal_cmd ret val:%zd\n", ret);
+ printf("test_run_normal_cmd result:%s\n", result);
+}
+void test_run_root_cmd(const char * cmd) {
+ printf("test_run_root_cmd(%s)\n", cmd);
+ char result[0x1000] = { 0 };
+ ssize_t ret = inject_adbd64_run_cmd_wrapper(ROOT_KEY, cmd, result, sizeof(result));
+ printf("test_run_root_cmd ret val:%zd\n", ret);
+ printf("test_run_root_cmd result:%s\n", result);
+}
+void test_set_adbd_root_uid() {
+ printf("test_set_adbd_root_uid\n");
+ char result[0x1000] = { 0 };
+ ssize_t ret = inject_adbd64_run_cmd_wrapper(ROOT_KEY, "id", result, sizeof(result), false, true, false, false);
+ printf("test_set_adbd_root_uid ret val:%zd\n", ret);
+}
+void test_su_env_inject(const char* target_pid_cmdline)
+{
+ char myself_path[1024] = { 0 };
+ char processname[1024];
+ get_executable_path(myself_path, processname, sizeof(myself_path));
+ TRACE("my directory:%s\nprocessname:%s\n", myself_path, processname);
+
+
+ //1.安装su工具套件
+ std::string su_hidden_path;
+ int install_su_tools_ret = install_su_tools(ROOT_KEY, myself_path, su_hidden_path);
+ printf("install_su_tools ret val:%d\n", install_su_tools_ret);
+ if (install_su_tools_ret != 0) {
+ return;
+ }
+
+ //2.杀光所有历史进程
+ std::vector vOut;
+ int find_all_cmdline_process_ret = find_all_cmdline_process(ROOT_KEY, target_pid_cmdline, vOut);
+ printf("find_all_cmdline_process ret val:%d, cnt:%d\n", find_all_cmdline_process_ret, vOut.size());
+ if (find_all_cmdline_process_ret != 0) {
+ return;
+ }
+ std::string kill_cmd;
+ for (pid_t t : vOut) {
+ kill_cmd += "kill -9 ";
+ kill_cmd += std::to_string(t);
+ kill_cmd += ";";
+ }
+ int kill_ret = run_normal_cmd(ROOT_KEY, kill_cmd.c_str());
+ printf("kill_ret ret val:%d\n", kill_ret);
+ if (kill_ret != 0) {
+ return;
+ }
+
+ //3.注入su环境变量到指定进程
+ printf("test_auto_su_env_inject Waiting for process creation(%s)\n", target_pid_cmdline);
+ int pid = wait_and_find_cmdline_process(ROOT_KEY, target_pid_cmdline);
+ printf("test_auto_su_env_inject(%d)\n", pid);
+
+ ssize_t ret = inject_process_env64_PATH_wrapper(ROOT_KEY, pid, su_hidden_path.c_str());
+ printf("test_auto_su_env_inject ret val:%zd, error:%s\n", ret, strerror(errno));
+}
+
+void test_clean_su_env() {
+ char myself_path[1024] = { 0 };
+ char processname[1024];
+ get_executable_path(myself_path, processname, sizeof(myself_path));
+ TRACE("my directory:%s\nprocessname:%s\n", myself_path, processname);
+
+ int uninstall_su_tools_ret = uninstall_su_tools(ROOT_KEY, myself_path);
+ printf("test_clean_su_env ret val:%d\n", uninstall_su_tools_ret);
+}
+
+int main(int argc, char *argv[])
+{
+ printf(
+ "======================================================\n"
+ "本工具名称: Linux ARM64 完美隐藏ROOT演示\n"
+ "本工具功能列表:\n"
+ "\t1.显示自身权限信息\n"
+ "\t2.获取ROOT权限\n"
+ "\t3.绕过SELinux\n"
+ "\t4.还原SELinux\n"
+ "\t5.执行普通命令\n"
+ "\t6.执行ROOT命令\n"
+ "\t7.授予ADB最高级别权限\n"
+ "\t8.授权ROOT到其他进程\n"
+ "\t9.清理ROOT授权痕迹\n"
+ "\t新一代root,跟面具完全不同思路,摆脱面具被检测的弱点,完美隐藏root功能(免root级别),兼容安卓APP直接JNI稳定调用。\n"
+ "======================================================\n"
+ );
+
+
+ ++argv;
+ --argc;
+
+ if (strcmp(argv[0], "show") == 0) { //1.显示自身权限信息
+ show_capability_info();
+ }
+ else if (strcmp(argv[0], "get") == 0) { //2.获取ROOT权限
+ test_root();
+ }
+ else if (argc >= 2 && strcmp(argv[0], "selinux") == 0 && strcmp(argv[1], "disable") == 0) {//3.绕过SELinux
+ test_disable_selinux();
+ }
+ else if (argc >= 2 && strcmp(argv[0], "selinux") == 0 && strcmp(argv[1], "enable") == 0) { //4.还原SELinux
+ test_enable_selinux();
+ }
+ else if (argc >= 2 && strcmp(argv[0], "shell") == 0) { //5.执行普通命令
+ std::stringstream sstrCmd;
+ for (int i = 1; i < argc; i++) {
+ sstrCmd << argv[i];
+ if (i != argc) {
+ sstrCmd << " ";
+ }
+ }
+ test_run_normal_cmd((char*)sstrCmd.str().c_str());
+ }
+ else if (argc >= 2 && strcmp(argv[0], "root") == 0) { //6.执行ROOT命令
+ std::stringstream sstrCmd;
+ for (int i = 1; i < argc; i++) {
+ sstrCmd << argv[i];
+ if (i != argc) {
+ sstrCmd << " ";
+ }
+ }
+ test_run_root_cmd((char*)sstrCmd.str().c_str());
+ }
+ else if (strcmp(argv[0], "adb") == 0) { //7.授予ADB最高级别权限
+ test_set_adbd_root_uid();
+ }
+ else if (argc > 1 && strcmp(argv[0], "su") == 0) { //8.授权ROOT到其他进程
+ std::stringstream sstrCmd;
+ sstrCmd << argv[1];
+ if (sstrCmd.str().length()) {
+ test_su_env_inject(sstrCmd.str().c_str());
+ }
+ }
+ else if (strcmp(argv[0], "cleansu") == 0) { //9.清理ROOT授权痕迹
+ test_clean_su_env();
+ }
+ else {
+ printf("unknown command.\n");
+ return 1;
+ }
+
+ return 0;
+}
\ No newline at end of file
diff --git a/testRoot/testRoot.h b/testRoot/testRoot.h
new file mode 100644
index 00000000..55ffe87c
--- /dev/null
+++ b/testRoot/testRoot.h
@@ -0,0 +1,255 @@
+#ifndef TEST_ROOT_H_
+#define TEST_ROOT_H_
+#include
+#include
+#include
+#include
+#include
+#include
+#include "kernel_root_helper.h"
+
+//安静输出模式
+#define QUIET_PRINTF
+
+#ifdef QUIET_PRINTF
+#undef TRACE
+#define TRACE(fmt, ...)
+#else
+#ifdef __ANDROID__
+#include
+#define LOG_TAG "JNIGlue"
+//#define TRACE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
+#define TRACE(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define TRACE(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#endif
+#endif
+
+
+static size_t get_executable_path(char* processdir, char* processname, size_t len)
+{
+ char* path_end;
+ if (readlink("/proc/self/exe", processdir, len) <= 0)
+ {
+ return -1;
+ }
+ path_end = strrchr(processdir, '/');
+ if (path_end == NULL)
+ {
+ return -1;
+ }
+ ++path_end;
+ strcpy(processname, path_end);
+ *path_end = '\0';
+ return (size_t)(path_end - processdir);
+}
+static int find_all_cmdline_process(unsigned int root_key, const char* target_cmdline, std::vector & vOut)
+{
+ int id;
+ DIR* dir;
+ FILE *fp;
+ char filename[32];
+ char cmdline[256];
+
+ struct dirent * entry;
+
+ if (get_root(root_key) != 0) {
+ return -1;
+ }
+
+ if (!is_disable_selinux_status()) { //要关掉SELinux才能找到进程PID
+ if (disable_selinux(root_key) != 0) {
+ return -2;
+ }
+ }
+
+ dir = opendir("/proc");
+ if (dir == NULL)
+ return -3;
+
+ while ((entry = readdir(dir)) != NULL) {
+ // 如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
+ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ else if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ else if (strspn(entry->d_name, "1234567890") != strlen(entry->d_name)) {
+ continue;
+ }
+
+ id = atoi(entry->d_name);
+ if (id != 0) {
+ sprintf(filename, "/proc/%d/cmdline", id);
+ fp = fopen(filename, "r");
+ if (fp) {
+ fgets(cmdline, sizeof(cmdline), fp);
+ fclose(fp);
+ //TRACE("[+] find %d process cmdline: %s\n", id, cmdline);
+ if (strstr(cmdline, target_cmdline)) {
+ /* process found */
+ vOut.push_back(id);
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+ return 0;
+}
+
+static int safe_find_all_cmdline_process(unsigned int root_key, const char* target_cmdline, std::vector & vOut)
+{
+ int fd[2];
+ if (pipe(fd)) {
+ return -1000;
+ }
+
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ //fork error
+ return -1001;
+
+ }
+ else if (pid == 0) { /* 子进程 */
+ close(fd[0]); //close read pipe
+ int ret = find_all_cmdline_process(root_key, target_cmdline, vOut);
+ write(fd[1], &ret, sizeof(ret));
+ size_t cnt = vOut.size();
+ write(fd[1], &cnt, sizeof(cnt));
+ for (pid_t t : vOut) {
+ write(fd[1], &t, sizeof(t));
+ }
+ close(fd[1]); //close write pipe
+ exit(0);
+ }
+ else { /*父进程*/
+
+ close(fd[1]); //close write pipe
+
+ int status;
+ /* 等待目标进程停止或终止. WUNTRACED - 解释见参考手册 */
+ if (waitpid(pid, &status, WNOHANG | WUNTRACED) < 0) { return -6; }
+
+ int ret = -1002;
+ read(fd[0], (void*)&ret, sizeof(ret));
+ size_t cnt = 0;
+ read(fd[0], (void*)&cnt, sizeof(cnt));
+ for (size_t i = 0; i < cnt; i++) {
+ pid_t t;
+ read(fd[0], (void*)&t, sizeof(t));
+ vOut.push_back(t);
+ }
+
+ close(fd[0]); //close read pipe
+ return ret;
+ }
+ return -1003;
+}
+
+
+static int wait_and_find_cmdline_process(unsigned int root_key, const char* target_cmdline)
+{
+ int id;
+ pid_t pid = -1;
+ DIR* dir;
+ FILE *fp;
+ char filename[32];
+ char cmdline[256];
+
+ struct dirent * entry;
+
+ if (get_root(root_key) != 0) {
+ return -1;
+ }
+
+ if (!is_disable_selinux_status()) { //要关掉SELinux才能找到进程PID
+ if (disable_selinux(root_key) != 0) {
+ return -2;
+ }
+ }
+
+ while (1) {
+ sleep(0);
+
+ dir = opendir("/proc");
+ if (dir == NULL)
+ return -3;
+
+ while ((entry = readdir(dir)) != NULL) {
+ // 如果读取到的是"."或者".."则跳过,读取到的不是文件夹名字也跳过
+ if ((strcmp(entry->d_name, ".") == 0) || (strcmp(entry->d_name, "..") == 0)) {
+ continue;
+ }
+ else if (entry->d_type != DT_DIR) {
+ continue;
+ }
+ else if (strspn(entry->d_name, "1234567890") != strlen(entry->d_name)) {
+ continue;
+ }
+
+ id = atoi(entry->d_name);
+ if (id != 0) {
+ sprintf(filename, "/proc/%d/cmdline", id);
+ fp = fopen(filename, "r");
+ if (fp) {
+ fgets(cmdline, sizeof(cmdline), fp);
+ fclose(fp);
+ //TRACE("[+] find %d process cmdline: %s\n", id, cmdline);
+ if (strstr(cmdline, target_cmdline)) {
+ /* process found */
+ pid = id;
+ break;
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+ if (pid <= 0) {
+ continue;
+ }
+ break;
+ }
+ return pid;
+}
+
+static int safe_wait_and_find_cmdline_process(unsigned int root_key, const char* target_cmdline)
+{
+ int fd[2];
+ if (pipe(fd)) {
+ return -1000;
+ }
+
+ pid_t pid;
+ if ((pid = fork()) < 0) {
+ //fork error
+ return -1001;
+
+ }
+ else if (pid == 0) { /* 子进程 */
+ close(fd[0]); //close read pipe
+ int ret = wait_and_find_cmdline_process(root_key, target_cmdline);
+ write(fd[1], &ret, sizeof(ret));
+ close(fd[1]); //close write pipe
+ exit(0);
+ }
+ else { /*父进程*/
+
+ close(fd[1]); //close write pipe
+
+ int status;
+ /* 等待目标进程停止或终止. WUNTRACED - 解释见参考手册 */
+ if (waitpid(pid, &status, WNOHANG | WUNTRACED) < 0) { return -6; }
+
+ int ret = -1002;
+ read(fd[0], (void*)&ret, sizeof(ret));
+ close(fd[0]); //close read pipe
+ return ret;
+ }
+ return -1003;
+}
+
+
+#endif /* TEST_ROOT_H_ */
diff --git a/testRoot/testRoot.vcxproj b/testRoot/testRoot.vcxproj
index 87beb8d5..97680c23 100644
--- a/testRoot/testRoot.vcxproj
+++ b/testRoot/testRoot.vcxproj
@@ -75,8 +75,11 @@
-
-
+
+
+
+
+
@@ -84,9 +87,14 @@
-
+
+
+
+
-
+
+
+
diff --git a/testRoot/testRoot.vcxproj.filters b/testRoot/testRoot.vcxproj.filters
index 52b27543..e3a40fb6 100644
--- a/testRoot/testRoot.vcxproj.filters
+++ b/testRoot/testRoot.vcxproj.filters
@@ -1,9 +1,12 @@
-
-
+
+
+
+
+
@@ -19,8 +22,13 @@
-
-
+
+
+
+
+
+
+
\ No newline at end of file