Shamrock: 金メダル免除、システムレベルの保活をサポートする

This commit is contained in:
WhiteChi 2023-11-23 23:53:11 +08:00
parent ee8dc75be3
commit 0d35d5834b
4 changed files with 51 additions and 19 deletions

View File

@ -47,7 +47,7 @@
android:value="基于 Xposed 实现 OneBot 标准的 QQ 机器人框架" /> android:value="基于 Xposed 实现 OneBot 标准的 QQ 机器人框架" />
<meta-data <meta-data
android:name="xposedminversion" android:name="xposedminversion"
android:value="23" /> android:value="93" />
<meta-data <meta-data
android:name="xposedscope" android:name="xposedscope"
android:resource="@array/xposed_scope" /> android:resource="@array/xposed_scope" />

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.shamrock.ui.fragment package moe.fuqiuluo.shamrock.ui.fragment
import android.content.Context
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.absolutePadding import androidx.compose.foundation.layout.absolutePadding
@ -22,6 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import moe.fuqiuluo.shamrock.R import moe.fuqiuluo.shamrock.R
import moe.fuqiuluo.shamrock.ui.app.AppRuntime import moe.fuqiuluo.shamrock.ui.app.AppRuntime
import moe.fuqiuluo.shamrock.ui.app.Level
import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig import moe.fuqiuluo.shamrock.ui.app.ShamrockConfig
import moe.fuqiuluo.shamrock.ui.theme.GlobalColor import moe.fuqiuluo.shamrock.ui.theme.GlobalColor
import moe.fuqiuluo.shamrock.ui.theme.LocalString import moe.fuqiuluo.shamrock.ui.theme.LocalString
@ -90,7 +92,7 @@ fun LabFragment() {
modifier = Modifier.padding(top = 12.dp), modifier = Modifier.padding(top = 12.dp),
painter = painterResource(id = R.drawable.round_logo_dev_24), painter = painterResource(id = R.drawable.round_logo_dev_24),
title = "实验功能" title = "实验功能"
) { ) { color ->
Column { Column {
Divider( Divider(
modifier = Modifier, modifier = Modifier,
@ -101,7 +103,7 @@ fun LabFragment() {
Function( Function(
title = "自动清理QQ垃圾", title = "自动清理QQ垃圾",
desc = "也许会导致奇怪的问题(无效)。", desc = "也许会导致奇怪的问题(无效)。",
descColor = it, descColor = color,
isSwitch = ShamrockConfig.isAutoClean(ctx) isSwitch = ShamrockConfig.isAutoClean(ctx)
) { ) {
ShamrockConfig.setAutoClean(ctx, it) ShamrockConfig.setAutoClean(ctx, it)
@ -112,7 +114,7 @@ fun LabFragment() {
Function( Function(
title = "拦截QQ无用收包", title = "拦截QQ无用收包",
desc = "测试阶段,可能导致网络异常或掉线。", desc = "测试阶段,可能导致网络异常或掉线。",
descColor = it, descColor = color,
isSwitch = ShamrockConfig.isInjectPacket(ctx) isSwitch = ShamrockConfig.isInjectPacket(ctx)
) { ) {
ShamrockConfig.setInjectPacket(ctx, it) ShamrockConfig.setInjectPacket(ctx, it)
@ -123,7 +125,7 @@ fun LabFragment() {
Function( Function(
title = "自动唤醒QQ", title = "自动唤醒QQ",
desc = "QQ进程死亡时重新打开QQ进程前提本进程存活。", desc = "QQ进程死亡时重新打开QQ进程前提本进程存活。",
descColor = it, descColor = color,
isSwitch = ShamrockConfig.enableAutoStart(ctx) isSwitch = ShamrockConfig.enableAutoStart(ctx)
) { ) {
ShamrockConfig.setAutoStart(ctx, it) ShamrockConfig.setAutoStart(ctx, it)
@ -133,12 +135,30 @@ fun LabFragment() {
Function( Function(
title = "开启Shell接口", title = "开启Shell接口",
desc = "可能导致设备被入侵,请勿随意开启。", desc = "可能导致设备被入侵,请勿随意开启。",
descColor = it, descColor = color,
isSwitch = ShamrockConfig.allowShell(ctx) isSwitch = ShamrockConfig.allowShell(ctx)
) { ) {
ShamrockConfig.setShellStatus(ctx, it) ShamrockConfig.setShellStatus(ctx, it)
return@Function true return@Function true
} }
kotlin.runCatching {
ctx.getSharedPreferences("shared_config", Context.MODE_WORLD_READABLE)
}.onSuccess {
Function(
title = "免死金牌",
desc = "由系统复活QQ和Shamrock需要重新启动系统。",
descColor = color,
isSwitch = it.getBoolean("persistent", false)
) { v ->
it.edit().putBoolean("persistent", v).apply()
scope.toast(ctx, "重启系统生效哦!")
return@Function true
}
}.onFailure {
AppRuntime.log("无法启用免死金牌选项当前Lsposed模块未激活或者不支持NewSharedPreferences。", Level.WARN)
}
} }
} }

View File

@ -4,6 +4,7 @@ import io.ktor.http.ContentType
import io.ktor.http.content.PartData import io.ktor.http.content.PartData
import io.ktor.http.content.forEachPart import io.ktor.http.content.forEachPart
import io.ktor.http.content.streamProvider import io.ktor.http.content.streamProvider
import io.ktor.server.application.Application
import io.ktor.server.application.call import io.ktor.server.application.call
import io.ktor.server.request.receiveMultipart import io.ktor.server.request.receiveMultipart
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
@ -27,6 +28,7 @@ import moe.fuqiuluo.shamrock.tools.fetchOrThrow
import moe.fuqiuluo.shamrock.tools.fetchPostJsonArray import moe.fuqiuluo.shamrock.tools.fetchPostJsonArray
import moe.fuqiuluo.shamrock.tools.getOrPost import moe.fuqiuluo.shamrock.tools.getOrPost
import moe.fuqiuluo.shamrock.tools.isJsonArray import moe.fuqiuluo.shamrock.tools.isJsonArray
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.respond import moe.fuqiuluo.shamrock.tools.respond
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
@ -39,7 +41,7 @@ fun Routing.otherAction() {
post("/shell") { post("/shell") {
val runtime = Runtime.getRuntime() val runtime = Runtime.getRuntime()
val dir = fetchOrThrow("dir") val dir = fetchOrThrow("dir")
val out = StringBuilder() val out = hashMapOf<String, Any>()
withTimeoutOrNull(5000L) { withTimeoutOrNull(5000L) {
if (isJsonArray("cmd")) { if (isJsonArray("cmd")) {
val cmd = fetchPostJsonArray("cmd").map { val cmd = fetchPostJsonArray("cmd").map {
@ -59,17 +61,15 @@ fun Routing.otherAction() {
respond(false, Status.IAmTired, "执行超时") respond(false, Status.IAmTired, "执行超时")
} else { } else {
it.inputStream.use { it.inputStream.use {
out.append("stdout:\n") out["out"] = it.readBytes().toString(Charsets.UTF_8)
out.append(it.readBytes().toString(Charsets.UTF_8))
} }
it.errorStream.use { it.errorStream.use {
out.append("\nstderr:\n") out["err"] = it.readBytes().toString(Charsets.UTF_8)
out.append(it.readBytes().toString(Charsets.UTF_8))
} }
} }
} }
call.respondText(out.toString()) call.respondText(out.json.toString(), ContentType.Application.Json)
} }
} }

View File

@ -3,6 +3,8 @@ package moe.fuqiuluo.shamrock.xposed.loader
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.os.Build import android.os.Build
import com.arthenica.ffmpegkit.BuildConfig
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers import de.robv.android.xposed.XposedHelpers
import moe.fuqiuluo.shamrock.tools.hookMethod import moe.fuqiuluo.shamrock.tools.hookMethod
@ -16,6 +18,7 @@ internal object FuckAMS {
private lateinit var KeepThread: Thread private lateinit var KeepThread: Thread
private lateinit var METHOD_IS_KILLED: Method private lateinit var METHOD_IS_KILLED: Method
private var allowPersistent: Boolean = false
fun injectAMS(loader: ClassLoader) { fun injectAMS(loader: ClassLoader) {
kotlin.runCatching { kotlin.runCatching {
@ -24,15 +27,24 @@ internal object FuckAMS {
increaseAdj(it.result) increaseAdj(it.result)
} }
}.onFailure { }.onFailure {
XposedBridge.log("Plan A failed: ${it.message}") XposedBridge.log("[Shamrock] Plan A failed: ${it.message}")
} }
val pref = XSharedPreferences("moe.fuqiuluo.shamrock", "shared_config")
if (pref.file.canRead()) {
allowPersistent = pref.getBoolean("persistent", false)
XposedBridge.log("[Shamrock] allowPersistent = $allowPersistent")
} else {
XposedBridge.log("[Shamrock] unable to load XSharedPreferences")
}
kotlin.runCatching { kotlin.runCatching {
val ProcessList = XposedHelpers.findClass("com.android.server.am.ProcessList", loader) val ProcessList = XposedHelpers.findClass("com.android.server.am.ProcessList", loader)
ProcessList.hookMethod("newProcessRecordLocked").after { ProcessList.hookMethod("newProcessRecordLocked").after {
increaseAdj(it.result) increaseAdj(it.result)
} }
}.onFailure { }.onFailure {
XposedBridge.log("Plan B failed: ${it.message}") XposedBridge.log("[Shamrock] Plan B failed: ${it.message}")
} }
} }
@ -75,14 +87,14 @@ internal object FuckAMS {
if (!it.isAccessible) it.isAccessible = true if (!it.isAccessible) it.isAccessible = true
}.get(record) as ApplicationInfo }.get(record) as ApplicationInfo
if(applicationInfo.processName in KeepPackage) { if(applicationInfo.processName in KeepPackage) {
XposedBridge.log("Process is keeping: $record") XposedBridge.log("[Shamrock] Process is keeping: $record")
KeepRecords.add(record) KeepRecords.add(record)
keepByAdj(record) keepByAdj(record)
// Error // Error
//if (noDied.exists()) { if (allowPersistent) {
// XposedBridge.log("Open NoDied Mode!!!") XposedBridge.log("[Shamrock] Open NoDied Mode!!!")
// keepByPersistent(record) keepByPersistent(record)
//} }
checkThread() checkThread()
} }
} }