Skip to content

GangJust/xpler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xpler

Xposed Kotlin 开发模板,更适合Kotlin编码风格。

将本项目作为 module 引入项目,然后正常Xposed编写流程操作即可。

Xpler 在原 Xposed Api 基础上进一步封装,使其支持Kotlin的DSL特性,更简洁的编写Hook逻辑。

注意:使用本模板,你仍需要手动创建和配置 xposed_initapplication meta-data

Xpler Api

作为原 Xposed Api 的封装项目,Xpler 提供了部分基本Api。

HookEntrance.kt

作为 XplerXposed 提供的抽象入口类,你只需要继承HookEntrance<T : HookStart>,然后实现相应接口即可。

  • ApplicationHookStart

    class HookInit : HookEntrance<HookInit>(), ApplicationHookStart {
        override val modulePackage: String
            get() = "com.example.module"
    
        override val scopes: Set<ApplicationHookStart.Scope>
            get() = arrayOf(
                "packageName" at "applicationClassName",
                "packageName1" at ("applicationClassName1" to "processName"),
            )
    
        override fun onCreateBefore(lpparam: XC_LoadPackage.LoadPackageParam, hostApp: Application) {
            // Do not write or call the same Hook logic in onBefore and onAfter, as this is meaningless
        }
    
        override fun onCreateAfter(lpparam: XC_LoadPackage.LoadPackageParam, hostApp: Application) {
            HActivity()
        }
    }

    实现该接口后,将自动为宿主注入类加载器,你只需要在 onCreateBeforeonCreateAfter 中书写 Hook逻辑即可。

    modulePackage 为模块包名,必须提供,Xpler 会用它去加载 HookState,以便对于模块启用/未启用状态的获取。

    scopes 为宿主列表,需提供 宿主包名宿主启动应用程序(Application)宿主进程名(prossName)可选,不在 scopes 列表中的包名,尽管在Xposed中加入生效列表,Xpler也不会对该宿主生效。

    而如果,你只是需要一个简单的Hook,并不需要复杂操作,可以试试 DefaultHookStart 接口。

  • DefaultHookStart

    class HookInit() : HookEntrance<HookInit>(), DefaultHookStart {
        override val modulePackage: String
            get() = "com.example.module"
    
        override fun loadPackage(lpparam: XC_LoadPackage.LoadPackageParam) {
            // the original calling logic
        }
    }

    该接口提供的 loadPackage 方法就是原始的 handleLoadPackage 操作。

记得修改xposed_init 中的入口类,如上述的入口类名为:com.example.module.HookInit

还有,如果有混淆优化,记得保留 HookInit 入口类。

HookState.kt

该类汇总了框架状态,如果你想要判断模块是否生效、框架类型,可使用该类。

KtXposedHelpers.kt

区别于原 XposedHelpers 该类提供了更符合Kotlin的编码风格:

  • XposedHelpers 写法

    XposedHelpers.findAndHookMethod(
            Activity.class,
            "onCreate",
            Bundle.class,
            new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log(param.method.getName() + " Before!");
                }
    
                @Override
                protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                    XposedBridge.log(param.method.getName() + " After!");
                }
            }
    );
  • KtXposedHelpers 写法

    KtXposedHelpers
        .hookClass(Activity::class.java)
        .method("onCreate", Bundle::class.java) {
            onBefore {
                XposedBridge.log("${this.method.name} Before!")
            }
    
            onAfter {
                XposedBridge.log("${this.method.name} After!")
            }
        }

    并且 KtXposedHelpers 在原基础上缓存了目标 Class ,使同一个 Class 支持链式 Hook,如:

    KtXposedHelpers
        .hookClass(Activity::class.java)
        .method("onCreate", Bundle::class.java) {
            onBefore {
                XposedBridge.log("${this.method.name} Before!")
            }
    
            onAfter {
                XposedBridge.log("${this.method.name} After!")
            }
        }
        .method("onResume") {
            onBefore {
                XposedBridge.log("${this.method.name} Before!")
            }
    
            onAfter {
                XposedBridge.log("${this.method.name} After!")
            }
            
            // 当 onReplace 出现时, onBefore、onAfter 将失去意义,它们将不会被执行
            onReplace {
                XposedBridge.log("${this.method.name} Replace!")
            }
            
            // 在Hook逻辑被执行一次之后立即解除
            onUnhook { hookMethod, callback -> 
                
            }
        }

    而对于 XC_MethodHook.MethodHookParam 的使用,相信通过以上例子已经很明显了。

    得益于Kotlin的扩展特性,在 onBefoe{..}onAfter{..}onReplace{..}作用域内,都属于 XC_MethodHook.MethodHookParam;故此,你可以使用 this 来表示 param 参数。

    不过,值得注意的是,onBefore{..}onAfter{..} 属于同类方法,它们允许同时出现,并分别响应其对应执行周期;而 onReplace{..} 出现时,则代表了Hook方法会被直接替换,因此 onBefore{..}onAfter{..} 不会被执行。

    onUnhook{hookMethod, callback -> ..} 属于选写(可写/可不写)方法,当它被书写之后,该Hook 方法都会在执行一次之后被立即解开(即Hook逻辑至少被执行一次);或许你并未在 onUnhook 作用域内书写任何代码,但只要它出现了都会立即解开Hook,而 KtXposedHelpersonUnhook 提供的作用域只是为了善后处理,仅此而已。

  • KtXposedHelpers 的其他操作:

    获取模块中的Layout:

    val moduleLayout = KtXposedHelpers.inflateView<LinearLayout>(R.id.module_layout)

    获取模块中的资源:

    val moduleDrawable = KtXposedHelpers.getDrawable(R.drawable.module_background)

    更多:getStringgetColorgetAnimation 等,请自行阅读方法注释。

KtXposedMore.kt

得益于Kotlin的扩展特性,Xpler 在部分类型的基础上增加系列扩展,以下是常用Api示例:

  • XC_LoadPackage.LoadPackageParam 类增加的扩展:

    lpparam.hookClass(Activity::class.java)
        .method("onCreate", Bundle::class.java) { ... }
        .method("onResume") { ... }
        .method("onStart") { ... }
  • XC_MethodHook.MethodHookParam 类增加的扩展:

    ...
    onAfter {
        thisContext //将当前hook对象转换为context, 如果转换失败抛出异常
        thisActivity //将当前hook对象转换为ctivity, 如果转换失败抛出异常
    }
    ...
  • Context 类增加的扩展:

    val moduleLayout = context.inflateModuleView<LinearLayout>(R.id.module_layout)
    
    val moduleDrawable = context.getModuleDrawable(R.drawable.module_background)
  • Any 增加 lpparam 的扩展,可在任意对象中直接使用 lpparam 实例。

  • 其他扩展,请自行阅读方法注释。

XplerLog.kt

在模块开发中更具通俗的Log工具类,与Log类的调用基本一致,支持LogCat面板等级输出日志。

HookEntity.kt

为了更合适通俗的编码方式,对于需要被Hook的目标类及其方法 HookEntity 支持以传统类定义的方式来书写Hook逻辑,下称Hook逻辑类

对于某个Class目标的Hook,Hook逻辑类需要继承 HookEntity<T> 并将泛型 <T> 修改为目标Class,然后通过系列注解完成Hook逻辑的编写,最后在 主逻辑 中完成实例化,即可注入相关方法的Hook逻辑,以下是简单示例:

// 目标类 Activity
class HActivity : HookEntity<Activity>(){
   
    @OnBefore("onCreate")
    fun onCreateBefore(params: XC_MethodHook.MethodHookParam, savedInstanceState: Bundle?) {
        hookBlockRunning(params) { // this: XC_MethodHook.MethodHookParam
            XplerLog.d(
                "savedInstanceState: $savedInstanceState",
                "method: ${this.method}"
            )
        }
    }
    
    @OnAfter("onResume")
    fun onCreateBefore(params: XC_MethodHook.MethodHookParam) {
        hookBlockRunning(params) { // this: XC_MethodHook.MethodHookParam
            XplerLog.d(
                "thisObject: ${this.thisObject}",
                "method: ${this.method}"
            )
        }
    }
    
    ...
}

//////////////////////////

// HookInit
override fun onCreateAfter(lpparam: XC_LoadPackage.LoadPackageParam, hostApp: Application) {
    HActivity()
}

没错,参数 params: XC_MethodHook.MethodHookParam 不能被省略,并且它只能被放在首位。

以下是一个稍复杂的写法,自行体会:

class HMainActivity : HookEntity<MainActivity>(){
    
    @OnConstructorBefore
    fun constructorBefore(params: XC_MethodHook.MethodHookParam){
        hookBlockRunning(params) {
            XplerLog.d("thisObject: $thisObject")
        }.onFailure {
            XplerLog.e(it)
        }
    }
    
    @OnAfter("getUser")
    @ReturnType(name = "com.example.bean.User")
    fun getUserAfter(name: String, @Param("com.example.config.UserConfig") config: Any?){
        hookBlockRunning(params) { // this: XC_MethodHook.MethodHookParam
            XplerLog.d(
                "name: ${name}",
                "config: ${config}",
                "result: ${this.result}"
            )
        }
    }
}

和前文一样 Xpler 提供的时机注解 @..Before@..After@..Replace,中的 @..Replace 仍然会替换对应目标方法的逻辑,而这时对于 @..Before@..After 则不会生效。

Live Template

当大量的模板代码在代码中重复出现时,你可以很好的运用 Android Studio 的 Live Template 来快速生成它们的基础代码。

Xpler提供了一个模板代码列表,以下是相应截图:

xpler_live_template

你可以下载 xpler-templates.zip,然后通过 Android Studio 菜单: File -> Manage IDE Settings -> Import Settings 导入该 Live Template 模板代码。

Xpler 在 FreedomPlus 中被很好的实践运用,如果你想要更多示例,请点击 这里

Releases

No releases published

Packages

No packages published

Languages