Android Binder Hook的實(shí)現(xiàn)

1. 簡(jiǎn)述

Binder Hook可以Hook掉當(dāng)前App用到的系統(tǒng)Service服務(wù)。
以LocationManager為例,在獲取一個(gè)LocationManager時(shí)分為兩步。第一,獲取IBinder對(duì)象;第二:IBinder對(duì)象通過(guò)asInterface()轉(zhuǎn)化為L(zhǎng)ocationMangerService對(duì)象。最后初始化LocationManager,application層用到的都是LocationManager。
Hook的大致原理是:ServiceManager在獲取某個(gè)Binder時(shí),如果本地有緩存的Binder,就不再跨進(jìn)程請(qǐng)求Binder了。我們可以在緩存中加入自己的Binder,使得ServiceManager查詢本地緩存時(shí)得到一個(gè)自定義的CustomBinder對(duì)象,不再跨進(jìn)程向系統(tǒng)請(qǐng)求。并且ILocationManager.Stub.asInterface(CustomBinder)方法返回我們自定義的Service對(duì)象。
這里面有兩個(gè)地方需要用到自定義的對(duì)象。由于我們只Hook其中一部分的功能,其他功能還需要保留,所以用動(dòng)態(tài)代理的方式創(chuàng)建自定義的Binder和自定義的Service。

在理解后面的內(nèi)容前你需要了解這些知識(shí)點(diǎn):

  1. 一點(diǎn)點(diǎn)Binder的知識(shí),知道IBinder轉(zhuǎn)IInterface的大致流程;
  2. Java的動(dòng)態(tài)代理。

2. Context 獲取系統(tǒng) Service 的流程

Activity等類在獲取系統(tǒng)Service時(shí),都是調(diào)用getSystemService(serviceName)方法獲取的。

Title: Context 獲取系統(tǒng)Service的流程
Context->SystemServiceRegistry: getSystemService(name)
SystemServiceRegistry->ServiceFetcher: getSystemService(name)
ServiceFetcher->ServiceManager: getService(contetx)
ServiceManager->return: getService(name)

Context 的 getSystemService() 方法調(diào)用了 SystemServiceRegistry 的 getSystemService() 方法。
SystemServiceRegistry 中有一個(gè)常量 SYSTEM_SERVICE_FETCHERS,這是一個(gè)Map。保存了ServiceName和對(duì)應(yīng)的ServiceFetcher。ServicFetcher是用于創(chuàng)建具體Service的類。ServiceFetcher 的關(guān)鍵方法是 createService() 方法。
在 ServiceFetcher 的 createService() 方法中,調(diào)用了 ServiceManager.getService(name)方法。以 LocationManager 對(duì)應(yīng)的 ServiceFetcher 為例,它的createService()方法源碼如下:

@Override
public LocationManager createService(ContextImpl ctx) {
    IBinder b = ServiceManager.getService("location");
    return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}

假如我們要修改 LocationManager 的 getLastKnownLocation() 方法(下文都是)。我們要做的就是讓ServiceManager.getService("location")返回我們自定義的Binder。先看一下這個(gè)方法簡(jiǎn)化后的源碼:

public static IBinder getService(String name) {
    IBinder service = sCache.get(name);
    if (service != null) {
        return service;
    } else {
        return getIServiceManager().getService(name);
    }
}

sCache是一個(gè)Map,緩存了已經(jīng)向系統(tǒng)請(qǐng)求過(guò)的Binder。如果我們需要讓這個(gè)方法返回我們我們自己的binder,只需要事先往sCache中put一個(gè)自定義的Binder就行了。
在put之前,需要先創(chuàng)建出一個(gè)自定義的Binder。這個(gè)Binder在被 ILocationManager.Stub.asInterface 處理后,可以返回一個(gè)自定義的 LocationManagerService。
先看一下Binder的 asInterface() 的實(shí)現(xiàn):

public static android.location.ILocationManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof android.location.ILocationManager))) {
        return ((android.location.ILocationManager) iin);
    }
    return new android.location.ILocationManager.Stub.Proxy(obj);
}

如果把 queryLocalInterface()方法返回一個(gè)自定義的Service,使得走if語(yǔ)句內(nèi)部,不走else,那就算是Hook成功了。

誤區(qū): asInterface(binder) 就是把 binder 做了一次類型轉(zhuǎn)換
實(shí)際上XXX service = XXX.Stub.asInterface(binder)的返回值根據(jù) binder 的來(lái)源有兩種情況:

  1. 跨進(jìn)程時(shí),service 的類型是 Test.Stub.Proxy
  2. 相同進(jìn)程時(shí),service 的類型是 Test.Stub
    X.Stub.asInterface(binder);得到的返回值并不一定是binder自己,并且調(diào)用系統(tǒng)服務(wù)時(shí)肯定不是binder自己。

3 創(chuàng)建自定義的Service和Binder

3.1 自定義的Service對(duì)象

假設(shè)我們想讓系統(tǒng)的LocationManager返回的位置信息全是在天安門(116.23, 39.54)。那我們需要使得 LocatitionManagerService 的 getLastLocation() 方法 返回的全是 (116.23, 39.54)。
由于我們不能直接拿到系統(tǒng)的這個(gè)Service對(duì)象,可以先用反射的方式拿到系統(tǒng)的LocationManagerService。然后攔截getLastLocation()方法。

package com.zhp.binderhook;

import android.location.Location;
import android.location.LocationManager;
import android.os.IBinder;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 動(dòng)態(tài)代理時(shí)用到的Handler
 * @author ZHP
 * @since 16/12/25 17:36
 */

public class ServiceHookHandler implements InvocationHandler {

    private Object mOriginService;

    /**
     * @param binder 系統(tǒng)原始的Binder對(duì)象
     */
    @SuppressWarnings("unchecked")
    public ServiceHookHandler(IBinder binder) {
        try {
            // 由于可以拿到Binder對(duì)象,但無(wú)法拿到Service的對(duì)象,所以我們要手動(dòng)獲取
            // 即: this.mOriginService = ILocationManager.Stub.asInterface(binder);
            Class ILocationManager$Stub = Class.forName("android.location.ILocationManager$Stub");
            Method asInterface = ILocationManager$Stub.getDeclaredMethod("asInterface", IBinder.class);
            this.mOriginService = asInterface.invoke(null, binder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        switch(method.getName()) {
            case "getLastLocation":
                // 一直返回天安門的坐標(biāo)
                Location location = new Location(LocationManager.GPS_PROVIDER);
                location.setLongitude(116.23);
                location.setLatitude(39.54);
                return location;

            default:
                return method.invoke(this.mOriginService, args);
        }
    }
}

3.2 自定義的Binder對(duì)象

原生的Binder對(duì)象在調(diào)用 queryLocalInterface() 方法時(shí)會(huì)返回原生的Service對(duì)象。我們希望返回3.1中的自定義Service。所以這里攔截 queryLocalInterface() 方法。

package com.zhp.binderhook;

import android.os.IBinder;
import android.os.IInterface;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 動(dòng)態(tài)代理時(shí)用到的Handler,用于創(chuàng)建自定義Binder
 * @author ZHP
 * @since 16/12/25 17:36
 */

public class BinderHookHandler implements InvocationHandler {

    private IBinder mOriginBinder;

    private Class ILocationManager;

    @SuppressWarnings("unchecked")
    public BinderHookHandler(IBinder binder) {
        this.mOriginBinder = binder;
        try {
            this.ILocationManager = Class.forName("android.location.ILocationManager");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        switch (method.getName()) {
            // 使得返回自定義的Service
            case "queryLocalInterface":
                ClassLoader classLoader = mOriginBinder.getClass().getClassLoader();
                Class[] interfaces = new Class[] {IInterface.class, IBinder.class, ILocationManager};
                ServiceHookHandler handler = new ServiceHookHandler(this.mOriginBinder);
                return Proxy.newProxyInstance(classLoader, interfaces, handler);

            default:
                return method.invoke(mOriginBinder, args);

        }
    }
}

4. 將自定義的Binder注入到ServiceManager中

有了自定義的Binder后,將它注入到ServiceManger的sCache變量中就完成Hook了~

package com.zhp.binderhook;

import android.content.Context;
import android.os.IBinder;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

/**
 * @author ZHP
 * @since 16/12/25 17:56
 */

public class HookManager {

    @SuppressWarnings("unchecked")
    public static boolean hookLocationManager() {
        try {
            // 1. 獲取系統(tǒng)自己的Binder
            Class ServiceManager = Class.forName("android.os.ServiceManager");
            Method getService = ServiceManager.getDeclaredMethod("getService", String.class);
            IBinder binder = (IBinder) getService.invoke(null, Context.LOCATION_SERVICE);

            // 2. 創(chuàng)建我們自己的Binder
            ClassLoader classLoader = binder.getClass().getClassLoader();
            Class[] interfaces = {IBinder.class};
            BinderHookHandler handler = new BinderHookHandler(binder);
            IBinder customBinder = (IBinder) Proxy.newProxyInstance(classLoader, interfaces, handler);

            // 3. 獲取ServiceManager中的sCache
            Field sCache = ServiceManager.getDeclaredField("sCache");
            sCache.setAccessible(true);
            Map<String, IBinder> cache = (Map<String, IBinder>) sCache.get(null);

            // 4. 將自定義的Binder替換掉舊的系統(tǒng)Binder
            cache.put(Context.LOCATION_SERVICE, customBinder);
            sCache.setAccessible(false);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

5. 測(cè)試

package com.zhp.binderhook;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Hook
        HookManager.hookLocationManager();
    }

    /**
     * 請(qǐng)求定位信息
     */
    private void requestLocation() {
        // 定位權(quán)限檢測(cè)
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            Log.i("鄭海鵬", "沒(méi)有定位權(quán)限");
            Toast.makeText(this, "沒(méi)有定位權(quán)限", Toast.LENGTH_SHORT).show();
            return;
        }

        // 獲取位置并顯示
        LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
        Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        String message = "(" + location.getLongitude() + ", " + location.getLatitude() + ")";
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
        Log.i("鄭海鵬", message);
    }

    public void onClick(View view) {
        this.requestLocation();
    }
}

當(dāng)onClick被調(diào)用的時(shí)候,Toast和Log都會(huì)顯示天安門的坐標(biāo)(116.23, 39.54)。證明Hook成功!

你甚至可以用Binder Hook的方式Hook掉 ActivityManager


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容