Hook过程:
寻找 Hook 点,原则是静态变量或者单例对象,尽量 Hook public 的对象和方法。 Hook的选择点:静态变量和单例,因为一旦创建对象,它们不容易变化,非常容易定位。
选择合适的代理方式,如果是接口可以用动态代理。
偷梁换柱——用代理对象替换原始对象。 注意Android 的 API 版本比较多,方法和类可能不一样,所以要做好 API 的兼容工作。还有不要hook太底层的东西,各个厂商的rom代码不一样
hookView的点击事件 先来个简单点的,View的点击事件。
hookOnLongClick 顺着View的setOnClickListener
方法找到了getListenerInfo
方法,进而找到了ListenerInfo
类,而view的click,longClick,ScrollChange的监听事件都存放在这里面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 private void hookOnLongCLickListener (View view) { try { Class<?> clazzView = Class.forName("android.view.View" ); Method getListenerInfoMethod = clazzView.getDeclaredMethod("getListenerInfo" ); getListenerInfoMethod.setAccessible(true ); Object listenerInfo = getListenerInfoMethod.invoke(view); Class<?> clazz = Class.forName("android.view.View$ListenerInfo" ); Field field = clazz.getDeclaredField("mOnLongClickListener" ); field.setAccessible(true ); View.OnLongClickListener raw = (View.OnLongClickListener) field.get(listenerInfo); field.set(listenerInfo, new HookOnLongClickListener (raw)); } catch (Exception e) { e.printStackTrace(); } }class HookOnLongClickListener implements View .OnLongClickListener{ private View.OnLongClickListener raw; public HookOnLongClickListener (View.OnLongClickListener raw) { this .raw = raw; } @Override public boolean onLongClick (View v) { Log.e("HookUtil" ,"HookOnLongClickListener" ); Toast.makeText(MainActivity.this ,"替换之后" ,Toast.LENGTH_SHORT).show(); if (raw!=null ){ raw.onLongClick(v); } return false ; } }
这样调用hookOnLongCLickListerner(view)
方法即可在原有逻辑不变的情况下添加我们自己的逻辑。
hookOnLongClick 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 private void hookOnClickListener (View view) { try { Method getListenerInfoMethod = view.getClass().getDeclaredMethod("getListenerInfo" ); getListenerInfoMethod.setAccessible(true ); Object listenerInfo = getListenerInfoMethod.invoke(view); Class<?> listenerInfoClz = Class.forName("android.view.View$ListenerInfo" ); Field mOnClickListener = listenerInfoClz.getDeclaredField("mOnClickListener" ); mOnClickListener.setAccessible(true ); View.OnClickListener originOnClickListener = (View.OnClickListener) mOnClickListener.get(listenerInfo); View.OnClickListener hookedOnClickListener = new HookedOnClickListener (originOnClickListener); mOnClickListener.set(listenerInfo, hookedOnClickListener); } catch (Exception e) { } }class HookedOnClickListener implements View .OnClickListener { private View.OnClickListener origin; HookedOnClickListener(View.OnClickListener origin) { this .origin = origin; } @Override public void onClick (View v) { Toast.makeText(MainActivity.this , "hook click" , Toast.LENGTH_SHORT).show(); Log.i("hook" , "Before click, do what you want to to." ); if (origin != null ) { origin.onClick(v); } Log.i("hook" , "After click, do what you want to to." ); } }
这样调用hookOnLongClickListerner(view)
方法即可在原有逻辑不变的情况下添加我们自己的逻辑。
hookAlarmManager 在设置Alarm的过程中,会调用AlarmManager.set方法,而AlarmManager对象又很方便得到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE); Class<?> alarmManagerClass = alarm.getClass();Field mService = alarmManagerClass.getDeclaredField("mService" ); mService.setAccessible(true );Object mSerViceInstant = mService.get(alarm);AlarmManagerInvocationHandler handler = new AlarmManagerInvocationHandler (mSerViceInstant); Class<?> IActivityManagerIntercept = Class.forName("android.app.IAlarmManager" );Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class <?>[]{IActivityManagerIntercept}, handler); mService.set(alarm, proxy);class AlarmManagerInvocationHandler implements InvocationHandler { private Object iAlarmManagerObject; private AlarmManagerInvocationHandler (Object iAlarmManagerObject) { this .iAlarmManagerObject = iAlarmManagerObject; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Log.i("alarm_manager" , method.getName()); if ("set" .equals(method.getName())) { Log.e("alarm_manager" , "调用了mService.set()" ); try { long interval = 0 ; int alarmManagerTimeType = Integer.valueOf(args[1 ].toString()); long alarmManagerAtTime = Long.valueOf(args[2 ].toString()); Log.e("alarm_manager_interval" , "alarmManagerTimeType:" +alarmManagerTimeType); Log.e("alarm_manager_interval" , "alarmManagerAtTime:" +alarmManagerAtTime); switch (alarmManagerTimeType) { case AlarmManager.RTC_WAKEUP: case AlarmManager.RTC: Log.e("alarm_manager_interval" , "currentTimeMillis--RTC:" +System.currentTimeMillis()); interval = alarmManagerAtTime - System.currentTimeMillis(); break ; case AlarmManager.ELAPSED_REALTIME: case AlarmManager.ELAPSED_REALTIME_WAKEUP: Log.e("alarm_manager_interval" , "currentTimeMillis--ELAPSED_REALTIME:" +SystemClock.elapsedRealtime()); interval = alarmManagerAtTime - SystemClock.elapsedRealtime(); break ; } Log.e("alarm_manager_interval" ,interval+"-->" + interval/1000 /60 ); } catch (Exception e) { e.printStackTrace(); } } return method.invoke(iAlarmManagerObject, args); } }
hookAMS 对于Activity的启动过程,我们可以hook它的startActivity方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public void hookASM () { try { Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative" ); Field field = activityManagerNativeClass.getDeclaredField("gDefault" ); field.setAccessible(true ); Object gDefault= field.get(null ); Class<?> singletonClass = Class.forName("android.util.Singleton" ); Field mInstance = singletonClass.getDeclaredField("mInstance" ); mInstance.setAccessible(true ); Object iActivityManagerObject = mInstance.get(gDefault); AmsInvocationHandler amsInvocationHandler = new AmsInvocationHandler (iActivityManagerObject); Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), iActivityManagerObject.getClass().getInterfaces(), amsInvocationHandler); mInstance.set(gDefault, proxy); }catch (Exception e){ e.printStackTrace(); } } private class AmsInvocationHandler implements InvocationHandler { private Object iActivityManagerObject; private AmsInvocationHandler (Object iActivityManagerObject) { this .iActivityManagerObject = iActivityManagerObject; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { Log.i("hookASM" , method.getName()); if ("startActivity" .contains(method.getName())) { Log.e("hookASM" ,"Activity已经开始启动" ); } return method.invoke(iActivityManagerObject, args); } }
既然我们能够接管startActivity方法,我们就可以伪造一个Intent去启动一个没有在清单文件中注册的Activity。 以上