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.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.getRandomString
import com.xml.guard.utils.lowercaseFirstChar
import com.xml.guard.utils.replaceLast
import com.xml.guard.utils.replaceWords
import groovy.util.Node
import groovy.xml.XmlParser
import java.io.BufferedWriter
import java.io.File
import java.io.FileWriter
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 }
/**
* 遍历文件夹下的所有资源,混淆名称
*/
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
}
/**
* 将映射写入文件
*/
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 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 { (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
}
// 获取标签名称
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.entries.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) {
val value = MappingParser.checkObfuscate(rawName)
obfuscateName = value.ifBlank {
getRandomString { text ->
val result = text.lowercaseFirstChar()
MappingParser.verifyObfuscateName(result)
}
}
layoutMapping[rawName] = obfuscateName
}
return obfuscateName
}
private fun obfuscateResName(
rawName: String,
mapping: MutableMap
): String {
var obfuscateName = mapping[rawName]
if (obfuscateName == null) {
val value = MappingParser.checkObfuscate(rawName, isLowercase = true)
obfuscateName = value.ifBlank {
getRandomString { text ->
val result = text.lowercase()
MappingParser.verifyObfuscateName(result)
}
}
mapping[rawName] = obfuscateName
}
return obfuscateName
}
}