All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xml.guard.model.MappingParser.kt Maven / Gradle / Ivy

There is a newer version: 3.0.5
Show newest version
package com.xml.guard.model

import com.xml.guard.entensions.GuardExtension
import com.xml.guard.utils.inBlackList
import com.xml.guard.utils.inClassNameBlackList
import com.xml.guard.utils.isLowercase
import com.xml.guard.utils.replaceLast
import org.gradle.api.Project
import java.io.File
import java.util.regex.Pattern

/**
 * User: ljx
 * Date: 2022/3/25
 * Time: 11:15
 */
object MappingParser {
    private val mappingPattern: Pattern by lazy { Pattern.compile("(.*)->(.*)") }

    @Suppress("unused")
    private val uppercasePattern: Pattern by lazy { Pattern.compile("^[A-Z]+$") }

    @Suppress("unused")
    private val lowerPattern: Pattern by lazy { Pattern.compile("^[a-z]+$") }

    internal const val MAPPING_XML_CLASS = "guard-xml-class-mapping.txt"
    internal const val MAPPING_RES = "guard-res-mapping.txt"
    internal const val MAPPING_STRING_FOR = "guard-string-fog-mapping.txt"
    internal const val MAPPING_DECLARATION = "guard-declaration-mapping.txt"
    internal const val MAPPING_CUSTOM_STRING = "guard-custom-string-mapping.txt"

    internal const val DIR_MAPPING = "dir mapping:"
    internal const val CLASS_MAPPING = "class mapping:"
    internal const val RES_MAPPING = "res mapping:"
    internal const val LAYOUT_MAPPING = "layout mapping:"
    internal const val VALUES_MAPPING = "values mapping:"
    internal const val STATIC_FIELD_MAPPING = "static field mapping:"
    internal const val NON_STATIC_FIELD_MAPPING = "non static field mapping:"
    internal const val METHOD_NAME_MAPPING = "method name mapping:"
    internal const val METHOD_PARAMETER_MAPPING = "method parameter mapping:"
    internal const val ENUM_CONSTANT_MAPPING = "enum constant mapping:"

    internal const val STRING_FOG_KEY = "stringFogKey"
    internal const val STRING_FOG_METHOD = "stringFogMethodName"
    internal const val STRING_FOG_CLASS_NAME = "stringFogClassName"
    internal const val STRING_FOG_DIR_NAME = "stringFogDirName"

    // 记录其他 Mapping 的混淆记录
    private val otherMappingMap by lazy { mutableMapOf() }

    /**
     * 检查名称是否符合混淆条件
     */
    fun checkObfuscate(
        rawName: String,
        rawValue: String = "",
        isLowercase: Boolean = false
    ): String {
        val obfuscateName = otherMappingMap[rawName]
        if (obfuscateName.isNullOrBlank() || obfuscateName == rawValue) {
            return rawValue
        }
        if (rawValue.isNotBlank()) {
            throw IllegalArgumentException("`${rawName}` -> `${rawValue}` is illegal, `${rawName}` is obfuscated")
        }
        if (isLowercase && !obfuscateName.isLowercase()) {
            throw IllegalArgumentException("`${rawName}` is illegal, `${rawName}` is obfuscated")
        }
        return obfuscateName
    }

    /**
     * 校验混淆名称
     */
    fun verifyObfuscateName(obfuscateName: String): String? {
        return if (otherMappingMap.containsValue(obfuscateName)) null else obfuscateName
    }

    /**
     * 解析自定义字符混淆
     */
    fun parseCustomString(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isSelfParse: Boolean = false
    ): CustomStringMapping {
        if (isSelfParse) {
            mergeOtherMapping(project, guardExtension, mappingFile)
        }
        val mapping = CustomStringMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping
        }

        if (isSelfParse) {
            println("parseCustomString --- otherMappingMapSize:${otherMappingMap.size}")
        }

        parse(mappingFile) { _, _, key, value ->
            val rawName = key.trim()
            val rawValue = value.trim()

            if (!isSelfParse) {
                otherMappingMap[rawName] = rawValue
                return@parse
            }

            val obfuscateName = checkObfuscate(rawName, rawValue)
            mapping.customStringMapping[rawName] = obfuscateName
        }
        return mapping
    }

    /**
     * 解析声明字符混淆
     */
    fun parseDeclaration(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isSelfParse: Boolean = false
    ): DeclarationMapping {
        if (isSelfParse) {
            mergeOtherMapping(project, guardExtension, mappingFile)
        }
        val mapping = DeclarationMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping
        }

        if (isSelfParse) {
            println("parseDeclaration --- otherMappingMapSize:${otherMappingMap.size}")
        }

        parse(mappingFile) { _, mappingType, key, value ->
            val rawName = key.trim()
            val rawValue = value.trim()

            // 白名单、已混淆
            if (mapping.inWhiteList(rawName) || mapping.isObfuscated(rawName)) {
                return@parse
            }
            if (!isSelfParse) {
                otherMappingMap[rawName] = rawValue
                return@parse
            }

            when (mappingType) {
                STATIC_FIELD_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.allMappingMap[rawName] = obfuscateName
                    mapping.staticFieldMapping[rawName] = obfuscateName
                }

                NON_STATIC_FIELD_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.allMappingMap[rawName] = obfuscateName
                    mapping.nonStaticFieldMapping[rawName] = obfuscateName
                }

                METHOD_NAME_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.allMappingMap[rawName] = obfuscateName
                    mapping.methodNameMapping[rawName] = obfuscateName
                }

                METHOD_PARAMETER_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.allMappingMap[rawName] = obfuscateName
                    mapping.methodParameterMapping[rawName] = obfuscateName
                }

                ENUM_CONSTANT_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.allMappingMap[rawName] = obfuscateName
                    mapping.enumConstantMapping[rawName] = obfuscateName
                }
            }
        }
        return mapping
    }

    /**
     * 解析字符串加解密混淆
     */
    fun parseStringFog(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isSelfParse: Boolean = false
    ): StringFogMapping {
        if (isSelfParse) {
            mergeOtherMapping(project, guardExtension, mappingFile)
        }
        val mapping = StringFogMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping
        }

        if (isSelfParse) {
            println("parseStringFog --- otherMappingMapSize:${otherMappingMap.size}")
        }

        parse(mappingFile) { line, _, key, value ->
            var rawName = key.trim()
            var rawValue = value.trim()
            when (rawName) {
                STRING_FOG_KEY -> {
                    if (rawValue.length != 8) {
                        throw IllegalArgumentException("`${line.trim()}` is illegal, $rawValue is wrong")
                    }
                    if (!isSelfParse) {
                        otherMappingMap[rawValue] = rawValue
                        return@parse
                    }

                    checkObfuscate(rawValue)
                    mapping.stringFogKey = rawValue
                }

                STRING_FOG_METHOD -> {
                    if (!isSelfParse) {
                        otherMappingMap[rawValue] = rawValue
                        return@parse
                    }

                    checkObfuscate(rawValue)
                    mapping.stringFogMethodName = rawValue
                }

                STRING_FOG_CLASS_NAME -> {
                    if (!isSelfParse) {
                        otherMappingMap[rawValue] = rawValue
                        return@parse
                    }

                    checkObfuscate(rawValue)
                    mapping.stringFogClassName = rawValue
                }

                STRING_FOG_DIR_NAME -> {
                    if (!isSelfParse) {
                        otherMappingMap[rawValue] = rawValue
                        return@parse
                    }

                    checkObfuscate(rawValue)
                    mapping.stringFogDirName = rawValue
                }

                else -> {
                    rawName = key.replaceLast(" ", "")
                    rawValue = value.trim()

                    // 白名单
                    if (mapping.isTextWhiteList(rawName)) {
                        return@parse
                    }
                    if (!isSelfParse) {
                        otherMappingMap[rawName] = rawValue
                        return@parse
                    }

                    checkObfuscate(rawName)
                    mapping.stringFogMapping[rawName] = rawValue
                }
            }
        }
        return mapping
    }

    /**
     * 解析资源混淆
     */
    fun parseRes(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isSelfParse: Boolean = false
    ): ResMapping {
        if (isSelfParse) {
            mergeOtherMapping(project, guardExtension, mappingFile)
        }
        val mapping = ResMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping
        }

        if (isSelfParse) {
            println("parseRes --- otherMappingMapSize:${otherMappingMap.size}")
        }

        parse(mappingFile) { _, mappingType, key, value ->
            val rawName = key.trim()
            val rawValue = value.trim()

            if (!isSelfParse) {
                otherMappingMap[rawName] = rawValue
                return@parse
            }

            when (mappingType) {
                LAYOUT_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.layoutMapping[rawName] = obfuscateName
                }

                RES_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue, isLowercase = true)
                    mapping.resMapping[rawName] = obfuscateName
                }

                VALUES_MAPPING -> {
                    val obfuscateName = checkObfuscate(rawName, rawValue, isLowercase = true)
                    mapping.valuesMapping[rawName] = obfuscateName
                }
            }
        }
        return mapping
    }

    fun parseXmlClass(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isSelfParse: Boolean = false
    ): XmlClassMapping {
        if (isSelfParse) {
            mergeOtherMapping(project, guardExtension, mappingFile)
        }
        val mapping = XmlClassMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping
        }

        if (isSelfParse) {
            println("parseXmlClass --- otherMappingMapSize:${otherMappingMap.size}")
        }

        parse(mappingFile) { line, mappingType, key, value ->
            val rawName = key.trim()
            val rawValue = value.trim()

            when (mappingType) {
                DIR_MAPPING -> {
                    // 白名单
                    if (mapping.inWhiteList2(rawName)) {
                        return@parse
                    }
                    var obfuscateName = (rawValue.split(".").lastOrNull() ?: rawValue).lowercase()
                    if (obfuscateName.inBlackList()) {
                        throw IllegalArgumentException("`${line.trim()}` is illegal, $obfuscateName is a blacklist")
                    }
                    if (!isSelfParse) {
                        otherMappingMap[rawName] = rawValue
                        return@parse
                    }

                    obfuscateName = checkObfuscate(rawName, rawValue, isLowercase = true)
                    mapping.dirMapping[rawName] = obfuscateName
                }

                CLASS_MAPPING -> {
                    // 白名单
                    if (mapping.inWhiteList(rawName)) {
                        return@parse
                    }
                    val index = rawValue.lastIndexOf(".")
                    if (index == -1) {
                        //混淆路径必须要有包名
                        throw IllegalArgumentException("`$rawValue` is illegal, must have a package name")
                    }
                    val obfuscateClassPath = rawValue.substring(0, index) //混淆后的包名
                    val obfuscateClassName = rawValue.substring(index + 1) //混淆后的类名

                    if (obfuscateClassName.inClassNameBlackList()) {
                        throw IllegalArgumentException("`$rawValue` is illegal, It cannot be defined as a class name")
                    }
                    val dirMapping = mapping.dirMapping
                    if (!dirMapping.containsValue(obfuscateClassPath)) {
                        val rawClassPath = rawName.substring(0, rawName.lastIndexOf(".")) //原始包名
                        if (dirMapping.containsKey(rawClassPath)) {
                            //类混淆的真实路径与混淆的目录不匹配
                            throw IllegalArgumentException("$rawName -> $rawValue is illegal should be\n$rawName -> ${dirMapping[rawClassPath]}.$obfuscateClassName")
                        }
                        dirMapping[rawClassPath] = obfuscateClassPath
                    }
                    if (!isSelfParse) {
                        otherMappingMap[rawName] = rawValue
                        return@parse
                    }

                    val obfuscateName = checkObfuscate(rawName, rawValue)
                    mapping.classMapping[rawName] = obfuscateName
                }
            }
        }
        return mapping
    }

    fun parse(
        mappingFile: File,
        callback: (line: String, mappingType: String, key: String, value: String) -> Unit
    ) {
        if (!mappingFile.exists() || !mappingFile.isFile) return
        var mappingType = ""
        mappingFile.forEachLine { line ->
            val mat = mappingPattern.matcher(line)
            if (mat.find()) {
                val key = mat.group(1)
                val value = mat.group(2)
                callback.invoke(line, mappingType, key, value)
            } else {
                mappingType = line
            }
        }
    }

    private fun mergeOtherMapping(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File
    ) {
        if (otherMappingMap.isNotEmpty()) {
            otherMappingMap.clear()
        }
        val mappingFileName = if (mappingFile.exists()) mappingFile.name else ""
        if (mappingFileName != MAPPING_RES) {
            parseRes(project, guardExtension, project.file(MAPPING_RES))
        }
        if (mappingFileName != MAPPING_XML_CLASS) {
            parseXmlClass(project, guardExtension, project.file(MAPPING_XML_CLASS))
        }
        if (mappingFileName != MAPPING_STRING_FOR) {
            parseStringFog(project, guardExtension, project.file(MAPPING_STRING_FOR))
        }
        if (mappingFileName != MAPPING_DECLARATION) {
            parseDeclaration(project, guardExtension, project.file(MAPPING_DECLARATION))
        }
        if (mappingFileName != MAPPING_CUSTOM_STRING) {
            parseCustomString(project, guardExtension, project.file(MAPPING_CUSTOM_STRING))
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy