Hook
Hook
Hook,英文直译是”钩子“的意思。在程序中将其理解为”劫持“可能会更好理解,我们可以通过hook技术来劫持某个对象,从而控制它与其他对象的交互。
导读:Hook 技术(一) - 简书 (jianshu.com)
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点,原则是
静态变量
或者单例对象
,尽量Hookpublic
的对象和方法,非public不保证每个版本都一样,需要适配。Step2. 选择合适的代理方式,如果是
接口
可以用动态代理
;如果是类可以用静态代理
。 Step3. 偷梁换柱——用代理对象替换原始对象。
Hook Activity的startActivity
寻找Hook点:
Activity的 startActivity 方法的调用链:
// android/app/Activity.java
// Step1
@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}
// Step2
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
startActivityForResult(intent, -1);
}
}
// Step3
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
// Hook点
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. InstrumentationProxy继承Instrumentation
* 2. InstrumentationProxy持有Instrumentation实例的引用
* 3. 实现execStartActivity方法,并在内部通过反射调用Instrumentation的execStartActivity方法
*/
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:
public class HookHelper {
public static void hookActivityInstrumentation(Activity activity) {
try {
// 得到Activity的mInstrumentation字段
Field field = Activity.class.getDeclaredField("mInstrumentation");
field.setAccessible(true);
// 得到Activity中的Instrumentation对象
Instrumentation instrumentation = (Instrumentation) field.get(activity);
// 创建InstrumentationProxy对象来代理Instrumentation对象
InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
// 用代理去替换Activity中的Instrumentation对象
field.set(activity, instrumentationProxy);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
执行Hook:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Hook
HookHelper.hookActivityInstrumentation(this);
Intent intent = new Intent(this, DetailActivity.class);
startActivity(intent);
}
}
运行结果:
I/InstrumentationProxy: Hook成功--who:com.github.xch168.hooktest.MainActivity@bd3e1b1
Hook Context的startActivity
Context的实现类为ContextImpl。
寻找Hook点:
ContextImpl中startActivity的调用链:
// Step1
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
// Step2
@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?");
}
// Hook点
mMainThread.getInstrumentation().execStartActivity(getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options);
}
Hook点分析:
- 调用ActivityThread的getInstrumentation方法获取Instrumentation。
- ActivityThread是主线程的管理类,Instrumentation是ActivityThread的成员变量,一个进程只有一个ActivityThread。
- 选择Instrumentation作为Hook点,通过代理类进行替换。
Hook代码:
public class HookHelper {
public static void hookContextInstrumentation() {
try {
// 获取ActivityThread类
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
// 获取ActivityThread类中的静态变量sCurrentActivityThread
Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
currentActivityThreadField.setAccessible(true);
// 获取sCurrentActivityThread字段的值,即ActivityThread的对象
Object currentActivityThread = currentActivityThreadField.get(null);
// 获取ActivityThread的mInstrumentation字段
Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
// 获取mInstrumentation对象
Instrumentation instrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
// 创建Instrumentation的代理对象
InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
// 用InstrumentationProxy替换ActivityThread中的Instrumentation
mInstrumentationField.set(currentActivityThread, instrumentationProxy);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
执行Hook:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Hook
HookHelper.hookContextInstrumentation();
Intent intent = new Intent(this, DetailActivity.class);
getApplicationContext().startActivity(intent);
}
}
运行结果:
I/InstrumentationProxy: Hook成功--who:android.app.Application@7e13696