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

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

package com.xml.guard.model

import com.xml.guard.entensions.GuardExtension
import com.xml.guard.utils.KtFileParser
import com.xml.guard.utils.aidlDirs
import com.xml.guard.utils.findLocationProject
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.insertImportXxxIfAbsent
import com.xml.guard.utils.isAidl
import com.xml.guard.utils.isJava
import com.xml.guard.utils.isKt
import com.xml.guard.utils.javaDirs
import com.xml.guard.utils.removeSuffix
import com.xml.guard.utils.replaceLast
import com.xml.guard.utils.uppercaseFirstChar
import org.gradle.api.Project
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter

/**
 * User: ljx
 * Date: 2022/3/16
 * Time: 22:02
 */
class XmlClassMapping(private val guardExtension: GuardExtension) {
    internal val dirMapping by lazy { mutableMapOf() }
    internal val classMapping by lazy { mutableMapOf() }

    // 内部类正则表达式
    private val innerClassRegex by lazy { "[a-zA-Z0-9_]+\\$[a-zA-Z0-9_]+".toRegex() }

    // 白名单
    private val classWhiteList by lazy { guardExtension.classWhiteList }

    /**
     * 遍历文件夹下的所有直接子类,混淆文件名及移动目录
     */
    fun obfuscateAllClass(project: Project, variantName: String): MutableMap {
        val classMapped = mutableMapOf()
        val iterator = dirMapping.iterator()
        while (iterator.hasNext()) {
            val entry = iterator.next()
            val rawDir = entry.key
            val locationProject = project.findLocationProject(rawDir, variantName)
            if (locationProject == null) {
                iterator.remove()
                continue
            }
            val manifestPackage = locationProject.findPackage()
            // 过滤目录的直接子文件
            val dirPath = rawDir.replace(".", File.separator) // xx.xx  不带文件名
            val dirs = mutableListOf()
            dirs.addAll(locationProject.javaDirs(variantName))
            dirs.addAll(locationProject.aidlDirs(variantName))
            val childFiles = dirs.flatMap {
                File(it, dirPath).listFiles { f ->
                    var bool = (f.isFile && (f.isJava || f.isKt || f.isAidl))
                    if (bool && f.isAidl) {
                        val classPackage = getClassPackage(locationProject, f)
                        if (!dirMapping.containsKey(classPackage)) {
                            bool = false
                        }
                    }
                    bool
                }?.toList() ?: emptyList()
            }
            if (childFiles.isEmpty()) continue
            for (file in childFiles) {
                file.insertImportXxxIfAbsent(manifestPackage)

                val rawClassPath = "${rawDir}.${file.name.removeSuffix()}" // 原始 xx.Xxx
                // 已经混淆
                if (isClassObfuscatedOrWhiteList(rawClassPath)) {
                    continue
                }

                val obfuscatePath = obfuscatePath(locationProject, rawClassPath)  // 混淆后 xx.Xxx
                val obfuscateRelativePath = obfuscatePath.replace(".", File.separator) // 混淆后 xx/Xxx
                val rawRelativePath = rawClassPath.replace(".", File.separator) // 原始 xx/Xxx
                // 替换原始类路径
                val newFile =
                    File(file.absolutePath.replace(rawRelativePath, obfuscateRelativePath))
                if (!newFile.exists()) newFile.parentFile.mkdirs()
                if (file.renameTo(newFile)) {
                    classMapped[rawClassPath] = obfuscatePath

                    // 处理顶级类、方法及变量
                    val obfuscateDir = obfuscatePath.getDirPath()
                    val filename = file.name.removeSuffix()
                    val ktParser = KtFileParser(newFile, filename)
                    val jvmName = ktParser.jvmName
                    if (jvmName != null && jvmName != filename) {
                        classMapped["$rawDir.$jvmName"] = "$obfuscateDir.$jvmName"
                    } else if (jvmName == null &&
                        (ktParser.topFunNames.isNotEmpty() || ktParser.topFieldNames.isNotEmpty())
                    ) {
                        classMapped["${rawClassPath}Kt"] = "${obfuscatePath}Kt"
                    }
                    ktParser.getTopClassOrFunOrFieldNames().forEach {
                        classMapped["$rawDir.$it"] = "$obfuscateDir.$it"
                    }
                }
            }
        }
        return classMapped
    }

    /**
     * 白名单、类已混淆
     */
    fun isClassObfuscatedOrWhiteList(rawClassPath: String): Boolean {
        // 白名单 || 已混淆
        return inWhiteList(rawClassPath) || classMapping.containsValue(rawClassPath)
    }

    /**
     * 需要混淆的类文件包
     */
    fun obfuscateOtherDir(androidProjects: List, variantName: String) {
        androidProjects.forEach {
            val dirs = mutableListOf()
            dirs.addAll(it.aidlDirs(variantName))
            dirs.addAll(it.javaDirs(variantName))
            it.files(dirs).asFileTree.forEach asFileTree@{ file ->
                val rawPackage = getClassPackage(it, file)
                val rawClassPath = "${rawPackage}.${file.nameWithoutExtension}"
                // 白名单
                if (inWhiteList(rawClassPath)) {
                    return@asFileTree
                }
                if (!dirMapping.containsKey(rawPackage)
                    && !dirMapping.containsValue(rawPackage)
                ) {
                    obfuscatePackage(it, rawPackage)
                }
            }
        }
    }

    /**
     * 混淆包名+类名,返回混淆后的包名+类名
     */
    fun obfuscatePath(project: Project, classPath: String): String {
        var innerClassName: String? = null //内部类类名
        val rawClassPath = if (isInnerClass(classPath)) {
            val arr = classPath.split("$")
            innerClassName = arr[1]
            arr[0]
        } else {
            classPath
        }
        // TODO 内部类混淆有问题,暂时禁止混淆
        if (isClassObfuscatedOrWhiteList(rawClassPath)) {
            return classPath
        }
        var obfuscateClassPath = classMapping[rawClassPath]
        if (obfuscateClassPath == null) {
            val rawPackage = rawClassPath.getDirPath()
            val obfuscatePackage = obfuscatePackage(project, rawPackage)
            val obfuscateClassName = getRandomString { text ->
                val result = text.uppercaseFirstChar()
                MappingParser.verifyObfuscateName(result)
            }
            obfuscateClassPath = "${obfuscatePackage}.${obfuscateClassName}"
            classMapping[rawClassPath] = obfuscateClassPath
        }
        return if (innerClassName != null) "$obfuscateClassPath\$$innerClassName" else obfuscateClassPath
    }

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

        writer.write("${MappingParser.DIR_MAPPING}\n")
        for ((key, value) in dirMapping) {
            writer.write(String.format("\t%s -> %s\n", key, value))
        }
        writer.write("\n")
        writer.flush()

        writer.write("${MappingParser.CLASS_MAPPING}\n")
        for ((key, value) in classMapping.entries) {
            writer.write(String.format("\t%s -> %s\n", key, value))
        }
        writer.flush()
        writer.close()
    }

    /**
     * 白名单
     */
    fun inWhiteList(rawClassPath: String): Boolean {
        return classWhiteList.any {
            return classWhiteList.any {
                (it == rawClassPath
                        || it == rawClassPath.getDirPath()
                        || (it.endsWith("*") && rawClassPath.startsWith(it.replaceLast("*", ""))))
            }
        }
    }

    /**
     * 白名单
     */
    fun inWhiteList2(rawDirPath: String): Boolean {
        return classWhiteList.any {
            (it == rawDirPath
                    || it == rawDirPath.getDirPath()
                    || (it.endsWith("*") && "${rawDirPath}.".startsWith(it.replaceLast("*", ""))))
        }
    }

    // 混淆包名,返回混淆后的包名
    private fun obfuscatePackage(project: Project, rawPackage: String): String {
        var obfuscatePackage = dirMapping[rawPackage]
        if (obfuscatePackage == null) {
            obfuscatePackage = getRandomString { text ->
                val result = text.lowercase()
                MappingParser.verifyObfuscateName(result)
            }
            val prefix = project.findPackage() + "."
            obfuscatePackage = prefix + obfuscatePackage
            dirMapping[rawPackage] = obfuscatePackage
        }
        return obfuscatePackage
    }

    private fun isInnerClass(classPath: String): Boolean {
        return classPath.contains(innerClassRegex)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy