Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.xml.guard.model.MappingParser.kt Maven / Gradle / Ivy
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))
}
}
}