Android remote shell command control and whitelist

Keywords: shell Java Mobile Android

How to use java code to execute shell commands in Android system

In java, we can't run shell commands directly. We need to use Runtime class to execute shell commands. Runtime's introduction, I can't say it clearly, is not a mistake for our children. But it is emphasized that Runtime class is a villain singleton pattern. When jvm is created, objects are created and constructed. Methods are also private constructs, so we need to get the object of the Runtime class through Runtime.getRuntime(), with some source code attached below.

public class Runtime {
    private static final Runtime currentRuntime = new Runtime();
   
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    
    private Runtime() {}
    }

After introducing our tools, let's put them into practice.
Anyone who has ever played Android mobile adb shell knows that they have su and sh rights. It's okay if they haven't. As soon as you understand, su is super authority, SH is ordinary authority. How super and ordinary are they? I don't remember very clearly. After all, this thing has been written for nearly half a year, and today I suddenly saw the code and wanted to share it with you. But that's not a problem. You just need to remember to use su privilege after the mobile phone root, not sh privilege for the root. The default is sh privilege. To show the root situation in the code, first get the Runtime object - myRuntime, and execute our first shell command with myRuntime. Exc (), and then return to us a process for use. Execute the remaining shell commands. Note that there is only one process returned to us here. The specific reason is not clear. But if the process is stuck, your program will be stuck. This is also the reason why the whitelist appears later. Explain it later.

Runtime myRuntime = Runtime.getRuntime();
Process process = myRuntime.exec('su')

Now let's use this process to execute the rest of the shell commands. Since we used the su command to get this process, all of our next commands will be executed with root privileges. First, we get a data output stream from the acquired Runtime process, which is used to execute our instructions. As for why the output stream is here, my understanding is that our instructions are loaded and in memory at this time, but where the output goes is unknown. Finally, the cached character input stream is used to obtain the execution results.

DataOutputStream os = new DataOutputStream(process.getOutputStream());
//Command is the command you need to execute
os.write(command.getBytes());
os.writeBytes("exit\n");
os.flush();
process.waitFor();
//Get execution results
StringBuilder successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
StringBuilder errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));

shell command Toolset

The toolset is my encapsulation of the code of the big man on the internet. It is easy to use and suitable for the object-oriented idea. If you don't need further study, you can use the toolset best.

package com.example.shellcmd;


import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;



public class ShellUtils {

    private ShellUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }


    public static CommandResult execCmd(String command, boolean isRoot) {
        return execCmd(new String[]{command}, isRoot ? "su" : "sh", true);
    }


    public static CommandResult execCmd(List<String> commands, boolean isRoot) {
        return execCmd(commands == null ? null : commands.toArray(new String[]{}),
                isRoot ? "su" : "sh",
                true);
    }


    public static CommandResult execCmd(String[] commands, boolean isRoot) {
        return execCmd(commands, isRoot ? "su" : "sh", true);
    }


    public static CommandResult execCmd(String command, boolean isRoot, boolean isNeedResultMsg) {
        return execCmd(new String[]{command}, isRoot ? "su" : "sh", isNeedResultMsg);
    }


    public static CommandResult execCmd(List<String> commands, boolean isRoot, boolean isNeedResultMsg) {
        return execCmd(commands == null ? null : commands.toArray(new String[]{}),
                isRoot ? "su" : "sh",
                isNeedResultMsg);
    }


    public static CommandResult execCmd(String[] commands, String suName, boolean isNeedResultMsg) {
        int result = -1;
        if (commands == null || commands.length == 0) {
            return new CommandResult(result, null, null);
        }
        Process process = null;
        BufferedReader successResult = null;
        BufferedReader errorResult = null;
        StringBuilder successMsg = null;
        StringBuilder errorMsg = null;
        DataOutputStream os = null;
        try {
            process = Runtime.getRuntime().exec(suName);
            os = new DataOutputStream(process.getOutputStream());
            for (String command : commands) {
                if (command == null) continue;
                os.write(command.getBytes());
                os.writeBytes("\n");
                os.flush();
            }
            os.writeBytes("exit\n");
            os.flush();
             process.waitFor();

            if (isNeedResultMsg) {
                successMsg = new StringBuilder();
                errorMsg = new StringBuilder();
                successResult = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
                errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));
                String s;
                while ((s = successResult.readLine()) != null) {
                    successMsg.append(s).append("\n");
                }
                while ((s = errorResult.readLine()) != null) {
                    errorMsg.append(s).append("\n");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
                successResult.close();
                errorResult.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (process != null) {
                process.destroy();
            }
        }
        return new CommandResult(
                errorMsg.toString().equals("")? 0:1,
                successMsg.equals("")? "" : successMsg.toString(),
                errorMsg.equals("")? "": errorMsg.toString()
        );
    }


    public static class CommandResult {

        public int result;

        public String successMsg;

        public String errorMsg;

        public CommandResult(int result, String successMsg, String errorMsg) {
            this.result = result;
            this.successMsg = successMsg;
            this.errorMsg = errorMsg;
        }

        public String toString() {
            return "[" + this.result + ": successMsg:" + this.successMsg + ", errorMsg:" + this.errorMsg + "]";
        }
    }
}


How to use it? Here's a code example

 commandResult =ShellUtils.execCmd(CommandList.toArray((new String[]{})), 'u', true);

White List of Android Mobile shell Commands

Do you remember that once the process is blocked, the program will also be blocked? Generally, when the current shell command is executed without return value, the specific reason is not known, but I know what shell command will cause the process card owner, but I found a mobile shell file to use. As a result of the afternoon tests, the following shell commands are executed without blocking the process. Every time you execute a shell command, you just need to determine whether it is in this array to know whether you can execute the current command.

String [] whiteList ={"acpi","am","applypatch","appops","appwidget","app_process","app_process32",
                    "atrace","basename","bcc","blockdev","bmgr","bu","cal","cat","chcon","chgrp",
                    "chmod","chown","chroot","clear","cmp","comm","content","cp","cpio","cut",
                    "dalvikvm","dalvikvm32","date","debuggerd","debuggerd64","debuggerd_real",
                    "dex2oat","df","diag_klog","diag_mdlog","dirname","dmesg","dpm","ds_fmc_appd",
                    "du","echo","egrep","env","expr","fallocate","false","fgrep","find","free",
                    "getenforce","getprop","grep","groups","hid","hostname","hwclock","id","idmap",
                    "ifconfig","iftop","ime","inotifyd","input","insmod","ioctl","ionice","ip",
                    "ip6tables","iptables","it","kill","ld.mc","linker","ln","load_policy",
                    "log","logcat","logname","logwrapper","losetup","ls","lsmod","lsof","lsusb",
                    "make_ext4fs","make_f2fs","media","mkdir","mknod","mkswap","mktemp","modinfo",
                    "monkey","mount","mountpoint","mv","nandread","ndc","newfs_msdos","nice",
                    "nohup","oatdump","paste","pgrep","pidof","ping","ping6","pkill","pm","pmap",
                    "port-bridge","printenv","printf","prlimit","ps","pwd","radish","readlink",
                    "realpath","reboot","renice","requestsync","resize2fs","restorecon","rm",
                    "rmdir","rmmod","route","run-as","runcon","secdiscard","sed","sendevent",
                    "seq","service","setenforce","setprop","setsid","settings","sm","start",
                    "stat","stop","svc","swapoff","swapon","sync","sysctl","tar","taskset",
                    "tc","telecom","time","timeout","toolbox","touch","toybox","tr","true",
                    "truncate","uiautomator","umount","uname","uptime","usbhub","usbhub_init",
                    "usleep","vmstat","which","whoami","wm","wenxiaoye"} ;

Ha-ha, here suddenly saw the last command, of course, this is my own shell command, used for their own play, you can also try, it is addictive.
The above articles are very shallow. If there are any mistakes or different opinions in the articles, you are welcome to leave a comment.

Posted by mjr on Tue, 17 Sep 2019 03:06:05 -0700