文章目录
- Android跨进程通信,binder传输数据过大导致Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。
- 1.binder在做跨进程传输时,最大可以携带多少数据
- 1.1有时候这个1m的崩溃系统捕获不到异常,
- 2.监测异常,提前上报
- Hook IActivityTaskManager
- 扩展:Hook AMS绕过Manifest的Activity注册检测
Android跨进程通信,binder传输数据过大导致Crash,异常捕获,监听异常的数值临界值,提前Hook拦截。
Java Crash捕获
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
Log.e("crash", "当前进程id:" + android.os.Process.myPid());
Log.e("crash", Log.getStackTraceString(e));
if (uncaughtExceptionHandler != null) {
uncaughtExceptionHandler.uncaughtException(t, e);
}
});
}
}
1.binder在做跨进程传输时,最大可以携带多少数据
测试代码,跨进程传输1m数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startBinding();
mBtn = findViewById(R.id.btn);
mBtn.setOnClickListener(v -> {
Bundle bundle = new Bundle();
bundle.putByteArray("binder_data", new byte[1 * 1024 * 1024]);
Intent intent = new Intent();
intent.putExtras(bundle);
ComponentName componentName = new ComponentName("com.example.myapplication", "com.example.myapplication.MainActivity");
intent.setComponent(componentName);
startActivity(intent);
});
}
不出意外崩了
1.1有时候这个1m的崩溃系统捕获不到异常,
把数据传输从1M改成了800k测试
还是崩了,崩溃的数据量大概是500bk(不崩溃)-600kb(崩溃)之间。
核心log
IActivityTaskManager是个aidl文件;
IActivityTaskManager$Stub是binder服务端的类,运行在system进程的
Proxy对象是运行在我们app进程的,称之为binder代理
Proxy对象通过transact方法调用到Stub对象的onTransact方法。这个异常发生在App进程,所以代码捕获到了这个异常。
2.监测异常,提前上报
大概500k就是危险的极限大小,容易发生异常。
hook拦截startActivity的方法,到达一个危险的大小容易崩溃的时候,我们就上报。
android.os.BinderProxy.transact(BinderProxy.java:510)
这里面Parcel有个dataSize记录数据的大小
Hook IActivityTaskManager
看日志调用流程,在startActivity时候就可以拦截数据,Instrumentation.execStartActivity
Caused by: android.os.TransactionTooLargeException: data parcel size 1049052 bytes
at android.os.BinderProxy.transactNative(Native Method)
at android.os.BinderProxy.transact(BinderProxy.java:510)
at android.app.IActivityTaskManager$Stub$Proxy.startActivity(IActivityTaskManager.java:3823)
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1705)
at android.app.Activity.startActivityForResult(Activity.java:5173)
ActivityTaskManager对象
final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
return IActivityTaskManager.Stub.asInterface(b);
sCache,是个静态的ArrayMap对象:
@UnsupportedAppUsage
private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();
反射换掉IActivityTaskManager对象的IBinder对象,IBinder有监控的transact方法。
invoke方法中,关注transact方法获得跨进程传输的参数的大小
IBinde的transact方法:Parcel data
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
/**
* Returns the total amount of data contained in the parcel.
*/
public final int dataSize() {
return nativeDataSize(mNativePtr);
}
完整Demo
public static void hook() {
Log.e(TAG, "hook: ");
try {
Class serviceManager = Class.forName("android.os.ServiceManager");
Method getServiceMethod = serviceManager.getMethod("getService", String.class);
Field sCacheField = serviceManager.getDeclaredField("sCache");
sCacheField.setAccessible(true);
Map<String, IBinder> sCache = (Map<String, IBinder>) sCacheField.get(null);
Map<String, IBinder> sNewCache;
sNewCache = new ArrayMap<>();
sNewCache.putAll(sCache);
IBinder activityTaskRemoteBinder = (IBinder) getServiceMethod.invoke(null, "activity_task");
sNewCache.put("activity_task", (IBinder) Proxy.newProxyInstance(serviceManager.getClassLoader(),
new Class[]{IBinder.class},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Log.e(TAG, "activity_task method = " + method.getName() + ", args = " + Arrays.toString(args));
if ("transact".equals(method.getName())) {
if (args != null && args.length > 1) {
Object arg = args[1];
if (arg instanceof Parcel) {
Parcel parcelArg = (Parcel) arg;
int dataSize = parcelArg.dataSize();
if (dataSize > 300 * 1024) {
// TODO 报警
Log.e(TAG, Log.getStackTraceString(new RuntimeException("[error]TransactionTooLargeException: 300Kb:" + dataSize)));
if (BuildConfig.DEBUG) {
if (dataSize > 512 * 1024) {
throw new RuntimeException("[error]TransactionTooLargeException:300Kb:" + dataSize);
}
}
}
}
}
}
return method.invoke(activityTaskRemoteBinder, args);
}
}));
sCacheField.set(null, sNewCache);
} catch (Exception e) {
e.printStackTrace();
}
}
测试
从日志看出我们是hook系统服务成功了。
总结:
- binder数据量过大崩溃,Java异常捕获机制捕获不到,提前拦截。
- DEBUG,超过512k,崩溃可以看到日志;
- ServiceManager中的sCache获取到原来activity_task对应的IBinder实例对象; IBinder是接口,通过动态代理创造一个IBinder的代理对象IBinderProxy; 把IBinderProxy放到ServiceManager的sCache,Application attachBaseContext中调用Hook方法;
扩展:Hook AMS绕过Manifest的Activity注册检测
思路,先启动一个注册的代理的Activity,然后绕过manifest的检测后,把代理的Activity替换成未注册的
package com.example.myapplication;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
public class HookUtil {
private static final String TARGET_INTENT = "target_intent";
// 使用代理的Activity替换需要启动的未注册的Activity
public static void hookAMS() {
try {
Class<?> clazz = Class.forName("android.app.ActivityTaskManager");
Field singletonField = clazz.getDeclaredField("IActivityTaskManagerSingleton");
singletonField.setAccessible(true);
Object singleton = singletonField.get(null);
Class<?> singletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = singletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
Method getMethod = singletonClass.getMethod("get");
Object mInstance = getMethod.invoke(singleton);
Class IActivityTaskManagerClass = Class.forName("android.app.IActivityTaskManager");
Object mInstanceProxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class[]{IActivityTaskManagerClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("startActivity".equals(method.getName())) {
int index = -1;
// 获取 Intent 参数在 args 数组中的index值
for (int i = 0; i < args.length; i++) {
if (args[i] instanceof Intent) {
index = i;
break;
}
}
// 生成代理proxyIntent
Intent proxyIntent = new Intent();
proxyIntent.setClassName("com.example.myapplication",
ProxyActivity.class.getName());
// 保存原始的Intent对象
Intent intent = (Intent) args[index];
proxyIntent.putExtra(TARGET_INTENT, intent);
// 使用proxyIntent替换数组中的Intent
args[index] = proxyIntent;
}
// 被代理对象调用
return method.invoke(mInstance, args);
}
});
// 用代理的对象替换系统的对象
mInstanceField.set(singleton, mInstanceProxy);
} catch (Exception e) {
e.printStackTrace();
}
}
// 需要启动的未注册的Activity 替换回来 ProxyActivity
public static void hookHandler() {
try {
Class<?> clazz = Class.forName("android.app.ActivityThread");
Field activityThreadField = clazz.getDeclaredField("sCurrentActivityThread");
activityThreadField.setAccessible(true);
Object activityThread = activityThreadField.get(null);
Field mHField = clazz.getDeclaredField("mH");
mHField.setAccessible(true);
final Handler mH = (Handler) mHField.get(activityThread);
Field mCallbackField = Handler.class.getDeclaredField("mCallback");
mCallbackField.setAccessible(true);
mCallbackField.set(mH, new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 159:
try {
Field mActivityCallbacksField = msg.obj.getClass()
.getDeclaredField("mActivityCallbacks");
mActivityCallbacksField.setAccessible(true);
List mActivityCallbacks = (List) mActivityCallbacksField.get(msg.obj);
for (int i = 0; i < mActivityCallbacks.size(); i++) {
if (mActivityCallbacks.get(i).getClass().getName()
.equals("android.app.servertransaction.LaunchActivityItem")) {
Object launchActivityItem = mActivityCallbacks.get(i);
Field mIntentField = launchActivityItem.getClass()
.getDeclaredField("mIntent");
mIntentField.setAccessible(true);
Intent proxyIntent = (Intent) mIntentField.get(launchActivityItem);
Intent intent = proxyIntent.getParcelableExtra(TARGET_INTENT);
if (intent != null) {
mIntentField.set(launchActivityItem, intent);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
break;
}
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}