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 {
project.tasks.findByPath(":app:test")?.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) {
super.onSecurityLogsAvailable(context, intent)
if(VERSION.SDK_INT >= 24) {
CoroutineScope(Dispatchers.IO).launch {
handleSecurityLogs(context)
}
}
}
override fun onTransferOwnershipComplete(context: Context, bundle: PersistableBundle?) {
super.onTransferOwnershipComplete(context, bundle)

View File

@@ -8,6 +8,7 @@ import android.app.admin.DevicePolicyManager
import android.app.admin.DnsEvent
import android.app.admin.FactoryResetProtectionPolicy
import android.app.admin.IDevicePolicyManager
import android.app.admin.SecurityLog
import android.app.admin.SystemUpdatePolicy
import android.content.ComponentName
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.DhizukuBinderWrapper
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
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.encodeToStream
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.put
import java.io.IOException
import java.io.InputStream
import kotlin.io.path.inputStream
@@ -322,7 +329,6 @@ fun permissionList(): List<PermissionItem>{
}
@RequiresApi(26)
@OptIn(ExperimentalSerializationApi::class)
fun handleNetworkLogs(context: Context, batchToken: Long) {
val networkEvents = context.getDPM().retrieveNetworkLogs(context.getReceiver(), batchToken) ?: return
val file = context.filesDir.toPath().resolve("NetworkLogs.json")
@@ -370,7 +376,6 @@ data class NetworkEventItem(
@SerialName("addresses") val hostAddresses: List<String?>? = null
)
@OptIn(ExperimentalSerializationApi::class)
@RequiresApi(24)
fun handleSecurityLogs(context: Context) {
val file = context.filesDir.resolve("SecurityLogs.json")
@@ -378,31 +383,220 @@ fun handleSecurityLogs(context: Context) {
if(!file.exists()) file.writeText("[]")
val securityEvents = context.getDPM().retrieveSecurityLogs(context.getReceiver())
securityEvents ?: return
val logs: MutableList<SecurityEventItem>
file.inputStream().use {
logs = json.decodeFromStream(it)
val logArray = json.parseToJsonElement(file.readText()).jsonArray
val newLogs = buildJsonArray {
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 {
json.encodeToStream(logs, it)
json.encodeToStream(logArray + newLogs, it)
}
}
@Serializable
data class SecurityEventItem(
val id: Long?,
val tag: Int,
@SerialName("time_nanos") val timeNanos: Long,
@SerialName("log_level") val logLevel: Int?,
val data: String
)
@RequiresApi(24)
fun parseSecurityEventData(event: SecurityLog.SecurityEvent): JsonElement? {
return when(event.tag) { //TODO: backup service tag (API35)
SecurityLog.TAG_ADB_SHELL_CMD -> JsonPrimitive(event.data as String)
SecurityLog.TAG_ADB_SHELL_INTERACTIVE -> null
SecurityLog.TAG_APP_PROCESS_START -> {
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) {
if(VERSION.SDK_INT < 26) return

View File

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

View File

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

View File

@@ -1,19 +1,8 @@
## For more details on how to configure your build environment visit
# 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.useAndroidX=true
kotlin.code.style=official
org.gradle.jvmargs=-Xmx1536M -Dkotlin.daemon.jvm.options\="-Xmx1536M" -Dfile.encoding\=UTF-8
org.gradle.parallel=true
org.gradle.caching=true