highcapable / yukihookapi Goto Github PK
View Code? Open in Web Editor NEW⛱️ An efficient Hook API and Xposed Module solution built in Kotlin.
Home Page: https://highcapable.github.io/YukiHookAPI/
License: Apache License 2.0
⛱️ An efficient Hook API and Xposed Module solution built in Kotlin.
Home Page: https://highcapable.github.io/YukiHookAPI/
License: Apache License 2.0
This is a proposal.
Since the API's original design mode of constructing a Hook only for a Class
does not meet some exceptions, and there are some adaptability problems, so we make a new Hook code style is given below.
The following example
// Single Method Hook
"com.demo.Test".toClass().method {
name = "test"
param(StringClass)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
// Multiple Methods Hook
"com.demo.Test".toClass().method {
name = "test"
}.all().hook {
before {
// Do something...
}
after {
// Do something...
}
}
// A Class with multiple Methods Hook
"com.demo.Test".toClass().apply {
method {
name = "test"
param(StringClass)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
method {
name = "another"
param(IntType)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
}
Below is the current Hook code style.
The following example
// Single Method Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
param(StringClass)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
// Multiple Methods Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
}.all()
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
// A Class with multiple Methods Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
param(StringClass)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
injectMember {
method {
name = "another"
param(IntType)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
这是一个提案。
由于 API 最初设计的仅针对一个 Class
构造一个 Hook 的模式不符合一些例外情况,也存在一些适配性问题,所以我们在下方给出了一种新的 Hook 代码样式。
示例如下
// 单一方法 Hook
"com.demo.Test".toClass().method {
name = "test"
param(StringClass)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
// 多重方法 Hook
"com.demo.Test".toClass().method {
name = "test"
}.all().hook {
before {
// Do something...
}
after {
// Do something...
}
}
// 一个 Class 多个方法 Hook
"com.demo.Test".toClass().apply {
method {
name = "test"
param(StringClass)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
method {
name = "another"
param(IntType)
}.hook {
before {
// Do something...
}
after {
// Do something...
}
}
}
下方是现在采用的 Hook 代码样式。
示例如下
// 单一方法 Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
param(StringClass)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
// 多重方法 Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
}.all()
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
// 一个 Class 多个方法 Hook
"com.demo.Test".toClass().hook {
injectMember {
method {
name = "test"
param(StringClass)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
injectMember {
method {
name = "another"
param(IntType)
}
beforeHook {
// Do something...
}
afterHook {
// Do something...
}
}
}
"com.qidian.QDReader.ui.activity.MoreActivity".toClass().apply {
method {
name = "initWidget"
emptyParam()
returnType = UnitType
}.hook().after {
instance<Activity>().apply {
startActivity(Intent(this, MainActivity::class.java))
}
}
method {
name = "onCreate"
param(BundleClass)
returnType = UnitType
}.hook().after {
instance<Activity>().registerModuleAppActivities()
}
}
class MainActivity : ModuleAppCompatActivity() {
}
使用场景: 在Andorid 9 不管模拟器还是真机,启动Activity,不管继承ModuleAppCompatActivity 还是ModuleActivity 都无法显示布局,其他版本暂时未发现问题。
其他信息:另外尝试过多个不同继承的代理,如果代理的Activity继承的是android.app.Activity则会完全透明显示上一个Activity,继承的是androidx.appcompat.app.AppCompatActivity则会显示代理Activity的基础布局,想要启动的Activity布局不显示。使用布局分析软件发现启动流程是com.qidian.QDReader.ui.activity.SplashActivity > cn.xihan.qdds.MainActivity,日志也没有任何相关内容或者报错,不知道是我对于该功能理解错误还是框架bug?
近期收到部分用户反馈,在模块激活状态正常的情况下,模块功能无法生效,经排查,均在其日志中发现了如下内容:
[ 2023-02-05T19:02:19.473 1000: 30319: 30339 I/LSPosed-Bridge ] [SuperMIUI][E][com.miui.screenrecorder]--> HookClass [xxx] not found
[ 2023-02-05T19:02:19.473 1000: 30319: 30339 E/LSPosed-Bridge ] java.lang.NoClassDefFoundError: Can't find this Class in [dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/base.apk"],nativeLibraryDirectories=[/data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/lib/arm64, /data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]]]:
-> name:[xxx]
Generated by YukiHookAPI#ReflectionTool
at ZQDesigned.江来.雨(Unknown Source:184)
at ZQDesigned.江来.生(Unknown Source:172)
at ZQDesigned.说死.晴(Unknown Source:59)
at ZQDesigned.长利.趋(Unknown Source:59)
at ZQDesigned.说死.生(Unknown Source:50)
at ZQDesigned.有膜.invoke(Unknown Source:32)
at ZQDesigned.来气.晴(Unknown Source:516)
at UTSSG.ZQDesigned.miuiupdater.hook.initHook.handleLoadPackage(Unknown Source:10)
at de.robv.android.xposed.IXposedHookLoadPackage$Wrapper.handleLoadPackage(Unknown Source:2)
at de.robv.android.xposed.callbacks.XC_LoadPackage.call(Unknown Source:6)
at de.robv.android.xposed.callbacks.XCallback.callAll(Unknown Source:26)
at E.afterHookedMethod(Unknown Source:207)
at de.robv.android.xposed.XposedBridge$AdditionalHookInfo.callback(Unknown Source:147)
at LSPHooker_.getClassLoader(Unknown Source:8)
at android.app.LoadedApk.getResources(LoadedApk.java:1402)
at android.app.ContextImpl.createAppContext(ContextImpl.java:3101)
at android.app.ContextImpl.createAppContext(ContextImpl.java:3093)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6871)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2228)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8136)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1028)
[ 2023-02-05T19:02:19.476 1000: 30319: 30342 I/LSPosed-Bridge ] [SuperMIUI][E][com.miui.screenrecorder]--> HookClass [xxx] not found
[ 2023-02-05T19:02:19.477 1000: 30319: 30342 E/LSPosed-Bridge ] java.lang.NoClassDefFoundError: Can't find this Class in [dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/base.apk"],nativeLibraryDirectories=[/data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/lib/arm64, /data/app/~~ihnnvDp5iD3hkGxyW5_E1g==/com.miui.screenrecorder-YB5tO2wcREqzWyojhoPibg==/base.apk!/lib/arm64-v8a, /system/lib64, /system_ext/lib64]]]]:
-> name:[xxx]
Generated by YukiHookAPI#ReflectionTool
at ZQDesigned.江来.雨(Unknown Source:184)
at ZQDesigned.江来.生(Unknown Source:172)
at ZQDesigned.说死.晴(Unknown Source:59)
at ZQDesigned.长利.趋(Unknown Source:430)
at ZQDesigned.说死.生(Unknown Source:50)
at ZQDesigned.有膜.invoke(Unknown Source:32)
at ZQDesigned.来气.晴(Unknown Source:516)
at UTSSG.ZQDesigned.miuiupdater.hook.initHook.handleLoadPackage(Unknown Source:10)
at de.robv.android.xposed.IXposedHookLoadPackage$Wrapper.handleLoadPackage(Unknown Source:2)
at de.robv.android.xposed.callbacks.XC_LoadPackage.call(Unknown Source:6)
at de.robv.android.xposed.callbacks.XCallback.callAll(Unknown Source:26)
at E.afterHookedMethod(Unknown Source:207)
at de.robv.android.xposed.XposedBridge$AdditionalHookInfo.callback(Unknown Source:147)
at LSPHooker_.getClassLoader(Unknown Source:8)
at android.app.LoadedApk.getResources(LoadedApk.java:1402)
at android.app.ContextImpl.createAppContext(ContextImpl.java:3101)
at android.app.ContextImpl.createAppContext(ContextImpl.java:3093)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6871)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2228)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:210)
at android.os.Looper.loop(Looper.java:299)
at android.app.ActivityThread.main(ActivityThread.java:8136)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1028)
目前已知该问题并非100%复现,且Hooker逻辑无误,目标class和method在用户对应的版本中均存在。
Already used xsharedpreference to implement configuration read/write and reread configuration on each hook, but without restarting the hook behaviour doesn't change, would like to know if the api might support this functionality or if the author understands how this can be implemented, respect
关键代码
context.injectModuleAppResources()
ModuleClassLoader.excludeModuleClasses("androidx.appcompat.widget.AppCompatImageView")
ModuleClassLoader.excludeHostClasses("androidx.appcompat.widget.AppCompatImageView")
val moduleContext = context.applyModuleTheme(androidx.appcompat.R.style.Base_Theme_AppCompat, null)
rootView = LayoutInflater.from(moduleContext).inflate(R.layout.test, null)
布局代码
<com.xiaolang.wechathelper.widget.BlurView
android:id="@+id/blur"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@id/bottom_bar"
android:layout_alignBottom="@id/bottom_bar" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/bottom_bar"
android:tag="bottom_bar"
app:abb_tabs="@menu/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>`
异常
java.lang.ClassCastException: androidx.appcompat.widget.AppCompatImageView cannot be cast to androidx.appcompat.widget.AppCompatImageView at com.xiaolang.wechathelper.plugins.Test2BottomBar.getBottomBar(Test2BottomBar.kt:48) at com.xiaolang.wechathelper.plugins.BaseBottomBar.addBottomBar(BaseBottomBar.kt:26) at com.xiaolang.wechathelper.hooker.HomeHooker$onHook$2$1.invoke(HomeHooker.kt:52) at com.xiaolang.wechathelper.hooker.HomeHooker$onHook$2$1.invoke(HomeHooker.kt:46) at com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator$MemberHookCreator$hook$beforeAfterHook$1.afterHookedMember$yukihookapi_core_release( at com.highcapable.yukihookapi.hook.core.api.factory.YukiHookDelegateFactoryKt.callAfterHookedMember(YukiHookDelegateFactory.kt:102) at com.highcapable.yukihookapi.hook.core.api.compat.HookCompatHelper$compat$9.afterHookedMethod(HookCompatHelper.kt:86) at A.BhBfpdEl.eyyOj.qImQN.XposedBridge$LegacyApiSupport.handleAfter(Unknown Source:33) at J.callback(Unknown Source:292) at LSPHooker_.d(Unknown Source:8) at com.tencent.mm.ui.HomeUI.h(Unknown Source:457) at com.tencent.mm.ui.LauncherUI.onResume(Unknown Source:228) at android.app.Instrumentation.callActivityOnResume(Unknown Source:3) at com.highcapable.yukihookapi.hook.xposed.parasitic.activity.delegate.InstrumentationDelegate.callActivityOnResume(InstrumentationDelegate.kt:284) at android.app.Activity.performResume(Unknown Source:68) at android.app.ActivityThread.performResumeActivity(Unknown Source:167) at android.app.ActivityThread.handleResumeActivity(Unknown Source:6) at android.app.servertransaction.ResumeActivityItem.execute(Unknown Source:12) at android.app.servertransaction.ActivityTransactionItem.execute(Unknown Source:4) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(Unknown Source:32) at android.app.servertransaction.TransactionExecutor.execute(Unknown Source:79) at android.app.ActivityThread$H.handleMessage(Unknown Source:172) at android.os.Handler.dispatchMessage(Unknown Source:19) at android.os.Looper.loopOnce(Unknown Source:182) at android.os.Looper.loop(Unknown Source:82) at android.app.ActivityThread.main(Unknown Source:123) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(Unknown Source:11) at com.android.internal.os.ZygoteInit.main(Unknown Source:312)
目前在debug环境中,每一个回调函数的执行都会在日志上输出记录,比如
After Hook Member [public com.android.systemui.qs.QSTileHost(android.content.Context,com.android.systemui.statusbar.phone.StatusBarIconController,com.android.systemui.plugins.qs.QSFactory,java.util.concurrent.Executor,com.android.systemui.shared.plugins.PluginManager,com.android.systemui.tuner.TunerService,javax.inject.Provider,com.android.systemui.dump.DumpManager,com.android.systemui.broadcast.BroadcastDispatcher,java.util.Optional,com.android.systemui.qs.logging.QSLogger,com.android.internal.logging.UiEventLogger,com.android.systemui.settings.UserTracker,com.android.systemui.util.settings.SecureSettings,com.android.systemui.qs.external.CustomTileStatePersister,com.android.systemui.qs.external.TileServiceRequestController$Builder,com.android.systemui.qs.external.TileLifecycleManager$Factory,com.android.systemui.plugins.statusbar.StatusBarStateController,com.android.systemui.qs.MiuiQSTileHostInjector,com.android.systemui.controlcenter.policy.ControlCenterControllerImpl)] done [Default]
但是有一些目标函数仅是简单的替换返回值为true或false之类的也会输出日志。而且io的速度是最慢的,会很影响调试的流畅性,尤其是对手势或者动画部分进行的hook
我的想法是
类似:com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator.MemberHookCreator.Result#ignoredHookingFailure
添加一个忽略log输出的方法
然后在:com.highcapable.yukihookapi.hook.core.YukiMemberHookCreator.MemberHookCreator#onHookLogMsg
添加多一个判断
当前的环境类似:
@YukiGenerateApi
enum class HookEntryType {
/** 装载 Zygote */
ZYGOTE,
/** 装载 APP */
PACKAGE,
/** 装载 Resources Hook */
RESOURCES
}
我发现com.highcapable.yukihookapi.hook.entity.YukiBaseHooker#onHook
被设计在PACKAGE
和 RESOURCES
中执行,所以才会导致同样的日志会输出两个,当然hook方法应该仅对PACKAGE
生效。所以用作者的sdk是能正常使用。但是原因和上面的log一样,在高频调用的函数下我没办法用作者的sdk进行hook,只好根据需求用原生api设计了自己的sdk:感觉风格也挺不错的hh
"com.test.Test".hook {
// 第一个方法
method {
name = "method"
}.before {
// doSomeThings
}.after {
// doSomeThings
}
// 第二个方法
method {
name = "method"
}.before {
// doSomeThings
}.after {
// doSomeThings
} // 链式调用 方法异常监听、设置TAG、忽略异常等
} // 链式调用 类异常监听,如找不到类之类的
由于默认在PACKAGE
和 RESOURCES
中执行的原因,上面的自定义方式hook会被执行两遍,而且在运行时也会执行两遍。。。
我希望可以在Hooker下可以提供一个能判断当前环境的api,或者让我可以在hook时使用变量设置自己的hook环境
if androidx is enabled, building time is so long and big apk size so i want remove androidx but i cannot remove androidx because used androidx this project
how to remove androidx or close this issues if cant remove androidx
android.useAndroidX=false
sorry for bad english
The API currently uses annotations and KSP to complete the automatic generation of entry class code.
Some codes still need to be manually configured, such as meta-data
of the Xposed Module.
Currently, Android projects are managed uniformly using the Gradle build system, so we decided to remove KSP, launch a Gradle plugin to fully automate the entire Xposed Module configuration, and use YAML as a new configuration file language for management.
The following example
generator:
target-type: COMPAT
use-activity-proxy: true
use-data-channel: true
xposed-module:
package-name: com.mydemo
java-entry: com.mydemo.hook.MyModule
native-entry: mymodule # libmymodule.so
description: My new Xposed Module.
api-level: 100
host-scopes:
com.android.systemui
com.android.phone
The Gradle plugin will automatically generate various types of Xposed Modules based on the configuration file content, and automatically generate corresponding types of configuration files, such as traditional Rovo89 and modern libxposed as well as more compatible Xposed Module types.
The style of the configuration file is only a first draft and will be modified later for actual functionality.
API 目前使用注解配合 KSP 来完成入口类代码的自动生成,部分代码依然需要手动进行配置,例如 Xposed 模块的 meta-data
。
目前 Android 项目统一使用 Gradle 构建系统进行管理,所以我们决定去掉 KSP,推出一个 Gradle 插件来实现整个 Xposed 模块配置的完全自动化,并采用 YAML 作为新的配置文件语言进行管理。
示例如下
generator:
target-type: COMPAT
use-activity-proxy: true
use-data-channel: true
xposed-module:
package-name: com.mydemo
java-entry: com.mydemo.hook.MyModule
native-entry: mymodule # libmymodule.so
description: My new Xposed Module.
api-level: 100
host-scopes:
com.android.systemui
com.android.phone
Gradle 插件会根据配置文件内容自动生成各种类型的 Xposed 模块,自动生成对应类型的配置文件,例如传统的 Rovo89 和现代的 libxposed 以及更多兼容的 Xposed 模块类型。
配置文件的样式只是一个初稿,后期会针对实际功能进行修改。
刚学XP没多久,好多不是很懂233,findclass一个类,hook了A方法,替换A方法内容为 调用这个类的B方法,该如何操作,XP里大概是XposedHelpers.callMethod
在win下无法识别路径,编译失败。在linux下可正常编译。
test项目 MIUINativeNotifyIcon
> Task :app:kspDebugKotlin FAILED
e: [ksp] [YukiHookAPI] Project Source Path "src\main" not matched
Looking for help? see https://github.com/fankes/YukiHookAPI/wiki/%E4%BD%9C%E4%B8%BA-Xposed-%E6%A8%A1%E5%9D%97%E4%BD%BF%E7%94%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE
e: [ksp] java.lang.RuntimeException: [YukiHookAPI] Project Source Path "src\main" not matched
Looking for help? see https://github.com/fankes/YukiHookAPI/wiki/%E4%BD%9C%E4%B8%BA-Xposed-%E6%A8%A1%E5%9D%97%E4%BD%BF%E7%94%A8%E7%9A%84%E7%9B%B8%E5%85%B3%E9%85%8D%E7%BD%AE
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.error(YukiHookXposedProcessor.kt:89)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.access$error(YukiHookXposedProcessor.kt:49)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1$injectAssets$1.invoke(YukiHookXposedProcessor.kt:172)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1$injectAssets$1.invoke(YukiHookXposedProcessor.kt:162)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.environment(YukiHookXposedProcessor.kt:78)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.environment$default(YukiHookXposedProcessor.kt:76)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.injectAssets(YukiHookXposedProcessor.kt:162)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.access$injectAssets(YukiHookXposedProcessor.kt:49)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1$injectProcess$1.invoke$lambda-4$fetchKSClassDeclaration(YukiHookXposedProcessor.kt:119)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1$injectProcess$1.invoke(YukiHookXposedProcessor.kt:148)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1$injectProcess$1.invoke(YukiHookXposedProcessor.kt:107)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.environment(YukiHookXposedProcessor.kt:78)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.environment$default(YukiHookXposedProcessor.kt:76)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.injectProcess(YukiHookXposedProcessor.kt:107)
at com.highcapable.yukihookapi_ksp_xposed.YukiHookXposedProcessor$create$1.process(YukiHookXposedProcessor.kt:99)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$4$1.invoke(KotlinSymbolProcessingExtension.kt:195)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension$doAnalysis$4$1.invoke(KotlinSymbolProcessingExtension.kt:193)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.handleException(KotlinSymbolProcessingExtension.kt:287)
at com.google.devtools.ksp.AbstractKotlinSymbolProcessingExtension.doAnalysis(KotlinSymbolProcessingExtension.kt:193)
at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:120)
at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:96)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:262)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:53)
at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:113)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:253)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:100)
at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli$default(KotlinToJVMBytecodeCompiler.kt:58)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:170)
at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:52)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:92)
at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1618)
at jdk.internal.reflect.GeneratedMethodAccessor97.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
at java.base/java.lang.Thread.run(Thread.java:832)
e: Error occurred in KSP, check log for detail
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:kspDebugKotlin'.
> Compilation error. See log for more details
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 1s
14 actionable tasks: 1 executed, 13 up-to-date
因为还不太了解这个开发框架的Hook生命周期,所以特地来问问。
主要是想在打日志的时候顺带输出packageName
,然后又懒得每次传参,所以盯上了debugTag
这个config
。
按照文档配置项目,报错如下:
Plugin [id: 'com.google.devtools.ksp', version: '1.7.20-1.0.8'] was not found in any of the following sources
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Plugin Repositories (could not resolve plugin artifact 'com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin:1.7.20-1.0.8')
Searched in the following repositories:
Gradle Central Plugin Repository
Google
MavenRepo (3 s 807 ms)
所使用的配置文件如下:
build.gradle(app)
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
// 作为 Xposed 模块使用务必添加,其它情况可选
id 'com.google.devtools.ksp' version '1.7.20-1.0.8'
}
android {
namespace 'com.example.yukihookdemo'
compileSdk 32
defaultConfig {
applicationId "com.example.yukihookdemo"
minSdk 24
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// 基础依赖
implementation 'com.highcapable.yukihookapi:api:1.2.0'
// 作为 Xposed 模块使用务必添加,其它情况可选
compileOnly 'de.robv.android.xposed:api:82'
// 作为 Xposed 模块使用务必添加,其它情况可选
ksp 'com.highcapable.yukihookapi:ksp-xposed:1.2.0'
}
build.gradle(project)
plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.20' apply false
}
settings.gradle
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
// 作为 Xposed 模块使用务必添加,其它情况可选
maven { url 'https://api.xposed.info/' }
// MavenCentral 有 2 小时缓存,若无法集成最新版本请添加此地址
maven { url 'https://s01.oss.sonatype.org/content/repositories/releases/' }
}
}
rootProject.name = "YukiHookDemo"
include ':app'
对于gradle我不是很熟悉,具体的配置也参考了一些使用YukiHookAPI的其他项目,如 MiFreeFormX,但问题依旧没解决,感谢任何的帮助🙏。
Device: Pixel 7 Pro Android 13 May update
App: https://github.com/KitsunePie/AppErrorsTracking
Log file: log.log
在Activity中,使用以下代码保存配置。
val strongMode = sharedPreferences.getBoolean("strong_mode", false)
modulePrefs.putBoolean("strong_mode", strongMode)
保存后,在Activity中再使用modulePrefs
的getBoolean
方法进行检查,发现设置已经保存。
但是在hook中,prefs.getBoolean
始终返回默认值false,perfs.all
返回为空。
Target API 32,运行系统API 31,已经按照文档中说明进行配置,也参考了demo中的代码,使用PrefsData
问题依然出现,使用String
进行数据保存问题依旧。
请问是我用法不对,还是这是一个bug。
为了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()
}
你好,我正在浏览并尝试使用Yukihook,但是我在api中没有发现类似于XposedHelper.set...Field的实现,所以想请问使用Yukihook的api我该如何实现。是否应该使用原生的Xposed api
能够跳转到设定的proxyClassName值,但是ModuleAppActivity继承的类,onCreate等函数并不会被创建。
onAppLifecycle {
attachBaseContext { baseContext, hasCalledSuper ->
if (hasCalledSuper) run {
appClassLoader = baseContext.classLoader
// YLog.warn("HookEntry attachBaseContext success")
}
}
onCreate {
// 注入模块资源
injectModuleAppResources()
// 注册模块activity
registerModuleAppActivities()
}
而注册的activiy代码为:
class HOOKActivity : ModuleAppActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
YLog.info("HOOKActivity onCreate")
}
}
intent进入活动使用的是:
val intent=Intent(context, HOOKActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
YLog.info("intent:$intent")
能够打印intent的值,并且能根据设定的proxyClassName进入到宿主app的指定活动,但初始化代码并不会完成。
可能是因为使用
YukiXposedEvent.events {
onHandleLoadPackage {
XposedHelpers.findAndHookMethod(className.toClass(),
methodName,
object : XC_MethodHook() {
override fun afterHookedMethod(param: MethodHookParam?) {
来获取context的方法,用yuki框架自带的挂载不上,少部分函数能挂载,但无法获取context和其他值。
复现步骤:
loadApp(name = "com.wibo.bigbang.ocr") {
findClass(name = "okhttp3.Request\$Builder").hook {
injectMember {
method {
name = "addHeader"
param(StringClass,StringClass)
returnType = "okhttp3.Request\$Builder"
}
beforeHook {
val a1 = args().first().string();
loggerD("checkEqual", "", LoggerType.LOGD)
val a2 = args().first().string();
loggerD(
tag = "checkEqual",
msg = "${a1 == a2} $a1 $a2"
)
}
}
}
}
大概有这么一个场景,需要 Hook 的目标 App 在旧版本和新版本中存在同名但参数列表不同的方法:
// 这是旧版本 App 的方法
public static String methodName(Context context, boolean z, boolean z2)
// 这是新版本 App 的方法
public static String methodName(Context context, View view, boolean z, boolean z2, Intent intent, int i2)
我需要在目标 App 原本的 methodName
方法执行前进行一些操作。尽管新版本增加了几个参数,但这些并不是我需要用到的。也就是说除了需要 Hook 的方法签名不同之外,我不需要对我自行实现的模块中的方法进行修改。
YukiHookAPI 的文档中指出“存在多个 BaseFinder.IndexTypeCondition
时除了 order
只会生效最后一个”,这样的话我就需要将完全相同的代码复制两遍,比较繁琐。
因此,不知道能否允许通过多个 IndexTypeCondition
来查找方法呢?或者我的这个需求有其他的实现方法吗?
突然又发现了一个bug,before 和 after 的回调里替换 result 无效。
"com.android.systemui.qs.QSTileHost".hook {
injectMember {
method { name = "createTile"; param(StringClass) }
beforeHook {
if (args(0).string() == "nfc") {
val tile = XposedHelpers.callMethod(instance, "createTile", "bt")
result = tile
}
}
}
}
}
无效
XposedHelpers.findAndHookMethod("com.android.systemui.qs.QSTileHost".toClass(), "createTile", String::class.java, object : XC_MethodHook() {
override fun beforeHookedMethod(param: MethodHookParam) {
super.beforeHookedMethod(param)
if (param.args[0] == "nfc") {
val tile = XposedHelpers.callMethod(param.thisObject, "createTile", "bt")
param.result = tile
}
}
})
有效
test
对kotlin不熟悉,大概看了下源码,好像是必须指定,广告的action中带有packageName的hashcode?
hook的目标不限定,无法指定packagename
Could you please help? In the module app, I set prefs. But it doesn't work in the host app. The hook is working, but prefs.getString(Constants.KEY_ID)
always returns an empty string.
Module App
class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun onCreate() {
...
val button = findViewById<Button>(R.id.setup)
button.setOnClickListener {
prefs().edit {
putString(KEY_ID, androidIdText.text.toString()).apply()
}
}
}
...
Host App
override fun onHook() = encase {
classOf<Settings.Secure>().hook {
injectMember {
method {
name = "getString"
}.all()
beforeHook {
result = prefs.getString(Constants.KEY_ID)
}
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.naggi.devicex">
<application
android:name=".application.DefaultApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppDefault"
tools:targetApi="31">
<meta-data
android:name="xposedsharedprefs"
android:value="true" />
<meta-data
android:name="xposedscope"
android:resource="@array/module_scope" />
<meta-data
android:name="xposeddescription"
android:value="@string/xposed_desc" />
<meta-data
android:name="xposedminversion"
android:value="93" />
<activity
android:name=".ui.activity.MainActivity"
android:exported="true"
android:screenOrientation="behind">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="de.robv.android.xposed.category.MODULE_SETTINGS" />
</intent-filter>
</activity>
<activity-alias
android:name=".Home"
android:exported="true"
android:label="@string/app_name"
android:screenOrientation="behind"
android:targetActivity=".ui.activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>
</manifest>
驾校一点通,不进行任何Hook操作,只要勾选了作用域就会卡第一屏
部分用户反馈YukiHookPrefsBridge.Editor无法保存键值
//Kotlin
val global: PrefsData<Boolean> = PrefsData("global", true)
prefs().edit {
putLong("id", 0)
put(global, item.isChecked)
}
//Java
prefs.edit().putLong("id", 0).apply();
Hello, I have a few questions about the new code style and I don't know where else to ask if not here. I managed to migrate my module's syntax to 1.2.0 (Mesa-Labs-Archive/KnoxPatch@ae4caaa), but I currently have two doubts:
onHookClassNotFoundFailure
?I'm currently handling this exception to show a prettier error message in logs when the class to hook is not found:
findClass("com.samsung.android.security.keystore.AttestParameterSpec").hook {
injectMember {
method {
name = "isVerifiableIntegrity"
emptyParam()
returnType = BooleanType
}
replaceToTrue()
}
}.onHookClassNotFoundFailure {
loggerE(msg = "$TAG: couldn't access class " +
"com.samsung.android.security.keystore.AttestParameterSpec " +
"(${it.javaClass.simpleName})")
}
This is because the class I'm trying to hook isn't part of android frameworks (framework.jar) but rather of a separate library (samsungkeystoreutils.jar), that if not declared in the manifest of the app:
<uses-library android:name="samsungkeystoreutils" android:required="false" />
will make hooking the class to fail. This is what I'm currently using, but I'm not sure if it's correct:
"com.samsung.android.security.keystore.AttestParameterSpec".toClass()
.method {
name = "isVerifiableIntegrity"
emptyParam()
returnType = BooleanType
}.hook {
replaceToTrue()
}.onAllFailure {
YLog.error(msg = "$TAG: couldn't access class " +
"com.samsung.android.security.keystore.AttestParameterSpec " +
"(${it.javaClass.simpleName})")
}
.toClass()
twice?With the old syntax, I could hook multiple methods of the same class like this:
findClass("android.os.SemSystemProperties").hook {
injectMember {
method {
name = "get"
param(String::class.java)
returnType = StringClass
}
beforeHook {
val key: String = args(0).string()
// Fixes:
// - Legacy Secure Wi-Fi (ICD)
// - SPCMAgent (SAK)
when (key) {
"ro.build.type" -> result = "eng"
"ro.security.keystore.keytype" -> result = ""
}
}
}
injectMember {
method {
name = "get"
param(String::class.java, String::class.java)
returnType = StringClass
}
beforeHook {
val key: String = args(0).string()
val def: String = args(1).string()
if (key == "ro.boot.flash.locked"
|| key == "ro.boot.verifiedbootstate"
|| key == "ro.boot.warranty_bit"
|| key == "ro.config.iccc_version") {
result = def
}
}
}
}
With the new syntax I ended up with something like this:
"android.os.SemSystemProperties".toClass()
.method {
name = "get"
param(String::class.java)
returnType = StringClass
}.hook {
before {
val key: String = args(0).string()
// Fixes:
// - Legacy Secure Wi-Fi (ICD)
// - SPCMAgent (SAK)
when (key) {
"ro.build.type" -> result = "eng"
"ro.security.keystore.keytype" -> result = ""
}
}
}
"android.os.SemSystemProperties".toClass()
.method {
name = "get"
param(String::class.java, String::class.java)
returnType = StringClass
}.hook {
before {
val key: String = args(0).string()
val def: String = args(1).string()
if (key == "ro.boot.flash.locked"
|| key == "ro.boot.verifiedbootstate"
|| key == "ro.boot.warranty_bit"
|| key == "ro.config.iccc_version") {
result = def
}
}
}
The entry class currently used by the API is designed with reference to the working mode and principle of the native Xposed API.
The biggest problem encountered so far is the logical confusion of the entry class.
For example, we cannot clearly get a Host App's lifecycle from the beginning.
The current API solution is to use onAppLifecycle
to monitor the Host App's lifecycle method.
So we may adopt a new entry class design pattern in later versions to bring better experience to Xposed Module developers.
The following example
@YukiXposedModule
class YourModule(hookContext: HookContext) : YukiBaseModule(hookContext: HookContext) {
init {
YukiHookAPI.configs {
isDebug = false
}
// HookContext means the global system context
hookContext.packageName // "android"
// Listen to the loading events of the native Xposed API
YukiXposedEvent.events {
onInitZygote {
// The it object is [StartupParam]
}
onHandleLoadPackage {
// The it object is [LoadPackageParam]
}
onHandleInitPackageResources {
// The it object is [InitPackageResourcesParam]
}
}
}
override fun onHook() {
loadApp(name = "com.example.demo") {
// Do something...
}
}
}
Below are the current entry class schemes.
The following example
@InjectYukiHookWithXposed
object HookEntry : IYukiHookXposedInit {
override fun onInit() = configs {
isDebug = false
}
override fun onHook() = encase {
loadApp(name = "com.example.demo") {
// Do something...
}
}
override fun onXposedEvent() {
// Listen to the loading events of the native Xposed API
YukiXposedEvent.events {
onInitZygote {
// The it object is [StartupParam]
}
onHandleLoadPackage {
// The it object is [LoadPackageParam]
}
onHandleInitPackageResources {
// The it object is [InitPackageResourcesParam]
}
}
}
}
API 目前采用的入口类参考了原生 Xposed API 的工作模式与原理而设计,目前遇到的最大的问题就是入口类逻辑上的混乱问题。
例如我们无法从开始就明确地得到一个宿主的生命周期,目前 API 给出的解决方案是使用 onAppLifecycle
来监听宿主的生命周期方法。
所以我们可能会在后期的版本采用一种新的入口类设计模式来给 Xposed 模块开发者带来更好的体验。
示例如下
@YukiXposedModule
class YourModule(hookContext: HookContext) : YukiBaseModule(hookContext: HookContext) {
init {
YukiHookAPI.configs {
isDebug = false
}
// HookContext 即全局系统上下文
hookContext.packageName // "android"
// 监听原生 Xposed API 的装载事件
YukiXposedEvent.events {
onInitZygote {
// it 对象即 [StartupParam]
}
onHandleLoadPackage {
// it 对象即 [LoadPackageParam]
}
onHandleInitPackageResources {
// it 对象即 [InitPackageResourcesParam]
}
}
}
override fun onHook() {
loadApp(name = "com.example.demo") {
// Do something...
}
}
}
以下是目前采取的入口类方案。
示例如下
@InjectYukiHookWithXposed
object HookEntry : IYukiHookXposedInit {
override fun onInit() = configs {
isDebug = false
}
override fun onHook() = encase {
loadApp(name = "com.example.demo") {
// Do something...
}
}
override fun onXposedEvent() {
// 监听原生 Xposed API 的装载事件
YukiXposedEvent.events {
onInitZygote {
// it 对象即 [StartupParam]
}
onHandleLoadPackage {
// it 对象即 [LoadPackageParam]
}
onHandleInitPackageResources {
// it 对象即 [InitPackageResourcesParam]
}
}
}
}
My module implements the logic of saving some parameters to disk using YukiHookPrefsBridge. When the method hook I need is called, I read the stored value using YukiHookPrefsBridge and substitute it as the return value. The problem is that in the debug build everything works, but as soon as I switch to the release, reading the value stops working and starts returning to the default value specified in the shared preferences. I can’t figure out what the problem is, maybe I need to specify some additional rules for obfuscation (I use R8 in the release build)?
美团众包的版本号:9.6.5.3347 (没加固)
用 @KyuubiRan 的 EzXHelper 去 Hook 有效果
具体代码:
object TestHooker : YukiBaseHooker() {
override fun onHook() {
"com.meituan.banma.main.activity.MainActivity".hook {
injectMember {
method { name("onCreate") }
afterHook {
loggerD(msg = "YukiHook:执行后")
}
}
}
"com.meituan.banma.main.activity.MainActivity".toClass()
.method { name("onCreate") }
.give()!!.createHook {
after {
loggerD(msg = "EzXHelper:执行后")
}
}
}
}
log输出:
YukiHook:Find Method [public void com.meituan.banma.main.activity.MainActivity.onCreate(android.os.Bundle)] takes 0ms [Default]
但没执行后
EzXHelper: 有执行后
试过只用其中一个Hook,效果一样
EzXHelper 底层也是用 XposedBridge#hookMethod 的方法实现的
框架是Lsposed 1.8.5 Zygisk
实在搞不清楚是哪里出了问题
现在 LSP 团队出新的 api 和 模块配置方法了,这是示例
可以适配一下吗,不然 LSP 的日志每次都提示
[ LSPosed-Bridge ] Loading legacy module xxx
用如下代码Hook 腾讯的广告会概率出现如题的闪退(点击桌面图标直接闪退,这个时候hook貌似都没加载到这,关掉这个就不会),特别是Android 9 以上并且在MIUI系统最为严重,我实在是找不到原因,想来问问您
/**
* 免广告领取奖励
*/
fun PackageParam.freeAdReward(versionCode: Int) {
when (versionCode) {
in 854..878 -> {
findClass("com.qq.e.comm.managers.plugin.PM").hook {
injectMember {
method {
name = "getPluginClassLoader"
emptyParam()
returnType = "java.lang.ClassLoader".toClass()
}
afterHook {
val classLoader = result as? ClassLoader
classLoader?.let {
/**
* com.qq.e.comm.plugin.tangramrewardvideo.g.K() : void
* int v = this.p.aQ()
*/
findClass(
"com.qq.e.comm.plugin.tangramrewardvideo.c.b", it
).hook {
injectMember {
method {
name = "p"
emptyParam()
returnType = IntType
}
replaceTo(0)
}
}
/**
* com.qq.e.comm.plugin.tangramrewardvideo.f.onAfterCreate
*/
findClass("com.qq.e.comm.plugin.tangramrewardvideo.f", it).hook {
injectMember {
method {
name = "ao"
emptyParam()
returnType = UnitType
}
afterHook {
val r = instance.getParam<Any>("R")
val g = r?.getParam<Any>("g")
val d = g?.getView<ImageView>("d")
d?.postDelayed(
{
instance.current {
method {
name = "s"
emptyParam()
}.call()
method {
name = "onBackPressed"
emptyParam()
}.call()
}
},
(optionEntity.mainOption.freeAdRewardAutoExitTime * 1000).toLong()
)
}
}
}
findClass(
"com.qq.e.comm.plugin.tangramrewardvideo.widget.j", it
).hook {
injectMember {
method {
name = "b"
paramCount(1)
returnType = UnitType
}
afterHook {
val h = instance.getParam<TextView>("h")
h?.let { tvh ->
if (tvh.text == "跳过视频") {
tvh.performClick()
}
}
}
}
}
findClass(
"com.qq.e.comm.plugin.tangramrewardvideo.c.a", it
).hook {
injectMember {
method {
name = "aQ"
emptyParam()
returnType = IntType
}
replaceTo(-0x64)
}
}
findClass(
"com.qq.e.comm.plugin.tangramrewardvideo.g", it
).hook {
injectMember {
method {
name = "L"
emptyParam()
returnType = IntType
}
replaceTo(0)
}
}
}
}
}
}
findClass("com.qq.e.comm.managers.setting.SM").hook {
injectMember {
method {
name = "getInteger"
param(StringClass, IntType)
returnType = IntType
}
afterHook {
args(1).set(0)
}
}
}
}
else -> "免广告领取奖励".printlnNotSupportVersion(versionCode)
}
}
[ 2023-03-04T12:28:18.571 0: 857: 1336 I/LSPosedLogcat ] New log file: /data/adb/lspd/log/verbose_2023-03-04T12:28:18.570797.log [ 2023-03-04T12:28:22.897 10263: 22360: 22360 I/LSPosed ] Loading xposed for com.qidian.QDReader/10263 [ 2023-03-04T12:28:22.900 10263: 22360: 22360 I/LSPosed-Bridge ] Loading module cn.xihan.qdds from /data/app/~~U4MIEf5JRh2Q2uwjeh9XVw==/cn.xihan.qdds-rvGW3iNliX6mnvQni3zWeg==/base.apk [ 2023-03-04T12:28:22.911 10263: 22360: 22360 I/LSPosed-Bridge ] Loading class cn.xihan.qdds.HookEntry_YukiHookXposedInit [ 2023-03-04T12:28:23.389 10263: 22360: 9897 F/libc ] Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9897 (pool-68-thread-), pid 22360 (qidian.QDReader) [ 2023-03-04T12:28:23.705 10263: 10153: 10153 I/LSPosed ] Loading xposed for com.qidian.QDReader:pushcore/10263 [ 2023-03-04T12:28:23.710 10263: 10153: 10153 I/LSPosed-Bridge ] Loading module cn.xihan.qdds from /data/app/~~U4MIEf5JRh2Q2uwjeh9XVw==/cn.xihan.qdds-rvGW3iNliX6mnvQni3zWeg==/base.apk [ 2023-03-04T12:28:23.731 10263: 10153: 10153 I/LSPosed-Bridge ] Loading class cn.xihan.qdds.HookEntry_YukiHookXposedInit [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Build fingerprint: 'Xiaomi/venus/venus:13/TKQ1.220829.002/V14.0.23.2.23.DEV:user/release-keys' [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Revision: '0' [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] ABI: 'arm64' [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Timestamp: 2023-03-04 12:28:23.454317380+0800 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Process uptime: 318s [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Cmdline: com.qidian.QDReader [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] pid: 22360, tid: 9897, name: pool-68-thread- >>> com.qidian.QDReader <<< [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] uid: 10263 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr -------- [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] Abort message: 'JNI DETECTED ERROR IN APPLICATION: java_object == null in call to MonitorEnter from boolean org.lsposed.lspd.nativebridge.HookBridge.hookMethod(java.lang.reflect.Executable, java.lang.Class, int, java.lang.Object)' [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x0 0000000000000000 x1 00000000000026a9 x2 0000000000000006 x3 0000007074bf0df0 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x4 5151441f43445342 x5 5151441f43445342 x6 5151441f43445342 x7 7f7f7f7f7f7f7f7f [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x8 00000000000000f0 x9 00000071a94fecb0 x10 0000000000000001 x11 00000071a9573748 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x12 0000007082347030 x13 0000000000deb4c8 x14 0000000000deb388 x15 0000000034155555 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x16 00000071a95dea70 x17 00000071a95b73d0 x18 0000007072de0000 x19 0000000000005758 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x20 00000000000026a9 x21 00000000ffffffff x22 000000000000006e x23 0000007074bf1008 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x24 000000710c616000 x25 0000007074bf1000 x26 0000007074bf2000 x27 000000710c40b038 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] x28 000000710c40b268 x29 0000007074bf0e70 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] lr 00000071a95646c8 sp 0000007074bf0dd0 pc 00000071a95646f4 pst 0000000000001000 [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] backtrace: [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #00 pc 000000000008d6f4 /apex/com.android.runtime/lib64/bionic/libc.so (abort+168) (BuildId: 2bb0d7188c0db2e8beecb24658ba9d71) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #01 pc 000000000061f104 /apex/com.android.art/lib64/libart.so (art::Runtime::Abort(char const*)+1204) (BuildId: 4ecc70344e180e7eb5404cd12fba6904) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #02 pc 0000000000017114 /apex/com.android.art/lib64/libbase.so (android::base::SetAborter(std::__1::function&&)::$_3::__invoke(char const*)+84) (BuildId: ef369bfbad96b532c6d8e0b144a68b96) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #03 pc 0000000000016648 /apex/com.android.art/lib64/libbase.so (android::base::LogMessage::~LogMessage()+356) (BuildId: ef369bfbad96b532c6d8e0b144a68b96) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #04 pc 0000000000459d44 /apex/com.android.art/lib64/libart.so (art::JavaVMExt::JniAbort(char const*, char const*)+2368) (BuildId: 4ecc70344e180e7eb5404cd12fba6904) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #05 pc 00000000004bfe50 /apex/com.android.art/lib64/libart.so (art::JNI::MonitorEnter(_JNIEnv*, _jobject*)+1380) (BuildId: 4ecc70344e180e7eb5404cd12fba6904) [ 2023-03-04T12:28:24.261 10263: 9996: 9996 F/DEBUG ] #06 pc 0000000000013c18 /memfd:jit-cache (deleted) [ 2023-03-04T12:28:26.640 10263: 22361: 22361 I/LSPosed ] Loading xposed for com.qidian.QDReader/10263 [ 2023-03-04T12:28:26.642 10263: 22361: 22361 I/LSPosed-Bridge ] Loading module cn.xihan.qdds from /data/app/~~U4MIEf5JRh2Q2uwjeh9XVw==/cn.xihan.qdds-rvGW3iNliX6mnvQni3zWeg==/base.apk [ 2023-03-04T12:28:26.654 10263: 22361: 22361 I/LSPosed-Bridge ] Loading class cn.xihan.qdds.HookEntry_YukiHookXposedInit [ 2023-03-04T12:28:27.289 10263: 11141: 11141 I/LSPosed ] Loading xposed for com.qidian.QDReader:pushcore/10263 [ 2023-03-04T12:28:27.294 10263: 11141: 11141 I/LSPosed-Bridge ] Loading module cn.xihan.qdds from /data/app/~~U4MIEf5JRh2Q2uwjeh9XVw==/cn.xihan.qdds-rvGW3iNliX6mnvQni3zWeg==/base.apk [ 2023-03-04T12:28:27.311 10263: 11141: 11141 I/LSPosed-Bridge ] Loading class cn.xihan.qdds.HookEntry_YukiHookXposedInit [ 2023-03-04T12:28:32.698 0: 857: 23105 I/LSPosedLogcat ] !!refresh_verbose!!
编写的模块在Xpatch不生效
框架中貌似没有封装获取app资源的方法
例如我要获取app的string,要经过以下步骤:
val id = appResources!!.getIdentifier(
"example_name",
"string",
packageName
)
appResources.getString(id)
这样略显麻烦,有什么优雅的方法可以代替吗
I manually use method loggerI in beforehook, but it seems that it does not work properly and I can’t find log in LSPosed. Could I use it in beforehook?
在 resources().hook -> injectResource -> conditions 的相关类型中希望添加Long数据类型
It looks like DataChannel does not support Android 14. Crashes when trying to register receivers on YukiHookDataChannel class line 211.
Possible fix - TheranicaRD-SW/Fetch@f82b752
targetSdk = 34
YukiHookAPI 1.1.11
Stacktrace:
FATAL EXCEPTION: main
Process: ru.bluecat.alfabankpatcher, PID: 6272
java.lang.RuntimeException: Unable to create application ru.bluecat.alfabankpatcher.ui.App: java.lang.SecurityException: ru.bluecat.alfabankpatcher: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7003)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: java.lang.SecurityException: ru.bluecat.alfabankpatcher: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
at android.os.Parcel.createException(Parcel.java:3041)
at android.os.Parcel.readException(Parcel.java:3024)
at android.os.Parcel.readException(Parcel.java:2966)
at android.app.IActivityManager$Stub$Proxy.registerReceiverWithFeature(IActivityManager.java:5684)
at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1852)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1792)
at android.app.ContextImpl.registerReceiver(ContextImpl.java:1780)
at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:755)
at com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel.register$yukihookapi_release(YukiHookDataChannel.kt:211)
at com.highcapable.yukihookapi.hook.xposed.channel.YukiHookDataChannel.register$yukihookapi_release$default(YukiHookDataChannel.kt:208)
at com.highcapable.yukihookapi.hook.xposed.application.ModuleApplication.onCreate(ModuleApplication.kt:86)
at ru.bluecat.alfabankpatcher.ui.App.onCreate(App.kt:16)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1316)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6998)
at android.app.ActivityThread.-$$Nest$mhandleBindApplication(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2236)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:205)
at android.os.Looper.loop(Looper.java:294)
at android.app.ActivityThread.main(ActivityThread.java:8177)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.am.ActivityManagerService.registerReceiverWithFeature(ActivityManagerService.java:13908)
at android.app.IActivityManager$Stub.onTransact(IActivityManager.java:2570)
at com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2720)
at android.os.Binder.execTransactInternal(Binder.java:1339)
at android.os.Binder.execTransact(Binder.java:1275)
For Example:
// getSystemProperty is a method read var from android.os.SystemProperties
val x: String? = getSystemProperty("debug.xxx")
if (x != null && x != "") {
result = x.split(",")
}
// logger.debug is some wrap of YukiHookLogger
logger.debug(
"Invoke X", "${appInfo.packageName}, $x, $result"
)
When debug.xxx
is set to 1,2
the log out pkg_name, 1,2, [1, 2]
, but upgrade to 1.1.6 it became pkg_name, 1,2, [x, y]
, the x and y is it's original value.
Using System.out.println
has same phenomenon.
关于sp的使用疑问:
1.在点击事件中写入状态-代码不报错正常运行;
2.读取状态报错,YukiHookPrefsBridge missing Context instance
我想在软件设置的时候加个开关如上图,然后在hook的时候根据开关来判断是否执行,如下,不知是否可行;
目前图一读取的时候就报错了,想知道一般都是怎么使用的,谢谢
Originally posted by @wangyuan0217 in #35 (comment)
项目地址 https://github.com/cledwynl/mbga/tree/34c32c470ba26153242064f5ba765eddf9611de3
模块的流程是这样的:
初步排查下来发现是模块的BroadcastReceiver在收到广播后调用的YukiHookDataChannel.isCurrentBroadcast
中获取不到task
接着我尝试将模块Activity加上了
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
再试就成功收到消息了。有没有什么方式可以让不展示在启动器的Activity接收消息
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.