1. 程式人生 > >AccessibilityService防禦

AccessibilityService防禦

前面講解了AccessibilityService知多少,詳細描述了使用方法已經內部的原理,這節主要是防禦手段。在網上也找到了很多資料,作為參考。下面就簡單的說一說。

1、檢測輔助模式的開啟

之前提到過AccessibilityService類使用的是觀察者模式,通過Binder機制在系統App1 view層->os->App2Service進行事件傳遞。由AccessibilityManagerService註冊AccessibilityService,那如何檢測到安裝並啟用輔助模式App2呢?系統提供瞭如下方法:

@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,還是很貼心的。

返回值AccessibilityServiceInfo是一些我們使用的AccessibilityService的配置資訊,包括packageNames(AccessibilityService 監控哪些package發出的Event),如下:

java

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);
複製程式碼

xml

<?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包下,也就是系統內部類,不能直接用。

那應該怎麼做呢?可以通過AccessibilityManager間接的操作AccessibilityManagerService,由上次分析系統原始碼可知,系統內部利用Binder機制呼叫了AccessibilityManagerService,拿到這個列表後遍歷出自己的應用正在被誰監控或“輔助”了。

看一下怎麼施工,向下看,

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時,表示監控所有包名。

2、干擾執行邏輯

AccessibilityServices在監控目標App發出的AccessibilityEvent時,對應的作出某些事件操作。比如,AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED。

某些微信紅包外掛會監控Notification的彈出,那麼我們是否可以隨意傳送這樣的Event出來,從而混干擾外掛外掛的執行邏輯,比如

textView.sendAccessibilityEvent(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
複製程式碼

大部分的外掛外掛對特定型別的事件並不是特別感興趣,他們僅在收到Event後檢查頁面上是否有某些特定的元素,從而決定是否進行下一步操作。

3、遮蔽文案檢查

我們知道系統內部原理就是呼叫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上失效。

4、遮蔽點選事件

AccessibilityServices執行點選事件,最終會去呼叫View的OnClickListener監聽事件,那我們就利用onTouch代替onClick即可。

總結

檢測並禁止相關App開啟輔助模式;

重寫TextView 的 findViewsWithText方法,遮蔽文案檢查;

onTouch替換onClick,遮蔽View的點選事件;

隨機發送AccessibilityEvent,使外掛執行邏輯錯誤;

通過PackageManager檢測並禁止相關軟體安裝;

古人云:道高一尺,魔高一丈;下篇見Xposed相關文章。

關注微信公眾號,最新技術乾貨實時推送

image