一、Xposed使用步骤
- Gradle引用依赖
- Manifest中添加meta内容识别
- 添加assets文件夹,创建xposed_init文件,用于hook文件识别
- 编写hook代码,hook对应的类和方法
二、流程解析
接上篇内容,依旧以打开LOG为目的,之前我们已经找到了对应的自定义log类,下面只需要打开log开关。
- 添加Gradle依赖
compileOnly 'de.robv.android.xposed:api:82'
- Manifest添加meta内容识别
<meta-data
android:name="xposedmodule"
android:value="true"/>
<meta-data
android:name="xposeddescription"
android:value="描述"/>
<!-- 支持的xposed的最低版本 -->
<meta-data
android:name="xposedminversion"
android:value="82"/>
- 添加xposed_init文件,并放到assets文件夹下
对应的是XposedInstaller会回调类。
com.alzzz.xxxx.xposed.Hook
- hook对应的package和对应的类
1、实现IXposedHookLoadPackage接口,实现handleLoadPackage类
2、判断包名,是否是想要hook的apk(非必须)
3、找到Hook时机(这次是在Application的attach方法进行hook的)
4、找到对应的类,通过反射修改对应字段
三、代码实现
1、Hook.java
主要工作:hook在Application的attach方法,读取文件中的json配置,看是否开启了log开关,开了就进行hook
public class Hook implements IXposedHookLoadPackage {
private static final String CLASS_NAME = "com.xxx.xxxx";
private IConfig mConfig = new IConfig();
private Context mApplicationContext;
private ClassLoader mClassLoader;
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
if (CLASS_NAME.equalsIgnoreCase(lpparam.packageName)){
//初始化配置
initConfig();
setupHookComponent();
}
}
/**
* 组建context和appliction组件
*/
private void setupHookComponent() {
XposedHelpers.findAndHookMethod(Application.class, "attach",
Context.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
mApplicationContext = (Context) param.args[0];
mClassLoader = mApplicationContext.getClassLoader();
LOGGER.d("mClassLoader = "+mClassLoader);
startHook();
}
});
}
/**
* 开始hook
*/
private void startHook() {
if (mConfig.isLogHook()){
//开启了log hook开关
LOGGER.d("starting log hook");
IFishHook logHook = new FishLogHook(mApplicationContext, mClassLoader);
logHook.startHook();
}
}
/**
* 初始化设置,必须在最前
*/
private void initConfig() {
mConfig = new IConfig();
try {
String content = FileUtils.getFileContent(FileUtils.PATH_FILE_DIR, FileUtils.FILE_NAME_CONFIG);
if (!TextUtils.isEmpty(content)){
JSONObject jsonObject = new JSONObject(content);
mConfig.decode(jsonObject);
LOGGER.d(content);
}
} catch (IOException e) {
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
2、FishLogHook
主要功能:反射打开LOG开关
public class FishLogHook implements IFishHook {
private Context mApplicationContext;
private ClassLoader mClassLoader;
Class<?> instanceClazz;
boolean hasHooked = false;
public FishLogHook(Context mApplicationContext, ClassLoader mClassLoader) {
this.mApplicationContext = mApplicationContext;
this.mClassLoader = mClassLoader;
}
@Override
public void startHook() {
if (instanceClazz == null) {
try {
instanceClazz = Class.forName("com.xxx.xxxx.xframework.util.Log", true, mClassLoader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
LOGGER.e(e.getMessage());
}
}
doLogProxy();
}
private void doLogProxy() {
if (instanceClazz != null){
try {
LOGGER.d("Loghook ===> start log proxy");
Field field = instanceClazz.getDeclaredField("enable");
field.setAccessible(true);
LOGGER.d("Loghook ===> field = "+field.get(null));
field.set(null, true);
LOGGER.d("Loghook ===> field = "+field.get(null));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
至此就打开了LOG开关了,这里需要注意两个事:
1、在Android 7.0以后XSharedPreference已经不好用了,因为SharedPreference的Mode类型只能是Private的了,在其他的应用内是打不开的,所以我采用了文件的方式,通过将config的内容存在文件中进行配置的管理。
2、如果想要加载外部的类或方法,需要拿到真正可以用的ClassLoader,这个比较关键。
由此,可以考虑一下是否可以直接把OkHttp的Log拦截器打开呢?是否可以Hook住网络请求,默认不验证签名呢?更换View功能是不是也可以?想想热更新相关,是不是可以替某些用三方热更的Apk,热更一些dex进去呢?这当然都可以。而且代码量也不多,只需要读懂想要hook的App的代码就行了。