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

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

package com.xml.guard.model

import com.xml.guard.entensions.GuardExtension
import com.xml.guard.utils.createStringFog
import com.xml.guard.utils.findPackage
import com.xml.guard.utils.getClassPackage
import com.xml.guard.utils.getDirPath
import com.xml.guard.utils.getRandomString
import com.xml.guard.utils.inBlackList
import com.xml.guard.utils.insertImportStringFog
import com.xml.guard.utils.isJava
import com.xml.guard.utils.isKt
import com.xml.guard.utils.javaDirs
import com.xml.guard.utils.lowercaseFirstChar
import com.xml.guard.utils.replaceLast
import com.xml.guard.utils.replaceWords
import com.xml.guard.utils.stringFogEncrypt
import com.xml.guard.utils.uppercaseFirstChar
import org.gradle.api.Project
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import java.security.SecureRandom

class StringFogMapping(private val guardExtension: GuardExtension) {
    private val charSet by lazy { "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%^&*-_+=" }
    private val noteRegex by lazy { Regex(pattern = "@\\w+\\s*\\(\\s*\"([^\"]*)\"\\s*\\)") }
    private val stringsRegex by lazy { Regex(pattern = "\\s*\"([^\"]*)\"\\s*") }

    private val fogKey by lazy { generateFogKey() }
    private val fogKeyByte by lazy { fogKey.toByteArray() }
    private val fogMethodName by lazy { generateFogMethod() }
    private val fogClassName by lazy { generateFogClassName() }
    private val fogDirName by lazy { generateFogDirName() }

    internal val stringFogMapping by lazy { mutableMapOf() }
    private val stringFogWhiteList by lazy { guardExtension.stringFogWhiteList }

    internal var stringFogKey: String = ""
    internal var stringFogMethodName: String = ""
    internal var stringFogClassName: String = ""
    internal var stringFogDirName: String = ""

    /**
     * 混淆所有字符串
     */
    fun fogAllStrings(project: Project, variantName: String) {
        val javaDirs = project.javaDirs(variantName)
        val packageName = "${project.findPackage()}.${fogDirName}"
        val fogPackage = "${packageName}.${fogClassName}"
        var stringFogFile: File? = null

        project.files(javaDirs).asFileTree.forEach fileTree@{ file ->
            if ((!file.isJava && !file.isKt)
                || file.name == "${fogClassName}.java"
                || isClassWhiteList(project, file)
            ) {
                return@fileTree
            }

            val stringFogMap = mutableMapOf()
            val constValMap = mutableMapOf()
            file.forEachLine { line ->
                extractStrings(file, line, stringFogMap, constValMap)
            }

            // 处理 const val 表达式
            if (constValMap.isNotEmpty()) {
                handleConstValExpression(file, constValMap)
            }

            if (stringFogMap.isEmpty()) {
                return@fileTree
            }
            if (stringFogFile == null) {
                val classPackage = getClassPackage(project, file)
                val classPackagePath = classPackage.replace(".", File.separator)
                val packageNamePath = packageName.replace(".", File.separator)
                val parent = file.parent.replace(classPackagePath, "") + packageNamePath
                val fogParent = File(parent)
                if (!fogParent.exists()) {
                    fogParent.mkdirs()
                }

                stringFogFile = File(parent, "${fogClassName}.java")
                if (stringFogFile?.exists() != true) {
                    stringFogFile?.createStringFog(packageName, fogClassName, fogMethodName, fogKey)
                }
            }

            var content = file.insertImportStringFog(fogPackage)
            stringFogMap.forEach { (value, encrypt) ->
                stringFogMapping[value] = encrypt

                val oldValue = "\"${value}\""
                val newValue = "${fogClassName}.${fogMethodName}(\"${encrypt}\")"
                content = content.replaceWords(oldValue, newValue)
            }
            file.writeText(content)
        }
    }

    /**
     * 将映射写入文件
     */
    fun writeMappingToFile(mappingFile: File) {
        val writer = BufferedWriter(FileWriter(mappingFile, false))

        val configs = mapOf(
            MappingParser.STRING_FOG_KEY to stringFogKey,
            MappingParser.STRING_FOG_METHOD to stringFogMethodName,
            MappingParser.STRING_FOG_CLASS_NAME to stringFogClassName,
            MappingParser.STRING_FOG_DIR_NAME to stringFogDirName
        )
        for ((key, value) in configs) {
            writer.write(String.format("%s -> %s\n", key, value))
        }
        writer.flush()

        writer.write("\n")
        for ((key, value) in stringFogMapping) {
            writer.write(String.format("%s -> %s\n", key, value))
        }
        writer.flush()

        writer.close()
    }

    /**
     * 白名单
     */
    fun isTextWhiteList(text: String): Boolean {
        return stringFogWhiteList.any { it == text }
    }

    // 白名单
    private fun isClassWhiteList(project: Project, file: File): Boolean {
        val rawPackage = getClassPackage(project, file)
        val rawClassPath = "${rawPackage}.${file.nameWithoutExtension}"
        return stringFogWhiteList.any {
            (it == rawClassPath
                    || it == rawClassPath.getDirPath()
                    || (it.endsWith("*") && rawClassPath.startsWith(it.replaceLast("*", ""))))
        }
    }

    // 已混淆
    private fun isObfuscated(rawValue: String): Boolean {
        return stringFogMapping.containsValue(rawValue)
    }

    /**
     * 处理 const val 表达式(当前表达式符合字符串加密混淆)
     *
     * 示例:const val AA = "aa" -> val AA by lazy { "aa" }
     */
    private fun handleConstValExpression(file: File, constValMap: MutableMap) {
        var fileText = file.readText()
        constValMap.forEach { (line, value) ->
            val newLine = line.replace("const", "").replace("=", "")
            fileText = fileText.replaceWords(line, newLine).let {
                val newValue = """
                    by lazy { $value }
                """.trimIndent()
                it.replaceWords(value, newValue)
            }
        }
        file.writeText(fileText)
    }

    private fun extractStrings(
        file: File,
        content: String,
        stringFogMap: MutableMap,
        constValMap: MutableMap
    ) {
        // 匹配注解、注释并忽略
        if (content.trim().startsWith("@")
            || content.trim().startsWith("*")
            || content.trim().startsWith("//")
            || content.trim().startsWith("/*")
            || noteRegex.find(content) != null
        ) {
            return
        }

        // 正则表达式匹配字符串
        val matches = stringsRegex.findAll(content)
        matches.forEach { match ->
            val result = match.groupValues.getOrNull(1)
            if (result.isNullOrBlank()
                || result.trim().startsWith("\\")
                || (file.isKt && result.contains("$"))
            ) {
                return@forEach
            }

            // 白名单
            if (isTextWhiteList(result)) {
                return@forEach
            }
            // 已混淆
            if (isObfuscated(result)) {
                return@forEach
            }

            if (file.isKt && content.contains("const")) {
                val constFieldName = match.groupValues.getOrNull(0)
                if (!constFieldName.isNullOrBlank()) {
                    constValMap[content] = constFieldName
                }
            }

            val encrypt = result.stringFogEncrypt(fogKeyByte)
            stringFogMap[result] = encrypt

//            val decrypt = encrypt.stringFogDecrypt(fogKeyByte)
//            println("extractStrings --- result:$result   encrypt:${encrypt}   decrypt:${decrypt}")
        }
    }

    // 生成目录名称
    private fun generateFogDirName(): String {
        if (stringFogDirName.isNotBlank()) {
            return stringFogDirName
        }

        stringFogDirName = getRandomString { text ->
            val result = text.lowercase()
            MappingParser.verifyObfuscateName(result)
        }
        return stringFogDirName
    }

    // 生成类名
    private fun generateFogClassName(): String {
        if (stringFogClassName.isNotBlank()) {
            return stringFogClassName
        }

        stringFogClassName = getRandomString { text ->
            val result = text.uppercaseFirstChar()
            MappingParser.verifyObfuscateName(result)
        }
        return stringFogClassName
    }

    // 生成函数名称
    private fun generateFogMethod(): String {
        if (stringFogMethodName.isNotBlank()) {
            return stringFogMethodName
        }

        stringFogMethodName = getRandomString { text ->
            val result = text.lowercaseFirstChar()
            MappingParser.verifyObfuscateName(result)
        }
        return stringFogMethodName
    }

    // 生成 key
    private fun generateFogKey(): String {
        if (stringFogKey.isNotBlank()) {
            return stringFogKey
        }

        // 密钥长度
        val keyLength = 8
        // 创建一个SecureRandom对象
        val secureRandom = SecureRandom()
        while (true) {
            var key = ""
            for (i in 0 until keyLength) {
                // 随机生成字符索引
                val randomIndex = secureRandom.nextInt(charSet.length)
                // 从字符集合中获取随机字符并添加到密钥字符串中
                val randomChar = charSet[randomIndex]
                key += randomChar
            }
            // 输出生成的密钥
            if (!key.inBlackList()
                && MappingParser.verifyObfuscateName(key) != null
            ) {
                stringFogKey = key
                return key
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy