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 机器人框架" />
<meta-data
android:name="xposedminversion"
android:value="23" />
android:value="93" />
<meta-data
android:name="xposedscope"
android:resource="@array/xposed_scope" />

View File

@ -1,5 +1,6 @@
package moe.fuqiuluo.shamrock.ui.fragment
import android.content.Context
import android.widget.Toast
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.absolutePadding
@ -22,6 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import moe.fuqiuluo.shamrock.R
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.theme.GlobalColor
import moe.fuqiuluo.shamrock.ui.theme.LocalString
@ -90,7 +92,7 @@ fun LabFragment() {
modifier = Modifier.padding(top = 12.dp),
painter = painterResource(id = R.drawable.round_logo_dev_24),
title = "实验功能"
) {
) { color ->
Column {
Divider(
modifier = Modifier,
@ -101,7 +103,7 @@ fun LabFragment() {
Function(
title = "自动清理QQ垃圾",
desc = "也许会导致奇怪的问题(无效)。",
descColor = it,
descColor = color,
isSwitch = ShamrockConfig.isAutoClean(ctx)
) {
ShamrockConfig.setAutoClean(ctx, it)
@ -112,7 +114,7 @@ fun LabFragment() {
Function(
title = "拦截QQ无用收包",
desc = "测试阶段,可能导致网络异常或掉线。",
descColor = it,
descColor = color,
isSwitch = ShamrockConfig.isInjectPacket(ctx)
) {
ShamrockConfig.setInjectPacket(ctx, it)
@ -123,7 +125,7 @@ fun LabFragment() {
Function(
title = "自动唤醒QQ",
desc = "QQ进程死亡时重新打开QQ进程前提本进程存活。",
descColor = it,
descColor = color,
isSwitch = ShamrockConfig.enableAutoStart(ctx)
) {
ShamrockConfig.setAutoStart(ctx, it)
@ -133,12 +135,30 @@ fun LabFragment() {
Function(
title = "开启Shell接口",
desc = "可能导致设备被入侵,请勿随意开启。",
descColor = it,
descColor = color,
isSwitch = ShamrockConfig.allowShell(ctx)
) {
ShamrockConfig.setShellStatus(ctx, it)
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.forEachPart
import io.ktor.http.content.streamProvider
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.request.receiveMultipart
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.getOrPost
import moe.fuqiuluo.shamrock.tools.isJsonArray
import moe.fuqiuluo.shamrock.tools.json
import moe.fuqiuluo.shamrock.tools.respond
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5
@ -39,7 +41,7 @@ fun Routing.otherAction() {
post("/shell") {
val runtime = Runtime.getRuntime()
val dir = fetchOrThrow("dir")
val out = StringBuilder()
val out = hashMapOf<String, Any>()
withTimeoutOrNull(5000L) {
if (isJsonArray("cmd")) {
val cmd = fetchPostJsonArray("cmd").map {
@ -59,17 +61,15 @@ fun Routing.otherAction() {
respond(false, Status.IAmTired, "执行超时")
} else {
it.inputStream.use {
out.append("stdout:\n")
out.append(it.readBytes().toString(Charsets.UTF_8))
out["out"] = it.readBytes().toString(Charsets.UTF_8)
}
it.errorStream.use {
out.append("\nstderr:\n")
out.append(it.readBytes().toString(Charsets.UTF_8))
out["err"] = 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.os.Build
import com.arthenica.ffmpegkit.BuildConfig
import de.robv.android.xposed.XSharedPreferences
import de.robv.android.xposed.XposedBridge
import de.robv.android.xposed.XposedHelpers
import moe.fuqiuluo.shamrock.tools.hookMethod
@ -16,6 +18,7 @@ internal object FuckAMS {
private lateinit var KeepThread: Thread
private lateinit var METHOD_IS_KILLED: Method
private var allowPersistent: Boolean = false
fun injectAMS(loader: ClassLoader) {
kotlin.runCatching {
@ -24,15 +27,24 @@ internal object FuckAMS {
increaseAdj(it.result)
}
}.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 {
val ProcessList = XposedHelpers.findClass("com.android.server.am.ProcessList", loader)
ProcessList.hookMethod("newProcessRecordLocked").after {
increaseAdj(it.result)
}
}.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
}.get(record) as ApplicationInfo
if(applicationInfo.processName in KeepPackage) {
XposedBridge.log("Process is keeping: $record")
XposedBridge.log("[Shamrock] Process is keeping: $record")
KeepRecords.add(record)
keepByAdj(record)
// Error
//if (noDied.exists()) {
// XposedBridge.log("Open NoDied Mode!!!")
// keepByPersistent(record)
//}
if (allowPersistent) {
XposedBridge.log("[Shamrock] Open NoDied Mode!!!")
keepByPersistent(record)
}
checkThread()
}
}