天天看點

談Android PowerManager的Hook技術

在插件中,需要禁止插件調用系統原生接口持有WakeLock,Hook技術是不錯的解決方案。

通過閱讀系統源碼,可以了解到PowerManager持有WakeLock操作最後都通過Binder跨程序調到系統服務中,我們隻要攔截這個Binder的所有調用即可。

思路是先通過反射拿到這個IPowerManager的Binder,然後動态生成該Binder的代理對象,再覆寫原有的Binder即可。

public class PowerManagerHook {

    public static void hook(Context context) throws Throwable {
        PowerManager manager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        Field field = FieldUtils.getField(PowerManager.class, "mService", true);

        BinderHook hook = new BinderHook(field.get(manager), new BinderHook.BinderHookInvoker() {
            @Override
            public Object onInvoke(Object original, Method method, Object[] args) throws Throwable {
                return method.invoke(original, args);
            }
        });

        field.set(manager, hook.proxyInterface);
    }
}
           

以下BinderHook是一個關于Binder Hook的通用類,可用于各類Android系統服務的Binder的Hook。

接下來介紹一下Binder Hook的核心原理,我們拿到Binder對象之後,首先要将其轉成業務接口類,通常是通過Stub.asInterface,而Binder對象可能是Binder實體,也可能是Binder Proxy,然而不管怎樣它們都是IBinder對象。Stub.asInterface的實作是先通過queryLocalInterface擷取本地接口,如果傳回非空,則表示目前程序和Binder實體是同一個程序,直接傳回本地接口即可。如果傳回為空,則表示目前程序和Binder實體分屬不同程序,此時會将BinderProxy封裝一層業務接口傳回。是以可見這裡的關鍵在于queryLocalInterface函數,這個函數屬于IBinder的接口,是以我們拿到Binder對象之後,生成一個代理對象,攔截這個queryLocalInterface,傳回我們生成的代理接口。

public class BinderHook {

    public Class<?> binderIntfClazz;

    public Object originalInterface;

    public IBinder originalBinder;

    public Object proxyInterface;

    public IBinder proxyBinder;

    public BinderHook(Object object, BinderHookInvoker invoker) {
        this.originalInterface = object;
        this.binderIntfClazz = BinderUtils.getBinderInterface(object);
        this.originalBinder = getOriginalBinder();
        this.proxyInterface = getProxyInterface(invoker);
        this.proxyBinder = getProxyBinder();
    }

    private IBinder getOriginalBinder() {
        Method method = MethodUtils.getAccessibleMethod(binderIntfClazz, "asBinder");
        try {
            return (IBinder) method.invoke(originalInterface);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return null;
    }

    private IBinder getProxyBinder() {
        return (IBinder) Proxy.newProxyInstance(IBinder.class.getClassLoader(),
                new Class<?>[]{IBinder.class}, new BinderHookHandler(originalBinder, "Binder") {
                    @Override
                    public Object onInvoke(Object original, Method method, Object[] args) throws Throwable {
                        if (method.getName().equals("queryLocalInterface")) {
                            return proxyInterface;
                        }
                        return method.invoke(original, args);
                    }
                });
    }

    private Object getProxyInterface(final BinderHookInvoker invoker) {
        return Proxy.newProxyInstance(originalInterface.getClass().getClassLoader(),
                ClassUtils.getAllInterfaces(originalInterface.getClass()).toArray(new Class<?>[]),
                new BinderHookHandler(originalInterface, "Interface") {
                    @Override
                    public Object onInvoke(Object original, Method method, Object[] args) throws Throwable {
                        return invoker.onInvoke(original, method, args);
                    }
                });
    }

    public interface BinderHookInvoker {
        Object onInvoke(Object original, Method method, Object[] args) throws Throwable;
    }

    abstract static class BinderHookHandler implements InvocationHandler, BinderHookInvoker {

        Object originalObject;

        String tag;

        public BinderHookHandler(Object originalObject, String tag) {
            this.tag = tag;
            this.originalObject = originalObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (Object.class == method.getDeclaringClass()) {
                String name = method.getName();
                if ("equals".equals(name)) {
                    return proxy == args[];
                } else if ("hashCode".equals(name)) {
                    return System.identityHashCode(proxy);
                } else if ("toString".equals(name)) {
                    return proxy.getClass().getName() + "@" +
                            Integer.toHexString(System.identityHashCode(proxy));
                } else {
                    throw new IllegalStateException(String.valueOf(method));
                }
            }
            return onInvoke(originalObject, method, args);
        }
    }
}
           

源碼連結