任意版本界面劫持

任意版本界面劫持

界面劫持攻击分为界面劫持和钓鱼攻击两部分。恶意程序会对目标应用的客户端进行监控,当应用客户端程序调用Acitivity组件显示 窗口,且该窗口界面组件是恶意程序预设的攻击对象,恶意程序就会立即启动自己的仿冒界面使之替换或覆盖在客户端程序界面之上,这就是界面劫持。恶意攻击者在发动界面劫持后,用户所面对并操作的其实已经是被替换后的仿冒页面,这意味着用户在毫无察觉的情况下将自己的账号、密码等信息输入到了仿冒界面中,这些数据最终会返回到恶意程序的服务器里,这个过程便是钓鱼攻击。
我们安卓APP检测本身就有界面劫持的检测项目,检查是否存在Activity界面劫持,确认app是否能够发现被劫持并提示用户。具体的效果检测如下:
(这里我们使用的工具是一个专门用来进行界面劫持的工具ActivityHijack.apk)
任意版本界面劫持任意版本界面劫持
但是在检测中发现了一个特殊的问题,就是这款工具,针对于Android5.0版本以下的系统可以轻松的对app进行界面劫持,但是针对于Android5.0版本以上的系统根本无法对任何app进行劫持。所以本文章就针对于该问题进行讲解,并实现无论任何版本进行界面劫持。
界面劫持的原理:
首先Activity相当于一个与用户交互的界面。而Activity的调度是交由Android系统中的ActivityManagerService(Activity管理服务)管理的,各个应用想启动或停止一个进程,都是先报告给AmS。 当AmS收到要启动或停止Activity的消息时,它先更新内部记录,再通知相应的进程运行或停止指定的Activity。当新的Activity启动,前一个Activity就会停止,这些Activity都保留在系统中的一个Activity历史栈中。每有一个Activity启动,它就压入历史栈顶,并在手机上显示。当用户按下back键时,顶部Activity弹出,恢复前一个Activity,栈顶指向当前的Activity。
如果在启动一个Activity时,给它加入一个标志位FLAG_ACTIVITY_NEW_TASK,就能使它置于栈顶并立马呈现给用户。 所以在这里开启一个service;这个service,会启动一个计时器,不停枚举当前进程中是否有预设的进程启动,如果发现有预设进程,则使用FLAG_ACTIVITY_NEW_TASK启动自己的钓鱼界面,截获正常应用的登录凭证。
具体代码如下:
首先我们启动一个service。(这里是简单的点击事件).
任意版本界面劫持
如果要实现开机自启动就建立一个receiver,接收到android.intent.action.BOOT_COMPLETED的广播,然后是启动service服务。
任意版本界面劫持
接着我们建立最关键的service类,实现获取当前的app进程然后实现界面劫持替换为我们的自己的界面。这里要获取系统的ActivityManager,然后之后getRunningTasks(1).get(0).topActivity.getClassName()获取到当前界面的Activity,后面的getRunningAppProcesses()是获取当前运行APP的进程。然后通过importance值得对比,这个是进程在系统中的重要级别,通过这个可以来判断进程的类型,前台进程的级别就是MPORTANCE_FOREGROUND,后台进程的级别是IMPORTANCE_BACKGROUND等等,所以这里只要比较importance值,只要是与前台进程匹配就直接进行界面劫持。这里我们给调用的intent直接添加flag(FLAG_ACTIVITY_NEW_TASK),这样就可以直接把该Activity压到栈顶,直接显示给用户。
任意版本界面劫持
劫持的效果如下:
任意版本界面劫持任意版本界面劫持任意版本界面劫持
这里需要加上一个handle机制,不停的枚举进程,不然点击劫持后,也只是获取该劫持应用的当前界面,然后就不会再进行枚举其他进程。
Android5.0版本之后:
然后我们到Android5.0以后的系统,进行界面劫持。发现点击劫持后,其他应用根本没有反应,通过日志查看到我们没有获取到其他应用的进程。
任意版本界面劫持
通过网上查询资料后发现,在Android5.0版本之后,Android对getRunningTasks接口进行限制使用,移除了getRunningAppProcesses() 方法,应用要使用该接口必须声明权限android.permission.REAL_GET_TASKS而这个权限是不对三方应用开放的。(在Manifest里申请了也没有作用),系统应用(有系统签名)可以调用该权限。所以我们根本是无法获取到APP进程信息。
这时候我们可以使用UsageStatsManage来查看应用使用情况,UsageStatsManager是使用情况统计管理者,通过它可以获取应用的使用情况。这个API是Android5.0之后的版本才拥有的,刚好适合我们的劫持情况,使用其中的调用UsageStatsManager的queryUsageStats(int intervalType, long beginTime, long endTime)方法可以查看最后APP使用情况,在这里其中的intervalType是时间间隔类型,有(INTERVAL_DAILY、INTERVAL_WEEKLY、INTERVAL_MONTHLY、INTERVAL_YEARLY、INTERVAL_BEST),beginTime为开始统计的时间,endTime为结束的时间。之后使用getpackageName可以获取到当前的包名,之后我们直接进行劫持。
任意版本界面劫持
这里需要注意一个地方就是,使用它之前需要在清单文件中配置 “android.permission.PACKAGE_USAGE_STATS”的权限,用户必须在“设置–安全–有权查看使用情况的应用中”勾选相应的应用,但是一般这个界面是无法在其中设置找到的,所以我们可以自己声明一个点击事件,进入到“有权查看使用情况的应用中”的界面。设置intent为Settings.ACTION_USAGE_ACCESS_SETTINGS。这样就可以进入到相关的界面,设置权限。
任意版本界面劫持任意版本界面劫持任意版本界面劫持任意版本界面劫持
所以我们只需要进行过版本判断就可以进行任意版本的界面劫持,不需要ROOT权限。
任意版本界面劫持
修复建议:
因为这本身是属于Android对于Activity的调动机制,是无法避免的,这里就需要客户拥有一些安全意识,同时我给的APP开发建议:在正常Activity的登陆界面(也就是MainActivity)中重写onKeyDown方法和onPause方法,当其被覆盖时,就能够弹出警示信息。最好写个循环,客户端app是否在栈顶,不在就弹出提示。这样方法是最简单也是最方便的修改建议。