Support nested structures in desensitizer

This commit is contained in:
Him188 2021-08-27 19:44:32 +08:00
parent 82719d6018
commit ec9ad7f0f1
5 changed files with 97 additions and 19 deletions

View File

@ -12,6 +12,10 @@ package net.mamoe.mirai.internal.network.protocol.data.jce
import kotlinx.serialization.Serializable
import net.mamoe.mirai.internal.network.Packet
import net.mamoe.mirai.internal.utils.io.JceStruct
import net.mamoe.mirai.internal.utils.io.NestedStructure
import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
import net.mamoe.mirai.internal.utils.io.serialization.loadAs
import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
import net.mamoe.mirai.utils.EMPTY_BYTE_ARRAY
@ -46,6 +50,7 @@ internal class MsgInfo(
@TarsId(3) @JvmField val shMsgSeq: Short,
@TarsId(4) @JvmField val strMsg: String?,
@TarsId(5) @JvmField val uRealMsgTime: Int?,
@param:NestedStructure(VMsgDesensitizationSerializer::class)
@TarsId(6) @JvmField val vMsg: ByteArray,
@TarsId(7) @JvmField val uAppShareID: Long?,
@TarsId(8) @JvmField val vMsgCookies: ByteArray? = EMPTY_BYTE_ARRAY,
@ -62,6 +67,15 @@ internal class MsgInfo(
//@SerialId(19) @JvmField val stC2CTmpMsgHead: TempMsgHead?
) : JceStruct
internal object VMsgDesensitizationSerializer : NestedStructureDesensitizer<MsgInfo, ProtocolStruct> {
override fun deserialize(context: MsgInfo, byteArray: ByteArray): ProtocolStruct? {
return when (context.shMsgType.toUShort().toInt()) {
0x210 -> byteArray.loadAs(MsgType0x210.serializer())
else -> null
}
}
}
@Serializable
internal class ShareData(

View File

@ -9,6 +9,18 @@
package net.mamoe.mirai.internal.utils.io
import kotlin.reflect.KClass
internal interface ProtocolStruct
internal interface ProtoBuf : ProtocolStruct
internal interface JceStruct : ProtocolStruct
internal interface JceStruct : ProtocolStruct
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.VALUE_PARAMETER)
internal annotation class NestedStructure(
val serializer: KClass<out NestedStructureDesensitizer<*, *>>
)
internal interface NestedStructureDesensitizer<in C : ProtocolStruct, T : ProtocolStruct> {
fun deserialize(context: C, byteArray: ByteArray): T?
}

View File

@ -11,11 +11,17 @@ package net.mamoe.mirai.internal.notice
import kotlinx.serialization.decodeFromString
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.internal.notice.Desensitizer.Companion.generateAndDesensitize
import net.mamoe.mirai.internal.utils.codegen.*
import net.mamoe.mirai.internal.utils.io.NestedStructure
import net.mamoe.mirai.internal.utils.io.NestedStructureDesensitizer
import net.mamoe.mirai.internal.utils.io.ProtocolStruct
import net.mamoe.mirai.utils.*
import net.mamoe.yamlkt.Yaml
import net.mamoe.yamlkt.YamlBuilder
import kotlin.reflect.KType
import kotlin.reflect.full.createInstance
import kotlin.reflect.full.findAnnotation
import kotlin.reflect.typeOf
private val logger: MiraiLogger by lazy { MiraiLogger.Factory.create(Desensitizer::class) }
@ -179,4 +185,20 @@ private class DesensitizationVisitor(
desc.value = desensitizer.desensitize(desc.value as ByteArray)
}
}
override fun <T : Any> visitClass(desc: ClassValueDesc<T>) {
super.visitClass(desc)
desc.properties.replaceAll() { key, value ->
val annotation = key.findAnnotation<NestedStructure>()
if (annotation != null && value.origin is ByteArray) {
val instance = annotation.serializer.objectInstance ?: annotation.serializer.createInstance()
val result = instance.cast<NestedStructureDesensitizer<ProtocolStruct, ProtocolStruct>>()
.deserialize(desc.origin as ProtocolStruct, value.origin as ByteArray)
val generate = ConstructorCallCodegenFacade.generateAndDesensitize(result)
PlainValueDesc(desc, "$generate.toByteArray()", value.origin)
} else value
}
}
}

View File

@ -25,7 +25,7 @@ object ConstructorCallCodegenFacade {
* Analyze [value] and give its correspondent [ValueDesc].
*/
fun analyze(value: Any?, type: KType): ValueDesc {
if (value == null) return PlainValueDesc("null", null)
if (value == null) return PlainValueDesc(null, "null", null)
val clazz = value::class
@ -43,14 +43,15 @@ object ConstructorCallCodegenFacade {
prop.cast<KProperty1<Any, Any?>>()
map[valueParameter] = analyze(prop.get(value), prop.returnType)
}
return ClassValueDesc(value, map)
return ClassValueDesc(null, value, map)
}
ArrayValueDesc.createOrNull(value, type)?.let { return it }
ArrayValueDesc.createOrNull(value, type, null)?.let { return it }
if (value is Collection<*>) {
return CollectionValueDesc(value, arrayType = type, elementType = type.arguments.first().type!!)
return CollectionValueDesc(null, value, arrayType = type, elementType = type.arguments.first().type!!)
} else if (value is Map<*, *>) {
return MapValueDesc(
null,
value.cast(),
value.cast(),
type,
@ -61,12 +62,12 @@ object ConstructorCallCodegenFacade {
return when (value) {
is CharSequence -> {
PlainValueDesc('"' + value.toString() + '"', value)
PlainValueDesc(null, '"' + value.toString() + '"', value)
}
is Char -> {
PlainValueDesc("'$value'", value)
PlainValueDesc(null, "'$value'", value)
}
else -> PlainValueDesc(value.toString(), value)
else -> PlainValueDesc(null, value.toString(), value)
}
}

View File

@ -17,10 +17,23 @@ import kotlin.reflect.typeOf
sealed interface ValueDesc {
val origin: Any?
val parent: ValueDesc?
fun accept(visitor: ValueDescVisitor)
}
val ValueDesc.parents
get() = sequence {
var parent = parent
do {
parent ?: return@sequence
yield(parent)
parent = parent.parent
} while (true);
}
inline fun <reified T : ValueDesc> ValueDesc.findParent(): T? = parents.filterIsInstance<T>().firstOrNull()
sealed interface ArrayValueDesc : ValueDesc {
val value: Any
@ -30,17 +43,27 @@ sealed interface ArrayValueDesc : ValueDesc {
companion object {
@OptIn(ExperimentalStdlibApi::class)
fun createOrNull(array: Any, type: KType): ArrayValueDesc? {
if (array is Array<*>) return ObjectArrayValueDesc(array, arrayType = type)
fun createOrNull(array: Any, type: KType, parent: ValueDesc?): ArrayValueDesc? {
if (array is Array<*>) return ObjectArrayValueDesc(parent, array, arrayType = type)
return when (array) {
is IntArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Int>())
is ByteArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Byte>())
is ShortArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Short>())
is CharArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Char>())
is LongArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Long>())
is FloatArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Float>())
is DoubleArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Double>())
is BooleanArray -> PrimitiveArrayValueDesc(array, arrayType = type, elementType = typeOf<Boolean>())
is IntArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Int>())
is ByteArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Byte>())
is ShortArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Short>())
is CharArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Char>())
is LongArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Long>())
is FloatArray -> PrimitiveArrayValueDesc(parent, array, arrayType = type, elementType = typeOf<Float>())
is DoubleArray -> PrimitiveArrayValueDesc(
parent,
array,
arrayType = type,
elementType = typeOf<Double>()
)
is BooleanArray -> PrimitiveArrayValueDesc(
parent,
array,
arrayType = type,
elementType = typeOf<Boolean>()
)
else -> return null
}
}
@ -48,10 +71,11 @@ sealed interface ArrayValueDesc : ValueDesc {
}
class ObjectArrayValueDesc(
override val parent: ValueDesc?,
override var value: Array<*>,
override val origin: Array<*> = value,
override val arrayType: KType,
override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType()
override val elementType: KType = arrayType.arguments.first().type ?: Any::class.createType(),
) : ArrayValueDesc {
override val elements: MutableList<ValueDesc> by lazy {
value.mapTo(mutableListOf()) {
@ -65,6 +89,7 @@ class ObjectArrayValueDesc(
}
class CollectionValueDesc(
override val parent: ValueDesc?,
override var value: Collection<*>,
override val origin: Collection<*> = value,
override val arrayType: KType,
@ -82,6 +107,7 @@ class CollectionValueDesc(
}
class MapValueDesc(
override val parent: ValueDesc?,
var value: Map<Any?, Any?>,
override val origin: Map<Any?, Any?> = value,
val mapType: KType,
@ -103,6 +129,7 @@ class MapValueDesc(
}
class PrimitiveArrayValueDesc(
override val parent: ValueDesc?,
override var value: Any,
override val origin: Any = value,
override val arrayType: KType,
@ -128,6 +155,7 @@ class PrimitiveArrayValueDesc(
}
class PlainValueDesc(
override val parent: ValueDesc?,
var value: String,
override val origin: Any?
) : ValueDesc {
@ -141,6 +169,7 @@ class PlainValueDesc(
}
class ClassValueDesc<T : Any>(
override val parent: ValueDesc?,
override val origin: T,
val properties: MutableMap<KParameter, ValueDesc>,
) : ValueDesc {