AccessibilityService防禦

奇舞移動發表於2019-01-04

前面講解了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

相關文章