文章目錄
- 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 {@Overridepublic 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數據
@Overrideprotected 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替換需要啟動的未注冊的Activitypublic 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() {@Overridepublic 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;}}// 生成代理proxyIntentIntent proxyIntent = new Intent();proxyIntent.setClassName("com.example.myapplication",ProxyActivity.class.getName());// 保存原始的Intent對象Intent intent = (Intent) args[index];proxyIntent.putExtra(TARGET_INTENT, intent);// 使用proxyIntent替換數組中的Intentargs[index] = proxyIntent;}// 被代理對象調用return method.invoke(mInstance, args);}});// 用代理的對象替換系統的對象mInstanceField.set(singleton, mInstanceProxy);} catch (Exception e) {e.printStackTrace();}}// 需要啟動的未注冊的Activity 替換回來 ProxyActivitypublic 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() {@Overridepublic 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();}}}