Infecting Android processes for fun and profit
In this article we will discuss a way to inject code into a process running on Android and make it execute that code within the context of that running process. The motivation for this project started during my adventures with windows malware. It was very convenient for windows malware to inject malicious code in other processes such as IE etc. Being a passionate Linux programmer I thought of implementing an API similar to Windows provided API.
I started a Linux project a few years ago on creating an API similar to Windows which allows a process to create a thread within another process. The API provided by windows is CreateRemoteThread(). I was able to create a framework called Jugaad which allowed a program to inject shellcode within a victim process and execute it in a stealthy manner. It uses the ptrace() functionality which is the common way to manipulate remote processes and almost all Linux debuggers use this functionality to hook onto a process for debugging.
The detailed paper describing the technical details and source code of Jugaad can be found at –
1. Whitepaper – http://null.co.in/2011/07/03/project-jugaad/
2. Source code details – http://null.co.in/2011/07/03/project-jugaad-2/
I wanted to extend this functionality to other Linux platforms available and the most interesting one that caught my attention was Android. Android OS uses Linux as its kernel with some patches specific to Android. I needed to port my code to Android and as it turns out Android phones mostly run on ARM based processors. The Jugaad framework is dependent on the processor (x86) as it injects shellcode and other debugging instructions specific to the processor and also manipulates processor registers. Since, the implementation was processor specific I set out on the journey to learn ARM architecture and its instruction set.
I ported the code base to run on ARM Android and released the project as open source. The project is called Indroid. For the curious ones, if you are interested in playing around with it – the source code can be downloaded from http://bitbucket.org/aseemjakhar/indroid. The indroid toolkit allows a program to inject ARM shellcode into another process on Android.
How do I play around with Android
• Make sure you have only one connected device – either emulator or actual phone
• Make sure the ndk-build directory is in $PATH as the Makefile directly calls ndk-build without its path which will obvously be different for each machine based on where you store the Android ndk.
• The Android project has two sample programs
◦ Android – This implements the Indroid injector program
◦ testproc – A test program that is used as the victim process to test indroid. This program only prints some content on the terminal in a loop.
• $ emulator -avd <name> # Start the emulator called name
• $ adb devices # To check if adb can see the emulator
• $ git clone https://bitbucket.org/aseemjakhar/indroid.git
• $ cd indroid
• $ make install
• $ adb shell # To get a shell on the emulator for running indroid
• $ adb shell # Take another shell on the emulator for running testproc
• On the Emulator shell 1
◦ # cd /data/local/tmp
◦ # ./testproc
◦ Note down the PID of testproc
• On the Emulator shell 2
◦ # cd /data/local/tmp
◦ #./indroid <PID-of-testproc
◦ Type n for all the detach questions asked by indroid on the shell
◦ Once it exits, go to shell 1 and notice the string w00t!!! printed among other testproc output. This string is not printed by testproc (you can confirm that by looking at testproc source code) but it is the default shellcode used by Indroid to print the string and is injected into testproc and executed as a thread within testproc.
Before we dive into the technical details of Indroid, we will discuss some important functionality which will form the backbone of the implementation.
Windows OS provides the below API for creating a thread within another process.
HANDLE WINAPI CreateRemoteThread(__in HANDLE hProcess,
__in LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPTHREAD_START_ROUTINE lpStartAddress,
__in LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out LPDWORD lpThreadId),
Some parameters of interest are
1. hProcess – A handle to the process in which the thread is to be created.
2. dwStackSize – The initial size of the stack for the thread, in bytes.
3. lpStartAddress – A pointer to the application-defined function to be executed by the thread and represents the starting address of the thread in the remote process. Please note that the function code must exist within the remote process memory prior to calling CreateRemoteThread().
Awesomeness unlimited! Indeed ptrace() is one of the most powerful functions I have seen on Linux. Ptrace() is used by all the debuggers for manipulating debugee processes. The following is the API and operations that ptrace() performs
long ptrace(enum __ptrace_request request,
1. pid – The process identifier of the process being traced.
2. addr and data – The values depend on the type of operation.
3. request – The operation to be performed on the traced process. The following operations can be performed:
a) PTRACE_ATTACH – Attaches to the process specified in pid.
b) PTRACE_CONT – Restarts the stopped child process.
c) PTRACE_DETACH – Restarts the stopped child as for PTRACE_CONT, but first detaches from the process.
d) PTRACE_PEEKTEXT – Reads a word at the location addr in the child’s memory.
e) PTRACE_POKETEXT – Copies the word data to location addr in the child’s memory.
f) PTRACE_GETREGS – Copies the child’s general purpose registers to location data in the parent.
g) PTRACE_SETREGS – Copies the child’s general purpose or floating-point registers, respectively, from location data in the parent.
Running indroid on testproc (PID 1269)
Notice that testproc prints something that it is not suppposed to print
The run-time injection problem can be divided into
1. Code execution
2. Memory Allocation
4. Malicious payload
The first problem to solve is code execution within the victim process. We hook onto the victim process using the ptrace() attach functionality which allows a debugger process to attach to another process. Once attached, we can examine the registers and memory of the process. The ptrace(0 function interestingly also allows the attaching process to write to process memory and register values. The code execution can be achieved by changing the value of the PC register to point to a memory location within the process where our malicious code will reside. The PC holds the address of the next intruction to be fetched so, if we change its value the processor will pick up the instructions from there instead of the normal program flow.
We need a place within the victim process to hold our code which can be executed using the above technique. Why do we need space and what code? For starters we need to allocate some memory within the process to store our malicious code. We can simply take a backup of a predefined memory location within the process and overwrite it with shellcode that will allow us to allocate memory within the process. The process we follow is
1. Access a pre-defined memory location and backup data from that location. This can be performed using ptrace() peektext/peekdata functionality to read data.
2. Overwite that location using ptrace() poketext/pokedata functionality (allows writing to process memory) with a simple shellcode that calls mmap2() system call to allocate the amount of memory desired with execute permission. Append a breakpoint instruction to the shellcode so we get the control back after it executes. A breakpoint instruction (BKPT) is defined by the ARM processer and stops the process and sends a signal to the parent process. Breakpoint instructions are used by debuggers to set breakpoints in debugged process. This way our code gets the control back when the process executes that instruction.
3. Once we get the control back, we read the value R0 register using ptrace() getregs functionality. Why R0 register? Because the return value i.e. the address of memory allocated by mmap2() will be stored in R0 register. This is the standard system call convention for ARM Linux i.e. the return value of the system call is stored in R0 register. So, now we know where our allocated memory starts within the process.
This is an important aspect from execution and stealth perspective as we want the victim process to execute normally and at the same time also want our malicious code to independently execute within the context of the victim process. Linux provides clone() system call to create a thread within the process which we will utilise to threadify our malicious code. To perform the desired action we follow the below steps.
1. Create a shellcode that contains the clone() system. For people new to clone system call() it returns in two places, in the main thread(victim process) and in the newly created thread (which will execute our code). If the clone returns in the new thread we start executing the the malicious shellcode and become independent of the parent thread. In the parent thread we execute the breakpoint instruction and give control back to Indroid.
2. Allocate memory withing the victim process for our malicious shellcode and the clone system call shellcode (appended with BKPT instruction).
3. When we get the control back in Indroid, we restore the main process memory we had overwritten originally (to put mmap2() shellcode) and the registers and allow it execute using ptrace() continue functionality.
4. We finally detach from the victim process.
5. The outcome is that, our malicious code executes as a thread within the context of the victim process and the process also continues to execute where it stopped when we hooked into it.
This is what is needed by the program that wants to inject stealthy code into a victim process. All that is required is to write a shellcode that performs certain operations and pass it to Indroid API. Please note that the shellcode should be thread aware and not tamper with something that can cause harm to other threads or the main thread.
Why execute as a thread?
Because I wanted to execute the malicious code within the context of a victim process without the process or the sysadmin knowing that the process has been patched and the malicious code is running independently.
Why execute shellcode?
There are existing techniques that inject a library into the process memory. However the problem with this approach is that the library name can be seen in the process maps file, which means if someone is analysing known processes, a single glance at the process maps file would tell them that there is a fishy library loaded in the process memory. With shellcode all we do is allocate space within the process and put our shellocde which is executed. The process memory maps will not show anything other than information on memory mapped within the process, which is defficult to comprehend as being malicious.
Yes, Android sandboxing is an issue here due to the limited capabilities (permissions) of the malware that wants to infect other processes. However, there are certain mis-use cases that you can try out if you are still awake and reading the article:
1. adbd daemon on Android phones runs as SHELL user and you can connect to your phone using adb which drops you onto a shell with the same SHELL user privileges. You may want to try infecting adbd and share the results with me.
2. The project should easily compile for standard ARM Linux using the arm compiler toolchain. Embedded devices that run Linux are a good target for playing around as standard Linux does not have the same permission restrictions as put in Android a.k.a Sandboxing.
The Android API
It is a very simple API to use. However, there is a non-script kiddie version with more options available for people who are interested in more control and customization. The API is even more convenient than the windows counterpart as the caller can directly specify the shellcode to be injected as opposed to CreateRemoteThread() where the caller first needs to arrange for the code to be injected into the process before calling CreateRemoteThread().
In most cases you will need to use the extended API for specifying a custom backup address (bkpaddr) as the default backup address hardcoded in the program will not work for every process. The backup address is the memory location in victim process’s that is intially overwritten with the mmap2 shellcode.
The simple API
int create_remote_thread(pid_t pid,
unsigned char * tpayload,
1. pid – The process identifier of the process being infected.
2. stack_size – The initial size of the stack for the thread, in bytes.
3. tpayload – The shellcode to be injected and executed within the victim process. It is ok if the shellcode contains null bytes.
4. tpsize – The size of the shellcode in bytes.
The extended API
int create_remote_thread_ex(pid_t pid,
unsigned char * tpayload,
void * bkpaddr);
The details and documentation on the APIs can be found in jugaad.h header file within the project – <project-dir>/indroid/jni/jugaad.h
About The Author
Aseem Jakhar is the director, research at Payatu Technologies Pvt Ltd, a boutique security testing company and the founder of nullcon security conference. He is also the Founder of null – The open security community, a non-profit organization and largest security community in Asia. He trains budding smartphone hackers on advanced Android and iOS exploitation. He has extensive experience in system programming, security research, consulting and managing security software development projects. He has designed and developed various security software including UTM appliances, messaging/security appliances, anti-spam engine, anti-virus software, multicast packet reflector, Transparent HTTPS proxy with captive portal, bayesian spam filter to name a few. He is an active speaker at security and open source conferences; some of the conferences he has spoken at include AusCERT, BlackHat, Defcon, PHDays, Hack.lu, OSI Days, XCon among others.