Android 界面防劫持

一、什么是页面劫持

界面劫持是指在Android系统中,恶意软件通过监控目标软件的运行,当检测到当前运行界面为某个被监控应用的特定界面时(一般为登录或支付界面),弹出伪造的钓鱼页面,从而诱导用户输入信息,最终窃取用户的隐私(恶意盗取用户账号、卡号、密码等信息),或者利用假冒界面进行钓鱼欺诈。

二、页面劫持常用攻击手段

(1)监听系统Logocat日志,一旦监听到发生Activity界面切换行为,即进行攻击,覆盖上假冒Activity界面实施欺骗;

(2)监听系统API,一旦恶意程序监听到相关界面的API组件调用,即可发起攻击;

(3)5.0以下机型枚举获取栈顶Activity,监控到目标Activity出现,即可发起攻击;

(4) 恶意启动Service监听目标应用,在切换到目标Activity时,弹出对话框劫持当前界面迷惑用户。

(5)通过截屏录制屏幕,分析用户行为,对应弹窗。(自己臆想的)

ps:目前来看,只有系统app才有权限获取所有log、顶栈信息,普通app只能获取自己的数据,并没有权限。(可能有其他方式,我没找到)

三、如何防范页面劫持

3.1 用户方面

1、在支付等关键使用环境,看到应用切换后台的相关提示,得注意下
2、在支付等关键使用环境,查看后台,最新记录有不明应用。

3.2 开发者方面

针对Activity类型劫持,在登录窗口或者用户隐私输入等关键Activity的onPause方法中检测最前端Activity应用是不是自身,如果不是,则提示。
针对弹窗对话框类型的劫持。可以对自身弹窗设置标志位,如果acitvity失去焦点,并没有onpause,检查标志位即可判断是否是他人应用覆盖,可以对用户提醒。

针对弹窗,自行实践即可,针对Activity,下面有个工具类

package com.demo.ck.listenerdemo;

import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Looper;
import android.widget.Toast;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Description: Activity反劫持检测工具
 */
public class ViewInterceptUtil {
    public static void check(final Context context) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 白名单
                boolean safe = ViewInterceptUtil.checkActivity(context.getApplicationContext());
                // 系统桌面
                boolean isHome = ViewInterceptUtil.isHome(context.getApplicationContext());
                // 锁屏操作
                boolean isReflectScreen = ViewInterceptUtil.isReflectScreen(context.getApplicationContext());
                // 判断程序是否当前显示
                if (!safe && !isHome && !isReflectScreen) {
                    Looper.prepare();
                    Toast.makeText(context.getApplicationContext(), "注意-------",
                            Toast.LENGTH_LONG).show();
                    Looper.loop();
                }
            }
        }).start();
    }

    /**
     * 检测当前Activity是否安全
     */
    public static boolean checkActivity(Context context) {
        PackageManager pm = context.getPackageManager();
        // 查询所有已经安装的应用程序
        List<ApplicationInfo> listAppcations =
                pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
        Collections.sort(listAppcations, new ApplicationInfo.DisplayNameComparator(pm));// 排序

        List<String> safePackages = new ArrayList<>();
        // 得到所有的系统程序包名放进白名单里面.
//        for (ApplicationInfo app : listAppcations) {// 这个排序必须有.
//            if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//                safePackages.add(app.packageName);
//            }
//        }

        String runningActivityPackageName = getCurrentPkgName(context);
        if (runningActivityPackageName != null) {
            // 有些情况下在5x的手机中可能获取不到当前运行的包名,所以要非空判断。
            if (runningActivityPackageName.equals(context.getPackageName())) {
                return true;
            }
            // 白名单比对
            for (String safePack : safePackages) {
                if (safePack.equals(runningActivityPackageName)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static String getCurrentPkgName(Context context) {
        // 5x系统以后利用反射获取当前栈顶activity的包名.
        ActivityManager.RunningAppProcessInfo currentInfo = null;
        Field field = null;
        int START_TASK_TO_FRONT = 2;
        String pkgName = null;
        try {
            // 通过反射获取进程状态字段.
            field = ActivityManager.RunningAppProcessInfo.class.getDeclaredField("processState");
        } catch (Exception e) {
            e.printStackTrace();
        }
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List appList = am != null ? am.getRunningAppProcesses() : null;
        ActivityManager.RunningAppProcessInfo app;
        for (int i = 0; i < (appList != null ? appList.size() : 0); i++) {
            //ActivityManager.RunningAppProcessInfo app : appList
            app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
            //表示前台运行进程.
            if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                Integer state = null;
                try {
                    state = field != null ? field.getInt(app) : 0;// 反射调用字段值的方法,获取该进程的状态.
                } catch (Exception e) {
                    e.printStackTrace();
                }
                // 根据这个判断条件从前台中获取当前切换的进程对象
                if (state != null && state == START_TASK_TO_FRONT) {
                    currentInfo = app;
                    break;
                }
            }
        }
        if (currentInfo != null) {
            pkgName = currentInfo.processName;
        }
        return pkgName;
    }

    /**
     * 判断当前是否在桌面
     *
     * @param context 上下文
     */
    public static boolean isHome(Context context) {
        ActivityManager mActivityManager =
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
        return getHomes(context).contains(rti.get(0).topActivity.getPackageName());
    }

    /**
     * 获得属于桌面的应用的应用包名称
     *
     * @return 返回包含所有包名的字符串列表
     */
    private static List<String> getHomes(Context context) {
        List<String> names = new ArrayList<String>();
        PackageManager packageManager = context.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
                PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo ri : resolveInfo) {
            names.add(ri.activityInfo.packageName);
        }
        return names;
    }

    /**
     * 判断当前是否在锁屏再解锁状态
     *
     * @param context 上下文
     */
    public static boolean isReflectScreen(Context context) {
        KeyguardManager mKeyguardManager =
                (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        if (mKeyguardManager != null) {
            return mKeyguardManager.isKeyguardLocked();
        }
        return false;
    }
}


四、参考

https://dun.163.com/news/p/47d0c43eb1854bae91872edc656dbd9e
https://www.jianshu.com/p/d4677e837648

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值