我的应用程序针对根源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");
上述两个步骤都是成功的,但为什么我的应用程序仍然无法访问? 从search中,有人说应用程序的运行时权限与文件系统中文件的权限无关。 Android运行时的权限系统是什么? 我怎样才能让应用程序能够访问/dev/input
?
加成:
我的testing环境是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; }
来自/prog/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
正如在评论中指出的那样,现代Android除了Linux文件许可之外还有许多额外的防御层。 其中之一是SELinux。
即使提升了权限,在SELinux上工作也相当复杂 – 它是专门为了防止这种情况而设计的。 所有Android SELinux设置都存储在修改过的sepolicy格式的单个文件中。 该文件是只读系统映像的一部分,修补它基本上等于生根设备。 几乎所有在这方面工作的人都是超级用户应用程序的开发者,比如SuperSu的作者或者这个 。
不要试图自己克服SELinux,我建议你利用已安装的su应用程序所做的任何工作。 例如,SuperSu在不受限制的SELinux上下文中运行命令(参见上面Chainfire的站点链接),就好像SELinux不存在它一样。 这可以让你通过su运行专门的二进制文件来克服SELinux,它为你做了不好的工作。
令人遗憾的是,只有很少的公共高级API可用于纯粹的本地二进制文件。 你可以使用Linux内核的系统调用和一些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) { ... }
免责声明:我是链接库的作者。
“开放式服务器”的概念非常简单:
只要安装好的“su”应用程序成功克服了SELinux,并为通过它运行的命令提供不受限制的上下文,这个巧妙的技巧就可以工作。 我所知道的所有现代的人都这样做。
编辑 :这个答案已经写了一阵子。 最新的Android sepolicy格式不再被认为是“修改”的,它们的变化已经成功地成为了流行趋势(幽默地导致了另一种后向不兼容的sepolicy格式)。 上面链接的这个图书馆一般来说工作正常,但是它的功能已经被现代SEAndroid政策进一步限制了,所以你可能会对这个新的版本感兴趣。 由于事实上,SELinux策略除了在open
进行标准的Unix检查之外,还对每个read
/ write
执行附加的检查,使用共享内存和Linux管道仔细解决策略,而不是传递原始描述符来电。