进击的Android Hook 注入术《五》

继续

在Android,几乎所有的IPC通讯都是通过Binder,可以说Binder在Android中占据着非常重要的地位。IPC通讯一般涉及client和server两部分,在Android上,所有Binder的serivce部分统称为NativeService(跟平时所说的Service组件不一样),一个NativeService可以跟多个client通讯,如果想更详细地了解这方面的内容可以到 老罗的博客睢睢。
在日常开发过程中, 我们经常会使用到的ActivityManager、PackageManager就是一个client的调用,只是本身封装得比较好,让你感觉不到。而Service部分的逻辑,主要集中在system_process和com.android.phone这两个进程里头。
broadcastIntent是ActivityManagerService(AMS)的一个方法,AMS的宿主进程是system_process,毫无疑问我们需要先注入到system_process进程,至于接下来怎么做呢,正是本章的内容。

BinderProxy


原理

所有NativeService都继承到IBinder接口,BinderProxy原理很简单,就是先到找到要代理的NativeService引用,再通过自己编写的ProxyBinder对象代理NativeService,从而达到截获IPC通讯的目的。下面我们以AMS为例,做一个说明:


AMS跟binder进行通讯,是通过JNI实现的。AMS继承Binder(IBinder的子类,封装了IPC通讯公共部分的逻辑),Binder里保存着一个类型为int的mObject的字段,这个字段正是其C++对象JavaBBinder对象的地址,这个JavaBBinder才是AMS最终跟内核通讯的对象。代码如下:
  1. public class Binder implements IBinder {  
  2.     //...  
  3.   
  4.     /* mObject is used by native code, do not remove or rename */  
  5.     private int mObject; //这个对象保存的就是JavaBBinder的指针  
  6.     private IInterface mOwner;  
  7.     private String mDescriptor;  
  8.   
  9.     //...  
  10. }  
同样的,在JavaBBinder中,也保存着一个类型jobject的mObject,指向上层Java对象。看看JavaBBinder的代码:
  1. class JavaBBinder : public BBinder  
  2. {  
  3.   
  4. //...  
  5.   
  6.     jobject object() const  
  7.     {  
  8.         return mObject;  
  9.     }  
  10.   
  11.     //...  
  12.   
  13.     private:  
  14.         JavaVM* const   mVM;  
  15.         jobject const   mObject; //这个保存的是AMS的引用  
  16.     };  
  17. }  
Java和C++就是通过这两个字段相互连结在一起的。
其中JavaBBinder中的mObject是整个IPC关键的一节,所有的client请求,都是先到达JavaBBinder,然后JavaBBinder再通过JNI调用mObject的execTransact的方法,最终把请求发送到AMS。
因此,我们只要想办法找到AMS的对象的JavaBBinder,再把mObject替换为代理对象(记作ProxyBinder,一个Java对象的引用),即可实现BinderService代理,下面是示意图:

在实现这个代理,我们需要获取AMS和及对应用的JavaBBinder两个对象。


获取AMS引用

要获取AMS引用,通过ServiceManager即可,不过这类是隐藏类,通过反射才可以调用。通过ServiceManager.getService("activity")即可以拿到AMS。

获取JavaBBinder

通过前面的介绍,拿到AMS之后,就可以获取其mObject字段,这个对象正好就是JavaBBinder的地址。另外,也有一种比较简单的方式,那就是通过defaultServiceManager的getService方法获取到。

替换mObject对象

JavaBBinder的mObject对象并不能直接替换,因为mObject是const的,我写了一个DummyJavaBBinder的类,可以很容易地处理好这个问题,DummyJavaBBinder的实现如下:
  1. class DummyJavaBBinder : public BBinder{  
  2. public:  
  3.     virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) {  
  4.         return NO_ERROR;  
  5.     }  
  6.   
  7.     jobject object() const {  
  8.         return mObject;  
  9.     }  
  10.   
  11.     JavaVM* javaVM() const {  
  12.         return mVM;  
  13.     }  
  14.   
  15.     void changeObj(jobject newobj){  
  16.         const jobject* p_old_obj = &mObject;  
  17.         jobject* p_old_obj_noconst = const_cast<jobject *>(p_old_obj);  
  18.         *p_old_obj_noconst = newobj;  
  19.     }  
  20.   
  21. private:  
  22.     JavaVM* const   mVM;  
  23.     jobject const   mObject;  
  24. };  
这个类的作用主要添加了changeObj方法,主要功能是把mObject去掉const限制,并修改为的newobj。

示例四

示例四包含三部分代码,分别是com.demo.sms,com.demo.smstrojan,以及DemonInject3。
com.demo.sms和com.demo.smstrojan的逻辑是一样的,都是拦截短信,并打印短信内容,代码片断如下:
  1. public final class SmsReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.           
  6.         Bundle bundle = intent.getExtras();  
  7.   
  8.         if (bundle != null) {  
  9.             this.abortBroadcast();  
  10.               
  11.             Object[] pdus = (Object[]) bundle.get("pdus");  
  12.             SmsMessage[] messages = new SmsMessage[pdus.length];  
  13.   
  14.             for (int i = 0; i < pdus.length; i++) {  
  15.                 messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);  
  16.             }  
  17.   
  18.             for (SmsMessage message : messages) {  
  19.                 String msg = message.getMessageBody();  
  20.                 String to = message.getOriginatingAddress();  
  21.                   
  22.                 Log.i("TTT", context.getPackageName() + " To:" + to + " Msg:" + msg);  
  23.             }  
  24.         }  
  25.           
  26.     }  
  27.   
  28. }  
DemoInject3相对复杂,包含dex和proxybinder(被注入的so)两部分。dex的逻辑是生成代理的proxybinder,并通过invoke返回给lib,lib再通过DummyJavaBBinder修改其mObject为proxybinder,关键代码如下

dex代码
  1. package com.demo.inject3;  
  2.   
  3. import android.net.Uri;  
  4. import android.os.Binder;  
  5. import android.os.IBinder;  
  6. import android.os.Parcel;  
  7. import android.os.RemoteException;  
  8. import android.util.Log;  
  9.   
  10. /** 
  11.  *  
  12.  * @author boyliang 
  13.  *  
  14.  */  
  15. public final class EntryClass {  
  16.   
  17.     private static final class ProxyActivityManagerServcie extends Binder {  
  18.         private static final String CLASS_NAME = "android.app.IActivityManager";  
  19.         private static final String DESCRIPTOR = "android.app.IActivityManager";  
  20.         private static final int s_broadcastIntent_code;  
  21.   
  22.         private SmsReceiverResorter mResorter;  
  23.   
  24.         static {  
  25.             if (ReflecterHelper.setClass(CLASS_NAME)) {  
  26.                 s_broadcastIntent_code = ReflecterHelper.getStaticIntValue("BROADCAST_INTENT_TRANSACTION", -1);  
  27.             } else {  
  28.                 s_broadcastIntent_code = -1;  
  29.             }  
  30.         }  
  31.   
  32.         private IBinder mBinder;  
  33.   
  34.         public ProxyActivityManagerServcie(IBinder binder) {  
  35.             mBinder = binder;  
  36.             mResorter = new SmsReceiverResorter(binder);  
  37.         }  
  38.   
  39.         @Override  
  40.         protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {  
  41.   
  42.             if (code == s_broadcastIntent_code) {  
  43.                 mResorter.updatePriority("com.demo.sms");  
  44.             }  
  45.   
  46.             return mBinder.transact(code, data, reply, flags);  
  47.         }  
  48.     }  
  49.   
  50.     public static Object[] invoke(int i) {  
  51.         IBinder activity_proxy = null;  
  52.   
  53.         try {  
  54.             activity_proxy = new ProxyActivityManagerServcie(ServiceManager.getService("activity"));  
  55.   
  56.             Log.i("TTT"">>>>>>>>>>>>>I am in, I am a bad boy 3!!!!<<<<<<<<<<<<<<");  
  57.         } catch (Exception e) {  
  58.             e.printStackTrace();  
  59.         }  
  60.   
  61.         return new Object[] { "activity", activity_proxy };  
  62.     }  
  63. }  
看到onTransact中code的过滤处理,当code==s_broadcastIntent_code时,证明有client调用了sendBroadcast方法了,然后马上调用SmsReceiverRestorter中的updatePriority方法。
最后invoke返回的是一个Object数组,分别是"activity"字符串和activity_proxy对象,再看看proxybinder.cpp的中调用invoke方法的处理:
  1. <span style="white-space:pre">    </span>jmethodID invoke_method = jni_env->GetStaticMethodID(entry_class, "invoke""(I)[Ljava/lang/Object;");  
  2.     check_value(invoke_method);  
  3.   
  4.     jobjectArray objectarray = (jobjectArray) jni_env->CallStaticObjectMethod(entry_class, invoke_method, 0);  
  5.     check_value(objectarray);  
  6.   
  7.     jsize size = jni_env->GetArrayLength(objectarray);  
  8.     sp<IServiceManager> servicemanager = defaultServiceManager();  
  9.     for (jsize i = 0; i < size; i += 2) {  
  10.         jstring name = static_cast<jstring>(jni_env->GetObjectArrayElement(objectarray, i));  
  11.         jobject obj = jni_env->GetObjectArrayElement(objectarray, i + 1);  
  12.   
  13.         const char* c_name = jni_env->GetStringUTFChars(name, NULL);  
  14.         DummyJavaBBinder* binder = (DummyJavaBBinder*) servicemanager->getService(String16(c_name)).get();  
  15.         binder->changObj(jni_env->NewGlobalRef(obj));  
  16.     }  
lproxybinder.cpp中根据invoke返回的数组进行处理。
至此,整个BinderProxy技术的技术已经介绍完毕了,接下来看看SmsReceiverRestorter的代码,这个类主要是负责修改广播的发送顺序。跟广播发送顺序有关的变量位置ActivityManagerService.mReceiverResolver.mActionToFilter,其定义如下为private final HashMap<String, ArrayList<IntentFilter>> mActionToFilter。其中key是action,value是各个broadcast中的intentfilter描述,这个value本身是一个List,其顺序即为广播的发送顺序,调整这个顺序即可,见代码;
  1. final class SmsReceiverResorter {  
  2.     private static final String[] sActions = { "android.provider.Telephony.SMS_RECEIVED""android.provider.Telephony.SMS_RECEIVED2""android.provider.Telephony.GSM_SMS_RECEIVED" };  
  3.     private final String TAG = "SmsReceiverResorter";  
  4.     private HashMap<String, ArrayList<? extends IntentFilter>> mActionToFilter;  
  5.     private Field mPackageNameField;  
  6.   
  7.     @SuppressWarnings("unchecked")  
  8.     public SmsReceiverResorter(IBinder am) {  
  9.         Class<?> claxx = am.getClass();  
  10.         try {  
  11.             Field field = claxx.getDeclaredField("mReceiverResolver");  
  12.             field.setAccessible(true);  
  13.             Object mReceiverResolver = field.get(am);  
  14.   
  15.             claxx = mReceiverResolver.getClass();  
  16.               
  17.             field = claxx.getSuperclass().getDeclaredField("mActionToFilter");  
  18.             field.setAccessible(true);  
  19.   
  20.             mActionToFilter = (HashMap<String, ArrayList<? extends IntentFilter>>) field.get(mReceiverResolver);  
  21.               
  22.         } catch (Exception e) {  
  23.             Log.e(TAG, e.toString());  
  24.         }  
  25.     }  
  26.   
  27.     /** 
  28.      * 修改优先级 
  29.      */  
  30.     public void updatePriority(String target_pkg) {  
  31.           
  32.         if (mActionToFilter != null) {  
  33.               
  34.             for (String action : sActions) {  
  35.                   
  36.                 @SuppressWarnings("unchecked")  
  37.                 ArrayList<IntentFilter> filters = (ArrayList<IntentFilter>) mActionToFilter.get(action);  
  38.   
  39.                 if (filters != null) {  
  40.                     Log.i("TTT""send sms broadcast");  
  41.                       
  42.                     IntentFilter filter = null;  
  43.   
  44.                     for (IntentFilter f : filters) {  
  45.                         String pkg = getPackageName(f);  
  46.                         if (target_pkg.equals(pkg)) {  
  47.                             filter = f;  
  48.                             break;  
  49.                         }   
  50.                     }  
  51.   
  52.                     // 调整顺序  
  53.                     if (filter != null && filters.remove(filter) ) {  
  54.                           
  55.                         filters.add(0, filter);  
  56.                         filter = null;  
  57.                           
  58.                         Log.i("TTT", target_pkg + " is the first now");  
  59.                     }  
  60.                 }  
  61.             }  
  62.   
  63.         }  
  64.     }  
  65.   
  66.     private String getPackageName(IntentFilter filter) {  
  67.           
  68.         if (mPackageNameField == null && filter != null) {  
  69.             Class<?> claxx = filter.getClass();  
  70.             try {  
  71.                 mPackageNameField = claxx.getDeclaredField("packageName");  
  72.                 mPackageNameField.setAccessible(true);  
  73.             } catch (Exception e) {  
  74.                 Log.e(TAG, e.toString());  
  75.             }  
  76.         }  
  77.           
  78.         String result = null;  
  79.   
  80.         if (filter != null) {  
  81.             try {  
  82.                 result = (String) mPackageNameField.get(filter);  
  83.             } catch (Exception e) {  
  84.                 Log.e(TAG, e.toString());  
  85.             }  
  86.         }  
  87.           
  88.         return result;  
  89.     }  
  90. }  


最后

这次的示例代码有点多,我已经上传至https://github.com/boyliang/Hijack_AMS_broadIntent。
通过上面的方法,无论com.demo.sms是怎样落后于sms.demo.smstrojan注册广播,都可以最先拦截到短信。

终于把这个方案讲解完了,累死。。。
谁能坚持看到这里,也算是一种缘分吧。
在下一章里,我会全面介绍AIM这个框架的实现细节,AIM框架对前面所提及的技术点做了一个很好的汇总。


原文地址: http://blog.csdn.net/l173864930/article/details/38468433

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值