前面讲解了AccessibilityService知多少,详细描述了使用方法已经内部的原理,这节主要是防护手段。在网上也找到了不少资料,做为参考。下面就简单的说一说。php
以前提到过AccessibilityService类使用的是观察者模式,经过Binder机制在系统App1 view层->os->App2Service进行事件传递。由AccessibilityManagerService注册AccessibilityService,那如何检测到安装并启用辅助模式App2呢?系统提供了以下方法:java
@Override
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(int userId) {
synchronized (mLock)
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// The automation service is a fake one and should not be reported to clients as being installed - it really is not.
UserState userState = getUserStateLocked(resolvedUserId);
if (userState.mUiAutomationService != null) {
List<AccessibilityServiceInfo> installedServices = new ArrayList<>();
installedServices.addAll(userState.mInstalledServices);
installedServices.remove(userState.mUiAutomationService.mAccessibilityServiceInfo);
return installedServices;
}
return userState.mInstalledServices;
}
}
@Override
public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType, int userId) {
List<AccessibilityServiceInfo> result = null;
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
final int resolvedUserId = mSecurityPolicy
.resolveCallingUserIdEnforcingPermissionsLocked(userId);
// The automation service can suppress other services.
UserState userState = getUserStateLocked(resolvedUserId);
if (userState.isUiAutomationSuppressingOtherServices()) {
return Collections.emptyList();
}
result = mEnabledServicesForFeedbackTempList;
result.clear();
List<Service> services = userState.mBoundServices;
while (feedbackType != 0) {
final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackType));
feedbackType &= ~feedbackTypeBit;
final int serviceCount = services.size();
for (int i = 0; i < serviceCount; i++) {
Service service = services.get(i);
// Don't report the UIAutomation (fake service)
if (!sFakeAccessibilityServiceComponentName.equals(service.mComponentName)
&& (service.mFeedbackType & feedbackTypeBit) != 0) {
result.add(service.mAccessibilityServiceInfo);
}
}
}
}
return result;
}
复制代码
这个方法remove了UiAutomationService,仍是很贴心的。android
返回值AccessibilityServiceInfo是一些咱们使用的AccessibilityService的配置信息,包括packageNames(AccessibilityService 监控哪些package发出的Event),以下:微信
javaapp
AccessibilityServiceInfo serviceInfo = new AccessibilityServiceInfo();
serviceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
serviceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
serviceInfo.packageNames = new String[]{"com.tencent.mm"};
serviceInfo.notificationTimeout=100;
setServiceInfo(serviceInfo);
复制代码
xmlide
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault|flagRequestEnhancedWebAccessibility" android:canRetrieveWindowContent="true" android:description="@string/app_name" android:notificationTimeout="100" android:packageNames="com.tencent.mm" android:canRequestEnhancedWebAccessibility="true" />
复制代码
值得注意的是AccessibilityManagerService,属于com.android.server.accessibility包下,也就是系统内部类,不能直接用。this
那应该怎么作呢?能够经过AccessibilityManager间接的操做AccessibilityManagerService,由上次分析系统源码可知,系统内部利用Binder机制调用了AccessibilityManagerService,拿到这个列表后遍历出本身的应用正在被谁监控或“辅助”了。spa
看一下怎么施工,向下看,插件
private List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList(String targetPackage) {
List<AccessibilityServiceInfo> result = new ArrayList<>();
AccessibilityManager accessibilityManager = (AccessibilityManager) getApplicationContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (accessibilityManager == null) {
return result;
}
List<AccessibilityServiceInfo> infoList = accessibilityManager.getInstalledAccessibilityServiceList();
if (infoList == null || infoList.size() == 0) {
return result;
}
for (AccessibilityServiceInfo info : infoList) {
if (info.packageNames == null) {
result.add(info);
} else {
for (String packageName : info.packageNames) {
if (targetPackage.equals(packageName)) {
result.add(info);
}
}
}
}
return result;
}
复制代码
知识点:当info.packageNames为null时,表示监控全部包名。code
AccessibilityServices在监控目标App发出的AccessibilityEvent时,对应的做出某些事件操做。好比,AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED。
某些微信红包插件会监控Notification的弹出,那么咱们是否能够随意发送这样的Event出来,从而混干扰外挂插件的运行逻辑,好比
textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
复制代码
大部分的外挂插件对特定类型的事件并非特别感兴趣,他们仅在收到Event后检查页面上是否有某些特定的元素,从而决定是否进行下一步操做。
咱们知道系统内部原理就是调用TextView的findViewsWithText方法,咱们须要重写这个方法就能够
public class QTextView extends android.support.v7.widget.AppCompatTextView {
@Override
public void findViewsWithText(ArrayList<View> outViews, CharSequence searched, int flags) {
outViews.remove(this);
}
}
复制代码
这样AccessibilityServices文案检查将会在这个View上失效。
AccessibilityServices执行点击事件,最终会去调用View的OnClickListener监听事件,那咱们就利用onTouch代替onClick便可。
检测并禁止相关App开启辅助模式;
重写TextView 的 findViewsWithText方法,屏蔽文案检查;
onTouch替换onClick,屏蔽View的点击事件;
随机发送AccessibilityEvent,使外挂执行逻辑错误;
经过PackageManager检测并禁止相关软件安装;
古人云:道高一尺,魔高一丈;下篇见Xposed相关文章。