|
Technique summary |
Technique |
Recursive view hiding and restoring |
Against |
pixel stealing attacks |
Limitations |
None |
Side effects |
None |
Recommendations |
Recommended for use |
In the first steps of the Pixnapping attack, the target activity is launched and a stack of activities is injected on top of it. The defensive technique consists in recursively hiding all the views of our activity as it goes to the background, this frustrates the attack as no information is on the screen to be leaked anymore. The views are made visible again as the activity comes back to the foreground.
In order to implement the protection, we just need to add one instruction in the onPause
and onResume
methods of the activity we want to protect:
@Override
protected void onPause() {
super.onPause();
VisibilityManager.hideAllViews(this);
}
@Override
protected void onResume() {
super.onResume();
VisibilityManager.restoreAllViews(this);
}
The class that handles view hiding and restoring can be something like this:
public class VisibilityManager {
public static final String TAG = "VisibilityManager";
private static final WeakHashMap>
visibilitiesMap = new WeakHashMap<>();
private static final WeakHashMap
hiddenStateMap = new WeakHashMap<>();
public static void hideAllViews(Activity activity) {
Log.d(TAG, "[*] Hiding all the views");
int hideMode = View.INVISIBLE;
if (activity == null) return;
if (Boolean.TRUE.equals(hiddenStateMap.get(activity))) return;
View root = activity.getWindow().getDecorView();
WeakHashMap originalVisibilities = new WeakHashMap<>();
visibilitiesMap.put(activity, originalVisibilities);
saveAndSetVisibilityRecursive(root, hideMode, originalVisibilities);
hiddenStateMap.put(activity, true);
}
public static void restoreAllViews(Activity activity) {
Log.d(TAG, "[*] Restoring all the views");
if (activity == null) return;
WeakHashMap originalVisibilities =
visibilitiesMap.get(activity);
if (originalVisibilities == null) return;
for (View v : originalVisibilities.keySet()) {
Integer orig = originalVisibilities.get(v);
if (orig != null) v.setVisibility(orig);
}
visibilitiesMap.remove(activity);
hiddenStateMap.put(activity, false);
}
private static void saveAndSetVisibilityRecursive(View view,
int newVisibility,
WeakHashMap visMap) {
if (!visMap.containsKey(view)) {
visMap.put(view, view.getVisibility());
}
view.setVisibility(newVisibility);
if (view instanceof ViewGroup) {
ViewGroup vg = (ViewGroup) view;
int childCount = vg.getChildCount();
for (int i = 0; i < childCount; i++) {
saveAndSetVisibilityRecursive(vg.getChildAt(i), newVisibility, visMap);
}
}
}
}