xpler
xpler copied to clipboard
Xposed Kotlin 开发模板,更适合Kotlin编码风格
Xpler
Xposed Kotlin 开发模板,更适合Kotlin编码风格。
Xpler 在原 Xposed Api 基础上进一步封装,使其支持Kotlin的DSL特性,更简洁的编写Hook逻辑。
注意:使用本模板,你仍需要手动创建和配置 xposed_init、application meta-data。
Xpler Api
作为原 Xposed Api 的封装项目,Xpler 提供了部分基本Api。
implementation("io.github.thatworld:xpler:<version>")
HookEntrance.kt
作为 Xpler 给 Xposed 提供的抽象入口类,你只需要继承 HookEntrance,然后实现相应接口即可。
-
ApplicationHookStart
class HookInit : HookEntrance(), 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() } }实现该接口后,将自动为宿主注入类加载器,你只需要在
onCreateBefore或onCreateAfter中书写 Hook逻辑即可。modulePackage为模块包名,必须提供,Xpler会用它去加载HookState,以便对于模块启用/未启用状态的获取。scopes为宿主列表,需提供宿主包名和宿主启动应用程序(Application)、宿主进程名(prossName)可选,不在scopes列表中的包名,尽管在Xposed中加入生效列表,Xpler也不会对该宿主生效。而如果,你只是需要一个简单的Hook,并不需要复杂操作,可以试试
DefaultHookStart接口。 -
DefaultHookStart
class HookInit() : HookEntrance(), 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入口类。//proguard-rules.pro -keep class com.example.module.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,而KtXposedHelpers为onUnhook提供的作用域只是为了善后处理,仅此而已。 -
KtXposedHelpers的其他操作:获取模块中的Layout:
val moduleLayout = KtXposedHelpers.inflateView<LinearLayout>(R.id.module_layout)获取模块中的资源:
val moduleDrawable = KtXposedHelpers.getDrawable(R.drawable.module_background)更多:
getString、getColor、getAnimation等,请自行阅读方法注释。
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 并实现抽象方法setTargetClass 主动设置目标Class,然后通过系列注解完成Hook逻辑的编写,最后在 主逻辑 中完成实例化,即可注入相关方法的Hook逻辑,以下是简单示例:
// 目标类 Activity
class HActivity : HookEntity(){
override fun setTargetClass(): Class<*> {
return findClass("android.app.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(){
override fun setTargetClass(): Class<*> {
return findClass("android.app.Activity")
}
@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-templates.zip,然后通过 Android Studio 菜单: File -> Manage IDE Settings -> Import Settings 导入该 Live Template 模板代码。
Xpler 在 FreedomPlus 中被很好的实践运用,如果你想要更多示例,请点击 这里。