抢红包插件背后的Accessibility服务及启动原理

admin 2025年2月13日10:29:46评论7 views字数 8642阅读28分48秒阅读模式

抢红包插件背后的Accessibility服务及启动原理

前言

最近开发的一款设备使用到了Accessibility 功能,Android 提供了Accessibility功能和服务帮助这些用户更加简单地操作设备。

需要实现AccessibilityService,AccessibilityService是一个系统服务,它运行在后台,并且能够收到由系统发出的一些事件,比如通知状态、view 的一些相关事件,指纹,touch 等。

界面中产生的任何变化都会由系统通知给 AccessibilityService。大家熟知的抢红包软件,Talkback 都是使用AccessibilityService 实现的。自动化测试等等,都是基于 Accessibility。

后面几篇文章,将慢慢揭开Accessibility的神秘的面纱。请大家多多关注。

AccessibilityService是Service吗

简单例子这里面就不讲了,大家可以网上搜索下。

AndroidManifest.xml 配置如下

 <service
        android:name="XXX.Service"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">

        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessible_service_config" />
//对AccessibilityService 的配置文件
    </service>
</application>

AccessibilityService  写法就是 Serviceandroid:permissionintent-filter 都是必要配置。

貌似还是 Service 那一套,但又不仅限于此,后面的内容可以发现它是不一般的 Servcie。

AccessibilityService如何启动的

虽作为 Servcie,App 本身并没有启动和停止它,完全由系统调度,这是第一个不一样的地方。

在 AOSP 中类似的由系统调度的 App Service 有很多,比如 JobServiceAutoFillServiceNotificationAssistantService

下面将将具体解答这两个问题:

  1. 谁启动和停止 AccessibilityService ?
  2. 设备重启了,AccessbilityService 也会启动吗,不会被kill吗?

1. 谁启动了AccessibilityService

安装AccessibilityService  的应用,会出现在设置-无障碍-应用列表中,选择打开,应用中的AccessibilityService 就启动。

代码上,设置应用 会将AccessibilityService 的 ComponentName 信息 存入 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES

AccessibilityManagerService 系统服务将监听该 Map 的变化,启动或者关闭相应 ComponentName 的 AccessibilityService。

流程如下:

  1. 监听 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
  2. 更新 AccessibilityUserState
  3. 遍历 mEnableServices  ,如果没有bind 则调用 bindService  启动它
抢红包插件背后的Accessibility服务及启动原理

2、重启后AccessbilityService会启动吗,不会被kill?

AccessibilityManagerService 是 SystemService 进程,开机自启动,所以 AccessbilityService  的开机启动就简单了,源码是监听 unlock 广播,

bindService 则使用 Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE  FLAG 来启动前台级别的 service,所以不会被Kill。

frameworks/baseservices/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
public void bindLocked() 
{
    AccessibilityUserState userState = mUserStateWeakReference.get();
    if (userState == nullreturn;
    final long identity = Binder.clearCallingIdentity();
    try {
        int flags = Context.BIND_AUTO_CREATE
                | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE  //前台service,所以不会被Kill
                | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
                | Context.BIND_INCLUDE_CAPABILITIES;
        if (userState.getBindInstantServiceAllowedLocked()) {
            flags |= Context.BIND_ALLOW_INSTANT;
        }
        if (mService == null && mContext.bindServiceAsUser(
                mIntent, this, flags, new UserHandle(userState.mUserId))) {
            userState.getBindingServicesLocked().add(mComponentName);
        }
    } finally {
        Binder.restoreCallingIdentity(identity);
    }
    mActivityTaskManagerService.setAllowAppSwitches(mComponentName.flattenToString(),
            mAccessibilityServiceInfo.getResolveInfo().serviceInfo.applicationInfo.uid,
            userState.mUserId);
}

注意:开发中遇到过一种情况 AccessibilityService 不会启动,设置 App 中会显示无法运行。点按可查看相关信息的提示,这是由于 AccessibilityService 所在的 App 发生了 crash,而 crash 信息会记录在AccessibilityUserState中,不再自动 bind。

以上流程AccessibilityManagerService 与 应用中AccessibilityService通信,

AccessibilityService和 AccessibilityManagerService的关系

分析完以上流程,可以看到

  • AccessibilityManagerService  充当了 client
  • AccessibilityService 则是 service

所以调用过程的本质上是AccessibilityManagerService  -> AccessibilityService。

可能有朋友会有疑惑:AccessibilityService 虽然本质上是 Service ,但分明又提供了 onServiceConnected 的方法,那它到底属于 client 还是 service?

从以上时序图可以看出,AccessibilityManagerService  绑定 AccessibilityService 成功后,会立即调用 IAccessibilityServiceClient#init()。

init() 的目的很简单:将 AccessibilityManagerService  侧的 IAccessibilityServiceConnection 接口回调回来,可以方便AccessibilityService 反过来调用 AccessibilityManagerService。回调过来的接口正常的话会调用上述提供的 onServiceConnected()。

简言之,这个 onServiceConnected() 指的是 AccessibilityManagerService 的 IAccessibilityServiceConnection 服务准备好了。

public abstract class AccessibilityService extends Service {
    ...
    public static class IAccessibilityServiceClientWrapper extends IAccessibilityServiceClient.Stub
            implements HandlerCaller.Callback 
{
        ...
        public void init(IAccessibilityServiceConnection connection, int connectionId,
                IBinder windowToken)
 
{
            // Binder 线程切换到 Main 线程
            // 传递 id 和 AccessibilityManagerService 返回的 connection 接口
            Message message = mCaller.obtainMessageIOO(DO_INIT, connectionId,
                    connection, windowToken);
            mCaller.sendMessage(message);
        }
        ...

        @Override
        public void executeMessage(Message message) {
            switch (message.what) {
                ...
                case DO_INIT: {
                    mConnectionId = message.arg1;
                    SomeArgs args = (SomeArgs) message.obj;
                    IAccessibilityServiceConnection connection =
                            (IAccessibilityServiceConnection) args.arg1;
                    IBinder windowToken = (IBinder) args.arg2;
                    args.recycle();
                    // 如果系统返回的 connection 没有问题
                    // 回调 AccessibilityService 自己的初始化
                    // 并回调提供的 onServiceConnected()
                    if (connection != null) {
                        AccessibilityInteractionClient.getInstance().addConnection(mConnectionId,
                                connection);
                        mCallback.init(mConnectionId, windowToken);
                        mCallback.onServiceConnected();
                    } else {
                        AccessibilityInteractionClient.getInstance().removeConnection(
                                mConnectionId);
                        mConnectionId = AccessibilityInteractionClient.NO_ID;
                        AccessibilityInteractionClient.getInstance().clearCache();
                        mCallback.init(AccessibilityInteractionClient.NO_ID, null);
                    }
                    return;
                    ...
                }
            }
        }
    }
}

所以说:AccessibilityService 这种特殊的 Service,既是供 AccessibilityManagerService 传递无障碍事件的 service,同时又是会反向调用 AccessibilityManagerService 的 client

那 AccessibilityService 什么时候需要反向调用 AccessibilityManagerService 呢?

其实这种的场景很多,这构成了 AccessibilityService 功能的重要部分,包括:

  • 动态更新 Accessibility 的配置(serServiceInfo())
  • 发出具体的手势(dispatchGesture())
  • 发出截图的请求(takeScreenshot())
  • 发出屏幕缩放的请求(setMagnificationScaleAndCenter())
  • 等等

下面提及的 Accessibility 配置的动态更新正是这个场景之一!

Accessibility配置的加载

AccessibilityService 支持很多配置,但是很多配置在实际开发中都是用不到的。配置的方式有静态动态两种。

  • 静态配置(更为推荐) 就像文章开头在 meta-data 里配置信息

  • 动态配置 运行中调用 serServiceInfo() 根据需要动态更新配置

抢红包插件背后的Accessibility服务及启动原理

从上图可以看出,

  • 静态配置:AccessibilityManagerService 通过 PackageManager 获取 xml 的配置信息,就转化为 AccessibilityServiceInfo,AccessibilityServiceInfo 保存在 System Server进程

  • 动态配置:app 使用 IAccessbilityServiceConnection 接口作为桥梁,去调用 System Server  ,设置 AccessibilityServiceInfo

具体配置的内容,这里就不多讲了,整个Accessibility 的内容是很庞大的,所以相关配置也比较多。后面文章会结合功能跟大家分享,这样也更容易懂,这里就跟大家卖个关子吧。

AccessibilityService的调试

上面说过开启的 AccessibilityService 会存在 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES 中,通过相应的 adb 命令可以实时打印这些信息,高效调试。

adb shell settings get secure enabled_accessibility_services

可以读当然也可以写,免去了到 Settings App 里手动打开:

adb shell settings put secure enabled_accessibility_services <com.example.xxx/.xxxService>

注意:参数是所有打开的 Service 列表,新写入的 Service 名称要在已有的 Service 后面追加,用:隔开。不然会清除其他 开启的Servcie。

adb shell settings put secure enabled_accessibility_services <xxx/.xxxService:xxx/.xxxService:xxx/.xxxService>

除了查看开启,同时还有个命令可以观察 AccessibilityService 的 boundunboundcrash 等详细信息。

adb shell dumpsys accessibility

总结

本次主要分享了AccessibilityService 是如何启动, AccessibilityService 如何与SystemServer 中 AccessibilityManagerService 如何相互调用。

大概关系如图所示:抢红包插件背后的Accessibility服务及启动原理总结如下:

  • AccessibityManagerService 创建和管理 AccessibilityServiceConnection
  • AccessibilityServiceConnection 与 App 中的 AccessibilityService 一 一对应
  • AccessibilityService 将 IAccessibilityServiceClient 接口暴露给 AccessibityManagerService 调度
  • AccessibityManagerService 将 IAccessibilityServiceConnection 接口传递给 AccessibilityService 回调

再次梳理下 AccessibilityService 的特点:

  1. AccessibityManagerService bind 和 unbind
  2. 设备重启会自行启动
  3. 拥有前台 Service 的 Flag,优先级高不会被 kill 掉
  4. 接受 AccessibityManagerService 的调度,同时会反向调用,既是 service 又是 client

推荐阅读:

Android混淆规则

Oo0代码混淆实现方法

超实用的优质公众号推荐

Android应用安全方案梳理

加固不等于安全之APP应用更安全浅析

加密shared_prefs/xml中的内容防窃取

Android包体积优化(常规、进阶、极致)

bundletool工具使用(Android aab包安装)

Google Play上架App之aab转apk和apk转aab的使用方法

Android aab包google上架避免关联下架的解决方案(App出海企业的福利)

Android App Bundle混淆加密加壳加固保护的解决方案(过Google App上架审核)

最全Android及资源混淆方法汇总(无需加固节约成本并将APP上架Google Play成功的最佳方案)

抓包那些事

学抓包就来"哆啦安全"学

Android应用安全方案梳理

Android篡改检测应用程序

Android APK和API漏洞扫描器

微信小程序抓不到包的解决方法

玩转Android adb命令(adb降级)

检测Android手机病毒的方法浅析

Android App开发Bug解决完美方案

JNI与NDK编程(基础到精通)最全总结

Android系统run-as命令原理(权限访问)

Android11以上手机小程序抓包解决方案

frida hook so导出或未导出函数的方法

Android系统中run-as和su命令的源码解析(升降权限)

Android系统定制之Android.mk和Android.bp语法详解(精通版)

抢红包插件背后的Accessibility服务及启动原理

原文始发于微信公众号(哆啦安全):抢红包插件背后的Accessibility服务及启动原理

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年2月13日10:29:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   抢红包插件背后的Accessibility服务及启动原理https://cn-sec.com/archives/1065789.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息