Logo Computer scientist,
engineer, and educator
• Software • The KBOX project • Articles about software development

Building and running native code utilities on Android without ADB or rooting

This article describes a straightforward way to run simple native-code utilities such as a telnet client or server on an Android device, without all the hassle of rooting the device, or pushing binaries using ADB. By 'simple' I mean console-mode utilities that have few, if any, dependencies, and don't need root privileges. Despite common misconceptions, you don't need root privileges to run utilities like rsyncd, telnetd, although you do have to be careful about IP port numbers, and other considerations which I'll get to below. This procedure could, in principle, be used to install complex applications or sets of applications, but it would most likely be so fiddly that full-on rooting would be less hassle.

Most Android devices can be 'rooted', i.e., have their built-in application sandboxing mechanisms completely circumvented. Usually this involves a fairly invasive procedure of replacing the bootloader and kernel. In this article I'm specifically dealing with stock, unrooted devices. The procedure will work on rooted devices but, quite honestly, there would be no point.

This is not a method of rooting your device, or of circumventing any of Android's built-in security or sandboxing measures. As I said, many of the native code utilities I want to run will co-exist quite happily with Android security. Nothing I describe here is subversive, or even undocumented. Google does describe a way to execute native binaries by bundling them up inside .apk files and unpacking them into app storage. What I'm describing amounts to the same thing, but without the administrative overhead of building the .apk file. So it's not secret, but there doesn't seem to be a straightforward, step-by-step procedure written up anywhere. For the sake of example, I will describe building and installing a simple telnet server, utelnetd, that will let me log into my Xoom tablet remotely. I'll describe installation first, because there are plenty of pre-built binaries floating around the Web and not everybody wants to install a compiler toolchain just to run vim or whatever.

The method I'm describing has been tested on Android versions 3.x and later. It might work on other devices, but I haven't tried. Android is moving so quickly that, by the time you read this, the method may no longer work. In which case, sorry. I'm assuming a fair amount of general Linux knowledge — but then I suspect that nobody but a Linux developer would be remotely interested in any of this.

What you need

To install pre-built binaries you will need a terminal emulator (both to install them in the right place and to run them), and some way of getting your files onto the device.

To build, you'll need a cross-compiler toolchain for ARM. I'm using the official Google Android NDK. There are all sorts of problems with this, but it's good enough for my purposes.

Installing a native binary

It would be nice if you could put native binaries directly onto the SD card (either external or built-in) but you can't. Well, you can — but you won't be able to set execute permissions on them, which means they won't run. The SD card is mounted in such a way as to prohibit setting 'x' permissions. You can set execute permissions in the app storage area — that part of the filesystem that is rooted at /data/data. On an unrooted device you won't be able to create files just anywhere in this directory, because each app has its own security credentials — effectively each app runs as a different user. Only one directory under /data/data will be writeable by a specific app. Once you've found that directory, you can copy a binary into it, and run it at the terminal emulator prompt.

From the above, it should be clear that a straightforward place to write binaries is in the app storage area that belongs to your terminal emulator. There are various terminal emulators around — I am using the implementation written by Jack Palevich (thanks) which is available free of charge from the Android Market. Whatever terminal emulator you use, it is crucial that you know its internal package name. I don't believe that this is exposed anywhere that is easy to get at, but a simple way to find out is to ask the developer. In the case of Mr Palevich's product, the internal name is jackpal.androidterm.

The writeable storage area for this app will therefore be /data/data/jackpal.androidterm/shared_prefs. There are other directories under jackpal.androidterm, but these are probably not writeable. So — remember the writeable directory name.

Now you need a way to get the binary onto the device. You can use a wifi or 3G connection for this, or copy it direct using USB. So long as you can identify the Linux directory where the file comes in, that is sufficient. I run a Web server on my development machine, and use the built-in browser on my tablet to download binaries from there. The built-in browser by default stores downloads in the Download directory, which seen from the Linux level is /sdcard/Download.

So, assuming I've downloaded the utelnetd utility using the browser, I can copy it into an executable location using the terminal emulator simply by starting it, and running the following commands:

$ cd /data/data/jackpal.androidterm/shared_prefs
$ cat /sdcard/Download/utelnetd &  utelnetd
$ chmod 755 utelnetd
If you've copied the file via USB, then it will be somewhere else under /sdcard — doesn't matter where, so long as you can find it.

Incidentally, you might think you could do 'cp' rather than 'cat' in step 2 above, but you can't. Android won't let you create a file with execute permissions, but it will let you create a file without, and then set execute permissions on it. Answers on a postcard, please.

Now you can run the telnet server:

$ ./utelnetd -p 1025 -l /system/bin/sh
This command-line does illustrate some of the limitations of this technique. The -p 1025 argument tells the telnet server to listen on port 1025. Typically a telnet server uses port 23, but port 23 is not available to a non-root process (nor any other port below 1024). That's no problem, except that you'll have to tell your telnet client to use port 1025 rather than the default.

-l /system/bin/sh tells utelnetd to use the specified executable as the login program. Android does not have a login infrastructure (so far as I know), so you can't specify /bin/login as you might on a full-scale Linux machine. You can't even specify /bin/sh because there's nothing in directory /bin in a stock Android device — the usual Linux utilities like ls, cp, etc., are elsewhere.

It will be clear now, I hope (if it wasn't already), why running complex applications this way is likely to be very fiddly. Quite apart from the security limitations imposed by Android on the code you run, the Android filesystem layout is not very Linux-like. If you're running something that is hard-coded to find dependent libraries in (say) /usr/lib/[app_name] you're going to be out of luck — this directory does not even exist, and you can't create it. Many conventional files and directories are not where you might expect them to be, and it could be quite difficult to modify complex applications to account for that.

But for simple things, it's OK.

One last point: whatever you run using this method will run with the privileges of the terminal emulator. The terminal emulator will probably have rights to read and write the SD card, and make network connections. The rights an Android app gets are specified by the developer when the app is packaged. This leads to the anomalous situation that your native-code privileges are controlled by the developer of the terminal emulator you use to run them. Isn't Android wonderful?

Building native code uilities

For relatively simple C applications, this is surprisingly easy these days. The latest version of the Android Native Development Kit (NDK) has a ready-made toolchain and scripts to install it in a rational way. I believe that the NDK is available for OS/X and Windows, but I've only used the Linux version. All versions are available from the Android SDK Web site.

We don't need the 'ordinary' Android SDK for any of this (although if you're interested in poking around inside Android you probably already have it), and we certainly don't need to build Android from source. All we need to do for this exercise is to set up the NDK pre-built toolchain. This amounts to unpacking the distribution, and running the script build/tools/make-standalone-toolchain.sh. This script creates a version of the toolchain that is modified to know where all the installed bits are, and what command-line arguments to use. All the binaries are provided in the distribution — make-standalone-toolchain simply provides a simply way to use them. Without this step you need to specify a whole bunch of file location arguments to gcc which is a drag. It's particularly a drag if you porting an application and you don't want to fiddle around with its build machinery (Makefile, etc).

So I would run make-standalone-toolchain like this:

$ buld/tools/make-standalone-toolcharin.sh --platform=android-9 \\
Thereafter I can run the gcc compiler like this:
/home/kevin/lib/android-9-toolchain/bin/arm-linux-androideabi-gcc [files]
Or more likely, if I have a Makefile:
CC=/home/kevin/lib/android-9-toolchain/bin/arm-linux-androideabi-gcc make
For the telnet server example I referred to above, I got the single source file utelnetd.c from here.

Then to compile it for Android:

arm-linux-androideabi-gcc -o utelnetd utelnetd.c
And that's it. In this case no changes were necessary to make it work on Android, although I would not expect such good fortune with more complicated programs.

Final remarks

So it's possible to build and run native code utilities for Android without rooting and other invasive procedures. Such utiltities will run with the permissions of the terminal emulator that started them, and will remain subject to Android's security restrictions. So far as I know, what I'm describing is in keeping with Android's security model and is not a 'hack' of any kind. It probably does not have many applications, but it's a lot simpler and less invasive than rooting for those applications it does have.
Copyright © 1994-2015 Kevin Boone. Updated May 18 2017