[笔记]读Android系统篇之----免root实现Hook系统服务拦截方法

第二篇读书笔记

拜读姜维大神的Android系统篇之—-免root实现Hook系统服务拦截方法

梳理了下思路,解决了疑惑

我们使用剪切板服务的时候是调用了ContextImplgetSystemService方法

ContextImplgetSystemService方法

1
2
3
4
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}

该方法将返回Object类型对象,我们将它强制转换为一个ClipboardManager,也就是说它返回了一个ClipboardManager供我们使用。而这个方法最终将构造一个ClipboardManager,在ClipboardManager构造的过程中,将获取远端Binder并调用IClipboard.StubasInterface方法转化为本地代理对象保存在其中,

ClipboardManager的一个构造函数

1
2
3
4
5
6
/** {@hide} */
public ClipboardManager(Context context, Handler handler) throws ServiceNotFoundException {
mContext = context;
mService = IClipboard.Stub.asInterface(
ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE));
}

获取远端Binder的操作其实是调用了ServiceManager中的getService方法,它返回的是一个远端Binder,实际上也就是一个BinderProxy(当然ServiceManager会把这个远端对象缓存到sCache中以应对频繁调用),姜维的文章里就是从这里切入,第一步先动态代理了这个BinderProxy

这里需要铭记一点,远端Binder需要调用StubasInterface方法转化为本地代理对象才能使用(上面说到在ClipboardManager的构造函数中,这一步骤ClipboardManager帮我们封装了这一操作)

ServiceManager中的getService方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Returns a reference to a service with the given name.
*
* @param name the name of the service to get
* @return a reference to the service, or <code>null</code> if the service doesn't exist
*/
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
return Binder.allowBlocking(getIServiceManager().getService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}

下面继续解析姜维的文章中hook的流程,在上一步的动态代理之后,拦截了被代理对象(BinderProxy对象)的queryLocalInterface方法,下面是

BinderProxyqueryLocalInterface的实现

1
2
3
public IInterface queryLocalInterface(String descriptor) {
return null;
}

可以看见它直接返回了null,而这个方法是在哪里被调用的呢,反编译framework.jar发现,是在IClipboard.Stub中,这里IClipboard就是用aidl生成的,和我们自己生成的差不多
看看

IClipboard.StubasInterface方法

1
2
3
4
5
6
7
8
9
10
public static IClipboard asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin == null || !(iin instanceof IClipboard)) {
return new Proxy(obj);
}
return (IClipboard) iin;
}

回到正题,姜维的文章里拦截了queryLocalInterface以后,一开始我以为它又动态代理了一个叫做base的对象,因为这里new了一个HookBinderInvocationHandler,传入的第一个参数就是base突然有点蒙这个base是哪里冒出来的,看看上下文,发现是在第一个动态代理的Handler的构造函数里,传入了一个rawBinder,赋值给了成员变量base了,而这个rawBinder,就是第一次代理中,被代理的那个远端Binder,我就有点纳闷了,代理两次干啥?,仔细想,这只是个构造函数啊,我想传进去什么和我要动态代理什么对象没有关系呀。
于是翻回去看,动态代理的接口是this.iinterface,看了下第一次动态代理的Handler的构造函数,看到

1
this.iinterface = Class.forName("android.content.IClipboard")

仔细想想,这是要搞出来一个IClipboard啊,其实这个IClipboard我们前文接触过了,这里贴上IClipboard部分源码(主要看结构)

1
2
3
4
5
6
7
8
package android.content;
······
public interface IClipboard extends IInterface {

public static abstract class Stub extends Binder implements IClipboard {
······
private static class Proxy implements IClipboard {
······

梳理一遍,
第一次动态代理了远端Binder,Handler是IClipboardHookBinderHandler
在第一次代理的Handler里面,拦截了queryLocalInterface方法,
这个方法是在asInterface里面调用的,
拦截以后,开始第二次动态代理,
IClipboard这个接口合成了一个代理对象,Handler是HookBinderInvocationHandler
把这个合成的代理对象return了!!!

没错,这里是关键,它直接把它作为queryLocalInterface方法的返回值return

看一下原来的BinderProxy中queryLocalInterface的实现

再看一下IClipboard.Stub的asInterface方法

asInterface方法里,我们合成的代理对象,赋值给了iin,接下来

1
2
3
4
5
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin == null || !(iin instanceof IClipboard)) { //关键!!!!!
return new Proxy(obj); //没走这!!!
}
return (IClipboard) iin; //走了这里,我们合成的代理对象被强制转换以后直接返回了,被用来之后进行剪切板的一些操作

哇,几乎哭出来,看了那么久终于懂了关键部分,为什么作者不标记一下呢
/(ㄒoㄒ)/~~

我们比较一下:

hook前:

1
2
3
4
5
6
7
[调用 getSystemService ]
--> [ ClipboardManager 的构造函数]
--> [间接调用了 ServiceManager 中的 getService ]
--> [获得远端 Binder 对象]
--> [调用 IClipboard.Stub 的 asInterface 并把远端对象传入]
--> [获得 IClipboard.Stub.Proxy 对象]
--> [后续使用]

hook后:

1
2
3
4
5
6
7
8
9
10
11
[Hook开始]
--> [主动反射调用 ServiceManager 中的 getService 并动态代理远端对象]
--> [正常调用开始]
--> [调用 getSystemService ]
--> [ ClipboardManager 的构造函数]
--> [间接调用了 ServiceManager 中的 getService ]
--> [获得第一次动态代理生成的对象]
--> [调用 IClipboard.Stub 的 asInterface 并把远端对象传入]
--> [拦截 queryLocalInterface 并合成 IClipboard 接口的代理对象]
--> [返回合成的代理对象]
--> [后续使用]

就这样,两次动态代理,第一次代理远端对象,拦截queryLocalInterface方法,第二次动态代理合成了一个实现了IClipboard接口的对象,骗过了ClipboardManager