1. 簡述
Binder Hook 可以 Hook 掉當(dāng)前進程用到的系統(tǒng) Service 服務(wù)亦鳞。
以 LocationManager 為例,在獲取一個 LocationManager 時分為兩步:
(1) 獲取 IBinder 對象迅矛;
(2) 通過 IBinder 的 asInterface()
方法轉(zhuǎn)化為 LocationMangerService
對象妨猩,接著初始化 LocationManager
。
application 層用到的都是 LocationManager 對象秽褒。
原理:
- ServiceManager 在首次獲取某個 Service 的 Binder 后册赛,會把 Binder 對象緩存在
ServiceManager#sCahce
映射表中。后續(xù)再獲取時震嫉,會先檢查sCache
中是否已經(jīng)存在緩存對象,如果有則返回這個緩存對象牡属。所以我們可以通過反射的方式票堵,往sCahce
中 put 一個自定義的 Binder,這樣獲取到的 Binder 對象就會是我們自定義的 Binder 了逮栅。 - 上層代碼在調(diào)用 LocationManager 時悴势,用到的都是 Service 對象,這個對象是在
ILocationManager.Stub.asInterface(CustomBinder)
方法返回的措伐。asInterface(customeBinder)
最終會調(diào)用到CustomBinder # queryLocalInterface()
方法特纤,我們需要重寫這個方法,返回自定義的 Service 對象侥加。
整個過程需要利用反射設(shè)置一個自定義的 Binder 對象和一個自定義的 Service 對象捧存。由于我們只 Hook 其中一部分的功能,其他功能還需要保留担败,所以要用動態(tài)代理的方式創(chuàng)建自定義對象昔穴。
在理解后面的內(nèi)容前你需要了解這些知識點:
- 一點點 Binder 的知識,知道 IBinder 轉(zhuǎn) IInterface 的大致流程提前;
- Java 的動態(tài)代理吗货。
2. Context 獲取系統(tǒng) Service 的流程
Activity 等類在獲取系統(tǒng) Service 時,都是調(diào)用 getSystemService(serviceName)
方法獲取的狈网。
這是一段不太重要的過程:
Context # getSystemService()
方法調(diào)用了SystemServiceRegistry # getSystemService()
方法宙搬。
SystemServiceRegistry
中有一個常量SYSTEM\_SERVICE\_FETCHERS
笨腥,這是一個 Map。保存了 ServiceName 和對應(yīng)的 ServiceFetcher勇垛。ServiceFetcher
是用于創(chuàng)建具體 Service 對象的類脖母。ServiceFetcher
的關(guān)鍵方法是createService()
方法。
在ServiceFetcher # createService()
方法中窥摄,調(diào)用了ServiceManager.getService(name)
方法镶奉。
Context # getSystemService()
方法最終會調(diào)用到 ServiceManager # getService()
方法中。以 LocationManager 對應(yīng)的 ServiceFetcher 為例崭放,它的 createService() 方法源碼如下:
// Android 8.0 android.app.SystemServiceRegistry.java
@Override
public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
}
假如我們要 Hook 掉 LocationManager # getLastKnownLocation()
方法(下文都是)哨苛。我們要做的就是讓
ServiceManager.getService(Context.LOCATION_SERVICE)
返回我們自定義的 Binder 對象。
先看一下這個方法的源碼:
// Android 8.0 android.os.ServiceManager.java
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;
}
sCache 是一個 Map币砂,緩存了已經(jīng)向系統(tǒng)請求過的 Binder建峭。如果需要讓這個方法返回我們自己的 binder 對象,只需要事先往 sCache 中 put 一個自定義的 Binder 對象就行了决摧。
在 put 之前亿蒸,需要先創(chuàng)建出一個自定義的 Binder。這個 Binder 在被 ILocationManager.Stub.asInterface 處理后掌桩,可以返回一個自定義的 LocationManagerService 對象边锁。
先看一下 Binder 的 asInterface() 的實現(xiàn):
// Android 8.0 android.location.ILocationManager.java
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()方法返回一個自定義的Service,使得走 if 語句內(nèi)部波岛,不走 else茅坛,那就算是Hook 成功了。
誤區(qū):
asInterface(binder) 就是把 binder 做了一次類型轉(zhuǎn)換
實際上XXX service = XXX.Stub.asInterface(binder)
的返回值根據(jù) binder 的來源有兩種情況:
- 跨進程時则拷,service 的類型是 XXX.Stub.Proxy
-
相同進程時贡蓖,service 的類型是 XXX.Stub
XXX.Stub.asInterface(binder);得到的返回值并不一定是binder自己,并且調(diào)用系統(tǒng)服務(wù)時肯定不是binder自己煌茬。
3 創(chuàng)建自定義的 Service 和 Binder 對象
3.1 自定義的 Service 對象
假設(shè)我們想讓系統(tǒng)的 LocationManager 返回的位置信息全是在天安門(116.23, 39.54)斥铺。那我們需要使得 LocatitionManagerService 的 getLastLocation() 方法 返回的全是 (116.23, 39.54)。
由于我們不能直接拿到系統(tǒng)的這個Service對象坛善,可以先用反射的方式拿到系統(tǒng)的LocationManagerService晾蜘。然后攔截 getLastLocation() 方法。
import android.location.Location;
import android.location.LocationManager;
import android.os.IBinder;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 動態(tài)代理時用到的 Handler
* @author ZHP
* @since 16/12/25 17:36
*/
public class ServiceHookHandler implements InvocationHandler {
private Object mOriginService;
/**
* @param binder 系統(tǒng)原始的Binder對象
*/
@SuppressWarnings("unchecked")
public ServiceHookHandler(IBinder binder) {
try {
// 由于可以拿到 Binder 對象,但無法拿到 Service 的對象, 所以我們要手動獲取
// 即: 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 對象
原生的Binder對象在調(diào)用 queryLocalInterface() 方法時會返回原生的Service對象浑吟。我們希望返回3.1中的自定義Service笙纤。所以這里攔截 queryLocalInterface() 方法。
import android.os.IBinder;
import android.os.IInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 動態(tài)代理時用到的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 了~
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省容,動態(tài)代理了 queryLocalInterface 方法。
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. 測試
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();
}
/**
* 請求定位信息
*/
private void requestLocation() {
// 定位權(quán)限檢測
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("鄭海鵬", "沒有定位權(quán)限");
Toast.makeText(this, "沒有定位權(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)用的時候燎字,Toast和Log都會顯示天安門的坐標(biāo)(116.23, 39.54)腥椒。證明Hook成功阿宅!
你甚至可以用Binder Hook的方式Hook掉 ActivityManager。
團隊博客同名文章:http://www.reibang.com/p/fcb832a2b411
轉(zhuǎn)載必須注明出處:http://www.reibang.com/p/5c2c3fc4286b`