进击的Android Hook 注入术《五》
2016-03-07 18:23
459 查看
版权声明:本文为博主原创文章,未经博主允许不得转载。
目录(?)[-]
继续
BinderProxy
原理
获取AMS引用
获取JavaBBinder
替换mObject对象
示例四
最后
在日常开发过程中, 我们经常会使用到的ActivityManager、PackageManager就是一个client的调用,只是本身封装得比较好,让你感觉不到。而Service部分的逻辑,主要集中在system_process和com.android.phone这两个进程里头。
broadcastIntent是ActivityManagerService(AMS)的一个方法,AMS的宿主进程是system_process,毫无疑问我们需要先注入到system_process进程,至于接下来怎么做呢,正是本章的内容。
AMS跟binder进行通讯,是通过JNI实现的。AMS继承Binder(IBinder的子类,封装了IPC通讯公共部分的逻辑),Binder里保存着一个类型为int的mObject的字段,这个字段正是其C++对象JavaBBinder对象的地址,这个JavaBBinder才是AMS最终跟内核通讯的对象。代码如下:
[cpp]
view plain
copy
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的代码:
[cpp]
view plain
copy
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对象的引用),即可实现BinderService代理,下面是示意图:
在实现这个代理,我们需要获取AMS和及对应用的JavaBBinder两个对象。
[cpp]
view plain
copy
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;
};
这个类的作用主要添加了changeObj方法,主要功能是把mObject去掉const限制,并修改为的newobj。
com.demo.sms和com.demo.smstrojan的逻辑是一样的,都是拦截短信,并打印短信内容,代码片断如下:
[java]
view plain
copy
public final class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
this.abortBroadcast();
Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
for (SmsMessage message : messages) {
String msg = message.getMessageBody();
String to = message.getOriginatingAddress();
Log.i("TTT", context.getPackageName() + " To:" + to + " Msg:" + msg);
}
}
}
}
DemoInject3相对复杂,包含dex和proxybinder(被注入的so)两部分。dex的逻辑是生成代理的proxybinder,并通过invoke返回给lib,lib再通过DummyJavaBBinder修改其mObject为proxybinder,关键代码如下
dex代码
[java]
view plain
copy
package com.demo.inject3;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
/**
*
* @author boyliang
*
*/
public final class EntryClass {
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;
private SmsReceiverResorter mResorter;
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 {
if (code == s_broadcastIntent_code) {
mResorter.updatePriority("com.demo.sms");
}
return mBinder.transact(code, data, reply, flags);
}
}
public static Object[] invoke(int i) {
IBinder activity_proxy = null;
try {
activity_proxy = new ProxyActivityManagerServcie(ServiceManager.getService("activity"));
Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy 3!!!!<<<<<<<<<<<<<<");
} catch (Exception e) {
e.printStackTrace();
}
return new Object[] { "activity", activity_proxy };
}
}
看到onTransact中code的过滤处理,当code==s_broadcastIntent_code时,证明有client调用了sendBroadcast方法了,然后马上调用SmsReceiverRestorter中的updatePriority方法。
最后invoke返回的是一个Object数组,分别是"activity"字符串和activity_proxy对象,再看看proxybinder.cpp的中调用invoke方法的处理:
[cpp]
view plain
copy
<span style="white-space:pre"> </span>jmethodID invoke_method = jni_env->GetStaticMethodID(entry_class, "invoke", "(I)[Ljava/lang/Object;");
check_value(invoke_method);
jobjectArray objectarray = (jobjectArray) jni_env->CallStaticObjectMethod(entry_class, invoke_method, 0);
check_value(objectarray);
jsize size = jni_env->GetArrayLength(objectarray);
sp<IServiceManager> servicemanager = defaultServiceManager();
for (jsize i = 0; i < size; i += 2) {
jstring name = static_cast<jstring>(jni_env->GetObjectArrayElement(objectarray, i));
jobject obj = jni_env->GetObjectArrayElement(objectarray, i + 1);
const char* c_name = jni_env->GetStringUTFChars(name, NULL);
DummyJavaBBinder* binder = (DummyJavaBBinder*) servicemanager->getService(String16(c_name)).get();
binder->changObj(jni_env->NewGlobalRef(obj));
}
lproxybinder.cpp中根据invoke返回的数组进行处理。
至此,整个BinderProxy技术的技术已经介绍完毕了,接下来看看SmsReceiverRestorter的代码,这个类主要是负责修改广播的发送顺序。跟广播发送顺序有关的变量位置ActivityManagerService.mReceiverResolver.mActionToFilter,其定义如下为private final HashMap<String, ArrayList<IntentFilter>> mActionToFilter。其中key是action,value是各个broadcast中的intentfilter描述,这个value本身是一个List,其顺序即为广播的发送顺序,调整这个顺序即可,见代码;
[java]
view plain
copy
final class SmsReceiverResorter {
private static final String[] sActions = { "android.provider.Telephony.SMS_RECEIVED", "android.provider.Telephony.SMS_RECEIVED2", "android.provider.Telephony.GSM_SMS_RECEIVED" };
private final String TAG = "SmsReceiverResorter";
private HashMap<String, ArrayList<? extends IntentFilter>> mActionToFilter;
private Field mPackageNameField;
@SuppressWarnings("unchecked")
public SmsReceiverResorter(IBinder am) {
Class<?> claxx = am.getClass();
try {
Field field = claxx.getDeclaredField("mReceiverResolver");
field.setAccessible(true);
Object mReceiverResolver = field.get(am);
claxx = mReceiverResolver.getClass();
field = claxx.getSuperclass().getDeclaredField("mActionToFilter");
field.setAccessible(true);
mActionToFilter = (HashMap<String, ArrayList<? extends IntentFilter>>) field.get(mReceiverResolver);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
/**
* 修改优先级
*/
public void updatePriority(String target_pkg) {
if (mActionToFilter != null) {
for (String action : sActions) {
@SuppressWarnings("unchecked")
ArrayList<IntentFilter> filters = (ArrayList<IntentFilter>) mActionToFilter.get(action);
if (filters != null) {
Log.i("TTT", "send sms broadcast");
IntentFilter filter = null;
for (IntentFilter f : filters) {
String pkg = getPackageName(f);
if (target_pkg.equals(pkg)) {
filter = f;
break;
}
}
// 调整顺序
if (filter != null && filters.remove(filter) ) {
filters.add(0, filter);
filter = null;
Log.i("TTT", target_pkg + " is the first now");
}
}
}
}
}
private String getPackageName(IntentFilter filter) {
if (mPackageNameField == null && filter != null) {
Class<?> claxx = filter.getClass();
try {
mPackageNameField = claxx.getDeclaredField("packageName");
mPackageNameField.setAccessible(true);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
String result = null;
if (filter != null) {
try {
result = (String) mPackageNameField.get(filter);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
return result;
}
}
通过上面的方法,无论com.demo.sms是怎样落后于sms.demo.smstrojan注册广播,都可以最先拦截到短信。
终于把这个方案讲解完了,累死。。。
谁能坚持看到这里,也算是一种缘分吧。
在下一章里,我会全面介绍AIM这个框架的实现细节,AIM框架对前面所提及的技术点做了一个很好的汇总。
原文地址: http://blog.csdn.net/l173864930/article/details/38468433
目录(?)[-]
继续
BinderProxy
原理
获取AMS引用
获取JavaBBinder
替换mObject对象
示例四
最后
继续
在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最终跟内核通讯的对象。代码如下:
[cpp]
view plain
copy
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的代码:
[cpp]
view plain
copy
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对象的引用),即可实现BinderService代理,下面是示意图:
在实现这个代理,我们需要获取AMS和及对应用的JavaBBinder两个对象。
获取AMS引用
要获取AMS引用,通过ServiceManager即可,不过这类是隐藏类,通过反射才可以调用。通过ServiceManager.getService("activity")即可以拿到AMS。获取JavaBBinder
通过前面的介绍,拿到AMS之后,就可以获取其mObject字段,这个对象正好就是JavaBBinder的地址。另外,也有一种比较简单的方式,那就是通过defaultServiceManager的getService方法获取到。替换mObject对象
JavaBBinder的mObject对象并不能直接替换,因为mObject是const的,我写了一个DummyJavaBBinder的类,可以很容易地处理好这个问题,DummyJavaBBinder的实现如下:[cpp]
view plain
copy
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;
};
这个类的作用主要添加了changeObj方法,主要功能是把mObject去掉const限制,并修改为的newobj。
示例四
示例四包含三部分代码,分别是com.demo.sms,com.demo.smstrojan,以及DemonInject3。com.demo.sms和com.demo.smstrojan的逻辑是一样的,都是拦截短信,并打印短信内容,代码片断如下:
[java]
view plain
copy
public final class SmsReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
this.abortBroadcast();
Object[] pdus = (Object[]) bundle.get("pdus");
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
for (SmsMessage message : messages) {
String msg = message.getMessageBody();
String to = message.getOriginatingAddress();
Log.i("TTT", context.getPackageName() + " To:" + to + " Msg:" + msg);
}
}
}
}
DemoInject3相对复杂,包含dex和proxybinder(被注入的so)两部分。dex的逻辑是生成代理的proxybinder,并通过invoke返回给lib,lib再通过DummyJavaBBinder修改其mObject为proxybinder,关键代码如下
dex代码
[java]
view plain
copy
package com.demo.inject3;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
/**
*
* @author boyliang
*
*/
public final class EntryClass {
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;
private SmsReceiverResorter mResorter;
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 {
if (code == s_broadcastIntent_code) {
mResorter.updatePriority("com.demo.sms");
}
return mBinder.transact(code, data, reply, flags);
}
}
public static Object[] invoke(int i) {
IBinder activity_proxy = null;
try {
activity_proxy = new ProxyActivityManagerServcie(ServiceManager.getService("activity"));
Log.i("TTT", ">>>>>>>>>>>>>I am in, I am a bad boy 3!!!!<<<<<<<<<<<<<<");
} catch (Exception e) {
e.printStackTrace();
}
return new Object[] { "activity", activity_proxy };
}
}
看到onTransact中code的过滤处理,当code==s_broadcastIntent_code时,证明有client调用了sendBroadcast方法了,然后马上调用SmsReceiverRestorter中的updatePriority方法。
最后invoke返回的是一个Object数组,分别是"activity"字符串和activity_proxy对象,再看看proxybinder.cpp的中调用invoke方法的处理:
[cpp]
view plain
copy
<span style="white-space:pre"> </span>jmethodID invoke_method = jni_env->GetStaticMethodID(entry_class, "invoke", "(I)[Ljava/lang/Object;");
check_value(invoke_method);
jobjectArray objectarray = (jobjectArray) jni_env->CallStaticObjectMethod(entry_class, invoke_method, 0);
check_value(objectarray);
jsize size = jni_env->GetArrayLength(objectarray);
sp<IServiceManager> servicemanager = defaultServiceManager();
for (jsize i = 0; i < size; i += 2) {
jstring name = static_cast<jstring>(jni_env->GetObjectArrayElement(objectarray, i));
jobject obj = jni_env->GetObjectArrayElement(objectarray, i + 1);
const char* c_name = jni_env->GetStringUTFChars(name, NULL);
DummyJavaBBinder* binder = (DummyJavaBBinder*) servicemanager->getService(String16(c_name)).get();
binder->changObj(jni_env->NewGlobalRef(obj));
}
lproxybinder.cpp中根据invoke返回的数组进行处理。
至此,整个BinderProxy技术的技术已经介绍完毕了,接下来看看SmsReceiverRestorter的代码,这个类主要是负责修改广播的发送顺序。跟广播发送顺序有关的变量位置ActivityManagerService.mReceiverResolver.mActionToFilter,其定义如下为private final HashMap<String, ArrayList<IntentFilter>> mActionToFilter。其中key是action,value是各个broadcast中的intentfilter描述,这个value本身是一个List,其顺序即为广播的发送顺序,调整这个顺序即可,见代码;
[java]
view plain
copy
final class SmsReceiverResorter {
private static final String[] sActions = { "android.provider.Telephony.SMS_RECEIVED", "android.provider.Telephony.SMS_RECEIVED2", "android.provider.Telephony.GSM_SMS_RECEIVED" };
private final String TAG = "SmsReceiverResorter";
private HashMap<String, ArrayList<? extends IntentFilter>> mActionToFilter;
private Field mPackageNameField;
@SuppressWarnings("unchecked")
public SmsReceiverResorter(IBinder am) {
Class<?> claxx = am.getClass();
try {
Field field = claxx.getDeclaredField("mReceiverResolver");
field.setAccessible(true);
Object mReceiverResolver = field.get(am);
claxx = mReceiverResolver.getClass();
field = claxx.getSuperclass().getDeclaredField("mActionToFilter");
field.setAccessible(true);
mActionToFilter = (HashMap<String, ArrayList<? extends IntentFilter>>) field.get(mReceiverResolver);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
/**
* 修改优先级
*/
public void updatePriority(String target_pkg) {
if (mActionToFilter != null) {
for (String action : sActions) {
@SuppressWarnings("unchecked")
ArrayList<IntentFilter> filters = (ArrayList<IntentFilter>) mActionToFilter.get(action);
if (filters != null) {
Log.i("TTT", "send sms broadcast");
IntentFilter filter = null;
for (IntentFilter f : filters) {
String pkg = getPackageName(f);
if (target_pkg.equals(pkg)) {
filter = f;
break;
}
}
// 调整顺序
if (filter != null && filters.remove(filter) ) {
filters.add(0, filter);
filter = null;
Log.i("TTT", target_pkg + " is the first now");
}
}
}
}
}
private String getPackageName(IntentFilter filter) {
if (mPackageNameField == null && filter != null) {
Class<?> claxx = filter.getClass();
try {
mPackageNameField = claxx.getDeclaredField("packageName");
mPackageNameField.setAccessible(true);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
String result = null;
if (filter != null) {
try {
result = (String) mPackageNameField.get(filter);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
return result;
}
}
最后
这次的示例代码有点多,我已经上传至https://github.com/boyliang/Hijack_AMS_broadIntent。通过上面的方法,无论com.demo.sms是怎样落后于sms.demo.smstrojan注册广播,都可以最先拦截到短信。
终于把这个方案讲解完了,累死。。。
谁能坚持看到这里,也算是一种缘分吧。
在下一章里,我会全面介绍AIM这个框架的实现细节,AIM框架对前面所提及的技术点做了一个很好的汇总。
原文地址: http://blog.csdn.net/l173864930/article/details/38468433
相关文章推荐
- 进击的Android Hook 注入术《四》
- Android开发之发送有序广播+拦截广播
- 进击的Android Hook 注入术《三》
- CyanogenMode主题模块解析(中)
- 进击的Android Hook 注入术《二》
- Android-Parcelable
- Android Studio:Android Studio安装Genymotion插件
- Android 学习 2016.3.7
- 进击的Android Hook 注入术《一》
- android 泛型
- Android Hook Java的的一个改进版本
- Android账户系统相关
- android中点击事件的写法
- Android的开发框架学习分析
- 在android的jni线程C传回调函数给java
- Android中为你的应用程序添加桌面快捷方式
- Android Art Hook 技术方案
- Android ViewPager图片适配
- 如何在本地搭建一个Android应用crashing跟踪系统-ACRA
- Android适配NavigationBar问题