mirror of
https://github.com/fankes/TSBattery.git
synced 2025-09-04 17:55:30 +08:00
feat: simple support dexkit test
This commit is contained in:
@@ -71,6 +71,7 @@ dependencies {
|
||||
implementation(com.highcapable.yukihookapi.api)
|
||||
ksp(com.highcapable.yukihookapi.ksp.xposed)
|
||||
implementation(com.fankes.projectpromote.project.promote)
|
||||
implementation(org.luckypray.dexkit)
|
||||
implementation(com.github.duanhong169.drawabletoolbox)
|
||||
implementation(com.squareup.okhttp3.okhttp)
|
||||
implementation(androidx.core.core.ktx)
|
||||
|
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -37,6 +37,11 @@
|
||||
# 排除注入的 Activity
|
||||
-keep class com.fankes.tsbattery.ui.activity.parasitic.ConfigActivity
|
||||
|
||||
# DexKit
|
||||
-keep class org.luckypray.dexkit.DexKitBridge {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
# 防止某些类被 R8 混淆 (可能是 BUG)
|
||||
# FIXME: 已知问题字符串类名 (即使是常量) 也会被 R8 处理为混淆后的类名
|
||||
# FIXME: 所以目前只能把不允许 R8 处理的类 keep 掉,同时在当前模块中也不会被混淆就是了
|
||||
|
@@ -36,11 +36,11 @@ import com.fankes.tsbattery.R
|
||||
import com.fankes.tsbattery.const.ModuleVersion
|
||||
import com.fankes.tsbattery.const.PackageName
|
||||
import com.fankes.tsbattery.data.ConfigData
|
||||
import com.fankes.tsbattery.hook.HookEntry
|
||||
import com.fankes.tsbattery.hook.factory.hookSystemWakeLock
|
||||
import com.fankes.tsbattery.hook.factory.isQQNightMode
|
||||
import com.fankes.tsbattery.hook.factory.jumpToModuleSettings
|
||||
import com.fankes.tsbattery.hook.factory.startModuleSettings
|
||||
import com.fankes.tsbattery.hook.helper.DexKitHelper
|
||||
import com.fankes.tsbattery.utils.factory.appVersionName
|
||||
import com.fankes.tsbattery.utils.factory.dp
|
||||
import com.highcapable.yukihookapi.hook.bean.VariousClass
|
||||
@@ -50,7 +50,6 @@ import com.highcapable.yukihookapi.hook.factory.current
|
||||
import com.highcapable.yukihookapi.hook.factory.field
|
||||
import com.highcapable.yukihookapi.hook.factory.injectModuleAppResources
|
||||
import com.highcapable.yukihookapi.hook.factory.method
|
||||
import com.highcapable.yukihookapi.hook.factory.processName
|
||||
import com.highcapable.yukihookapi.hook.factory.registerModuleAppActivities
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.type.android.BuildClass
|
||||
@@ -67,6 +66,7 @@ import com.highcapable.yukihookapi.hook.type.java.ListClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.LongType
|
||||
import com.highcapable.yukihookapi.hook.type.java.StringClass
|
||||
import com.highcapable.yukihookapi.hook.type.java.UnitType
|
||||
import java.lang.reflect.Method
|
||||
import java.lang.reflect.Proxy
|
||||
|
||||
/**
|
||||
@@ -118,6 +118,16 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
)
|
||||
)
|
||||
|
||||
/**
|
||||
* DexKit 搜索结果数据实现类
|
||||
*/
|
||||
private object DexKitData {
|
||||
var BaseChatPie_RemainScreenOnMethod: Method? = null
|
||||
var BaseChatPie_CancelRemainScreenOnMethod: Method? = null
|
||||
var SimpleItemProcessorClass: Class<*>? = null
|
||||
var SimpleItemProcessorClass_OnClickMethod: Method? = null
|
||||
}
|
||||
|
||||
/** 一个内部进程的名称 (与 X5 浏览器内核有关) */
|
||||
private val privilegedProcessName = "$packageName:privileged_process"
|
||||
|
||||
@@ -147,6 +157,61 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
*/
|
||||
private fun Any.compatToActivity() = if (this is Activity) this else current().method { name = "getActivity"; superClass() }.invoke()
|
||||
|
||||
/** 使用 DexKit 进行搜索 */
|
||||
private fun searchUsingDexKit() {
|
||||
val classLoader = appClassLoader ?: return
|
||||
DexKitHelper.create(this) {
|
||||
BaseChatPieClass?.name?.also { baseChatPieClassName ->
|
||||
DexKitData.BaseChatPie_RemainScreenOnMethod =
|
||||
findMethod {
|
||||
matcher {
|
||||
declaredClass(baseChatPieClassName)
|
||||
usingStrings("remainScreenOn")
|
||||
paramCount = 0
|
||||
returnType = UnitType.name
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
DexKitData.BaseChatPie_CancelRemainScreenOnMethod =
|
||||
findMethod {
|
||||
matcher {
|
||||
declaredClass(baseChatPieClassName)
|
||||
usingStrings("cancelRemainScreenOn")
|
||||
paramCount = 0
|
||||
returnType = UnitType.name
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
}
|
||||
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
||||
findClass {
|
||||
searchPackages("${PackageName.QQ}.setting.processor")
|
||||
matcher {
|
||||
methods {
|
||||
add {
|
||||
name = "<init>"
|
||||
paramTypes(ContextClass.name, IntType.name, CharSequenceClass.name, IntType.name)
|
||||
}
|
||||
add {
|
||||
paramTypes(kotlinFunction0)
|
||||
returnType = UnitType.name
|
||||
}
|
||||
}
|
||||
fields { count(6..Int.MAX_VALUE) }
|
||||
}
|
||||
}.firstOrNull()?.name?.also { className ->
|
||||
DexKitData.SimpleItemProcessorClass = className.toClass()
|
||||
DexKitData.SimpleItemProcessorClass_OnClickMethod =
|
||||
findMethod {
|
||||
matcher {
|
||||
declaredClass = className
|
||||
paramTypes(kotlinFunction0)
|
||||
returnType = UnitType.name
|
||||
usingNumbers(2)
|
||||
}
|
||||
}.firstOrNull()?.getMethodInstance(classLoader)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 这个类 QQ 的 BaseChatPie 是控制聊天界面的
|
||||
*
|
||||
@@ -154,175 +219,16 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
*
|
||||
* remainScreenOn、cancelRemainScreenOn
|
||||
*
|
||||
* 这两个方法一个是挂起电源锁常驻亮屏
|
||||
*
|
||||
* 一个是停止常驻亮屏
|
||||
*
|
||||
* 不由分说每个版本混淆的方法名都会变
|
||||
*
|
||||
* 所以说每个版本重新适配 - 也可以提交分支帮我适配
|
||||
*
|
||||
* - ❗Hook 错了方法会造成闪退!
|
||||
* 这两个方法一个是挂起电源锁常驻亮屏 - 一个是停止常驻亮屏
|
||||
*/
|
||||
private fun hookQQBaseChatPie() {
|
||||
if (isQQ) when (hostVersionName) {
|
||||
"8.0.0" -> {
|
||||
hookBaseChatPie("bq")
|
||||
hookBaseChatPie("aL")
|
||||
}
|
||||
"8.0.5", "8.0.7" -> {
|
||||
hookBaseChatPie("bw")
|
||||
hookBaseChatPie("aQ")
|
||||
}
|
||||
"8.1.0", "8.1.3" -> {
|
||||
hookBaseChatPie("bE")
|
||||
hookBaseChatPie("aT")
|
||||
}
|
||||
"8.1.5" -> {
|
||||
hookBaseChatPie("bF")
|
||||
hookBaseChatPie("aT")
|
||||
}
|
||||
"8.1.8", "8.2.0", "8.2.6" -> {
|
||||
hookBaseChatPie("bC")
|
||||
hookBaseChatPie("aT")
|
||||
}
|
||||
"8.2.7", "8.2.8", "8.2.11", "8.3.0" -> {
|
||||
hookBaseChatPie("bE")
|
||||
hookBaseChatPie("aV")
|
||||
}
|
||||
"8.3.5" -> {
|
||||
hookBaseChatPie("bR")
|
||||
hookBaseChatPie("aX")
|
||||
}
|
||||
"8.3.6" -> {
|
||||
hookBaseChatPie("cp")
|
||||
hookBaseChatPie("aX")
|
||||
}
|
||||
"8.3.9" -> {
|
||||
hookBaseChatPie("cj")
|
||||
hookBaseChatPie("aT")
|
||||
}
|
||||
"8.4.1", "8.4.5" -> {
|
||||
hookBaseChatPie("ck")
|
||||
hookBaseChatPie("aT")
|
||||
}
|
||||
"8.4.8", "8.4.10", "8.4.17", "8.4.18", "8.5.0" -> {
|
||||
hookBaseChatPie("remainScreenOn")
|
||||
hookBaseChatPie("cancelRemainScreenOn")
|
||||
}
|
||||
"8.5.5" -> {
|
||||
hookBaseChatPie("bT")
|
||||
hookBaseChatPie("aN")
|
||||
}
|
||||
"8.6.0", "8.6.5", "8.7.0", "8.7.5", "8.7.8", "8.8.0", "8.8.3", "8.8.5" -> {
|
||||
hookBaseChatPie("ag")
|
||||
hookBaseChatPie("ah")
|
||||
}
|
||||
"8.8.11", "8.8.12" -> {
|
||||
hookBaseChatPie("bc")
|
||||
hookBaseChatPie("bd")
|
||||
}
|
||||
"8.8.17", "8.8.20" -> {
|
||||
hookBaseChatPie("bd")
|
||||
hookBaseChatPie("be")
|
||||
}
|
||||
"8.8.23", "8.8.28" -> {
|
||||
hookBaseChatPie("bf")
|
||||
hookBaseChatPie("bg")
|
||||
}
|
||||
"8.8.33" -> {
|
||||
hookBaseChatPie("bg")
|
||||
hookBaseChatPie("bh")
|
||||
}
|
||||
"8.8.35", "8.8.38" -> {
|
||||
hookBaseChatPie("bi")
|
||||
hookBaseChatPie("bj")
|
||||
}
|
||||
"8.8.50" -> {
|
||||
hookBaseChatPie("bj")
|
||||
hookBaseChatPie("bk")
|
||||
}
|
||||
"8.8.55", "8.8.68", "8.8.80" -> {
|
||||
hookBaseChatPie("bk")
|
||||
hookBaseChatPie("bl")
|
||||
}
|
||||
"8.8.83", "8.8.85", "8.8.88", "8.8.90" -> {
|
||||
hookBaseChatPie("bl")
|
||||
hookBaseChatPie("bm")
|
||||
}
|
||||
"8.8.93", "8.8.95" -> {
|
||||
hookBaseChatPie("J3")
|
||||
hookBaseChatPie("S")
|
||||
}
|
||||
"8.8.98" -> {
|
||||
hookBaseChatPie("M3")
|
||||
hookBaseChatPie("S")
|
||||
}
|
||||
"8.9.0", "8.9.1", "8.9.2" -> {
|
||||
hookBaseChatPie("N3")
|
||||
hookBaseChatPie("S")
|
||||
}
|
||||
"8.9.3", "8.9.5" -> {
|
||||
hookBaseChatPie("H3")
|
||||
hookBaseChatPie("P")
|
||||
}
|
||||
"8.9.8", "8.9.10" -> {
|
||||
hookBaseChatPie("H3")
|
||||
hookBaseChatPie("N")
|
||||
}
|
||||
"8.9.13" -> {
|
||||
hookBaseChatPie("y3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.15", "8.9.18", "8.9.19", "8.9.20" -> {
|
||||
hookBaseChatPie("w3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.23", "8.9.25" -> {
|
||||
hookBaseChatPie("z3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.28", "8.9.30", "8.9.33" -> {
|
||||
hookBaseChatPie("A3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.35", "8.9.38", "8.9.50" -> {
|
||||
hookBaseChatPie("B3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.53", "8.9.55", "8.9.58" -> {
|
||||
hookBaseChatPie("C3")
|
||||
hookBaseChatPie("H")
|
||||
}
|
||||
"8.9.63", "8.9.68" -> {
|
||||
hookBaseChatPie("t3")
|
||||
hookBaseChatPie("J")
|
||||
}
|
||||
"8.9.70", "8.9.71", "8.9.73", "8.9.75", "8.9.76" -> {
|
||||
hookBaseChatPie("u3")
|
||||
hookBaseChatPie("J")
|
||||
}
|
||||
"8.9.78", "8.9.80", "8.9.83" -> {
|
||||
hookBaseChatPie("v3")
|
||||
hookBaseChatPie("I")
|
||||
}
|
||||
else -> {
|
||||
HookEntry.isHookClientSupport = false
|
||||
YLog.warn("$hostVersionName not supported!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拦截 [BaseChatPieClass] 的目标方法体封装
|
||||
* @param methodName 方法名
|
||||
* 打印警告信息
|
||||
* @param index 序号
|
||||
*/
|
||||
private fun hookBaseChatPie(methodName: String) {
|
||||
BaseChatPieClass?.method {
|
||||
name = methodName
|
||||
emptyParam()
|
||||
returnType = UnitType
|
||||
}?.hook()?.intercept()
|
||||
fun warn(index: Int) = YLog.warn("$hostVersionName [$index] not support!")
|
||||
DexKitData.BaseChatPie_RemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 0)
|
||||
DexKitData.BaseChatPie_CancelRemainScreenOnMethod?.hook()?.intercept() ?: warn(index = 1)
|
||||
}
|
||||
|
||||
/** Hook CoreService QQ、TIM */
|
||||
@@ -527,16 +433,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
private fun hookQQSettingsUi() {
|
||||
if (MainSettingFragmentClass == null) return YLog.error("Could not found main setting class, hook aborted")
|
||||
val kotlinUnit = "kotlin.Unit"
|
||||
val kotlinFunction0 = "kotlin.jvm.functions.Function0"
|
||||
val simpleItemProcessorClass = searchClass {
|
||||
from("${PackageName.QQ}.setting.processor").absolute()
|
||||
constructor { param(ContextClass, IntType, CharSequenceClass, IntType) }
|
||||
method {
|
||||
param(kotlinFunction0)
|
||||
returnType = UnitType
|
||||
}
|
||||
field().count { it >= 6 }
|
||||
}.get() ?: return YLog.error("Could not found processor class, hook aborted")
|
||||
val simpleItemProcessorClass = DexKitData.SimpleItemProcessorClass ?: return YLog.error("Could not found processor class, hook aborted")
|
||||
|
||||
/**
|
||||
* 创建入口点条目
|
||||
@@ -550,11 +447,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
return simpleItemProcessorClass.buildOf(context, R.id.tsbattery_qq_entry_item_id, "TSBattery", iconResId) {
|
||||
param(ContextClass, IntType, CharSequenceClass, IntType)
|
||||
}?.also { entryItem ->
|
||||
val onClickMethod = simpleItemProcessorClass.method {
|
||||
param { it[0].name == kotlinFunction0 }
|
||||
paramCount = 1
|
||||
returnType = UnitType
|
||||
}.giveAll().firstOrNull() ?: error("Could not found processor method")
|
||||
val onClickMethod = DexKitData.SimpleItemProcessorClass_OnClickMethod ?: error("Could not found processor method")
|
||||
val proxyOnClick = Proxy.newProxyInstance(appClassLoader, arrayOf(onClickMethod.parameterTypes[0])) { any, method, args ->
|
||||
if (method.name == "invoke") {
|
||||
context.startModuleSettings()
|
||||
@@ -613,6 +506,7 @@ object QQTIMHooker : YukiBaseHooker() {
|
||||
}
|
||||
|
||||
override fun onHook() {
|
||||
searchUsingDexKit()
|
||||
onAppLifecycle(isOnFailureThrowToApp = false) {
|
||||
attachBaseContext { baseContext, hasCalledSuper ->
|
||||
if (hasCalledSuper.not()) baseConfiguration = baseContext.resources.configuration
|
||||
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* TSBattery - A new way to save your battery avoid cancer apps hacker it.
|
||||
* Copyright (C) 2017-2023 Fankes Studio(qzmmcn@163.com)
|
||||
* https://github.com/fankes/TSBattery
|
||||
*
|
||||
* This software is non-free but opensource software: you can redistribute it
|
||||
* and/or modify it under the terms of the GNU Affero General Public License
|
||||
* as published by the Free Software Foundation; either
|
||||
* version 3 of the License, or any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* and eula along with this software. If not, see
|
||||
* <https://www.gnu.org/licenses/>
|
||||
*
|
||||
* This file is created by fankes on 2023/10/8.
|
||||
*/
|
||||
@file:Suppress("MemberVisibilityCanBePrivate")
|
||||
|
||||
package com.fankes.tsbattery.hook.helper
|
||||
|
||||
import com.highcapable.yukihookapi.hook.log.YLog
|
||||
import com.highcapable.yukihookapi.hook.param.PackageParam
|
||||
import org.luckypray.dexkit.DexKitBridge
|
||||
|
||||
/**
|
||||
* DexKit 工具类
|
||||
*/
|
||||
object DexKitHelper {
|
||||
|
||||
/** 是否已装载 */
|
||||
private var isLoaded = false
|
||||
|
||||
/** 装载 */
|
||||
fun load() {
|
||||
if (isLoaded) return
|
||||
runCatching {
|
||||
System.loadLibrary("dexkit")
|
||||
isLoaded = true
|
||||
}.onFailure { YLog.error("Load DexKit failed!", it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 [DexKitBridge]
|
||||
* @param param 当前实例
|
||||
* @param initiate 方法体
|
||||
*/
|
||||
fun create(param: PackageParam, initiate: DexKitBridge.() -> Unit) {
|
||||
load()
|
||||
runCatching { DexKitBridge.create(param.appInfo.sourceDir)?.use { initiate(it) } }
|
||||
}
|
||||
}
|
@@ -50,29 +50,7 @@ import com.highcapable.yukihookapi.YukiHookAPI
|
||||
class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
|
||||
companion object {
|
||||
|
||||
private val qqSupportVersions = arrayOf(
|
||||
"8.0.0", "8.0.5", "8.0.7", "8.1.0", "8.1.3", "8.1.5", "8.1.8",
|
||||
"8.2.0", "8.2.6", "8.2.7", "8.2.8", "8.2.11", "8.3.0", "8.3.5",
|
||||
"8.3.6", "8.3.9", "8.4.1", "8.4.5", "8.4.8", "8.4.10", "8.4.17",
|
||||
"8.4.18", "8.5.0", "8.5.5", "8.6.0", "8.6.5", "8.7.0", "8.7.5",
|
||||
"8.7.8", "8.8.0", "8.8.3", "8.8.5", "8.8.11", "8.8.12", "8.8.17",
|
||||
"8.8.20", "8.8.23", "8.8.28", "8.8.33", "8.8.35", "8.8.38", "8.8.50",
|
||||
"8.8.55", "8.8.68", "8.8.80", "8.8.83", "8.8.85", "8.8.88", "8.8.90",
|
||||
"8.8.93", "8.8.95", "8.8.98", "8.9.0", "8.9.1", "8.9.2", "8.9.3",
|
||||
"8.9.5", "8.9.8", "8.9.10", "8.9.13", "8.9.15", "8.9.18", "8.9.19",
|
||||
"8.9.20", "8.9.23", "8.9.25", "8.9.28", "8.9.30", "8.9.33", "8.9.35",
|
||||
"8.9.38", "8.9.50", "8.9.53", "8.9.55", "8.9.58", "8.9.63", "8.9.68",
|
||||
"8.9.70", "8.9.71", "8.9.73", "8.9.75", "8.9.76", "8.9.78",
|
||||
"8.9.80", "8.9.83"
|
||||
)
|
||||
private val qqSupportVersion by lazy {
|
||||
if (qqSupportVersions.isNotEmpty()) {
|
||||
var value = ""
|
||||
qqSupportVersions.forEach { value += "$it、" }
|
||||
"${value.trim().let { it.substring(0, it.lastIndex) }}\n\n其余版本请自行测试是否有效。"
|
||||
} else "empty"
|
||||
}
|
||||
private const val QQ_SUPPORT_VERSION = "理论支持 8.0.0+ 及以上版本。"
|
||||
private const val TIM_SUPPORT_VERSION = "2+、3+ (并未完全测试每个版本)。"
|
||||
private const val WECHAT_SUPPORT_VERSION = "全版本仅支持基础省电,更多功能依然画饼。"
|
||||
}
|
||||
@@ -131,7 +109,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
||||
binding.mainQqItem.setOnClickListener {
|
||||
showDialog {
|
||||
title = "兼容的 QQ 版本"
|
||||
msg = qqSupportVersion
|
||||
msg = QQ_SUPPORT_VERSION
|
||||
confirmButton(text = "我知道了")
|
||||
}
|
||||
/** 振动提醒 */
|
||||
|
@@ -46,6 +46,9 @@ libraries:
|
||||
version: 1.2.0
|
||||
ksp-xposed:
|
||||
version-ref: <this>::api
|
||||
org.luckypray:
|
||||
dexkit:
|
||||
version: 2.0.0-rc4
|
||||
com.github.duanhong169:
|
||||
drawabletoolbox:
|
||||
version: 1.0.7
|
||||
|
Reference in New Issue
Block a user