为了hook动态加载的class,我使用了hook ClassLoader.loadClass(String)的方法,此方法在原生Xposed API上生效,但在Yuki上会有app卡死或代码执行中断的问题。
以下是原生Xposed API正常使用的代码:
public class ZuoyebangHook implements IXposedHookLoadPackage {
public String LOG_TAG = "ZuoyebangHook: ";
@Override
public void handleLoadPackage(final XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("Loaded app: " + lpparam.packageName);
if (lpparam.packageName.equals("com.baidu.homework")){
XposedBridge.log(LOG_TAG + "作业帮已启动");
findAndHookMethod(ClassLoader.class, "loadClass", String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
if (param.hasThrowable()) return;
super.afterHookedMethod(param);
//下面一行代码可以查看动态加载的类
XposedBridge.log(LOG_TAG + "已加载类:" + param.args[0]);
hook(lpparam, (String)param.args[0]);
}
});
}
}
public void hook(XC_LoadPackage.LoadPackageParam lpparam, String className) {
switch (className){
case "com.baidu.homework.activity.search.whole.PicManySearchActivity":
XposedBridge.log(LOG_TAG + "已加载" + className + ",并捕捉");
findAndHookMethod(
"com.baidu.homework.activity.search.whole.PicManySearchActivity",
lpparam.classLoader,
"onCreate",
Bundle.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
super.beforeHookedMethod(param);
XposedBridge.log(LOG_TAG + "PicManySearchActivity.onCreate已调用");
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
super.afterHookedMethod(param);
}
});
}
}
以下是使用Yuki不能正常工作的代码:
@InjectYukiHookWithXposed
class HookEntry : IYukiHookXposedInit {
override fun onInit() {
// 配置 YuKiHookAPI
// 可简写为 configs {}
YukiHookAPI.configs {
// 全局调试用的 TAG
// 在 Logcat 控制台过滤此 TAG 可找到详细日志
debugTag = "HomeworkKiller"
// 是否开启调试模式
// 请注意 - 若作为发布版本请务必关闭调试功能防止对用户设备造成大量日志填充
isDebug = true
// 是否启用调试日志的输出功能
// 一旦关闭后除手动日志外 API 将停止全部日志的输出 - 建议不要随意关掉这个选项
// 虽然说对用户的设备写入大量日志是不正确的 - 但是没有日志你将无法调试
// 关于日志是否会影响设备的流畅度一直是一个伪命题
// 但是不设置这个选项可能会引起一些非议 - 建议不要关闭就是了
isAllowPrintingLogs = true
// 是否启用 [YukiHookModulePrefs] 的键值缓存功能
// 若无和模块频繁交互数据在宿主重新启动之前建议开启
// 若需要实时交互数据建议关闭或从 [YukiHookModulePrefs] 中进行动态配置
isEnableModulePrefsCache = true
// 是否启用当前 Xposed 模块自身 [Resources] 缓存功能
// 一般情况下模块的 Resources 是不会改变的 - 但是在语言区域更改、分辨率更改等情况下 - 就需要刷新缓存
// 若无上述需求 - 在宿主重新启动之前建议开启
// 你可以手动调用 [PackageParam.refreshModuleAppResources] 来刷新缓存
isEnableModuleAppResourcesCache = true
// 是否启用 Hook Xposed 模块激活等状态功能
// 为原生支持 Xposed 模块激活状态检测 - 此功能默认启用
// 关闭后你将不能再在模块环境中使用 [YukiHookAPI.Status] 中的功能
// 功能启用后 - 将会在宿主启动时自动 Hook [YukiHookModuleStatus]
isEnableHookModuleStatus = true
// 是否启用当前 Xposed 模块与宿主交互的 [YukiHookDataChannel] 功能
// 请确保 Xposed 模块的 [Application] 继承于 [ModuleApplication] 才能有效
// 此功能默认启用 - 关闭后将不会在功能初始化的时候装载 [YukiHookDataChannel]
// 功能启用后 - 将会在宿主启动时自动 Hook [Application] 的生命周期方法进行注册
isEnableDataChannel = true
// 是否启用 [Member] 缓存功能
// 为防止 [Member] 复用过高造成的系统 GC 问题 - 此功能默认启用
// 除非缓存的 [Member] 发生了混淆的问题 - 否则建议启用
isEnableMemberCache = true
}
}
override fun onHook(){
YukiHookAPI.encase {
loadApp( APP_NAME ){
ClassLoader::class.java.hook {
injectMember {
method {
name = "loadClass"
//param(String::class.java)
}
afterHook {
val className = args[0] as String
//loggerI(msg = "loaded class:${className}")
loggerI(msg = "result:${hook_classes.contains(className)}")//正常打印,后面的代码不会运行
loggerI(msg = "现在要加载hooker")//不能打印
loadHooker(MethodHooks(className))
}
}
}
"com.baidu.homework.activity.search.whole.PicManySearchActivity".hook {
injectMember {
method {
name = "onCreate"
param(BundleClass)
}
beforeHook {
loggerI(msg = "Has hooked method:PicManySearchActivity.onCreate")
}
}
}
}
}
}
}
val hook_classes = listOf(
"com.baidu.homework.activity.search.whole.PicManySearchActivity",
)
class MethodHooks(className : String) : MyBaseHooker(className){
override fun onHook() {
loadApp(APP_NAME){
loggerI(msg = "loaded class:${className}")
//若执行以下代码,则app直接卡死
//loggerI(msg = hook_classes.contains(className) as String)
}
}
}
abstract class MyBaseHooker(class_name : String) : YukiBaseHooker() {
var className : String = "";
init {
className = class_name
}
abstract override fun onHook()
}
bug fixed