前面講解了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相關文章。