
写这篇之前先交代一个场景有个做自动化脚本的朋友问我为什么自己用AutoJsPro写的脚本每次一打开某些银行类、电商类APP就被秒检测日志里直接出现“疑似自动化工具运行”的提示。我把他的无障碍服务列表拉出来一看服务ID明晃晃挂着org.autojs.autojspro这类包名和AccessibilityService这个类名检测逻辑几乎不需要费任何力气就能匹配上。于是就有了这篇东西。AutoJsPro是Android平台上很主流的JavaScript自动化框架而它赖以工作的核心就是无障碍服务。很多做自动化、爬虫辅助、群控、App测试的人都会遇到“无障碍服务暴露”的问题解决思路之一就是把无障碍服务类名改掉让检测方无法通过名称特征直接识别。这篇文章就把这件事讲透底层原理是什么为什么改类名有效完整操作怎么做以及修改过程中那些没人提前告诉你的坑。1. 无障碍服务与类名AutoJsPro自动化脚本的底层密码想改类名先得搞清楚AutoJsPro和无障碍服务之间的关系不然你只是在机械地搜索替换字符串出了问题完全不知道从哪儿排查。1.1 AutoJsPro靠什么实现“看得见”的自动化AutoJsPro的自动化能力核心不在JavaScript引擎本身而在无障碍服务AccessibilityService这个Android系统级组件。普通App想要模拟点击、滑动、输入文本受制于Android的权限模型要么用root后执行shell命令要么就得借助无障碍服务这个合法的“系统辅助通道”。无障碍服务的本质是用户手动开启辅助功能后系统会把窗口内容变化、控件点击、手势操作等事件实时推送给注册了这个服务的应用。AutoJsPro就是基于这套事件流配合AccessibilityNodeInfo控件树来定位界面元素再通过dispatchGesture注入手势、通过performAction触发点击从而完成整套自动化流程。这就是为什么AutoJsPro必须在系统设置里手动开启无障碍权限而且无法用代码强行唤起——系统设计上就是为了防止App静默就能拿到这么强的辅助能力。1.2 无障碍服务的声明机制类名不是随便取的一个无障碍服务本质上就是一个继承自android.accessibilityservice.AccessibilityService的类它需要在AndroidManifest.xml里完成声明。以AutoJsPro为例核心服务类名通常是org.autojs.autojs.core.accessibility.AccessibilityService这样的路径声明长这样service android:nameorg.autojs.autojs.core.accessibility.AccessibilityService android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE android:exportedfalse intent-filter action android:nameandroid.accessibilityservice.AccessibilityService / /intent-filter meta-data android:nameandroid.accessibilityservice android:resourcexml/accessibility_service_config / /service这里有两个关键点决定了类名的“暴露面”android:name指定服务类的全限定名。系统在设置-无障碍界面展示的就是这个类名确切地说是它的可读形式。android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE这个权限是系统绑定无障碍服务时必须的缺失会导致服务根本无法开启。另外一个隐藏信息渠道是AccessibilityServiceInfo。系统会为每个已注册的无障碍服务生成一个唯一ID格式是包名/服务类名。任何应用在拿到AccessibilityManager后都可以调用getEnabledAccessibilityServiceList()列举出当前所有已开启的无障碍服务从这个列表里能直接读到每个服务的包名、类名、标签、描述。换句话说你在系统里开着AutoJsPro的无障碍服务就等于在Android系统这个“公共大厅”里立了一块写着“我是AutoJsPro”的牌子任何有权限的App都能看到。类名就是牌子上最显眼的那行字。2. 为什么改一个类名就能让脚本“隐身”这个问题的核心在于检测方怎么判断“当前设备有自动化工具在运行”。多数App内置的检测逻辑其实没有多高级看到特征字符串匹配就拦。2.1 检测方的三板斧从无障碍服务列表捞出“真人”在Android应用层枚举无障碍服务只需要几行代码AccessibilityManager am (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); ListAccessibilityServiceInfo services am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK); for (AccessibilityServiceInfo info : services) { String id info.getId(); // 返回格式包名/类名 String packageName info.getResolveInfo().serviceInfo.packageName; String className info.getResolveInfo().serviceInfo.name; String label info.loadLabel(context.getPackageManager()).toString(); // 匹配特征字符串 }检测方拿到id或className后会拿它和已知自动化工具的类名库做匹配。AutoJsPro的类名路径很长且非常固定比如上面提到的org.autojs.autojs.core.accessibility.AccessibilityService只要匹配到autojs这个子串基本就能断定设备上跑着AutoJsPro。除了类名info.loadLabel(...)返回的标签文本也会被检测。很多无障碍服务的标签就是“Auto.js”或者“自动化”这类文本同样会被收集进特征库。2.2 AutoJsPro的类名特征一眼可识别的暴露面AutoJsPro的问题在于类名的命名空间太有辨识度。你去看它的类名路径从org.autojs到autojs这些单词几乎是给检测方送分的。而官方版本并没有提供一个“自定义服务类名”的开关你装上是什么类名默认就是什么类名分布在全国各地的AutoJsPro用户全都长一个样。这就意味着不是你的脚本写得不如别人而是你的自动化框架本身在无障碍列表里的“门面”太容易被认出来了。检测方只要维护一份几十行的类名特征表就能把市面上主流自动化框架全部命中成本极低收益极高。2.3 修改类名的收益边界能防住谁防不住谁说句公道话改类名不是万能的。它有效的场景是检测方基于名称特征做粗粒度拦截比如全局搜索无障碍服务列表发现autojs关键字符串就直接标记风险。这种情况下把类名改成无特征的名字能让他们列表匹配不到从而以“正常辅助功能服务”的身份混过去。但它防不住三类检测这个必须提前有心理预期基于包名的检测服务ID里除了类名还有包名。当前设备上装了org.autojs.autojspro这个包本身就可能是信号。只改类名不改包名检测方仍然可以通过包名特征发现AutoJsPro存在。基于行为特征的风控无障碍服务开启后事件流频率、点击间隔、上下滑动模式都可能被采集并建模。比如每次屏幕变化后几毫秒就发生一次长按或点击这种操作节律不是改类名能掩盖的。基于设备环境的关联分析账号、设备指纹、IP等多维度信息关联之后即使本次没被名称匹配到后续操作异常依然可能被回溯标记。打个比方改类名相当于给你家换了个门牌号但房子的外观、窗户数量、进出习惯都没变。如果对方是靠门牌号找人就容易混过去如果对方是蹲在你家门口看你每天什么时候出门那换了门牌号也没用。3. 修改无障碍服务类名的完整操作链路前面原理讲得再多不如把操作链路趟一遍。下面这套流程基于PC端操作用apktool和jadx做主工具适配大多数AutoJsPro版本。3.1 前置准备工具选型与APK定位先把工具备齐版本很关键踩过坑的人都知道老版本apktool在处理新目标SDK时会出现资源解码失败或回编报错。工具用途建议版本apktool反编译/回编APK2.9.x及以上jadx-gui查看Java代码、定位类名引用1.4.x及以上zipalignAPK对齐优化Android SDK build-tools自带apksignerAPK签名Android SDK build-tools自带adb安装、查看系统服务状态最新版即可拿到AutoJsPro的APK后先做两件事确认包名和版本确认无障碍服务类的确切路径。包名等信息可以通过aapt快速读取aapt dump badging AutoJsPro.apk | head -20如果你用的是修改版或魔改版AutoJsPro类名可能已经被改过了所以不要依赖网上别人说的固定路径一定以自己手里这个APK实际反编译出的结果为准。3.2 第一步反编译定位无障碍服务声明用apktool解包apktool d AutoJsPro.apk -o autojs_src解包后打开AndroidManifest.xml搜索android.accessibilityservice.AccessibilityService这个action就能定位到无障碍服务的service标签。把android:name属性记下来这就是要修改的类名。同时用jadx-gui打开原始APK搜索继承AccessibilityService的类确认这个类的完整路径、依赖了哪些内部类。这一步不能省因为改类名不只是改一个入口很多内部类函数通过JNI或反射调用了这个主类。3.3 第二步同步修改smali目录与服务入口这是整个操作的核心步骤也是出错率最高的地方。先设计好新类名我建议不要包含任何与自动化相关的词比如com.example.android.core.ServiceHelper这样的观感就比较中性。我常用的一套做法是直接拿目标APP的包名风格做参考如果对方APP包名是com.xxx.yyy新类名就尽量往com.xxx.core这类路径上靠这样即便检测方人工查看第一眼也不会觉得突兀。操作分四步走修改AndroidManifest.xml中service标签的android:name改成新类名全限定路径。在smali目录里找到原类名对应的smali文件路径为smali/org/autojs/autojs/core/accessibility/AccessibilityService.smali这类结构。然后在目标路径下创建新目录比如smali/com/example/android/core/把原smali文件复制过去并重命名为ServiceHelper.smali。修改smali文件开头的类声明。打开文件前几行通常长这样.class public Lorg/autojs/autojs/core/accessibility/AccessibilityService; .super Landroid/accessibilityservice/AccessibilityService;把.class那一行的类路径改成新类名的描述符格式.class public Lcom/example/android/core/ServiceHelper;注意格式包名的点号换成斜杠末尾加分号。全局搜索所有smali文件中引用原类名的地方。搜索的关键词是原类名的Lxxx/xxx/...;描述符形式。所有引用点都要同步替换成新类名描述符。这个步骤不要手动一个个找直接借助VS Code或任意支持正则全局替换的编辑器先把Lorg/autojs/autojs/core/accessibility/AccessibilityService;全部替换成Lcom/example/android/core/ServiceHelper;再人工检查一遍是否有遗漏。3.4 第三步处理配置文件与资源很多人改完smali就兴冲冲去回编装了之后发现无障碍服务列表里还是显示“Auto.js Pro”这样的大字标签等于白改了一半。因为服务标签是通过accessibility_service_config.xml配置的。打开res/xml/accessibility_service_config.xml里面大概长这样accessibility-service xmlns:androidhttp://schemas.android.com/apk/res/android android:accessibilityEventTypestypeWindowStateChanged|typeWindowContentChanged android:accessibilityFeedbackTypefeedbackGeneric android:accessibilityFlagsflagDefault android:canRetrieveWindowContenttrue android:descriptionstring/accessibility_service_description android:labelstring/app_name /这里的label和description都指向strings.xml里的字符串资源。如果检测方读取info.loadLabel()或者info.loadDescription()拿到的就是这个资源里的文本。建议把这两个字符串也改掉不要让它包含“auto”“js”“自动化”之类的字眼。有一种情况要注意如果android:label直接写的是硬编码字符串而不是string/xxx直接改xml里的字面量即可。如果是资源引用一定要去strings.xml改对应的条目不然你改xml里的引用本身毫无意义。3.5 第四步重编译、签名与安装回编apktool b autojs_src -o autojs_modified.apk如果回编报错多半是资源或smali改动有语法问题具体排错思路我放到下一章。签名前先对齐zipalign -f 4 autojs_modified.apk autojs_aligned.apk然后生成一个自己的签名证书并签名keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000 apksigner sign --ks my.keystore --ks-key-alias mykey --out autojs_signed.apk autojs_aligned.apk最后安装到手机adb install -r autojs_signed.apk装好后去系统设置-无障碍找到对应的服务并开启。注意重打包后原来的用户数据可能失效授权记录、脚本里的某些持久化配置可能被系统清理或因为签名变化而无法读取。4. 修改过程中最容易踩的坑这部分才是本文真正的“干货区”。下面是几条我在实际操作中反复遇到的坑按出现频率排序。4.1 只改清单不改smali启动即崩溃有一个朋友把AndroidManifest.xml里的android:name改了但smali目录和文件根本没有动装上后去开启无障碍服务系统直接提示“服务无法启动”日志里报ClassNotFoundException。原因很简单清单文件里声明的是com.example.android.core.ServiceHelper但APK的类库里根本没有这个类系统在bindService时按类名去找找不到就宣告失败。这提醒了一个细节android:name、smali目录结构、.class声明这三者必须完全对应。改任何一处另外两处都要同步跟改。4.2 漏掉代码中的硬编码类名功能静默失效有次我改完顺利启动无障碍服务也开起来了但脚本跑起来后间隔性报错查日志发现NoClassDefFoundError指向一个内部类。顺着引用链查下去发现AutoJsPro的JavaScript绑定层代码里有一个字符串硬编码直接使用了原类名的描述符比如用Class.forName(org.autojs.autojs.core...)做反射。这提醒了另一个细节改类名时搜索范围不能只限于以.smali为后缀的文件。纷乱的manifest、xml配置、甚至是资源文件里的字符串常量都可能指向类名。稳妥起见你需要在反编译目录里做一次全量文本搜索关键词包括原类名的完整字符串org.autojs.autojs.core.accessibility.AccessibilityService原类名的描述符形式Lorg/autojs/autojs/core/accessibility/AccessibilityService;原包名中的关键特征词autojs以“autojs”这个特征词为例搜索出来的结果会很多需要逐个判断哪些是类引用、哪些是普通字符串资源。拿不准的尽量都改因为检测方搜特征字符串时也会搜这些字符串资源。4.3 签名校验重打包后的一道坎用apktool重打包必然会改变原签名。很多自动化工具或商业应用会内置签名校验在启动时比对当前签名和内置的合法签名哈希。AutoJsPro的某些授权版本也会有类似的完整性校验重打包后轻则弹窗提示重则直接闪退。处理签名校验的逻辑不在本文讨论范围内但你可以提前确认手里的版本是否存在校验。最简单的验证方式部署上去后如果打开闪退用adb logcat抓日志搜索signature或verify相关关键词大概率能看到风格各异的校验失败日志。有一个替代思路是使用Android 7.0以下的系统环境低版本对签名校验的兼容和绕过空间会大一些。但这已经在“旧设备反向兼容”的范畴里现在的设备普遍都是高版本这个思路的适用范围有限只作了解。4.4 无障碍服务开关灰色或无法开启有朋友修改完类名后设置页面的无障碍服务开关是灰色的点不动。排查了一圈发现是apktool在回编过程中把manifest里android:permissionandroid.permission.BIND_ACCESSIBILITY_SERVICE属性弄丢了或者自己编辑时误删了。这个权限是系统绑定无障碍服务的安全门槛没有它系统直接拒绝展示这个服务。改的时候不要动这个属性如果原文不长这样说明apktool版本太老或者manifest解析出了偏差。4.5 无障碍列表出现两个服务项有次改完类名装上发现无障碍列表里同时出现了两个服务项一个是改名前残留的旧服务一个是改后的新服务。出现这种问题的原因通常是没有卸载干净原包就覆盖安装系统的无障碍服务配置仍然记录了旧类名。解决方式很简单卸载旧包装完新包后重启一次手机再进设置开启无障碍。重启是为了让系统清掉无障碍服务绑定状态里的缓存数据。5. 修改完成后的验证链路与合规使用边界5.1 验证修改是否生效别只看设置页面最直观的验证方式当然是去设置-无障碍页面看服务名称和包信息但这个界面显示的内容不一定能体现类名是否改干净了。要验证系统层面识别到的服务ID还得用命令看adb shell dumpsys accessibility | grep -A 5 Service输出里能看到当前已注册的无障碍服务详情包括ComponentInfo{包名/类名}这样的信息直接查看类名是否已经变成你修改后的值。更接近检测方视角的验证方式是写一个几十行的小工具在目标设备上枚举无障碍服务列表并打印每个服务的id、packageName、className、label。写一个临时Demo工程或者在已有自动化环境里用反射直接调用AccessibilityManager的接口把数据dump出来对照检查。5.2 常见问题排查表现象可能原因排查方向开启服务时闪退清单类名与smali类不一致核对三个位置的类名是否完全同步服务能开但功能失效内部类引用漏改全量搜索原类名描述符并替换设置页显示名称未变label/description仍指向旧字符串资源检查strings.xml与accessibility_service_config.xml安装后快速闪退签名校验拦截抓logcat日志确认校验逻辑开关灰色无法开启BIND_ACCESSIBILITY_SERVICE权限丢失检查manifest中service节点权限声明系统列表出现两个同义服务未卸载干净的覆盖安装卸载重装并重启设备5.3 合规使用边界这篇文章能帮你做什么不能帮你做什么把类名修改这套技术讲完必须把边界说清楚。这套技术正当的用途有三个一是测试自己开发的应用时验证无障碍服务类名特征是否会成为被检测的暴露点以及如何设计更隐蔽的辅助功能方案二是在企业内部自动化测试框架中定制无障碍服务命名避免与竞品工具混淆三是学习Android无障碍机制的底层细节理解AccessibilityServiceInfo在系统层面的工作方式。它不应该被用于绕过平台反作弊规则、恶意刷量、批量注册、干扰他人服务等灰色甚至黑色用途。这类行为一旦被识别往往牵涉账号封禁、设备拉黑甚至更严重的后果风险远大于收益。而且从技术角度讲检测方只要把你的设备指纹和操作行为关联起来单纯换个类名并不能真正带来“隐身”效果。最后再分享一个经验实测过程中我最大的感受是改类名确实是AutoJsPro用户需要掌握的一项基础定制技能但不要对它抱有不切实际的期望。它解决的是“名称特征暴露”这一个层面的问题包名、行为节律、设备指纹等仍然可能暴露你。如果要把自动化脚本稳定运行当成一个长期工程来做更值得投入的方向有两个一是研究无障碍服务开启后的行为伪装比如适当增加事件间隔的随机性二是研究检测方常用的特征提取逻辑从客户端数据采集的角度反向优化自己的自动化链路。这些话题展开讲又是一大篇先把这个类名修改的坑填完后面有机会再聊。