package com.weishu.binder_hook.app;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import android.os.IBinder;
import android.util.Log;
/**
* 由于ServiceManager里面的sCache里面存储的 IBinder类型基本上都是BinderProxy
* 因此, ServiceManager的使用者调用getService之后不会直接使用这个map
* 而是先将他使用asInterface转成需要的接口
* <p/>
* asInterface函数的代码告诉我们, 它会先使用这个BinderPRoxy查询本进程是否有Binder对象
* 如果有就使用本地的, 这里恰好就是一个hook点
* <p/>
* 我们让所有的查询都返回一个"本地Binder"对象
* <p/>
* 当然,这是一个假象, 我们给它返回的Binder对象自然是符合要求的(要么是本地Binder,要么是Binder代理)
* 只不过,我们对需要hook的API做了处理
* <p/>
* 这个类仅仅Hook掉这个关键的 queryLocalInterface 方法
*
* @author weishu
* @date 16/2/15
*/
public class BinderProxyHookHandler implements InvocationHandler {
private static final String TAG = "BinderProxyHookHandler";
// 绝大部分情况下,这是一个BinderProxy对象
// 只有当Service和我们在同一个进程的时候才是Binder本地对象
// 这个基本不可能
IBinder base;
Class<?> stub;
Class<?> iinterface;
public BinderProxyHookHandler(IBinder base) {
this.base = base;
try {
this.stub = Class.forName("android.content.IClipboard$Stub");
this.iinterface = Class.forName("android.content.IClipboard");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("queryLocalInterface".equals(method.getName())) {
Log.d(TAG, "hook queryLocalInterface");
// 这里直接返回真正被Hook掉的Service接口
// 这里的 queryLocalInterface 就不是原本的意思了
// 我们肯定不会真的返回一个本地接口, 因为我们接管了 asInterface方法的作用
// 因此必须是一个完整的 asInterface 过的 IInterface对象, 既要处理本地对象,也要处理代理对象
// 这只是一个Hook点而已, 它原始的含义已经被我们重定义了; 因为我们会永远确保这个方法不返回null
// 让 IClipboard.Stub.asInterface 永远走到if语句的else分支里面
return Proxy.newProxyInstance(proxy.getClass().getClassLoader(),
// asInterface 的时候会检测是否是特定类型的接口然后进行强制转换
// 因此这里的动态代理生成的类型信息的类型必须是正确的
// 这里面Hook的是一个BinderProxy对象(Binder代理) (代理Binder的queryLocalInterface正常情况下是返回null)
// 因此, 正常情况下 在asInterface里面会由于BinderProxy的queryLocalInterface返回null导致系统创建一个匿名的代理对象, 这样我们就无法控制了
// 所以我们要伪造一个对象, 瞒过这个if检测, 使得系统把这个queryLocalInterface返回的对象透传给asInterface的返回值;
// 检测有两个要求, 其一: 非空, 其二, IXXInterface类型。
// 所以, 其实返回的对象不需要是Binder对象, 我们把它当作普通的对象Hook掉就ok(拦截这个对象里面对于IXXInterface相关方法的调用)
// tks jeremyhe_cn@qq.com
new Class[] { this.iinterface },
new BinderHookHandler(base, stub));
}
Log.d(TAG, "method:" + method.getName());
return method.invoke(base, args);
}
}