在插件中,需要禁止插件調用系統原生接口持有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);
}
}
}
源碼連結