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.
main.com.xml.guard.model.ResMapping.kt Maven / Gradle / Ivy
package com.xml.guard.model
import com.xml.guard.data.ResDefType
import com.xml.guard.entensions.GuardExtension
import com.xml.guard.utils.generateNumbers
import com.xml.guard.utils.inClassNameBlackList
import com.xml.guard.utils.inPackageNameBlackList
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.to26Long
import com.xml.guard.utils.toLetterStr
import groovy.util.Node
import groovy.xml.XmlParser
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
import kotlin.math.pow
import kotlin.math.roundToInt
import kotlin.random.Random
class ResMapping(private val guardExtension: GuardExtension) {
internal val resMapping by lazy { mutableMapOf() }
internal val layoutMapping by lazy { mutableMapOf() }
internal val valuesMapping by lazy { mutableMapOf() }
// 白名单
private val resWhiteList by lazy { guardExtension.resWhiteList }
// 混淆名索引
internal var obfuscateIndex = -1L
// 遍历文件夹下的所有资源,混淆名称
fun obfuscateAllRes(resFiles: List): MutableList {
if (resFiles.isEmpty()) {
return mutableListOf()
}
val infoList = mutableListOf()
resFiles.forEach { resFile ->
val fileRawName = resFile.nameWithoutExtension
val fileDefType = resFile.parentFile.name.split("-").firstOrNull() ?: ""
// 白名单
if (inWhiteList(fileDefType, fileRawName)) {
return@forEach
}
// 1、混淆资源文件名
val obfuscateFile = obfuscateResFileName(resFile, fileDefType, infoList)
// 2、混淆 layout 控件 id
if (fileDefType == ResDefType.LAYOUT.defType) {
obfuscateLayoutViewId(obfuscateFile, infoList)
}
// 3、混淆 values 下的资源标签名称
if (fileDefType == ResDefType.VALUES.defType) {
obfuscateLabel(obfuscateFile, infoList)
}
}
return infoList
}
private fun obfuscateLabel(resFile: File, infoList: MutableList) {
val labels = resFile.findLabels()
if (labels.isNotEmpty()) {
infoList.addAll(labels)
var fileText = resFile.readText()
labels.forEach { resInfo ->
fileText = fileText.replaceWords(resInfo.rawName, resInfo.obfuscateName)
}
resFile.writeText(fileText)
}
}
private fun obfuscateLayoutViewId(layoutFile: File, infoList: MutableList) {
val viewIds = mutableMapOf()
layoutFile.findViewIds().forEach viewId@{ viewId ->
// 白名单
if (inWhiteList(defType = ResDefType.ID.defType, viewId)) {
return@viewId
}
// 已混淆
if (layoutMapping.containsValue(viewId)) {
return@viewId
}
val obfuscateViewId = obfuscateViewId(viewId)
viewIds[viewId] = obfuscateViewId
ResInfo(layoutFile, defType = ResDefType.ID.defType, viewId, obfuscateViewId)
.let(infoList::add)
}
if (viewIds.isNotEmpty()) {
var fileText = layoutFile.readText()
viewIds.forEach viewId@{ (rawId, obfuscateId) ->
fileText = fileText.replaceWords(rawId, obfuscateId)
}
layoutFile.writeText(fileText)
}
}
private fun obfuscateResFileName(
resFile: File,
fileDefType: String,
infoList: MutableList
): File {
val fileRawName = resFile.nameWithoutExtension
// 已混淆
if (resMapping.containsValue(fileRawName)) {
return resFile
}
var obfuscateFile = resFile
val fileObfuscateName = obfuscateResName(fileRawName, resMapping)
val newFileName = "${fileObfuscateName}.${resFile.extension}"
val newFile = File(resFile.parent + File.separator + newFileName)
if (resFile.renameTo(newFile)) {
obfuscateFile = newFile
ResInfo(resFile, fileDefType, fileRawName, fileObfuscateName)
.let(infoList::add)
}
return obfuscateFile
}
fun writeMappingToFile(mappingFile: File) {
val writer = BufferedWriter(FileWriter(mappingFile, false))
writer.write("${MappingParser.RES_MAPPING}\n")
for ((key, value) in resMapping) {
writer.write(String.format("\t%s -> %s\n", key, value))
}
writer.flush()
writer.write("\n")
writer.write("${MappingParser.LAYOUT_MAPPING}\n")
for ((key, value) in layoutMapping) {
writer.write(String.format("\t%s -> %s\n", key, value))
}
writer.flush()
writer.write("\n")
writer.write("${MappingParser.VALUES_MAPPING}\n")
for ((key, value) in valuesMapping) {
writer.write(String.format("\t%s -> %s\n", key, value))
}
writer.flush()
writer.close()
}
// 获取标签名称
private fun File.findLabels(): MutableList {
val infoList = mutableListOf()
val childrenList = XmlParser(false, false).parse(this).breadthFirst()
for (children in childrenList) {
val childNode = children as? Node ?: continue
val nodeName = childNode.name() as? String ?: ""
val rawName = childNode.attribute("name") as? String ?: ""
// 白名单
if (inWhiteList(nodeName, rawName)) {
continue
}
// 已混淆
if (valuesMapping.containsValue(rawName)) {
continue
}
ResDefType.values().forEach { resDefType ->
if (resDefType.nodeNames.any { nodeName == it }) {
val defType = resDefType.defType
val obfuscateName = obfuscateResName(rawName, valuesMapping)
ResInfo(
resFile = this,
defType = defType,
rawName = rawName,
obfuscateName = obfuscateName
).let(infoList::add)
if (defType == ResDefType.STYLEABLE.defType) {
childNode.obfuscateStyleableChild(this, infoList, rawName, obfuscateName)
}
}
}
}
return infoList
}
// 混淆 styleable 标签子属性名称
private fun Node?.obfuscateStyleableChild(
resFile: File,
infoList: MutableList,
rawNamePrefix: String,
obfuscateNamePrefix: String
) {
val childrenList = this?.children()
if (childrenList.isNullOrEmpty()) return
for (children in childrenList) {
val childNode = children as? Node ?: continue
val rawName = childNode.attribute("name") as? String ?: ""
if (!valuesMapping.containsValue(rawName)) {
val obfuscateName = obfuscateResName(rawName, valuesMapping)
ResInfo(
resFile,
defType = ResDefType.STYLEABLE.defType,
rawName,
obfuscateName,
rawNamePrefix,
obfuscateNamePrefix
).let(infoList::add)
}
childNode.obfuscateStyleableChild(
resFile,
infoList,
rawNamePrefix,
obfuscateNamePrefix,
)
}
}
// 获取控件 id
private fun File.findViewIds(): Set {
val set = HashSet()
val childrenList = XmlParser(false, false).parse(this).breadthFirst()
for (children in childrenList) {
val childNode = children as? Node ?: continue
val viewId = childNode.attribute("android:id")?.toString()
if (viewId.isNullOrBlank()) continue
set.add(viewId.replace("@+id/", ""))
}
return set
}
private fun inWhiteList(defType: String, rawName: String): Boolean {
val str = "R.${defType}.${rawName}"
return resWhiteList.any {
(it == str) || (it.endsWith("*") && str.startsWith(it.replaceLast("*", "")))
}
}
private fun obfuscateViewId(rawName: String): String {
var obfuscateName = layoutMapping[rawName]
if (obfuscateName == null) {
obfuscateName = generateObfuscateViewId()
val len = (Random.nextDouble() * 3).roundToInt()
for (i in 0 until len) {
val index = (generateNumbers().toDouble()
.pow(Random.nextDouble(1.0, 3.0))).toLong()
obfuscateName += "_${index.toLetterStr()}"
}
layoutMapping[rawName] = obfuscateName
}
return obfuscateName
}
private fun obfuscateResName(
rawName: String,
mapping: MutableMap
): String {
var obfuscateName = mapping[rawName]
if (obfuscateName == null) {
obfuscateName = generateObfuscateRes()
val len = (Random.nextDouble() * 3).roundToInt()
for (i in 0 until len) {
val index = (generateNumbers().toDouble()
.pow(Random.nextDouble(1.0, 3.0))).toLong()
obfuscateName += "_${index.toLetterStr()}"
}
mapping[rawName] = obfuscateName
}
return obfuscateName
}
// 生成控件 id 的混淆名称
private fun generateObfuscateViewId(): String {
while (true) {
obfuscateIndex += (generateNumbers().toDouble()
.pow(Random.nextDouble(1.0, 3.0))).toLong()
val obfuscateIdName = obfuscateIndex.toLetterStr().randomizeCase().lowercaseFirstChar()
if (obfuscateIdName.length > 1 // 保证至少两位字母以上
&& !obfuscateIdName.inClassNameBlackList()
&& !obfuscateIdName.inPackageNameBlackList() // 过滤黑名单
) {
val minimumValue = obfuscateIdName.to26Long()
obfuscateIndex = obfuscateIndex.coerceAtLeast(minimumValue)
return obfuscateIdName
}
}
}
// 生成混淆的资源名
private fun generateObfuscateRes(): String {
while (true) {
obfuscateIndex += (generateNumbers().toDouble()
.pow(Random.nextDouble(1.0, 3.0))).toLong()
val obfuscateName = obfuscateIndex.toLetterStr()
if (obfuscateName.length > 1 // 保证至少两位字母以上
&& !obfuscateName.inPackageNameBlackList() // 过滤黑名单
) {
return obfuscateName
}
}
}
}