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

main.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.generateNumbers
import com.xml.guard.utils.getClassPackage
import com.xml.guard.utils.getDirPath
import com.xml.guard.utils.inClassNameBlackList
import com.xml.guard.utils.inPackageNameBlackList
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.randomizeCase
import com.xml.guard.utils.replaceLast
import com.xml.guard.utils.replaceWords
import com.xml.guard.utils.stringFogEncrypt
import com.xml.guard.utils.to26Long
import com.xml.guard.utils.toLetterStr
import com.xml.guard.utils.toUpperLetterStr
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
import kotlin.math.pow
import kotlin.random.Random

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 = ""

    // 混淆名索引
    internal var obfuscateIndex = -1L

    // 混淆所有字符串
    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)
        }
    }

    /**
     * 处理 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 isClassWhiteList(project: Project, file: File): Boolean {
        val rawPackage = getClassPackage(project, file).trim()
        val rawClassPath = "${rawPackage}.${file.nameWithoutExtension}"
        return stringFogWhiteList.any {
            (it == rawClassPath
                    || it == rawClassPath.getDirPath()
                    || (it.endsWith("*") && rawClassPath.startsWith(it.replaceLast("*", ""))))
        }
    }

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

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

    // 生成目录名称
    private fun generateFogDirName(): String {
        if (stringFogDirName.isNotBlank()) {
            return stringFogDirName
        }
        while (true) {
            obfuscateIndex += (generateNumbers().toDouble()
                .pow(Random.nextDouble(1.0, 3.0))).toLong()
            val obfuscateDirName = obfuscateIndex.toLetterStr()
            if (obfuscateDirName.length > 1 // 保证至少两位字母以上
                && !obfuscateDirName.inPackageNameBlackList() // 过滤黑名单
            ) {
                stringFogDirName = obfuscateDirName

                val minimumValue = obfuscateDirName.to26Long()
                obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                return obfuscateDirName
            }
        }
    }

    // 生成类名
    private fun generateFogClassName(): String {
        if (stringFogClassName.isNotBlank()) {
            return stringFogClassName
        }
        while (true) {
            obfuscateIndex += (generateNumbers().toDouble()
                .pow(Random.nextDouble(1.0, 3.0))).toLong()
            val obfuscateClassName =
                obfuscateIndex.toUpperLetterStr().randomizeCase().uppercaseFirstChar()
            if (obfuscateClassName.length > 1 // 保证至少两位字母以上
                && !obfuscateClassName.inClassNameBlackList() // 过滤黑名单
            ) {
                stringFogClassName = obfuscateClassName

                val minimumValue = obfuscateClassName.to26Long()
                obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                return obfuscateClassName
            }
        }
    }

    // 生成函数名称
    private fun generateFogMethod(): String {
        if (stringFogMethodName.isNotBlank()) {
            return stringFogMethodName
        }
        while (true) {
            obfuscateIndex += (generateNumbers().toDouble()
                .pow(Random.nextDouble(1.0, 3.0))).toLong()
            val obfuscateMethodName =
                obfuscateIndex.toLetterStr().randomizeCase().lowercaseFirstChar()
            if (obfuscateMethodName.length > 1 // 保证至少两位字母以上
                && !obfuscateMethodName.inClassNameBlackList() // 过滤黑名单
                && !obfuscateMethodName.inPackageNameBlackList()
            ) {
                stringFogMethodName = obfuscateMethodName

                val minimumValue = obfuscateMethodName.to26Long()
                obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
                return obfuscateMethodName
            }
        }
    }

    // 生成 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.inPackageNameBlackList()) {
                stringFogKey = key
                return key
            }
        }
    }

    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()
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy