为什么我的Android应用程序(具有root权限)无法访问/dev/input?

4

我的应用程序旨在针对已root的Android设备,它具有root权限并需要访问目录/dev/input,但为什么会抛出opendir failed, Permission denied的异常,即使/dev/input已经被chmod改为777

我使用下面的代码获取root权限:

Process root = Runtime.getRuntime().exec("su");

使用以下代码更改 /dev/input 的权限:

Shell.runCommand("chmod 777 /dev/input");

两个步骤都成功了,但为什么我的应用程序仍然无法访问它?从搜索中得知,应用程序的运行时权限与文件系统中文件的权限无关。那么,Android运行时权限系统是什么?我该如何使应用能够访问/dev/input
补充说明: 我的测试环境是Android 5.1.1,主要代码如下:
jint Java_com_foo_funnyapp_Native_scanInputDevicesJNI(JNIEnv* env, jclass clazz)
{
    const char *dirname = "/dev/input";

    DIR *dir;
    dir = opendir(dirname); // opendir failed, Permission denied
    if(dir == NULL)
        return -1;

    ......

    return 0;
}

来自 /proc/kmsg 的 SELinux 错误

<36>[19700411_05:32:43.957165]@0 type=1400 audit(8631163.939:1105): avc: denied { write } for pid=15706 comm="app_process64_o" name="system@framework@boot.art" dev="mmcblk0p43" ino=442379 scontext=u:r:shell:s0 tcontext=u:object_r:dalvikcache_data_file:s0 tclass=file permissive=0
<11>[19700411_05:32:44.118202]@0 init: untracked pid 15674 exited with status 0
<11>[19700411_05:32:44.202288]@0 init: untracked pid 15704 exited with status 224
<36>[19700411_05:32:44.225334]@0 type=1400 audit(8631164.209:1106): avc: denied { read } for pid=15734 comm="Thread-111" name="input" dev="tmpfs" ino=12525 scontext=u:r:untrusted_app:s0 tcontext=u:object_r:input_device:s0 tclass=dir permissive=0
<36>[19700411_05:32:44.332135]@0 type=1400 audit(8631164.319:1107): avc: denied { write } for pid=15742 comm="app_process64_o" name="system@framework@boot.art" dev="mmcblk0p43" ino=442379 scontext=u:r:shell:s0 tcontext=u:object_r:dalvikcache_data_file:s0 tclass=file permissive=0

1
无论你试图做什么,这可能都是一个坏主意。你可能正在遇到selinux而不是类Unix权限问题。 - Chris Stratton
检查“dmesg”(或“cat /proc/kmsg”)的输出,以查找selinux错误消息。此外,“su”命令不会更改当前进程的权限,因此无法确定您是否实际以root身份运行。 - fadden
@ChrisStratton,非常感谢您的指出!但是我能修改我的应用程序的SELinux策略吗?从搜索来看,好像我不能,因为SELinux策略被烧录到操作系统映像中。但是有一个奇怪的现象是,在我安装并运行了另一个从Google Play下载的访问/dev/input的应用程序之后,我的应用程序就可以访问/dev/input了。我不知道那个应用程序做了什么。 - Suge
@fadden,感谢您的回答。我已经将主要代码和错误信息添加到帖子内容中。看起来是SELinux阻止了我的访问,但我该如何修改呢?我知道Google Play中还有另一个应用程序需要访问/dev/input,在我安装并运行它之后,我的应用程序就能够访问它了,这可能是什么主要技术? - Suge
1个回答

8
正如评论中指出的那样,现代Android除了Linux文件权限之外还有许多其他防御层。其中之一是SELinux。
即使拥有提升的特权,绕过SELinux也相当复杂——它专门设计用于防止这种情况发生。所有Android SELinux设置都存储在一个修改后的sepolicy格式的单个文件中。该文件是只读系统映像的一部分,并且修补它基本上等于root设备。几乎唯一在处理此问题的人是Superuser应用程序的开发人员,例如SuperSu的作者或这个应用程序的作者。
我建议您利用已安装的su应用程序已经完成的工作,而不是自己克服SELinux。例如,SuperSu在无限制的SELinux上下文中运行传递给它的命令(请参见上面Chainfire网站的链接),基本上就像对它不存在SELinux一样。这使您可以通过通过su运行专业二进制文件来为您执行肮脏的工作来克服SELinux。

遗憾的是,对于这种纯本地二进制文件,可用的公共高级API非常少。您可以使用Linux内核系统调用和一些C库函数......就是这样。幸运的是,如果您只想打开一堆受保护的文件,则无需在本地辅助程序二进制文件中移动大量逻辑。相反,您可以使用“开放服务器”库,例如this one

Context context = ...

try (FileDescriptorFactory factory = FileDescriptorFactory.create(context);
     ParcelFileDescriptor fd = factory.open("/dev/input", 2))
{
  // the file descriptor is yours, as if you have gotten it by
  // calling ParcelFileDescriptor#open
  // You can use it from Java or pass to native code to read/write/ioctl on it
  ...
} catch (FactoryBrokenException oups) {
    // most likely the root access being denied
    ...
} catch (IOException ioerr) {
    ...
}

免责声明:我是链接库的作者。

"开放服务器"的概念非常简单:

  1. 普通的Android应用程序创建Linux域套接字
  2. 普通的Android应用程序通过系统"su"启动二进制文件。
  3. 二进制文件连接到套接字。
  4. 二进制文件读取应用程序写入套接字的文件名称,并打开它们。
  5. 二进制文件通过同一套接字将这些文件的文件描述符发送给应用程序(该技术也称为"文件描述符传递")。

只要安装的"su"应用程序成功地克服了SELinux并为通过它运行的命令提供了不受限制的上下文,这个巧妙的技巧就会起作用。我知道的所有现代应用程序都能做到这一点。


编辑:本答案写于一段时间之前。最新的Android sepolicy格式已不再被认为是“修改过的”,它们的更改已经成功地上游了(幽默地导致了另一个不兼容的sepolicy格式的创建)。上面链接的库总体上仍然可以正常工作,但其功能已经受到现代SEAndroid策略的进一步限制,因此您可能会对其新版本感兴趣。由于SELinux策略在open期间对每个单独的read/write执行额外的检查,所以使用共享内存和Linux管道来小心地绕过策略,而不是将原始描述符传递给调用者,可能更明智。

我很惊讶能得到这样好而详细的答案,非常感谢! - Suge

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