概述
Hook,英文直译是”钩子“的意思。在程序中将其理解为”劫持“可能会更好理解,我们可以通过hook技术来劫持某个对象,从而控制它与其他对象的交互。
Hook技术分类
- 根据Hook的API语言划分,分为Hook Java和Hook Native。
- Hook Java主要通过反射和代理来实现,用于在SDK开发环境中修改Java代码。
- Hook Native则应用于在NDK开发环境和系统开发中修改Native代码。
- 根据Hook的进程划分,分为应用程序进程Hook和全局Hook。
- 应用程序进程Hook只能Hook当前所在的应用程序进程。
- 应用程序进程是Zygote进程fork出来的,如果对Zygote进行Hook,就可以实现Hook系统所有的应用程序进程,这就是全局Hook。
- 根据Hook的实现方式划分,分为如下两种:
- 通过反射和代理实现,只能Hook当前的应用程序进程。
- 通过Hook框架实现,比如Xposed,可以实现全局Hook,但是需要root。
Hook原理
创建一个代理对象,然后把原始对象替换为我们的代理对象,这样就可以在这个代理对象为所欲为,修改参数或替换返回值。
正常的调用和返回:
Hook的调用和返回:
Hook的过程
Step1. 寻找Hook点,原则是静态变量
或者单例对象
,尽量Hook public
的对象和方法,非public不保证每个版本都一样,需要适配。
Step2. 选择合适的代理方式,如果是接口
可以用动态代理
;如果是类可以用静态代理
。
Step3. 偷梁换柱——用代理对象替换原始对象。
Hook Activity的startActivity
寻找Hook点:
Activity的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
|
@Override public void startActivity(Intent intent) { this.startActivity(intent, null); }
@Override public void startActivity(Intent intent, @Nullable Bundle options) { if (options != null) { startActivityForResult(intent, -1, options); } else { startActivityForResult(intent, -1); } }
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) { if (mParent == null) { options = transferSpringboardActivityOptions(options); Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options); if (ar != null) { mMainThread.sendActivityResult(mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData()); } if (requestCode >= 0) { mStartedActivity = true; }
cancelInputsAndStartExitTransition(options); } else { if (options != null) { mParent.startActivityFromChild(this, intent, requestCode, options); } else { mParent.startActivityFromChild(this, intent, requestCode); } } }
|
Hook点分析:
当调用Activity的startActivity方法时,最后会调用mInstrumentation
的execStartActivity方法来完成Activity的开启,而mInstrumentation
是Activity的成员变量,所以是一个很好的Hook点,用代理Instrumentation来替代原始的Instrumentation完成Hook。
Hook代码:
代理类:InstrumentationProxy.java
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
|
public class InstrumentationProxy extends Instrumentation { private static final String TAG = "InstrumentationProxy";
private Instrumentation的 mInstrumentation;
public InstrumentationProxy(Instrumentation instrumentation) { mInstrumentation = instrumentation; }
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
Log.i(TAG, "Hook成功" + "--who:" + who); try { Method execStartActivityMethod = Instrumentation.class.getDeclaredMethod("execStartActivity", Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class); return (ActivityResult) execStartActivityMethod.invoke(mInstrumentation, who, contextThread, token, target, intent, requestCode, options); } catch (Exception e) { throw new RuntimeException(e); } } }
|
用InstrumentationProxy来替换Instrumentation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class HookHelper {
public static void hookActivityInstrumentation(Activity activity) { try { Field field = Activity.class.getDeclaredField("mInstrumentation"); field.setAccessible(true); Instrumentation instrumentation = (Instrumentation) field.get(activity); InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation); field.set(activity, instrumentationProxy); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
|
执行Hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
HookHelper.hookActivityInstrumentation(this); Intent intent = new Intent(this, DetailActivity.class); startActivity(intent); } }
|
运行结果:
1
| I/InstrumentationProxy: Hook成功--who:com.github.xch168.hooktest.MainActivity@bd3e1b1
|
Hook Context的startActivity
Context的实现类为ContextImpl。
寻找Hook点:
ContextImpl中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
| @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); }
@Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess();
final int targetSdkVersion = getApplicationInfo().targetSdkVersion;
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }
|
Hook点分析:
- 调用ActivityThread的getInstrumentation方法获取Instrumentation。
- ActivityThread是主线程的管理类,Instrumentation是ActivityThread的成员变量,一个进程只有一个ActivityThread。
- 选择Instrumentation作为Hook点,通过代理类进行替换。
Hook代码:
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
| public class HookHelper {
public static void hookContextInstrumentation() { try { Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread"); currentActivityThreadField.setAccessible(true); Object currentActivityThread = currentActivityThreadField.get(null); Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation"); mInstrumentationField.setAccessible(true); Instrumentation instrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread); InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation); mInstrumentationField.set(currentActivityThread, instrumentationProxy);
} catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
|
执行Hook:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class MainActivity extends Activity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
HookHelper.hookContextInstrumentation();
Intent intent = new Intent(this, DetailActivity.class); getApplicationContext().startActivity(intent); } }
|
运行结果:
1
| I/InstrumentationProxy: Hook成功--who:android.app.Application@7e13696
|
参考链接
- Android插件化原理解析——Hook机制之动态代理
- Android Hook 机制之简单实战
- Android Hook Activity 的几种姿势
- 理解 Android Hook 技术以及简单实战
- 《Android进阶解密》