Android的Activity劫持提示功能

前言

今天被要求写一个关于Android Activity被劫持的检测以及提示功能,这个其实还是蛮简单的。
可能有人还不了解这个Activity劫持是什么意思,其实在Android系统当中,程序可以枚举当前运行的进程而不需要声明其他权限,我们可以写一个程序来实现,启动一个后台的服务,这个服务不断的扫描当前运行的进程,当发现目标进程启动时,就启动一个伪装的Activity。如果这个Activity是登录界面,那么就能获取到用户的账号和密码。
因此在用户使用App的时候如果被恶意跳转到别的界面,这个时候我们需要作出预警提示用户,告诉用户当前的界面已经不是我们的App了,具有潜在的风险。代码的工作原理很简单,就是在我们所写的Activity对象的onStop()方法中进行判断将要跳转的界面是否是安全的。

正篇

下面插入所用到的检测方法的工具类的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
package com.tp.claim.util;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;

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

public class AntiHijackingUtil {

public static final String TAG = "AntiHijackingUtil";

// 白名单列表
private static List<String> safePackages;

static {
safePackages = new ArrayList<String>();
}

public static void configSafePackages(List<String> packages) {
return;
}

private static PackageManager pm;
private List<ApplicationInfo> mlistAppInfo;

/**
* 检测当前Activity是否安全
*/
public static boolean checkActivity(Context context) {
boolean safe = false;
pm = context.getPackageManager();
// 查询所有已经安装的应用程序
List<ApplicationInfo> listAppcations = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Collections.sort(listAppcations, new ApplicationInfo.DisplayNameComparator(pm));// 排序
List<ApplicationInfo> appInfos = new ArrayList<ApplicationInfo>(); // 保存过滤查到的AppInfo
//appInfos.clear();
for (ApplicationInfo app : listAppcations) {//这个排序必须有.
if ((app.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//appInfos.add(getAppInfo(app));
safePackages.add(app.packageName);
}
}
//得到所有的系统程序包名放进白名单里面.

ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
String runningActivityPackageName;
int sdkVersion;

try {
sdkVersion = Integer.valueOf(android.os.Build.VERSION.SDK);
} catch (NumberFormatException e) {
sdkVersion = 0;
}

if (sdkVersion >= 21) {//获取系统api版本号,如果是5x系统就用这个方法获取当前运行的包名
runningActivityPackageName = getCurrentPkgName(context);
} else {
runningActivityPackageName = activityManager.getRunningTasks(1).get(0).topActivity.getPackageName();
}
//如果是4x及以下,用这个方法.


if (runningActivityPackageName != null) {//有些情况下在5x的手机中可能获取不到当前运行的包名,所以要非空判断。
if (runningActivityPackageName.equals(context.getPackageName())) {
safe = true;
}

// 白名单比对
for (String safePack : safePackages) {
if (safePack.equals(runningActivityPackageName)) {
safe = true;
}
}
}

return safe;
}


public 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.getRunningAppProcesses();
ActivityManager.RunningAppProcessInfo app;

for (int i = 0; i < appList.size(); i++) {
//ActivityManager.RunningAppProcessInfo app : appList
app = (ActivityManager.RunningAppProcessInfo) appList.get(i);
if (app.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {//表示前台运行进程.
Integer state = null;
try {
state = field.getInt(app);//反射调用字段值的方法,获取该进程的状态.
} catch (Exception e) {
e.printStackTrace();
}
if (state != null && state == START_TASK_TO_FRONT) {//根据这个判断条件从前台中获取当前切换的进程对象.
currentInfo = app;
break;
}
}
}

if (currentInfo != null) {
pkgName = currentInfo.processName;
}
return pkgName;
}
}

这里有几个注意事项:

  • 1.导包的时候注意不要导错:
1
2
import java.lang.reflect.Field;
import android.content.pm.PackageManager;
  • 2.用法很简单,只需要在需要使用检测方法的Activity的onStop()方法中调用工具类的checkActivity()方法,接收返回的boolean值进行判断即可,下面是一个简单示例:
1
2
3
4
5
6
7
8
9
10
@Override
protected void onStop() {
super.onStop();
boolean safe = AntiHijackingUtil.checkActivity(this);
if (safe){
ToastUtils.TextToast(this, "安全", Toast.LENGTH_LONG);
} else {
ToastUtils.TextToast(this, "不安全", Toast.LENGTH_LONG);
}
}
  • 3.在使用这个工具类的时候,调用了packageManager的getRunningTask()方法,因此需要在清单文件AndroidManifest.xml中声明GET_TASK权限:
1
2

<uses-permission android:name="android.permission.GET_TASKS"/>
  • 4.当你需要调用一个第三方的应用,就需要把这个应用的报名加入白名单,也就是safePakcages这个静态变量中去。