为什么我的Android应用程序(具有root特权)不能访问/ dev / input?

Sug*_*uge 2 linux android selinux root android-ndk

我的应用程序的目的是有根的Android设备,它具有超级用户权限,需要访问该目录/dev/input,但为什么它抛出opendir failed, Permission denied,甚至/dev/input已是chmod777

我使用以下代码获取root特权:

Process root = Runtime.getRuntime().exec("su");
Run Code Online (Sandbox Code Playgroud)

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

Shell.runCommand("chmod 777 /dev/input");
Run Code Online (Sandbox Code Playgroud)

以上两个步骤都成功,但是为什么我的应用程序仍无法访问它?通过搜索,有人说应用程序的运行时权限与文件系统中文件的权限无关。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;
}
Run Code Online (Sandbox Code Playgroud)

SELinux错误来自 /prog/kmsg

<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
Run Code Online (Sandbox Code Playgroud)

use*_*723 5

正如评论中指出的那样,现代Android除了Linux文件权限外还具有许多其他防御层。其中之一就是SELinux。

即使具有特权提升,围绕SELinux的工作也相当复杂 -它是专门为防止这种情况设计的。所有Android SELinux设置都存储在修改后的Sepolicy格式的单个文件中。该文件是只读系统映像的一部分,对其进行修补基本上等同于生根设备。几乎只有从事此工作的人是Superuser应用程序的开发人员,例如SuperSu的作者或this

建议您不要尝试自己克服SELinux,而要利用已安装的su应用程序已经完成的一切。例如,SuperSu在不受限制的SELinux上下文中运行传递给它的命令(请参见上面的Chainfire网站的链接),实质上就像SELinux不存在一样。这使您可以通过su运行专用二进制文件来克服SELinux,这会为您带来麻烦。

可悲的是,只有很少的公共高级API可用于此类纯本机二进制文件。您可以使用Linux内核syscall和一些C库函数...就是这样。幸运的是,如果您只想打开一堆受保护的文件,则无需在本机帮助程序二进制文件中移动大量逻辑。相反,您可以使用“开放服务器”库,例如以下库:

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) {
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

只要已安装的“ su”应用程序成功克服SELinux并为通过它运行的命令提供不受限制的上下文,此巧妙的技巧就将起作用。我所知道的所有现代人都在做。


编辑:这个答案已经写了一段时间。最新的Android sepolicy格式已不再视为“已修改”,其更改已成功上游(很遗憾导致创建了另一种向后不兼容的sepolicy格式)。上面链接的库在总体上仍然可以正常运行,但是其功能已受到现代SEAndroid策略的进一步限制,因此您可能会对它的新迭代感兴趣。由于这一事实,SELinux策略 在期间对每个单独的对象执行了额外的检查read/ write除了标准的Unix检查之外open,使用共享内存和Linux管道来仔细地解决该策略,而不是将原始描述符传递给调用者可能是更明智的选择。