AccessibilityService的具体应用场景

1、抢红包插件


先回顾一下抢红包的的流程:

  1. 状态栏出现"[微信红包]"的消息提示,点击进入聊天界面
  2. 点击相应的红包信息,弹出抢红包界面
  3. 在抢红包界面点击"开",打开红包
  4. 在红包详情页面,查看详情,点击返回按钮返回微信聊天界面.

以上是不在微信聊天界面时的流程.如果你所在的微信聊天窗口出现红包,则不会执行步骤1,而是直接执行2,3,4.如果是在微信好友列表时,收到红包,则会在列表项中显示[微信红包],需要点即该列表项,进入聊天界面,随后执行2,3,4.为了方便演示,这里我们暂时不考虑好友列表时出现红包的情况.

明白了抢红包流程,之后我们通过AccessibilityService获取通知栏信息及微信聊天窗口界面,继而通过模拟点击实现打开红包,抢红包等操作.
AccessibilityService配置如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
                       android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|
                                                        typeWindowContentChanged"
            android:accessibilityFeedbackType="feedbackGeneric"
            android:accessibilityFlags="flagDefault"
            android:canRetrieveWindowContent="true"
            android:notificationTimeout="100"
            android:packageNames="com.tencent.mm" />

具体实现代码如下:

public class RobService extends AccessibilityService {


    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        int eventType = event.getEventType();
        switch (eventType) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                handleNotification(event);
                break;
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:
                String className = event.getClassName().toString();
                if (className.equals("com.tencent.mm.ui.LauncherUI")) {
                    getPacket();
                } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) {
                    openPacket();
                } else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) {
                    close();
                }

                break;
        }
    }

    /**
     * 处理通知栏信息
     *
     * 如果是微信红包的提示信息,则模拟点击
     *
     * @param event
     */
    private void handleNotification(AccessibilityEvent event) {
        List<CharSequence> texts = event.getText();
        if (!texts.isEmpty()) {
            for (CharSequence text : texts) {
                String content = text.toString();
                //如果微信红包的提示信息,则模拟点击进入相应的聊天窗口
                if (content.contains("[微信红包]")) {
                    if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) {
                        Notification notification = (Notification) event.getParcelableData();
                        PendingIntent pendingIntent = notification.contentIntent;
                        try {
                            pendingIntent.send();
                        } catch (PendingIntent.CanceledException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 关闭红包详情界面,实现自动返回聊天窗口
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void close() {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null) {
            //为了演示,直接查看了关闭按钮的id
            List<AccessibilityNodeInfo> infos = nodeInfo.findAccessibilityNodeInfosByViewId("@id/ez");
            nodeInfo.recycle();
            for (AccessibilityNodeInfo item : infos) {
                item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    /**
     * 模拟点击,拆开红包
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private void openPacket() {
        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo != null) {
            //为了演示,直接查看了红包控件的id
            List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("@id/b9m");
            nodeInfo.recycle();
            for (AccessibilityNodeInfo item : list) {
                item.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }
    }

    /**
     * 模拟点击,打开抢红包界面
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void getPacket() {
        AccessibilityNodeInfo rootNode = getRootInActiveWindow();
        AccessibilityNodeInfo node = recycle(rootNode);

        node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        AccessibilityNodeInfo parent = node.getParent();
        while (parent != null) {
            if (parent.isClickable()) {
                parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                break;
            }
            parent = parent.getParent();
        }

    }

    /**
     * 递归查找当前聊天窗口中的红包信息
     *
     * 聊天窗口中的红包都存在"领取红包"一词,因此可根据该词查找红包
     * 
     * @param node
     */
    public AccessibilityNodeInfo recycle(AccessibilityNodeInfo node) {
        if (node.getChildCount() == 0) {
            if (node.getText() != null) {
                if ("领取红包".equals(node.getText().toString())) {
                    return node;
                }
            }
        } else {
            for (int i = 0; i < node.getChildCount(); i++) {
                if (node.getChild(i) != null) {
                    recycle(node.getChild(i));
                }
            }
        }
        return node;
    }

    @Override
    public void onInterrupt() {

    }

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
    }


}

注意:

上面的代码简单演示了抢红包的原理,为了方便起见,我直接通过findAccessibilityNodeInfosByViewId()获取制定id控件.在实际中,这种方法不太可靠,到目前为止,微信已经改过几次相关控件的id了.

2、app的自动安装

讲完了微信红包插件的实现原理,不难发现其本质是根据相关的界面状态,模拟后续的操作(比如点击等).
既然这样,那么我们完全可以利用该服务实现更多的功能,比如apk自动安装,传统的安装过程大概是如下流程:

点击apk文件,弹出安装信息界面,在该界面点击"下一步",然后在点击"安装",最后在安装完成界面点击"完成".

不难发现,该流程完全可以通过模拟点击操作完成.现在我们简单的讲一下AccessibilityService在这方面的具体应用:
我们知道系统的安装程序由PackageInstaller负责,其包名是com.android.packageinstaller,那么我们只需要监听该package下的安装信息界面和安装完成界面,并模拟点击"下一步","安装",完成""操作即可实现自动安装.

AccessibilityService配置如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:accessibilityEventTypes="typeAllMask"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagDefault"
        android:canRetrieveWindowContent="true"
        android:description="@string/auto_service_des"
        android:notificationTimeout="100"
        android:packageNames="com.android.packageinstaller"/>

具体实现代码如下:

public class InstallService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.d("InstallService", event.toString());
        checkInstall(event);
    }


    private void checkInstall(AccessibilityEvent event) {
        AccessibilityNodeInfo source = event.getSource();
        if (source != null) {
            boolean installPage = event.getPackageName().equals("com.android.packageinstaller");
            if (installPage) {
                installAPK(event);
            }
        }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void installAPK(AccessibilityEvent event) {
        AccessibilityNodeInfo source = getRootInActiveWindow();
        List<AccessibilityNodeInfo> nextInfos = source.findAccessibilityNodeInfosByText("下一步");
        nextClick(nextInfos);
        List<AccessibilityNodeInfo> installInfos = source.findAccessibilityNodeInfosByText("安装");
        nextClick(installInfos);
        List<AccessibilityNodeInfo> openInfos = source.findAccessibilityNodeInfosByText("打开");
        nextClick(openInfos);

        runInBack(event);

    }

    private void runInBack(AccessibilityEvent event) {
        event.getSource().performAction(AccessibilityService.GLOBAL_ACTION_BACK);
    }

    private void nextClick(List<AccessibilityNodeInfo> infos) {
        if (infos != null)
            for (AccessibilityNodeInfo info : infos) {
                if (info.isEnabled() && info.isClickable())
                    info.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    private boolean checkTilte(AccessibilityNodeInfo source) {
        List<AccessibilityNodeInfo> infos = getRootInActiveWindow().findAccessibilityNodeInfosByViewId("@id/app_name");
        for (AccessibilityNodeInfo nodeInfo : infos) {
            if (nodeInfo.getClassName().equals("android.widget.TextView")) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void onInterrupt() {

    }

    @Override
    protected void onServiceConnected() {
        Log.d("InstallService", "auto install apk");
    }
}

3、检测前台服务

在很多情况下,我们需要检测自己的app是不是处在前台,借助该服务同样也能够完成该检测操作.下面,我们就演示一下如何实现:

AccessibilityService配置如下:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"                 

        android:accessibilityEventTypes="typeWindowStateChanged"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagDefault"
        android:canRetrieveWindowContent="true"
        android:description="@string/auto_detection"
        android:notificationTimeout="100"
                       />

具体实现代码如下:

public class DetectionService extends AccessibilityService {
    private static volatile String foregroundPackageName = "error";

    /**
     * 检测是否是前台服务
     *
     * @param packagenName
     * @return
     */
    public static boolean isForeground(String packagenName) {
        return foregroundPackageName.equals(packagenName);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
            foregroundPackageName = event.getPackageName().toString();
        }
    }

    @Override
    public void onInterrupt() {

    }
}

在使用时,需要引导用户开启该服务,之后通过调用DetectionService.isForeground()即可.

4、窃取信息

上面的所有示例演示的都是AccessibilityService在具体应用中发挥的良好作用.但是该服务也存在一定的风险,很多人利用该服务做一些"坏事",比如窃取短信验证码,窃取短信内容,想要看看自己女朋友最近在和谁聊QQ,偷偷安装流氓软件等.

上面我们了解微信抢红包插件的原理,那么利用该AccessibilityService编写相应的反抢红包插件:通过模拟微信红包的通知信息,发送虚假的事件通知.不出意外,我们编写的反抢红包插件会让失眠绝大多数的抢红包插件.这里我就不做深入的解释,有兴趣的同学可以自行研究.

你现在是不是想能否借助该服务直接获取一些app的密码呢?凡是EditText中设置inputType为password类型的,都无法获取其输入值.除此之外,大多数软件都针对该中风险做了提前的防范.因此,你想要借助该服务来实现窃取密码还是比较有难度的。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值