Binder Proxy技術方案
Binder Proxy技術方案
作者 低端碼農
時間 2014.08.23
0x0
看到有多朋友嘗試通過hook系統進程system_process的ioctl,以企圖截獲系統的IPC通訊。這個方案的弊端是太偏低層了,當截獲成功了之後,要解析其中的通訊資料是比較麻煩了. 另外,其中還涉及一堆相容性的問題,由於不同的Android韌體版本,有好些Parcelable結構的欄位是有所改動,這些變化如果ioctl裡做解析,都需要一一顧及到才行,因此這個方案我認為距離產品化還有一段距離。
0x1
在Android上,所有系統的NativeService主要儲存在system_process和com.android.phone這兩個進程,比如我們經常看到的ActivityManagerService(AMS)。這些NativeService大部分都是繼承自Binder的Java對象,沒錯,他們是Java對象。
我們知道,Android上的大部分IPC通訊都是通過其特有的binder模組進行的,因此上述的Java的NativeService要跟核心進行通訊,因為只有c/c++才能直接跟核心互動,因此這裡必然存在JNI通訊。比如AMS的設計:
AMS繼承Binder(IBinder的子類,封裝了IPC通訊公用部分的邏輯),Binder裡儲存著一個類型為int的mObject的欄位,這個欄位正是其C++對象JavaBBinder對象的地址,這個JavaBBinder才是AMS最終跟核心通訊的對象。代碼如下:
public class Binder implements IBinder { //... /* mObject is used by native code, do not remove or rename */ private int mObject; //這個對象儲存的就是JavaBBinder的指標 private IInterface mOwner; private String mDescriptor; //...}
同樣的,在JavaBBinder中,也儲存著一個類型jobject的mObject,指向上層Java對象。看看JavaBBinder的代碼:
class JavaBBinder : public BBinder{//... jobject object() const { return mObject; } //... private: JavaVM* const mVM; jobject const mObject; //這個儲存的是AMS的引用 };}
Java和C++就是通過這兩個欄位相互連結在一起的。
其中JavaBBinder中的mObject是整個IPC關鍵的一節,所有的client請求,都是先到達JavaBBinder,然後JavaBBinder再通過JNI調用mObject的execTransact的方法,最終把請求發送到AMS。
因此,我們只要想辦法找到AMS的對象的JavaBBinder,再把mObject替換為代理對象(記作ProxyBinder,這個對象是我們實現的Java對象),就可以實現Binder Proxy了,下面是:
0x2
在Java層,要擷取AMS引用,通過ServiceManager即可,不過這類是隱藏類,通過反射才可以調用。通過ServiceManager.getService("activity")即可以拿到AMS。
在Native,要擷取AMS所對應的JavaBBinder地址,defaultServiceManager的getService方法擷取到。
接下來就是要替換mObject對象了,JavaBBinder的mObject對象並不能直接替換,因為mObject是const的,我寫了一個DummyJavaBBinder的類,可以很容易地處理好這個問題,DummyJavaBBinder的實現如下:
class DummyJavaBBinder : public BBinder{public: virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) { return NO_ERROR; } jobject object() const { return mObject; } JavaVM* javaVM() const { return mVM; } void changeObj(jobject newobj){ const jobject* p_old_obj = &mObject; jobject* p_old_obj_noconst = const_cast<jobject *>(p_old_obj); *p_old_obj_noconst = newobj; }private: JavaVM* const mVM; jobject const mObject;};
0x3
由於BinderProxy截獲的層次比較高,因此請求資料結構的解析都非常方便,所使用介面大部都是Framework的介面,相容性也不存在,下面是我寫的一個樣本,樣本主要是實現如何讓某個pkg免殺:
private static final class ProxyActivityManagerServcie extends Binder { private static final String CLASS_NAME = "android.app.IActivityManager"; private static final String DESCRIPTOR = "android.app.IActivityManager"; private static final int s_broadcastIntent_code; static { if (ReflecterHelper.setClass(CLASS_NAME)) { s_broadcastIntent_code = ReflecterHelper.getStaticIntValue("BROADCAST_INTENT_TRANSACTION", -1); } else { s_broadcastIntent_code = -1; } } private IBinder mBinder; public ProxyActivityManagerServcie(IBinder binder) { mBinder = binder; mResorter = new SmsReceiverResorter(binder); } @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { /** * * public int broadcastIntent(IApplicationThread caller, * Intent intent, String resolvedType, IIntentReceiver resultTo, * int resultCode, String resultData, Bundle map, * String requiredPermission, int appOp, boolean serialized, * boolean sticky, int userId) throws RemoteException */ if (code == s_broadcastIntent_code) { pos = data.dataposition(); data.enforceInterface(DESCRIPTOR); IBinder caller = data.readStrongBinder(); Intent intent = Intent.CREATOR.createFromParcel(data); String resolvedType = data.readString(); IBinder resultTo = data.readStrongBinder(); int resultCode = data.readInt(); String resultData = data.readString(); Bundle map = data.readBundle(); String requiredPermission = data.readString(); int appOp = data.readInt(); boolean serialized = data.readInt() != 0; boolean sticky = data.readInt() != 0; int userId = data.readInt(); //TODO 擷取到所有的參數,根據參數做各種截獲 //FINAL data.setDataPosition(pos); } return mBinder.transact(code, data, reply, flags); }}
0x4
這個技術方案,涉及到注入和dex載入,這方面網上教程非常多,so注入可以參考古河大神的LibInject, 而Java注入可以參考malokch的《注入安卓進程,並hook java世界的方法》.
另外也可以參考我之前寫的系列文章《進擊的Android注入術》。這個系列詳細講述了so注入,dex注入到binder proxy的整個技術細節。
樣本的源碼我已經上傳到https://github.com/boyliang/Hijack_AMS_broadIntent