Binder
是Android
系统的通讯桥梁。因此Hook Binder
就能够改变通讯行为.分析Hook
过程有利于理解Binder
机制
系统服务获取 1 ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE)
通过以上代码能够获取系统服务。我们来看看内部是如何获取的
1 2 3 4 5 6 7 8 9 @Override public Object getSystemService (String name) { return SystemServiceRegistry.getSystemService(this , name); } @Override public String getSystemServiceName (Class<?> serviceClass) { return SystemServiceRegistry.getSystemServiceName(serviceClass); }
ContextImpl
中通过SystemServiceRegistry
获取服务
1 2 3 4 5 6 7 8 public static Object getSystemService (ContextImpl ctx, String name) { ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name); return fetcher != null ? fetcher.getService(ctx) : null ; } public static String getSystemServiceName (Class<?> serviceClass) { return SYSTEM_SERVICE_NAMES.get(serviceClass); }
通过Class
或者类限定名都可以获取对应的服务
1 2 3 4 private static final HashMap<Class<?>, String> SYSTEM_SERVICE_NAMES = new HashMap <Class<?>, String>(); private static final HashMap<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new HashMap <String, ServiceFetcher<?>>();
这里有两个HashMap
,一个用于缓存服务名称,一个用于缓存服务获取器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static { registerService(Context.ACTIVITY_SERVICE, ActivityManager.class, new CachedServiceFetcher <ActivityManager>() { @Override public ActivityManager createService (ContextImpl ctx) { return new ActivityManager (ctx.getOuterContext(), ctx.mMainThread.getHandler()); }}); } private static <T> void registerService (String serviceName, Class<T> serviceClass, ServiceFetcher<T> serviceFetcher) { SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName); SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher); }
服务在首次使用的时候已经被初始化了。并且初始化时候会被缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 static abstract class CachedServiceFetcher <T> implements ServiceFetcher <T> { private final int mCacheIndex; public CachedServiceFetcher () { mCacheIndex = sServiceCacheSize++; } @Override @SuppressWarnings("unchecked") public final T getService (ContextImpl ctx) { final Object[] cache = ctx.mServiceCache; synchronized (cache) { Object service = cache[mCacheIndex]; if (service == null ) { try { service = createService(ctx); cache[mCacheIndex] = service; } catch (ServiceNotFoundException e) { onServiceNotFound(e); } } return (T)service; } } public abstract T createService (ContextImpl ctx) throws ServiceNotFoundException; }
每次CacheServiceFetcher
创建时,全局的服务缓存空间会增大,并将新的服务放到数组末端。每次需要获取服务的时候,先寻找缓存中的服务,如果不存在创建并缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton <IActivityManager>() { @Override protected IActivityManager create () { final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE); final IActivityManager am = IActivityManager.Stub.asInterface(b); return am; } }; 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 ; }
在ActivityManager
中可以看到ActivityManagerService
服务的获取方式,从ServiceManager
中获取。在ServiceManager
中,会先从缓冲中获取。
Hook IBinder 从上述流程中可以了解系统服务的获取过程,那么我们需要怎么去Hook
这些服务呢?我们可以将asInterface
返回的结果修改成我们Hook
过的对象。下面我们以ClipboardService
为例来实践Hook
过程
1 2 3 4 5 public ClipboardManager (Context context, Handler handler) throws ServiceNotFoundException { mContext = context; mService = IClipboard.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.CLIPBOARD_SERVICE)); }
ClipboardManager
在初始化的时候,从ServiceManager
获取ClipboardService
。从这里可以看出asInterface
需要一个IBinder
参数,这个参数从ServiceManager
获取。而在ServiceManager
中会首先从缓存中查找。因此我们可以提前往缓存冲插入我们Hook
的对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SuppressLint("PrivateApi" ) class BinderHookHelperKt { companion object { @Throws(Exception::class) fun hookClipboardService () { val CLIPBOARD_SERVICE = "clipboard" val serviceManager = Class.forName("android.os.ServiceManager" ) val getService = serviceManager.getDeclaredMethod("getService" , String::class .java) val rawBinder = getService.invoke(null , CLIPBOARD_SERVICE) as IBinder val hookedBinder = Proxy.newProxyInstance(serviceManager.classLoader, arrayOf(IBinder::class .java), BinderProxyHookHandlerKt(rawBinder)) val cacheField = serviceManager.getDeclaredField("sCache" ) cacheField.isAccessible = true val cache = cacheField.get (null ) as HashMap<String, IBinder> cache.put(CLIPBOARD_SERVICE, hookedBinder as IBinder) } } }
获取ServiceManager
的getService
方法,返回一个IBinder
对象。然后通过动态代理,代理此IBinder
对象。然后将插入ServiceManager
的缓存中。这样下次使用时就能够直接拿到这个代理对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class BinderProxyHookHandlerKt (val base: IBinder) : InvocationHandler { companion object { val TAG = BinderProxyHookHandlerKt::class .java.canonicalName } var stub = Any() var iinterface = Any() init { try { stub = Class.forName("android.content.IClipboard\$Stub" ) iinterface = Class.forName("android.content.IClipboard" ) } catch (e: Exception) { e.printStackTrace() } } override fun invoke (proxy: Any ?, method: Method ?, args: Array <out Any >?) : Any { if ("queryLocalInterface" == method!!.name) { Log.d(TAG, "hook queryLocalInterface" ) val classArray = arrayOf(iinterface as Class<Any>) return Proxy.newProxyInstance((proxy as Any).javaClass.classLoader, classArray, BinderHookHandlerKt(base, stub as Class<Any>)) } Log.d(TAG, "method: " + method.name) return method.invoke(base, args) } }
如果IBinder
的queryLocalInterface
触发,那么通过动态代理,代理IClipboard
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class BinderHookHandlerKt (base: IBinder, stubClass: Class<Any>) : InvocationHandler { companion object { val TAG = BinderHookHandlerKt::class .java.canonicalName } var obj = Any() init { try { val asInterfaceMethod: Method = stubClass.getDeclaredMethod("asInterface" , IBinder::class .java) obj = asInterfaceMethod.invoke(null , base) } catch (e: Exception) { throw RuntimeException("hooked failed" ) } } override fun invoke (proxy: Any ?, method: Method ?, args: Array <out Any >?) : Any { if ("getPrimaryClip" == method!!.name) { Log.d(TAG, "hook getPrimaryClip" ) return ClipData.newPlainText(null , "you have been hooked" ) } if ("hasPrimaryClip" == method.name) { return true } return method.invoke(obj, args) } }
获取IClipboard$Stub
的asInterface
方法,返回BinderProxy
。当getPrimaryClip
方法触发时,返回固定值。当hasPrimaryClip
触发时,总是返回true
表示粘贴板有内容。这样就成功的hook ClipboardService
运行程序之后,使用粘贴功能,会发现永远只能粘贴我们自己返回的内容
整个过程的思路是:ServiceManager
内部一张表管理着很多的Binder
对象。我们需要Hook
某个Binder
对象的queryLocalInterface
,并将其缓存。由于ServiceManager
的缓存表里的IBinder
大部分都是BinderProxy
对象。当使用时会调用asInterface
转换成需要的接口。