我目前正在开发一款应用程序,可在驾驶时读取短信/电子邮件。许多用户都希望得到WhatsApp / KakaoTalk的支持。
但是,由于没有“官方”方式来接收他们的消息,因此只有三个选项,都需要root:
在给定时间间隔内扫描数据库的更简单方法。
另一种方法是运行具有root权限的服务并注册侦听其推送通知的接收器。这必须使用root来完成,因为两个软件包都需要基于签名的权限才能接收其推送通知。
还有另一件事我想到:安装后是否可以手动添加APK权限?在这种情况下,我可以将c2dm权限添加到我的包中。
问题是,我究竟如何运行具有root权限的服务(实际上是否可能)?我知道如何使用root运行shell命令或二进制文件,但我不知道如何以root身份启动APK的一部分。
是否可以将BroadcastReceiver集成到二进制文件中?我实际上没有使用C / C ++的经验,尤其是在Android环境中。
你能帮帮我吗?谢谢。
编辑:就像我在评论中所说的那样,我不想使用AccesibilityService,因为它不符合我的需要(例如,如果有多个未读消息,它会给我“2条未读消息”,也不包括完整消息身体)。
edit2:只是澄清事情:我知道如何用root运行命令。我需要知道的是如何注册接收特定广播“普通”接收器的广播接收器,因为广播本身需要我没有的基于签名的许可。
这远非微不足道,但是当您要监视的应用程序使用sqlite数据库时,或者更一般地,当它们到达时将消息写入文件时,它应该起作用。
您确实需要具有对设备的root访问权限,因为这违反了android安全系统:
编写一个本机进程,它使用NDK作为守护进程运行,并在以root身份启动后生成一次。您现在有3个主要问题需要解决:
如何找出是否有变化?
这是简单的部分。您需要使用Linux inotify接口,该接口应该可以在每个Android手机上访问,因为SDK自API 1以来具有FileObserver,因此您在这里是安全的。
另一种有趣的方法可能是捕获C2DM消息。我找到了一个名为BroadcastReceiver的NDK类,因此NDK可能能够捕获它们。但我个人不会这样做,因为窃取意图感觉不对。此外,你必须重新分配它们或让它们前往真正的收件人,所以我不会在这里详细描述。它可能有用,但它可能更难,应该只是一个后备。
所以,当你解决了这个问题后,会出现下一个问题:
如何以安全的方式阅读更改?
你有一个问题,一个大问题,在这里。该文件不属于客户端,客户端甚至没有权限知道它(通常)。因此受监控的应用程序不知道客户端,并且将像文件仅由其自身独占。如果他们使用一些普通的旧文本文件来写消息,你必须找到一种方法来安全地读取它,因为它可能随时被覆盖或扩展。但是当你使用sqlite时你可能会很幸运,根据this的说法,只有一个同时读者,只有一个作者,这是完全有效的。我们在规格中,一切都很好。当您现在已经读出新数据时,需要解决的问题更多:
如何将新数据恢复到主应用程序?
你应该只在这个C / C ++程序中做到最低限度,因为它以root身份运行。您还应该保护您的应用用户免受安全漏洞的影响,因此请在编写程序时牢记这一点。我不知道这可能真的很好用,但这里有一些想法:
如上文所述,编写此守护程序时请小心,因为它可能存在安全隐患。当你完全不了解C / C ++时,可能很难做到这一点,即使你已经编写了简单的程序,这应该是一项非常重要的任务。
在我通过网络搜索时,我找到了上面提到的NDK C ++类。它可以在Google code找到。我既没有使用NDK也没有C ++包装器的经验,但是当你打算写这个时,它可能值得一看。
强制,我必须告诉你,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()方法。
如果您的目标是未经更改的非root用户设备,则无法以root用户身份运行服务(或任何其他应用程序组件)。允许这将使Android中的所有安全机制毫无意义。
也无法在运行时更改APK的权限。在APK安装时始终授予或拒绝权限。有关该主题的更多信息,请参阅http://developer.android.com/guide/topics/security/security.html。
“我需要知道的是如何注册接收特定广播”正常“接收器的广播接收器,因为广播本身需要基于签名的许可我没有。”
你不能。期。故事结局。感谢上帝。
是的,如果你使用可怕的有根设备设施让一些代码以root身份运行,理论上你可以做任何你想做的事情。在实践中,可能很难绕过这个限制,并且平台通常被设计成这样。您至少需要处理由程序包管理器维护和/或存储的状态,并且可能需要使用户重新启动设备以获取您作为root所做的更改才能实际产生影响。当然,您正在搞乱平台的深层内部实现细节,这意味着跨平台的不同版本和来自不同制造商的不同构建打破了整个地方。
您可以使用
pm grant your.permission
作为shell命令为您的应用授予其他权限。我认为该命令最近才被添加,所以如果您定位旧版本,则可能需要直接更改'packages.xml'。
可以使用app_process命令以root身份执行app / dex文件,但我还没有弄清楚如何获得有效的上下文(使用java.io.File api可以访问所有文件,但是像bindService等非静态android方法会失败,因为你在没有应用程序上下文的情况下运行)。
当然,您可以更改应用程序的权限。如果权限将被更改,则用户只需手动更新应用程序,新的权限将显示给用户。但我不知道如何更改您的应用程序权限将帮助您解决此问题。
我可以告诉你的另一件事是,你不能运行Service
或root
,只能在root设备上运行,并且通过你的应用程序生成设备并不是一件容易的事,而且它也不会是很多用户都想要。
您目前如何访问SMS
?如果你有BroadcastReceiver
you可以为你的MAX_PRIORITY
设置receiver
,也许它会在其他应用程序之前拦截消息。这可以按如下方式完成:
<receiver android:name=".SmsReceiver" >
<intent-filter android:priority="100" >
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
您也可以使用现在不公开的SMS Provider
,但是如果您在给定的时间间隔查询此Provider
,您可以检查新消息。你也可以看看这个帖子:Android SMS Provider如果你还没有完成这个。