Why can't my Android app (has root privilege) access /dev/input?

こ雲淡風輕ζ 提交于 2019-12-06 13:43:23

As was pointed out in comments, modern Android has many additional defensive layers besides Linux file permissions. One of them is SELinux.

Even with elevated privileges, working around SELinux is rather complex — it is designed specifically to prevent that. All Android SELinux settings are stored in a single file of modified sepolicy format. That file is a part of read-only system image, and patching it basically equals to rooting a device. Pretty much only people working on that are developers of Superuser apps, such as author of SuperSu or this one.

Instead of trying to overcome SELinux yourself, I recommend you to leverage whatever was already done by installed su app. For example, SuperSu runs commands, passed to it, in unrestricted SELinux context (see link to Chainfire's site above), essentially as if SELinux didn't exist for it. This allows you to overcome SELinux by running specialized binaries via su, which do the dirty job for you.

Sadly, there are very few public high-level APIs, available to such pure native binaries. You can use Linux kernel syscalls and some C library functions... and that is it. Fortunately, if all you want is opening a bunch of protected files, there is no need to move lots of logic in native helper binary. Instead you can use an "open server" library, such as 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) {
    ...
}

Disclaimer: I am the author of linked library.

The concept of "open server" is pretty simple:

  1. Normal Android app creates a Linux domain socket
  2. Normal Android app launches binary via system "su"
  3. The binary connects to the socket
  4. The binary reads names of files written by app to the socket and opens them
  5. The binary sends file descriptors of said files to app via the same socket (the technique also known as "file descriptor passing")

This neat trick is going to work as long as installed "su" app successfully overcomes SELinux and provides unrestricted context to commands being run via it. All modern ones I know about do.


EDIT: This answer has been written a while ago. The latest Android sepolicy format is no longer considered "modified", their changes has been successfully upstreamed (humorously leading to creation of yet another backward-incompatible sepolicy format). The library, linked above, still works fine in general, but it's functioning has been further restricted by modern SEAndroid policies, so you may be interested in it's new iteration. Due to the fact, that SELinux policies enforce additional checks on each individual read/write in addition to standard Unix checks during open, it might be wiser to use shared memory and Linux pipes to carefully work around the policy, rather then passing the original descriptors to caller.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!