Android 使用 shell 命令小结

一、背景说明

在 apk 开发过程中,难免遇到需要使用 apk 来执行相应的 shell 命令,本文档就是记录在 apk 内部如何执行 shell 命令。

二、准备知识

在使用 shell 命令之前,我们先了解一下 Android 进程基本信息。

ce

ce

如上图,可以看到当前进程空间内,存在好多进程分别以不同的用户权限在执行。除了常见的系统用户,还有 wifi 用户,nfc 用户等,其中我们比较关注的在于 u0_axxx 。关于此部分解释参考:关于android UID u0_axx是怎么来的

由于在 Android 系统中,单个 APK 都是一个独立的进程,因此,在 apk 内部执行 shell 命令,实际是以当前 apk 所在的进程用户权限去执行对应的命令。到这里就解释了执行命令的权限分配问题。

二、技术说明

1、基础用法

通过调用当前apk的运行时对象来执行 shell 命令:

  • Runtime.getRuntime(). exec( "ls");

运行之后,在进程中会随机创建一个新用户,然后以新用户的方式来执行 shell 命令。

上述代码有个缺点就是执行之后,无法获取返回值。下面进行改进。

2、执行命令获取返回值

上述命令有缺陷,现在进行改进,使其执行后可以获取返回值。

  • public static String runCmd( String shell) {
  • String data = null;
  • try {
  • Process p = Runtime.getRuntime().exec( new String[]{ "su", shell});
  • BufferedReader ie = new BufferedReader( new InputStreamReader(p.getErrorStream()));
  • BufferedReader in = new BufferedReader( new InputStreamReader(p.getInputStream()));
  • String error = null;
  • while ((error = ie.readLine()) != null
  • && !error.equals( "null")) {
  • data += error + "\n";
  • }
  • String line = null;
  • while ((line = in.readLine()) != null
  • && !line.equals( "null")) {
  • data += line + "\n";
  • }
  • Log.v( "cmd-test = " + shell, data);
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • return data;
  • }

3、apk 申请 root 权限

普通用法介绍完毕,还存在特殊情况,当我们需要以 root 命令来执行 shell 命令,和上面的代码有点区别的。

首先需要 apk 申请 root 权限:

  • public static boolean RootCommand(String command){
  • Process process = null;
  • DataOutputStream os = null;
  • try {
  • process = Runtime.getRuntime().exec( "su");
  • os = new DataOutputStream(process.getOutputStream());
  • os.writeBytes(command + "\n");
  • os.writeBytes( "exit\n");
  • os.flush();
  • process.waitFor();
  • } catch (Exception e) {
  • return false;
  • } finally {
  • try {
  • if (os != null) {
  • os.close();
  • }
  • process.destroy();
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • }
  • return true;
  • }

调用方式:

  • String apkRoot = "chmod 777 " + getPackageCodePath();
  • RootCommand(apkRoot);

执行以上代码之后,会弹出窗口提示申请 root 权限呢。允许之后当前 apk 就获取了 root 权限了。

至于检测是否获取到了 root 权限,就不细说了,提供一个小思路,执行 :ll /data/data,看看返回值是什么。

4、apk 以 root 权限执行 shell 命令

和普通命令有点区别的是,root 执行的时候需要在真正命令之前使用 su 去换到 root 用户,然后才开始执行 shell 命令的。

代码如下:

  • public static void runRootCommand(String command){
  • Process process = null;
  • DataOutputStream os = null;
  • try {
  • process = Runtime.getRuntime().exec( "su");
  • os = new DataOutputStream(process.getOutputStream());
  • os.writeBytes(command + "\n");
  • os.writeBytes( "exit\n");
  • os.flush();
  • process.waitFor();
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • try {
  • if (os != null) {
  • os.close();
  • }
  • process.destroy();
  • } catch (Exception e) {
  • }
  • }
  • }

以上代码依然没有返回值,只适合执行简单的shell命令。

5、apk 以 root 权限执行 shell 有返回值

修改以上代码,添加返回值:

  • public static String execRootCmd( String cmd) {
  • String result = "";
  • DataOutputStream dos = null;
  • DataInputStream dis = null;
  • try {
  • Process p = Runtime.getRuntime().exec( "su"); // 经过Root处理的android系统即有su命令
  • dos = new DataOutputStream(p.getOutputStream());
  • dis = new DataInputStream(p.getInputStream());
  • dos.writeBytes(cmd + "\n");
  • dos.flush();
  • dos.writeBytes( "exit\n");
  • dos.flush();
  • String line = null;
  • while ((line = dis.readLine()) != null) {
  • Log.d( "result", line);
  • result += line;
  • }
  • p.waitFor();
  • } catch (Exception e) {
  • e.printStackTrace();
  • } finally {
  • if (dos != null) {
  • try {
  • dos.close();
  • } catch (IOException e) {
  • e.printStackTrace();
  • }
  • }
  • if (dis != null) {
  • try {
  • dis.close();
  • } catch (IOException e) {
  • e.printStackTrace();
  • }
  • }
  • }
  • return result;
  • }

三、小结

apk 执行 shell 命令的场景还是比较多的,需要根据不同的需求选用合适的函数进行调用,通常来说选择有返回值的函数进行使用即可。
在这里可能还包含了超时命令,暂时没做处理,有需求,再继续分析实现吧。
以上。


版权声明:本文为qq_37101453原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。