现在的位置: 首页 > 综合 > 正文

Binder Proxy技术方案

2016年12月25日 ⁄ 综合 ⁄ 共 3838字 ⁄ 字号 评论关闭

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

抱歉!评论已关闭.