1. 程式人生 > >Android 防介面劫持方案,無視Android系統版本限制,無需操作棧

Android 防介面劫持方案,無視Android系統版本限制,無需操作棧

Android 防介面劫持方案

Demo下載地址

分析

要想真正做到防劫持,就需要在我們的程式執行時,不允許啟動其他應用程式。但是Android為了提高使用者的使用者體驗,對於不同的應用程式之間的切換,基本上是無縫,如果在啟動一個Activity時,給它加入一個標誌位FLAG_ACTIVITY_NEW_TASK,就能使它置於棧頂並立馬呈現給使用者。在這種機制下,無法做到真正的防劫持

那麼只能使用一種折中的方案,使用者使用app的時候,如果被惡意程式劫持跳轉到別的介面,這個時候我們就要做出預警提示使用者,告訴使用者當前介面已經不是我們的app了,有潛在的危險。

這種方案主要有一個問題需要攻克,就是監控是否有程式覆蓋了我們的APP,也就是我們需要監控棧頂的變化,在Android5.0之前,可以使用ActivityManager中提供的方法操作棧,但是在Android5.0時,谷歌由於使用者隱私的考慮,弱化了這個介面,ActivityManager只能管理自己APP的棧,如果想管理其他APP的棧,需要使用者主動授權,而好多手機廠商又對這個許可權做了進一步限制,只能為特定的APP授權(如系統捆綁應用)。這樣Android5.0之後就無法通過管理棧的方式來進行防劫持。

既然只能對自己的應用進行操作,我們可以從Activity的生命週期做些文章,具體方案往下看

方案

1. 在所有Activity的onPause()方法中,彈出提示使用者的警告,延遲1-2秒執行

2. 在所有Activity的onResume()方法中,取消在onPause中的提示

效果

● 當自己的應用切換頁面時,觸發當前頁面的onPause()方法,並立刻觸發要跳轉頁面的onResume()方法,這樣就不會發出警告提示使用者。

● 當用戶主動切換應用程式或者主動退回到桌面時,觸發當前介面的onPause()方法,1-2秒後出現警告

● 當應用介面遭到惡意程式劫持時,觸發當前介面的onPause()方法,1-2秒後出現警告

注意

1. 不能使用onStop()方法來代替onPause(),因為大部分劫持介面都採用透明主題,而跳轉到透明Activity時,是不觸發onStop()方法的,可以使用onStart()方法來代替onResume()

2. 當程式中需要載入第三方的Activity時,防劫持機制會判斷第三方的Activity是非法的,並彈出警告,專案上在使用時可自行呼叫onResume(),來取消警告

程式碼

BaseActivity.java

package com.example.test;

import android.app.Activity;

public class
BaseActivity extends Activity {
@Override protected void onResume() { Anti_hijackingUtils.getinstance().onResume(); super.onResume(); } @Override protected void onPause() { Anti_hijackingUtils.getinstance().onPause(this); super.onPause(); } }

Anti_hijackingUtils.java

package com.example.test;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.app.Activity;
import android.widget.Toast;

public class Anti_hijackingUtils {

    /**
     * 用於執行定時任務
     */
    private Timer timer = null;

    /**
     * 用於儲存當前任務
     */
    private List<MyTimerTask> tasks = null;

    /**
     * 唯一例項
     */
    private static Anti_hijackingUtils anti_hijackingUtils;

    private Anti_hijackingUtils() {
        // 初始化
        tasks = new ArrayList<MyTimerTask>();
        timer = new Timer();
    }

    /**
     * 獲取唯一例項
     * 
     * @return 唯一例項
     */
    public static Anti_hijackingUtils getinstance() {
        if (anti_hijackingUtils == null) {
            anti_hijackingUtils = new Anti_hijackingUtils();
        }
        return anti_hijackingUtils;
    }

    /**
     * 在activity的onPause()方法中呼叫
     * 
     * @param activity
     */
    public void onPause(final Activity activity) {
        MyTimerTask task = new MyTimerTask(activity);
        tasks.add(task);
        timer.schedule(task, 2000);
    }

    /**
     * 在activity的onResume()方法中呼叫
     */
    public void onResume() {
        if (tasks.size() > 0) {
            tasks.get(tasks.size() - 1).setCanRun(false);
            tasks.remove(tasks.size() - 1);
        }
    }

    /**
     * 自定義TimerTask類
     */
    class MyTimerTask extends TimerTask {
        /**
         * 任務是否有效
         */
        private boolean canRun = true;
        private Activity activity;

        public void setCanRun(boolean canRun) {
            this.canRun = canRun;
        }

        public MyTimerTask(Activity activity) {
            this.activity = activity;
        }

        @Override
        public void run() {
            activity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (canRun) {
                        // 程式退到後臺,進行風險警告
                        Toast.makeText(activity, "應用Test切換至後臺執行",
                                Toast.LENGTH_LONG).show();
                        tasks.remove(MyTimerTask.this);
                    }
                }
            });
        }
    }
}