Skip to content

Commit

Permalink
feat: 完善 WankkoRee#33
Browse files Browse the repository at this point in the history
  • Loading branch information
WankkoRee committed May 8, 2023
1 parent 516e659 commit e500a72
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 110 deletions.
6 changes: 3 additions & 3 deletions app/src/main/java/cn/wankkoree/xp/webviewpp/activity/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ class App : AppCompatActivity() {
menuInflater.inflate(R.menu.app_toolbar_menu, menu)
with(prefs("apps_$pkg")) {
menu.findItem(R.id.app_toolbar_menu_debug_mode).isChecked = get(AppSP.debug_mode)
menu.findItem(R.id.app_toolbar_menu_byass_packer).isChecked = get(AppSP.bypass_packer)
menu.findItem(R.id.app_toolbar_menu_app_is_protected).isChecked = get(AppSP.app_is_protected)
}
setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
Expand All @@ -301,8 +301,8 @@ class App : AppCompatActivity() {
}.show()
}
}
R.id.app_toolbar_menu_byass_packer -> {
modulePrefs("apps_$pkg").put(AppSP.bypass_packer, !menuItem.isChecked)
R.id.app_toolbar_menu_app_is_protected -> {
prefs("apps_$pkg").edit { put(AppSP.app_is_protected, !menuItem.isChecked) }
}
R.id.app_toolbar_menu_configure_in_other_apps -> {
startActivity(Intent.createChooser(
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/cn/wankkoree/xp/webviewpp/data/AppSP.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.highcapable.yukihookapi.hook.xposed.prefs.data.PrefsData
object AppSP {
val is_enabled = PrefsData("is_enabled", false)
val debug_mode = PrefsData("debug_mode", false)
val bypass_packer = PrefsData("bypass_packer", false)
val app_is_protected = PrefsData("app_is_protected", false)
/**
* 哈希去重的 Hook 规则名称集合,以|分隔,并且有多个对应的"hook_entry_$name"子变量
*/
Expand Down
245 changes: 143 additions & 102 deletions app/src/main/java/cn/wankkoree/xp/webviewpp/hook/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import com.highcapable.yukihookapi.YukiHookAPI
import com.highcapable.yukihookapi.annotation.xposed.InjectYukiHookWithXposed
import com.highcapable.yukihookapi.hook.log.*
import com.highcapable.yukihookapi.hook.param.PackageParam
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookModulePrefs
import com.highcapable.yukihookapi.hook.type.android.ContextClass
import com.highcapable.yukihookapi.hook.xposed.prefs.YukiHookPrefsBridge
import com.highcapable.yukihookapi.hook.xposed.proxy.IYukiHookXposedInit

@InjectYukiHookWithXposed(entryClassName = "Entry", isUsingResourcesHook = false)
Expand Down Expand Up @@ -55,130 +56,170 @@ class Main : IYukiHookXposedInit {
loggerW(msg = "do not hook webview library: $mProcessName")
return@encase // 不 hook WebView 本身
}
val pref = prefs("apps_$mProcessName")
if (!pref.get(AppSP.is_enabled)) {
loggerI(msg = "$mProcessName hooking not enabled")
return@encase // 目标 App 的 Hook 未启用
}
prefs("apps_$mProcessName").let { pref ->
if (!pref.get(AppSP.is_enabled)) {
loggerI(msg = "$mProcessName hooking not enabled")
return@encase // 目标 App 的 Hook 未启用
}

YukiHookLogger.Configs.tag = "WebViewPP<$packageName>"
YukiHookLogger.Configs.tag = "WebViewPP<$packageName>"

loggerI(msg = "hook $mProcessName which run in $processName")
loggerI(msg = "hook $mProcessName which run in $processName")

val mAppClassname = this.appInfo.className
if (pref.get(AppSP.bypass_packer) && mAppClassname != null && mAppClassname != "android.app.Application") {
loggerI(msg = "Try to get packer's classloader")
mAppClassname.hook {
injectMember {
method {
name = "attachBaseContext"
param("android.content.Context")
val mAppClassName = this.appInfo.className
if (pref.get(AppSP.app_is_protected)) {
loggerI(msg = "try to get classloader of protection")
when (mAppClassName) {
null -> {
// todo 感觉这个情况是不存在的?
loggerE(msg = "the className of applicationInfo is null, get classloader failed")
doHook()
}
afterHook {
val context = args(0).any()
if (context != null) {
appClassLoader = (context as Context).classLoader
loggerI(msg = "Get packer's classloader success")
} else {
loggerI(msg = "Get packer's classloader failed! Will use default classloader")
"android.app.Application" -> {
loggerE(msg = "the className of applicationInfo is Application, don't need to replace classloader")
doHook()
}
else -> {
mAppClassName.hook {
injectMember {
method {
name = "attachBaseContext"
param(ContextClass)
}
afterHook {
val context = args(0).cast<Context>()!!
appClassLoader = context.classLoader
loggerI(msg = "replace classloader to protection succeed")
doHook()
}
}.result {
onNoSuchMemberFailure {
loggerE(msg = "Hook.Member.NoSuchMember at preHook\uD83D\uDC49attachBaseContext", e = it)
doHook()
}
onHookingFailure {
loggerE(msg = "Hook.Member.HookFailure at preHook\uD83D\uDC49attachBaseContext", e = it)
doHook()
}
onHooked {
loggerI(msg = "Hook.Member.Ended at preHook\uD83D\uDC49attachBaseContext as [$it]")
}
onConductFailure { hookParam, it ->
loggerE(msg = "Hook.Member.ConductFailure at preHook\uD83D\uDC49attachBaseContext(${hookParam.args.joinToString(", ")})", e = it)
doHook()
}
}
}.result {
onHookClassNotFoundFailure {
loggerE(msg = "Hook.Class.NotFound at preHook\uD83D\uDC49$mAppClassName", e = it)
doHook()
}
onPrepareHook {
loggerI(msg = "Hook.Class.Started at preHook\uD83D\uDC49$mAppClassName")
}
}
doHook(pref)
}
}
} else {
doHook()
}
} else {
doHook(pref)
}
}
}

}

private fun PackageParam.doHook(pref: YukiHookModulePrefs) {

val cpuArch = with(appInfo.nativeLibraryDir) {
when {
endsWith("arm64") -> "arm64-v8a"
endsWith("arm") -> "armeabi-v7a"
else -> {
loggerE(msg = "the cpuArch(${toString()}) is not supported")
null
private fun PackageParam.doHook() {
prefs("apps_${Main.mProcessName}").let { pref ->
val cpuArch = with(appInfo.nativeLibraryDir) {
when {
endsWith("arm64") -> "arm64-v8a"
endsWith("arm") -> "armeabi-v7a"
else -> {
loggerE(msg = "the cpuArch(${toString()}) is not supported")
null
}
}
}
}

loggerI(msg = "loading rules")
loggerI(msg = "loading rules")

if (pref.get(AppSP.debug_mode)) {
findWebViewMethods()
}
if (pref.get(AppSP.debug_mode)) {
findWebViewMethods()
}

pref.getSet(AppSP.hooks).forEach { name ->
val hookJson = pref.getString("hook_entry_$name", "{}")
try {
when(val hookMethod = Gson().fromJson(hookJson, HookRules.HookRule::class.java).name) {
// TODO: 添加更多 hook 方法
"hookWebView" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookRuleWebView::class.java)
hookWebView(
Class_WebView = hookEntry.Class_WebView,
Method_getSettings = hookEntry.Method_getSettings,
Method_setWebContentsDebuggingEnabled = hookEntry.Method_setWebContentsDebuggingEnabled,
Method_setJavaScriptEnabled = hookEntry.Method_setJavaScriptEnabled,
Method_loadUrl = hookEntry.Method_loadUrl,
Method_setWebViewClient = hookEntry.Method_setWebViewClient,
)
}
"hookWebViewClient" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookRuleWebViewClient::class.java)
hookWebViewClient(
Class_WebViewClient = hookEntry.Class_WebViewClient,
Method_onPageFinished = hookEntry.Method_onPageFinished,
Class_WebView = hookEntry.Class_WebView,
Method_evaluateJavascript = hookEntry.Method_evaluateJavascript,
)
}
"replaceNebulaUCSDK" -> {
if (cpuArch != null) {
val hookEntry = Gson().fromJson(hookJson, HookRules.ReplaceNebulaUCSDK::class.java)
replaceNebulaUCSDK(
Class_UcServiceSetup = hookEntry.Class_UcServiceSetup,
Method_updateUCVersionAndSdcardPath = hookEntry.Method_updateUCVersionAndSdcardPath,
Field_sInitUcFromSdcardPath = hookEntry.Field_sInitUcFromSdcardPath,
cpuArch = cpuArch,
pref.getSet(AppSP.hooks).forEach { name ->
val hookJson = pref.getString("hook_entry_$name", "{}")
try {
when (val hookMethod = Gson().fromJson(hookJson, HookRules.HookRule::class.java).name) {
// TODO: 添加更多 hook 方法
"hookWebView" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookRuleWebView::class.java)
hookWebView(
Class_WebView = hookEntry.Class_WebView,
Method_getSettings = hookEntry.Method_getSettings,
Method_setWebContentsDebuggingEnabled = hookEntry.Method_setWebContentsDebuggingEnabled,
Method_setJavaScriptEnabled = hookEntry.Method_setJavaScriptEnabled,
Method_loadUrl = hookEntry.Method_loadUrl,
Method_setWebViewClient = hookEntry.Method_setWebViewClient,
)
}

"hookWebViewClient" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookRuleWebViewClient::class.java)
hookWebViewClient(
Class_WebViewClient = hookEntry.Class_WebViewClient,
Method_onPageFinished = hookEntry.Method_onPageFinished,
Class_WebView = hookEntry.Class_WebView,
Method_evaluateJavascript = hookEntry.Method_evaluateJavascript,
)
}

"replaceNebulaUCSDK" -> {
if (cpuArch != null) {
val hookEntry = Gson().fromJson(hookJson, HookRules.ReplaceNebulaUCSDK::class.java)
replaceNebulaUCSDK(
Class_UcServiceSetup = hookEntry.Class_UcServiceSetup,
Method_updateUCVersionAndSdcardPath = hookEntry.Method_updateUCVersionAndSdcardPath,
Field_sInitUcFromSdcardPath = hookEntry.Field_sInitUcFromSdcardPath,
cpuArch = cpuArch,
)
}
}

"hookCrossWalk" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookCrossWalk::class.java)
hookCrossWalk(
Class_XWalkView = hookEntry.Class_XWalkView,
Method_getSettings = hookEntry.Method_getSettings,
Method_setJavaScriptEnabled = hookEntry.Method_setJavaScriptEnabled,
Method_loadUrl = hookEntry.Method_loadUrl,
Method_setResourceClient = hookEntry.Method_setResourceClient,
Class_XWalkPreferences = hookEntry.Class_XWalkPreferences,
Method_setValue = hookEntry.Method_setValue,
)
}

"hookXWebView" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookXWebView::class.java)
hookXWebView(
Class_XWebView = hookEntry.Class_XWebView,
Method_initWebviewCore = hookEntry.Method_initWebviewCore,
Method_isXWeb = hookEntry.Method_isXWeb,
Method_isSys = hookEntry.Method_isSys,
Class_XWebPreferences = hookEntry.Class_XWebPreferences,
Method_setValue = hookEntry.Method_setValue,
)
}

else -> {
loggerE(msg = "Unknown Hook Method: $hookMethod")
}
}
"hookCrossWalk" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookCrossWalk::class.java)
hookCrossWalk(
Class_XWalkView = hookEntry.Class_XWalkView,
Method_getSettings = hookEntry.Method_getSettings,
Method_setJavaScriptEnabled = hookEntry.Method_setJavaScriptEnabled,
Method_loadUrl = hookEntry.Method_loadUrl,
Method_setResourceClient = hookEntry.Method_setResourceClient,
Class_XWalkPreferences = hookEntry.Class_XWalkPreferences,
Method_setValue = hookEntry.Method_setValue,
)
}
"hookXWebView" -> {
val hookEntry = Gson().fromJson(hookJson, HookRules.HookXWebView::class.java)
hookXWebView(
Class_XWebView = hookEntry.Class_XWebView,
Method_initWebviewCore = hookEntry.Method_initWebviewCore,
Method_isXWeb = hookEntry.Method_isXWeb,
Method_isSys = hookEntry.Method_isSys,
Class_XWebPreferences = hookEntry.Class_XWebPreferences,
Method_setValue = hookEntry.Method_setValue,
)
}
else -> {
loggerE(msg = "Unknown Hook Method: $hookMethod")
}
} catch (e: Exception) {
loggerE(msg = "Parse Failed!", e = e)
return@forEach // continue
}
} catch (e : Exception) {
loggerE(msg = "Parse Failed!", e = e)
return@forEach // continue
}
}
}
4 changes: 2 additions & 2 deletions app/src/main/res/menu/app_toolbar_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
android:title="@string/checking_for_rules_updates"
app:showAsAction="never"/>
<item
android:id="@+id/app_toolbar_menu_byass_packer"
android:title="@string/bypass_packer"
android:id="@+id/app_toolbar_menu_app_is_protected"
android:title="@string/app_is_protected"
app:showAsAction="never"
android:checkable="true"
/>
Expand Down
1 change: 0 additions & 1 deletion app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,4 @@
<string name="advance_setting_auto_check_update_desc">模块启动时是否检查更新。</string>
<string name="advance_setting_app_center_desc">AppCenter 是一个来自微软的数据匿名收集方案。我们使用它来匿名收集您使用本模块期间的崩溃日志、性能指标、交互习惯等数据。</string>
<string name="dialog_support_desc">对于中国用户:\n\t1. 支付宝红包码可以每天领取免费的红包,你可以把这个红包免费捐赠给我。\n\t2. 推荐通过支付宝捐赠,因为可以使用一些支付宝发放的红包,比如说上面那个(通过相册扫描可能不行,建议通过另一个屏幕扫码)。\n\t3. 不推荐通过微信捐赠,因为它存在手续费。\n\n对于全球用户:\n\t由于手续费等原因,我暂时不支持接受捐款。</string>
<string name="bypass_packer">支持加固应用</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-zh-rCN/words.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,5 @@
<string name="copied">已复制</string>
<string name="open_directly">直接打开</string>
<string name="copy_key">复制口令</string>
<string name="app_is_protected">应用被加固</string>
</resources>
1 change: 0 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,4 @@
<string name="advance_setting_auto_check_update_desc">Auto check update or not when module launched.</string>
<string name="advance_setting_app_center_desc">AppCenter is an anonymous data collection solution from Microsoft. We use it to anonymously collect crash logs, performance indicators, interaction habits and other data during your use of this module.</string>
<string name="dialog_support_desc">For Chinese user:\n\t1. Alipay Red Packet is which you can get a free red packet and pay it to me every day.\n\t2. AliPay is recommended because it can pay with some red packets you got from Alipay just like #1(Scanning through the album may not work, it is recommended to scan through another screen).\n\t3. WeChat is not recommended because of its handling fee for me.\n\nFor global user:\n\tI do not support accepting donations for the time being due to the handling fee and other reasons.</string>
<string name="bypass_packer">Support packed app</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values/words.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@
<string name="copied">Copied</string>
<string name="open_directly">Open Directly</string>
<string name="copy_key">Copy Key</string>
<string name="app_is_protected">App is Protected</string>
</resources>

0 comments on commit e500a72

Please sign in to comment.