Android VM injection and BinderJacking
Example code to inject and run Java(/Kotlin) code from your own package (or dex) in any Android JVM process, including
system_server. This part should be relatively stable and easily reusable.
Also includes example code to hijack/hook ("BinderJack") a system service's
ACTIVITY_SERVICE in this specific case) after injecting into
system_server. This part is not very stable, and would require extensive testing to be generally usable cross-OEM and cross-Android versions.
Basic testing has been done on various Google and Samsung devices running Android 7.0 through Q Preview 1. This code is published only to show the possibilities, if you need production-quality code and testing, you'll have to do the work. There's a non-zero chance this will not work out-of-the-box on random Android device you're testing with.
Root is obviously required. There is no typical exploiting code to be found here. This is just a write-up of interesting techniques to use when root is already available, and with the user's permission.
The example code logs both in the app and in logcat. Monitor both.
Please see the LICENSE file for the exact details.
Original ARM native code injector: Simone evilsocket Margaritelli's ARM Inject (© 2015, BSD 3-clause).
Injector improvements, VM injection and everything Java-related: Jorrit Chainfire Jongma (© 2015-2019, BSD 3-clause).
Excerpts from The Android Open Source Project (© 2008, APLv2).
Credits are always appreciated if you use my code.
Spaghetti Sauce Project
This release is part of the Spaghetti Sauce Project.
Native code injection
If you're reading this you're probably familiar with native code injection: running your own native code inside a different process. There are various ways to accomplish this, all with their pros and cons.
As both my hobby and professional work tends to resolve around systems where we already have significant access, but need to control/manipulate processes that we can't easily replace or don't have up-to-date sources for, my go-to methods are one of two:
Linker pre-loading (such as LD_PRELOAD) is an easy mechanism to get your code (in the form of a shared library) loaded, and makes it trivial to hijack calls to libraries, but you need to have control over a process' environment and its startup.
Ptrace-based injection loads your code (again in the form of shared library) into an existing process. Its main advantage over linker pre-loading is that you do not need control over the target process' environment or its startup, but major disadvantages include that your code isn't running from the start - state is hard to predict - and you cannot rely on the linker to hijack library calls for you. The latter is still possible, but you need to manipulate the GOT/PLT tables manually.
If you're looking for code that does the latter, the sources for CF.lumen's performance driver include it for Android, though it's a minefield and relatively unstable; almost every major Android release has required adjustments, and at the time of this writing there are still some issues with the latest Android version.
This project used ptrace-based injection, using a slightly modified version of the injector from that repo, but this project does not itself use any of the GOT/PLT hijacking.
As I never really explained in that release how the injector works, let me recap it here:
Ptrace is a *nix system call that allows one process to control another; debuggers tend to be based on this facility. Using this system call requires special privileges and is not available to standard Android processes.
The basic premise is simple:
- Attach to the target process
- Find the symbols for
calloc/free/dlopenin the target process
callocin the target process to allocate some memory
- Copy the path to the payload library to the target process
dlopenin the target process to load the payload library
- The code in the payload library now lives in the target process, and by utilizing a shared library constructor (or a manual call from the injector), may start executing
ptrace call itself can be quite tricky and unpredictable to work with, but the injector as it is now seems to work pretty reliably.
Ptrace works on a very low level. While it does provide the functionality to read and write the target process' memory, executing function calls has to be done by manipulating the stack and registers, which means the code to do so differs between architectures (and potentially OS). That part of the injector's code currently supports Android on arm, aarch64, i386, x86_64 - though not all of these have necessarily been tested recently.
Locating the symbols to use in the target process is conceptually simple. First we determine the symbol in the injector's memory, then we examine
/proc/<pid>/maps of both the injector and target process. The symbol's offset from the base load address of the library that defines the symbol is the same, so we can just do the math.
A minor snag can be locating the symbol in the injector's memory in the