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

main.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.inClassNameBlackList
import com.xml.guard.utils.inPackageNameBlackList
import com.xml.guard.utils.to26Long
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("\\s*(.*)->(.*)") }
    private val uppercasePattern: Pattern by lazy { Pattern.compile("^[A-Z]+$") }
    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"

    fun parseCustomString(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isComparison: Boolean = false
    ): CustomStringMapping {
        var obfuscateIndex = -1L
        if (isComparison) {
            obfuscateIndex = compareObfuscateIndex(project, guardExtension, mappingFile)
        }
        val mapping = CustomStringMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping.apply { this.obfuscateIndex = obfuscateIndex }
        }

        parse(mappingFile) { _, _, key, value ->
            val minimumValue = value.let {
                // 若字符串包含 _ 连接符,则取字符串首个 _ 前的字符(参与混淆名称的生成)
                (it.split("_").firstOrNull() ?: it).to26Long()
            }
            obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)

            mapping.customStringMapping[key] = value
        }

        mapping.obfuscateIndex = mapping.obfuscateIndex.coerceAtLeast(obfuscateIndex)
        return mapping
    }

    fun parseDeclaration(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isComparison: Boolean = false
    ): DeclarationMapping {
        var obfuscateIndex = -1L
        if (isComparison) {
            obfuscateIndex = compareObfuscateIndex(project, guardExtension, mappingFile)
        }
        val mapping = DeclarationMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping.apply { this.obfuscateIndex = obfuscateIndex }
        }

        parse(mappingFile) { _, mappingType, key, value ->
            // 已混淆
            if (mapping.isObfuscated(key)) {
                return@parse
            }
            // 白名单
            if (mapping.inWhiteList(key)) {
                return@parse
            }

            val minimumValue = value.let {
                // 若字符串包含 _ 连接符,则取字符串首个 _ 前的字符(参与混淆名称的生成)
                (it.split("_").firstOrNull() ?: it).to26Long()
            }
            obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)

            when (mappingType) {
                STATIC_FIELD_MAPPING -> {
                    mapping.allMappingMap[key] = value
                    mapping.staticFieldMapping[key] = value
                }

                NON_STATIC_FIELD_MAPPING -> {
                    mapping.allMappingMap[key] = value
                    mapping.nonStaticFieldMapping[key] = value
                }

                METHOD_NAME_MAPPING -> {
                    mapping.allMappingMap[key] = value
                    mapping.methodNameMapping[key] = value
                }

                METHOD_PARAMETER_MAPPING -> {
                    mapping.allMappingMap[key] = value
                    mapping.methodParameterMapping[key] = value
                }

                ENUM_CONSTANT_MAPPING -> {
                    mapping.allMappingMap[key] = value
                    mapping.enumConstantMapping[key] = value
                }
            }
        }

        mapping.obfuscateIndex = mapping.obfuscateIndex.coerceAtLeast(obfuscateIndex)
        return mapping
    }

    fun parseStringFog(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isComparison: Boolean = false
    ): StringFogMapping {
        var obfuscateIndex = -1L
        if (isComparison) {
            obfuscateIndex = compareObfuscateIndex(project, guardExtension, mappingFile)
        }
        val mapping = StringFogMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping.apply { this.obfuscateIndex = obfuscateIndex }
        }

        parse(mappingFile) { line, _, key, value ->
            when (key) {
                STRING_FOG_KEY -> {
                    if (value.length != 8) {
                        throw IllegalArgumentException("`${line.trim()}` is illegal, $value is wrong")
                    }
                    mapping.stringFogKey = value
                }

                STRING_FOG_METHOD -> {
                    mapping.stringFogMethodName = value
                    val minimumValue = value.to26Long()
                    obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                }

                STRING_FOG_CLASS_NAME -> {
                    mapping.stringFogClassName = value
                    val minimumValue = value.to26Long()
                    obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                }

                STRING_FOG_DIR_NAME -> {
                    mapping.stringFogDirName = value
                    val minimumValue = value.to26Long()
                    obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                }
            }
        }

        mapping.obfuscateIndex = mapping.obfuscateIndex.coerceAtLeast(obfuscateIndex)
        return mapping
    }

    fun parseRes(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isComparison: Boolean = false
    ): ResMapping {
        var obfuscateIndex = -1L
        if (isComparison) {
            obfuscateIndex = compareObfuscateIndex(project, guardExtension, mappingFile)
        }
        val mapping = ResMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping.apply { this.obfuscateIndex = obfuscateIndex }
        }

        parse(mappingFile) { line, mappingType, rawName, value ->
            if (value.inPackageNameBlackList()) {
                throw IllegalArgumentException("`${line.trim()}` is illegal, $value is a keyword")
            }
            val minimumValue = value.let {
                // 若字符串包含 _ 连接符,则取字符串首个 _ 前的字符(参与混淆名称的生成)
                (it.split("_").firstOrNull() ?: it).to26Long()
            }
            obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)

            when (mappingType) {
                LAYOUT_MAPPING -> {
                    mapping.layoutMapping[rawName] = value
                }

                RES_MAPPING -> {
                    mapping.resMapping[rawName] = value
                }

                VALUES_MAPPING -> {
                    mapping.valuesMapping[rawName] = value
                }

                else -> {}
            }
        }

        mapping.obfuscateIndex = mapping.obfuscateIndex.coerceAtLeast(obfuscateIndex)
        return mapping
    }

    fun parseXmlClass(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
        isComparison: Boolean = false
    ): XmlClassMapping {
        var obfuscateIndex = -1L
        if (isComparison) {
            obfuscateIndex = compareObfuscateIndex(project, guardExtension, mappingFile)
        }
        val mapping = XmlClassMapping(guardExtension)
        if (!mappingFile.exists()) {
            return mapping.apply { this.obfuscateIndex = obfuscateIndex }
        }

        parse(mappingFile) { line, mappingType, rawName, value ->
            when (mappingType) {
                DIR_MAPPING -> {
                    // 白名单
                    if (mapping.inWhiteList(rawName, isDirMapping = true)) {
                        return@parse
                    }
                    val obfuscateName = (value.split(".").lastOrNull() ?: value).lowercase()
                    if (obfuscateName.inPackageNameBlackList()) {
                        throw IllegalArgumentException("`${line.trim()}` is illegal, $obfuscateName is a keyword")
                    }
                    val minimumValue = obfuscateName.to26Long()
                    obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)

                    mapping.dirMapping[rawName] = value
                }

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

                    if (obfuscateClassName.inClassNameBlackList()) {
                        throw IllegalArgumentException("`$value` 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 -> $value is illegal should be\n$rawName -> ${dirMapping[rawClassPath]}.$obfuscateClassName")
                        }
                        dirMapping[rawClassPath] = obfuscateClassPath
                    }
                    val minimumValue = obfuscateClassName.to26Long()
                    obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)

                    mapping.classMapping[rawName] = value
                }
            }
        }

        mapping.obfuscateIndex = mapping.obfuscateIndex.coerceAtLeast(obfuscateIndex)
        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).trim()
                val value = mat.group(2).trim()
                callback.invoke(line, mappingType, key, value)
            } else {
                mappingType = line
            }
        }
    }

    private fun compareObfuscateIndex(
        project: Project,
        guardExtension: GuardExtension,
        mappingFile: File,
    ): Long {
        var obfuscateIndex = -1L
        val mappingFileName = if (mappingFile.exists()) mappingFile.name else ""

        if (mappingFileName != MAPPING_RES) {
            val mapping = parseRes(project, guardExtension, project.file(MAPPING_RES))
            obfuscateIndex = obfuscateIndex.coerceAtLeast(mapping.obfuscateIndex)
        }
        if (mappingFileName != MAPPING_XML_CLASS) {
            val mapping = parseXmlClass(project, guardExtension, project.file(MAPPING_XML_CLASS))
            obfuscateIndex = obfuscateIndex.coerceAtLeast(mapping.obfuscateIndex)
        }
        if (mappingFileName != MAPPING_STRING_FOR) {
            val mapping = parseStringFog(project, guardExtension, project.file(MAPPING_STRING_FOR))
            obfuscateIndex = obfuscateIndex.coerceAtLeast(mapping.obfuscateIndex)
        }
        if (mappingFileName != MAPPING_DECLARATION) {
            val mapping =
                parseDeclaration(project, guardExtension, project.file(MAPPING_DECLARATION))
            obfuscateIndex = obfuscateIndex.coerceAtLeast(mapping.obfuscateIndex)
        }
        if (mappingFileName != MAPPING_CUSTOM_STRING) {
            val mapping =
                parseCustomString(project, guardExtension, project.file(MAPPING_CUSTOM_STRING))
            obfuscateIndex = obfuscateIndex.coerceAtLeast(mapping.obfuscateIndex)
        }
        return obfuscateIndex
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy