Encode security log data payload to json

Initialize Dhizuku with context
fix #85
This commit is contained in:
BinTianqi
2024-11-30 15:12:04 +08:00
parent eadb2e9e8a
commit 690ac876ab
6 changed files with 245 additions and 49 deletions

View File

@@ -68,6 +68,14 @@ android {
} }
} }
kotlin {
sourceSets {
all {
languageSettings.optIn("kotlinx.serialization.ExperimentalSerializationApi")
}
}
}
gradle.taskGraph.whenReady { gradle.taskGraph.whenReady {
project.tasks.findByPath(":app:test")?.enabled = false project.tasks.findByPath(":app:test")?.enabled = false
project.tasks.findByPath(":app:lint")?.enabled = false project.tasks.findByPath(":app:lint")?.enabled = false

View File

@@ -66,9 +66,11 @@ class Receiver : DeviceAdminReceiver() {
override fun onSecurityLogsAvailable(context: Context, intent: Intent) { override fun onSecurityLogsAvailable(context: Context, intent: Intent) {
super.onSecurityLogsAvailable(context, intent) super.onSecurityLogsAvailable(context, intent)
if(VERSION.SDK_INT >= 24) { if(VERSION.SDK_INT >= 24) {
CoroutineScope(Dispatchers.IO).launch {
handleSecurityLogs(context) handleSecurityLogs(context)
} }
} }
}
override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) { override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) {
super.onTransferOwnershipComplete(context, bundle) super.onTransferOwnershipComplete(context, bundle)

View File

@@ -8,6 +8,7 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DnsEvent import android.app.admin.DnsEvent
import android.app.admin.FactoryResetProtectionPolicy import android.app.admin.FactoryResetProtectionPolicy
import android.app.admin.IDevicePolicyManager import android.app.admin.IDevicePolicyManager
import android.app.admin.SecurityLog
import android.app.admin.SystemUpdatePolicy import android.app.admin.SystemUpdatePolicy
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
@@ -31,12 +32,18 @@ import com.rosan.dhizuku.api.Dhizuku
import com.rosan.dhizuku.api.Dhizuku.binderWrapper import com.rosan.dhizuku.api.Dhizuku.binderWrapper
import com.rosan.dhizuku.api.DhizukuBinderWrapper import com.rosan.dhizuku.api.DhizukuBinderWrapper
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromStream import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.put
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import kotlin.io.path.inputStream import kotlin.io.path.inputStream
@@ -322,7 +329,6 @@ fun permissionList(): List<PermissionItem>{
} }
@RequiresApi(26) @RequiresApi(26)
@OptIn(ExperimentalSerializationApi::class)
fun handleNetworkLogs(context: Context, batchToken: Long) { fun handleNetworkLogs(context: Context, batchToken: Long) {
val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return
val file = context.filesDir.toPath().resolve("NetworkLogs.json") val file = context.filesDir.toPath().resolve("NetworkLogs.json")
@@ -370,7 +376,6 @@ data class NetworkEventItem(
@SerialName("addresses") val hostAddresses: List<String?>? = null @SerialName("addresses") val hostAddresses: List<String?>? = null
) )
@OptIn(ExperimentalSerializationApi::class)
@RequiresApi(24) @RequiresApi(24)
fun handleSecurityLogs(context: Context) { fun handleSecurityLogs(context: Context) {
val file = context.filesDir.resolve("SecurityLogs.json") val file = context.filesDir.resolve("SecurityLogs.json")
@@ -378,31 +383,220 @@ fun handleSecurityLogs(context: Context) {
if(!file.exists()) file.writeText("[]") if(!file.exists()) file.writeText("[]")
val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver()) val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver())
securityEvents ?: return securityEvents ?: return
val logs: MutableList<SecurityEventItem> val logArray = json.parseToJsonElement(file.readText()).jsonArray
file.inputStream().use { val newLogs = buildJsonArray {
logs = json.decodeFromStream(it) securityEvents.forEach { event ->
addJsonObject {
put("time_nanos", event.timeNanos)
put("tag", event.tag)
if(VERSION.SDK_INT >= 28) put("level", event.logLevel)
if(VERSION.SDK_INT >= 28) put("id", event.id)
parseSecurityEventData(event).let { if(it != null) put("data", it) }
}
} }
securityEvents.forEach {
logs += SecurityEventItem(
id = if(VERSION.SDK_INT >= 28) it.id else null,
tag = it.tag, timeNanos = it.timeNanos,
logLevel = if(VERSION.SDK_INT >= 28) it.logLevel else null,
data = it.data.toString()
)
} }
file.outputStream().use { file.outputStream().use {
json.encodeToStream(logs, it) json.encodeToStream(logArray + newLogs, it)
} }
} }
@Serializable @RequiresApi(24)
data class SecurityEventItem( fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
val id: Long?, return when(event.tag) { //TODO: backup service tag (API35)
val tag: Int, SecurityLog.TAG_ADB_SHELL_CMD -> JsonPrimitive(event.data as String)
@SerialName("time_nanos") val timeNanos: Long, SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null
@SerialName("log_level") val logLevel: Int?, SecurityLog.TAG_APP_PROCESS_START -> {
val data: String val payload = event.data as Array<*>
) buildJsonObject {
put("name", payload[0] as String)
put("start_time", payload[1] as Long)
put("uid", payload[2] as Int)
put("pid", payload[3] as Int)
put("seinfo", payload[4] as String)
put("apk_hash", payload[5] as String)
}
}
SecurityLog.TAG_BLUETOOTH_CONNECTION -> {
val payload = event.data as Array<*>
buildJsonObject {
put("mac", payload[0] as String)
put("successful", payload[1] as Int)
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
}
}
SecurityLog.TAG_BLUETOOTH_DISCONNECTION -> {
val payload = event.data as Array<*>
buildJsonObject {
put("mac", payload[0] as String)
(payload[2] as String).let { if(it != "") put("reason", it) }
}
}
SecurityLog.TAG_CAMERA_POLICY_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("disabled", payload[3] as Int)
}
}
SecurityLog.TAG_CERT_AUTHORITY_INSTALLED, SecurityLog.TAG_CERT_AUTHORITY_REMOVED -> {
val payload = event.data as Array<*>
buildJsonObject {
put("result", payload[0] as Int)
put("subject", payload[1] as String)
if(VERSION.SDK_INT >= 30) put("user", payload[2] as Int)
}
}
SecurityLog.TAG_CERT_VALIDATION_FAILURE -> JsonPrimitive(event.data as String)
SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED -> JsonPrimitive(event.data as Int)
SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("feature_mask", payload[3] as Int)
}
}
SecurityLog.TAG_KEYGUARD_DISMISSED -> null
SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT -> {
val payload = event.data as Array<*>
buildJsonObject {
put("result", payload[0] as Int)
put("strength", payload[1] as Int)
}
}
SecurityLog.TAG_KEYGUARD_SECURED -> null
SecurityLog.TAG_KEY_DESTRUCTION, SecurityLog.TAG_KEY_GENERATED, SecurityLog.TAG_KEY_IMPORT -> {
val payload = event.data as Array<*>
buildJsonObject {
put("result", payload[0] as Int)
put("alias", payload[1] as String)
put("uid", payload[2] as Int)
}
}
SecurityLog.TAG_KEY_INTEGRITY_VIOLATION -> {
val payload = event.data as Array<*>
buildJsonObject {
put("alias", payload[0] as String)
put("uid", payload[1] as Int)
}
}
SecurityLog.TAG_LOGGING_STARTED, SecurityLog.TAG_LOGGING_STOPPED -> null
SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL -> null
SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("value", payload[3] as Int)
}
}
SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("timeout", payload[3] as Long)
}
}
SecurityLog.TAG_MEDIA_MOUNT, SecurityLog.TAG_MEDIA_UNMOUNT -> {
val payload = event.data as Array<*>
buildJsonObject {
put("mount_point", payload[0] as String)
put("volume_label", payload[1] as String)
}
}
SecurityLog.TAG_OS_SHUTDOWN -> null
SecurityLog.TAG_OS_STARTUP -> {
val payload = event.data as Array<*>
buildJsonObject {
put("verified_boot_state", payload[0] as String)
put("dm_verify_state", payload[1] as String)
}
}
SecurityLog.TAG_PACKAGE_INSTALLED, SecurityLog.TAG_PACKAGE_UNINSTALLED, SecurityLog.TAG_PACKAGE_UPDATED -> {
val payload = event.data as Array<*>
buildJsonObject {
put("package_name", payload[0] as String)
put("version_code", payload[1] as Long)
put("user_id", payload[2] as Int)
}
}
SecurityLog.TAG_PASSWORD_CHANGED -> {
val payload = event.data as Array<*>
buildJsonObject {
put("complexity", payload[0] as Int)
put("user_id", payload[1] as Int)
}
}
SecurityLog. TAG_PASSWORD_COMPLEXITY_REQUIRED -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("complexity", payload[3] as Int)
}
}
SecurityLog.TAG_PASSWORD_COMPLEXITY_SET -> null //Deprecated
SecurityLog.TAG_PASSWORD_EXPIRATION_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("timeout", payload[3] as Long)
}
}
SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
put("length", payload[3] as Int)
}
}
SecurityLog.TAG_REMOTE_LOCK -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("target_user_id", payload[2] as Int)
}
}
SecurityLog.TAG_SYNC_RECV_FILE, SecurityLog.TAG_SYNC_SEND_FILE -> JsonPrimitive(event.data as String)
SecurityLog.TAG_USER_RESTRICTION_ADDED, SecurityLog.TAG_USER_RESTRICTION_REMOVED -> {
val payload = event.data as Array<*>
buildJsonObject {
put("admin", payload[0] as String)
put("admin_user_id", payload[1] as Int)
put("restriction", payload[2] as String)
}
}
SecurityLog.TAG_WIFI_CONNECTION -> {
val payload = event.data as Array<*>
buildJsonObject {
put("bssid", payload[0] as String)
put("type", payload[1] as String)
(payload[2] as String).let { if(it != "") put("failure_reason", it) }
}
}
SecurityLog.TAG_WIFI_DISCONNECTION -> {
val payload = event.data as Array<*>
buildJsonObject {
put("bssid", payload[0] as String)
(payload[1] as String).let { if(it != "") put("reason", it) }
}
}
SecurityLog.TAG_WIPE_FAILURE -> null
else -> null
}
}
fun setDefaultAffiliationID(context: Context) { fun setDefaultAffiliationID(context: Context) {
if(VERSION.SDK_INT < 26) return if(VERSION.SDK_INT < 26) return

View File

@@ -239,7 +239,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
} }
if(Dhizuku.isPermissionGranted()) { if(Dhizuku.isPermissionGranted()) {
sharedPref.edit().putBoolean("dhizuku", true).apply() sharedPref.edit().putBoolean("dhizuku", true).apply()
Dhizuku.init() Dhizuku.init(context)
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} else { } else {
Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() { Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() {
@@ -247,7 +247,7 @@ private fun toggleDhizukuMode(status: Boolean, context: Context) {
override fun onRequestPermission(grantResult: Int) { override fun onRequestPermission(grantResult: Int) {
if(grantResult == PackageManager.PERMISSION_GRANTED) { if(grantResult == PackageManager.PERMISSION_GRANTED) {
sharedPref.edit().putBoolean("dhizuku", true).apply() sharedPref.edit().putBoolean("dhizuku", true).apply()
Dhizuku.init() Dhizuku.init(context)
context.toggleInstallAppActivity() context.toggleInstallAppActivity()
backToHomeStateFlow.value = true backToHomeStateFlow.value = true
} else { } else {

View File

@@ -127,9 +127,11 @@ import com.bintianqi.owndroid.ui.TopBar
import com.bintianqi.owndroid.uriToStream import com.bintianqi.owndroid.uriToStream
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.addJsonObject
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.encodeToStream import kotlinx.serialization.json.encodeToStream
import kotlinx.serialization.json.put
import java.util.Date import java.util.Date
import java.util.TimeZone import java.util.TimeZone
import java.util.concurrent.Executors import java.util.concurrent.Executors
@@ -1000,7 +1002,6 @@ private fun CaCert() {
} }
} }
@OptIn(ExperimentalSerializationApi::class)
@SuppressLint("NewApi") @SuppressLint("NewApi")
@Composable @Composable
private fun SecurityLogs() { private fun SecurityLogs() {
@@ -1053,19 +1054,21 @@ private fun SecurityLogs() {
Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show() Toast.makeText(context, R.string.no_logs, Toast.LENGTH_SHORT).show()
return@Button return@Button
} else { } else {
val logsList = mutableListOf<SecurityEventItem>() val securityEvents = buildJsonArray {
logs.forEach { logs.forEach { event ->
logsList += SecurityEventItem( addJsonObject {
id = if(VERSION.SDK_INT >= 28) it.id else null, put("time_nanos", event.timeNanos)
tag = it.tag, timeNanos = it.timeNanos, put("tag", event.tag)
logLevel = if(VERSION.SDK_INT >= 28) it.logLevel else null, if(VERSION.SDK_INT >= 28) put("level", event.logLevel)
data = it.data.toString() if(VERSION.SDK_INT >= 28) put("id", event.id)
) parseSecurityEventData(event).let { if(it != null) put("data", it) }
}
}
} }
val preRebootSecurityLogs = context.filesDir.resolve("PreRebootSecurityLogs") val preRebootSecurityLogs = context.filesDir.resolve("PreRebootSecurityLogs")
preRebootSecurityLogs.outputStream().use { preRebootSecurityLogs.outputStream().use {
val json = Json { ignoreUnknownKeys = true; explicitNulls = false } val json = Json { ignoreUnknownKeys = true; explicitNulls = false }
json.encodeToStream(logsList, it) json.encodeToStream(securityEvents, it)
} }
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE) intent.addCategory(Intent.CATEGORY_OPENABLE)

View File

@@ -1,19 +1,8 @@
## For more details on how to configure your build environment visit ## For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
#
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx1024m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
#
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
#Sun Jan 21 20:24:56 CST 2024
android.nonTransitiveRClass=true android.nonTransitiveRClass=true
android.useAndroidX=true android.useAndroidX=true
kotlin.code.style=official kotlin.code.style=official
org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" -Dfile.encoding\=UTF-8
org.gradle.parallel=true org.gradle.parallel=true
org.gradle.caching=true org.gradle.caching=true