以Root权限运行服务或添加Root权限的权限

13
我目前正在开发一个可以在驾车时朗读短信/电子邮件的应用程序。许多用户希望支持WhatsApp / KakaoTalk,但由于没有“官方”方法来接收其消息,只有三个选项,都需要root:
1. 更容易的方法是扫描它们在给定时间间隔内的数据库。容易实现,但不太节能,而且消息不能立即被朗读出来。
2. 另一种方式是以root权限运行服务并注册一个接收器来监听其推送通知。由于这两个包都需要基于签名的权限来接收其推送通知,因此必须使用root进行操作。更难实现,但用户体验更好。
3. 还有一件事情:是否可以在安装后手动添加APK的权限?如果可以的话,我可以将c2dm权限添加到我的包中。这会使事情变得非常简单,但是我有点害怕更改我的应用程序的权限,因为这完全违反了Android Sandbox原则。如果可能的话,请告诉我!
问题是,我应该如何使用root权限确切地运行服务(这是否真的可能)?我知道如何使用root运行shell命令或二进制文件,但不知道如何作为root启动APK的一部分。此外,是否可以将BroadcastReceiver集成到二进制文件中?我实际上对C / C ++没有任何Android环境的经验。
编辑:正如我在评论中所说,我不想使用AccessibilityService,因为它不能满足我的需求(例如,如果有多条未读消息,它会给我“2条未读消息”,而且它不包括完整的消息内容)。
返回结果应该包含html标签。

编辑2:为了澄清事情:我知道如何以root身份运行命令。我需要知道的是如何注册一个Broadcastreceiver,接收一些“普通”接收器无法获取的特定广播,因为广播本身需要基于签名的权限,而我没有该权限。


我不知道如何以root身份运行二进制文件,但这应该是起点。不幸的是,据我所知,即使你有root权限,在Android中你也不能注册没有权限的BroadcastReceiver。原因是任何使用UID 0运行的进程都将被隔离并且不在Android的Zygote之内,因此没有Context,因此不能使用BroadcastReceiver。话虽如此,我对此远非确定。 - Tom
7个回答

3
这并不容易,但当你想监控的应用程序使用SQLite数据库或者通用地说,当它们到达时写入文件消息时,应该可以工作。你确实需要root设备才能执行此操作,因为这违反了Android安全系统:
编写一个本机进程,使用NDK作为守护进程运行,并在启动后以root身份生成它一次。现在你有三个主要问题要解决: 如何找出是否有变化? 这是容易的部分。你需要利用Linux inotify接口,在每个Android手机上都应该可以访问,因为SDK自API 1以来就有一个FileObserver,所以你在这里是安全的。
另一个有趣的方法可能是捕获C2DM消息。我发现了一个名为BroadcastReceiver的NDK类,因此NDK可能能够捕获它们。但我个人不会这样做,因为窃取意图感觉不对。此外,您必须重新分发它们或让它们传递给真正的接收者,因此我将不在此处详细描述此内容。它可能有效,但可能更难,并且应该只是备选方案。
因此,当您解决了这个问题后,下一个问题就出现了:
如何以安全的方式读取更改?
您有一个大问题。该文件不属于客户端,客户端甚至没有权限知道它在哪里(通常情况下)。因此,监视的应用程序不知道客户端,并且将像该文件仅由自身独占所有一样行事。如果他们使用一些普通的文本文件向您写消息,则必须找到一种安全地从中读取的方法,因为它可能随时被覆盖或扩展。但是,如果他们使用sqlite,根据this,拥有多个同时阅读器完全有效,只有一个编写器。我们符合规格,一切都好了。现在,当您读出新数据时,需要解决更多问题:

如何将新数据传回主应用程序?

由于此C/C++程序以root身份运行,因此您应该尽量做最少的工作。您还应该保护应用程序用户免受安全漏洞的影响,请以此为考虑编写程序。我真的不知道这会非常好地工作,但是这里有一些想法:

  • 将收集到的数据写入您自己的sqlite数据库中(在C/C++和Java中很容易实现),
  • 将收集到的数据写入纯文本文件中(不建议使用,非常麻烦),
  • 发送包含新数据的Intent(在C/C++中可能不太容易,但在Java中很容易),
  • 使用套接字/管道等任何Linux提供的RPC机制(与文件相同,不要这样做)

如上所述,请注意在编写此守护进程时小心,因为它是潜在的安全隐患。如果您完全不了解C/C++,即使您已经编写过简单的程序,这也应该是一个非常困难的任务。

在我的网络搜索中,我发现了上面提到的NDK C++类。它可以在Google code找到。我既没有使用NDK也没有使用C++包装器的经验,但在计划编写此代码时,它可能值得一看。


2
强制执行,我必须告诉你,Android服务不需要root访问权限,相反一些操作(例如访问、读取、写入系统资源)需要Root权限。在Android SDK提供的每个Android服务都可以在没有ROOT ACCESS的情况下运行。
您可以借助shell命令使操作以root权限执行。
我已经创建了一个抽象类来帮助您实现这一点。
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import android.util.Log;

public abstract class RootAccess {
    private static final String TAG = "RootAccess";
    protected abstract ArrayList<String> runCommandsWithRootAccess();

    //Check for Root Access
    public static boolean hasRootAccess() {
        boolean rootBoolean = false;
        Process suProcess;

        try {
            suProcess = Runtime.getRuntime().exec("su");

            DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());
            DataInputStream is = new DataInputStream(suProcess.getInputStream());

            if (os != null && is != null) {
                // Getting current user's UID to check for Root Access
                os.writeBytes("id\n");
                os.flush();

                String outputSTR = is.readLine();
                boolean exitSu = false;
                if (outputSTR == null) {
                    rootBoolean = false;
                    exitSu = false;
                    Log.d(TAG, "Can't get Root Access or Root Access deneid by user");
                } else if (outputSTR.contains("uid=0")) {
                    //If is contains uid=0, It means Root Access is granted
                    rootBoolean = true;
                    exitSu = true;
                    Log.d(TAG, "Root Access Granted");
                } else {
                    rootBoolean = false;
                    exitSu = true;
                    Log.d(TAG, "Root Access Rejected: " + is.readLine());
                }

                if (exitSu) {
                    os.writeBytes("exit\n");
                    os.flush();
                }
            }
        } catch (Exception e) {
            rootBoolean = false;
            Log.d(TAG, "Root access rejected [" + e.getClass().getName() + "] : " + e.getMessage());
        }

        return rootBoolean;
    }

    //Execute commands with ROOT Permission
    public final boolean execute() {
        boolean rootBoolean = false;

        try {
            ArrayList<String> commands = runCommandsWithRootAccess();
            if ( commands != null && commands.size() > 0) {
                Process suProcess = Runtime.getRuntime().exec("su");

                DataOutputStream os = new DataOutputStream(suProcess.getOutputStream());

                // Execute commands with ROOT Permission
                for (String currentCommand : commands) {
                    os.writeBytes(currentCommand + "\n");
                    os.flush();
                }

                os.writeBytes("exit\n");
                os.flush();

                try {
                    int suProcessRetval = suProcess.waitFor();
                    if ( suProcessRetval != 255) {
                        // Root Access granted
                        rootBoolean = true;
                    } else {
                        // Root Access denied
                        rootBoolean = false;
                    }
                } catch (Exception ex) {
                    Log.e(TAG, "Error executing Root Action", ex);

                }
            }
        } catch (IOException ex) {
            Log.w(TAG, "Can't get Root Access", ex);
        } catch (SecurityException ex) {
            Log.w(TAG, "Can't get Root Access", ex);
        } catch (Exception ex) {
            Log.w(TAG, "Error executing operation", ex);
        }

        return rootBoolean;
    }


}

通过使用RootAccess扩展您的类,或创建一个RootAccess类的实例并覆盖runCommandsWithRootAccess()方法。


1
非常感谢您的回答!就像我上面所说的,我已经知道如何运行root命令。我想以root身份运行服务的原因是,我需要注册一个需要我没有的签名级别权限的BroadcastReceiver。因此,更好的提问可能是“有没有一种方法可以使用root权限运行Broadcast Receiver”。 - Force
你尝试过将APK通过adb push推送到已root并重新挂载的设备中的system/app文件夹,然后重启设备吗? - Jens
@sam:我会将上面的代码更改为 rootBoolean = (process.waitFor() != 0) ? false : true;,因为已经获取了 root 权限的设备会返回零,而未获取 root 权限的设备则会返回一。在所有情况下都不适用于 255。 :) - ChuongPham

2

以root权限运行某些内容并不是解决此问题的正确方式。

相反,可以考虑使用辅助功能服务来监视新通知:

AccessibilityEvent


谢谢你的回答。我已经考虑过使用可访问性方法,但问题是,我需要完整的正文,而不仅仅是标题和前几个单词。;-) 此外,如果收到多条消息,则只会读出“2条新消息”,并且不会给我任何信息。 - Force

1

如果您的目标是未更改、未root的设备,则无法将服务(或任何其他应用程序组件)作为root运行。允许这样做将使Android中的所有安全机制变得毫无意义。

在运行时也无法更改APK的权限。权限始终在APK安装时授予或拒绝。有关此主题的更多信息,请参阅http://developer.android.com/guide/topics/security/security.html


我知道在未root的设备上无法运行root命令。但是,使用root权限修改软件包的权限是完全可能的,因为每个apk都有自己的用户ID,其中存储了权限。因此,为了更改权限,需要修改用户数据库(可能是/etc/目录下的文件)。 - Force
显然,开发者指南不会提供任何关于root相关的建议,因此我们不能将其作为有效的参考来源。 - Force
如果你的目标是root设备,请查看https://dev59.com/I2435IYBdhLWcg3wpx2x的被接受的答案,特别是答案中的链接。 - Martin Nordholts
请仔细阅读整个问题!我已经说过我知道如何以root身份运行shell命令或二进制文件。 - Force
1
如果我没有理解您的问题,我很抱歉,但是通过遵循上述步骤,您可以编写一个以root权限运行的服务,这似乎是您想要的。 在执行Runtime.getRuntime().exec(“su”)(可能需要等待用户确认)之后,调用该方法的进程将在此之后以root特权运行。 通过在运行您的服务的同一进程中进行调用,服务将以root特权运行。 这不是您想要的吗? - Martin Nordholts
@MartinNordholts - 不,这不会让应用程序或服务的进程以root身份运行 - 它只会让它启动的某些其他进程以root身份运行,而这正是帖子作者所寻求的。 - Chris Stratton

1
“我需要知道的是如何注册一个Broadcastreceiver,接收普通接收器无法接收到的特定广播,因为广播本身需要基于签名的权限,而我没有这个权限。”
“你不能。就这样。感谢上帝。”
“是的,如果您使用可怕的root设备功能来运行一些代码,理论上您可以做任何想做的事情。但实际上,绕过此限制可能会非常困难,平台通常被设计成这样。您至少需要处理包管理器维护和/或存储的状态,并且可能需要导致用户重新启动设备以使您作为root所做的更改真正产生影响。当然,您正在干扰平台的深层内部实现细节,这意味着在平台的不同版本和来自不同制造商的不同构建之间出现各种问题。”

-1

你可以使用

pm grant your.permission

作为一个 shell 命令,授予你的应用额外的权限。 我认为这个命令是最近添加的,所以如果你的目标是旧版本,你可能需要直接修改 'packages.xml'。

使用 app_process 命令可以将 app/dex 文件作为 root 执行,但我还没有弄清楚如何获得有效的上下文(有了这个,你可以使用 java.io.File api 访问所有文件,但非静态的 android 方法,如 bindService 等将失败,因为你在没有应用上下文的情况下运行)。


1
不能使用此命令授予所有的权限。收到以下消息:操作不允许:java.lang.SecurityException: Permission android.permission.MODIFY_PHONE_STATE 不是开发权限。 - Bao Le
@BaoLe那我们应该怎么处理这些权限呢?! - Dr.jacky

-2

当然,您可以更改应用程序的权限。如果更改了权限,则用户只需手动更新应用程序,新的权限就会显示给用户。但我不知道更改应用程序许可如何帮助您解决此问题。

另一件事是,您无法将Service或其他任何内容作为root运行,仅适用于已经root的设备,并且通过应用程序获得root设备不是易事,也不是许多用户想要的。

您当前如何访问SMS? 如果您有BroadcastReceiver,则可以为您的receiver设置MAX_PRIORITY,也许它会在其他应用程序之前拦截消息。可以按以下方式执行:

   <receiver android:name=".SmsReceiver" >
        <intent-filter android:priority="100" >
            <action android:name="android.provider.Telephony.SMS_RECEIVED" />
        </intent-filter>
    </receiver>

您也可以使用目前不公开的 SMS 提供程序,但如果您定期查询此提供程序,则可以检查新消息。如果您尚未执行此操作,请查看此线程:Android SMS Provider

谢谢您的回复。正如我所说,我知道设备必须先经过root。这也不是关于处理短信,而是基于签名的C2DM权限。您说可以添加这些权限 - 您能举个例子吗? - Force

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接