TWi*_*Rob 5 java debugging obfuscation android proguard
我在我的应用程序的预发布阶段,我开始编译发布版本,assembleRelease而不是assembleDebug.然而,混淆破坏了事情,很难破译什么是什么.调试几乎是不可能的,即使行号保持变量类是不可读的.虽然发布版本不稳定但我想让混淆不那么痛苦,但它应该仍然表现得像完全混淆一样.
通常,ProGuarded版本会转换名称
net.twisterrob.app.pack.MyClass
Run Code Online (Sandbox Code Playgroud)
至
b.a.c.b.a
Run Code Online (Sandbox Code Playgroud)
反射和Android布局/菜单资源可以打破,如果他们遇到我们没有保留名称的类.
对于预发布测试来说,能够对代码进行模糊处理会非常有用,但"不是那么多",比如从
net.twisterrob.app.pack.MyClass
Run Code Online (Sandbox Code Playgroud)
至
net.twisterrob.app.pack.myclass // or n.t.a.p.MC or anything in between :)
Run Code Online (Sandbox Code Playgroud)
-dontobfuscate当然,proguard 有所帮助,但随后它会使所有破碎的东西再次起作用,因为类名是正确的.
我正在寻找的东西将打破完全混淆会破坏的东西,但同时很容易弄清楚什么是没有使用mapping.txt,因为名称是人类可读的.
我正在寻找http://proguard.sourceforge.net/manual/usage.html#obfuscationoptions,但-*dictionary选项似乎没有这样做.
我可以自己生成一个重命名文件(它只是运行所有类并给它们一个toLowerCase或者什么):
net.twisterrob.app.pack.MyClassA -> myclassa
net.twisterrob.app.pack.MyClassB -> myclassb
Run Code Online (Sandbox Code Playgroud)
那么问题是如何将这样的文件提供给ProGuard以及格式是什么?
所以看起来我已经成功地跳过了-applymapping我链接的部分中的选项。
跳转到实现/详细信息部分,并将这两个 Gradle/Groovy 代码块复制到 Android 子项目的build.gradle文件中。
mapping.txt 的格式非常简单:
full.pack.age.Class -> obf.usc.ate.d:
Type mField -> mObfusc
#:#:Type method(Arg,s) -> methObfusc
kept.Class -> kept.Class:
Type mKept -> mKept
#:#:Type kept() -> kept
Run Code Online (Sandbox Code Playgroud)
收缩的类和成员根本没有列出。因此,所有可用的信息,如果我能够生成相同的信息或对其进行转换,那么成功的机会就很大。
我尝试根据传递给 proguard ( -injars) 的当前类路径生成输入映射.txt。我将所有类加载到一个URLClassLoader包含所有程序 jar 以及库jar 的文件中(例如,用于解析超类)。然后迭代每个类和每个声明的成员并输出我想要使用的名称。
这有一个大问题:这个解决方案包含应用程序中每个可重命名事物的模糊名称。这里的问题是,-applymapping从字面上理解并尝试应用输入映射文件中的所有映射,忽略-keep规则,从而导致有关重命名冲突的警告。所以我放弃了这条路,因为我不想重复 proguard 配置,也不想自己实现 proguard 配置解析器。
proguardRelease两次[失败]基于上述失败,我想到了另一种解决方案,该解决方案将利用所有配置并保留现有的配置。流程如下:
proguardRelease做它的工作mapping.txtmapping.txt为一个新文件proguardReleasegradle 任务并使用转换后的映射运行它问题在于,复制整个任务确实很复杂,所有的都是、、、、、等等inputs……outputs无论如何,我实际上已经开始走这条路线,但它很快就加入了第三个解决方案。doLastdoFirst@TaskAction
proguardRelease的输出 [成功]在尝试复制整个任务并分析 proguard/android 插件代码时,我意识到再次模拟正在执行的操作会容易得多proguardRelease。这是最终流程:
proguardRelease做它的工作mapping.txtmapping.txt为一个新文件结果就是我想要的:(
示例模式是<package>.__<class>__.__<field>__类名和字段名大小写颠倒)
java.lang.NullPointerException: Cannot find actionView! Is it declared in XML and kept in proguard?
at net.twisterrob.android.utils.tools.__aNDROIDtOOLS__.__PREPAREsEARCH__(AndroidTools.java:533)
at net.twisterrob.inventory.android.activity.MainActivity.onCreateOptionsMenu(MainActivity.java:181)
at android.app.Activity.onCreatePanelMenu(Activity.java:2625)
at android.support.v4.app.__fRAGMENTaCTIVITY__.onCreatePanelMenu(FragmentActivity.java:277)
at android.support.v7.internal.view.__wINDOWcALLBACKwRAPPER__.onCreatePanelMenu(WindowCallbackWrapper.java:84)
at android.support.v7.app.__aPPcOMPATdELEGATEiMPLbASE$aPPcOMPATwINDOWcALLBACK__.onCreatePanelMenu(AppCompatDelegateImplBase.java:251)
at android.support.v7.app.__aPPcOMPATdELEGATEiMPLv7__.__PREPAREpANEL__(AppCompatDelegateImplV7.java:1089)
at android.support.v7.app.__aPPcOMPATdELEGATEiMPLv7__.__DOiNVALIDATEpANELmENU__(AppCompatDelegateImplV7.java:1374)
at android.support.v7.app.__aPPcOMPATdELEGATEiMPLv7__.__ACCESS$100__(AppCompatDelegateImplV7.java:89)
at android.support.v7.app.__aPPcOMPATdELEGATEiMPLv7$1__.run(AppCompatDelegateImplV7.java:123)
at android.os.Handler.handleCallback(Handler.java:733)
Run Code Online (Sandbox Code Playgroud)
或者注意这里的下划线:

我试图使其尽可能简单,同时保持最大的灵活性。我称其为“不混淆”,因为它消除了适当的混淆,但仍然被认为是反射方面的混淆。
我实施了一些守卫,因为第二轮做了一些假设。显然,如果没有混淆,就没有必要取消混淆。此外,如果关闭调试,取消融合(并且可能会意外释放)几乎毫无意义,因为取消融合对 IDE 内部的帮助最大。如果应用程序经过测试和混淆,AndroidProguardTask 的内部正在使用映射文件,我现在不想处理它。
所以我继续创建了一个 unfuscate 任务,它进行转换并运行 proguard。遗憾的是,混淆器配置没有在 中公开proguard.gradle.ProguardTask,但这什么时候阻止了任何人?!:)
有一个缺点,它需要双倍的时间来保护它,我想如果你真的需要调试它,这是值得的。
这是 Gradle 的 android hooking 代码:
afterEvaluate {
project.android.applicationVariants.all { com.android.build.gradle.api.ApplicationVariant variant ->
Task obfuscateTask = variant.obfuscation
def skipReason = [ ];
if (obfuscateTask == null) { skipReason += "not obfuscated" }
if (!variant.buildType.debuggable) { skipReason += "not debuggable" }
if (variant.testVariant != null) { skipReason += "tested" }
if (!skipReason.isEmpty()) {
logger.info("Skipping unfuscation of {} because it is {}", variant.name, skipReason);
return;
}
File mapping = variant.mappingFile
File newMapping = new File(mapping.parentFile, "unmapping.txt")
Task unfuscateTask = project.task("${obfuscateTask.name}Unfuscate") {
inputs.file mapping
outputs.file newMapping
outputs.upToDateWhen { mapping.lastModified() <= newMapping.lastModified() }
doLast {
java.lang.reflect.Field configField =
proguard.gradle.ProGuardTask.class.getDeclaredField("configuration")
configField.accessible = true
proguard.Configuration config = configField.get(obfuscateTask) as proguard.Configuration
if (!config.obfuscate) return; // nothing to unfuscate when -dontobfuscate
java.nio.file.Files.copy(mapping.toPath(), new File(mapping.parentFile, "mapping.txt.bck").toPath(),
java.nio.file.StandardCopyOption.REPLACE_EXISTING)
logger.info("Writing new mapping file: {}", newMapping)
new Mapping(mapping).remap(newMapping)
logger.info("Re-executing {} with new mapping...", obfuscateTask.name)
config.applyMapping = newMapping // use our re-written mapping file
//config.note = [ '**' ] // -dontnote **, it was noted in the first run
LoggingManager loggingManager = getLogging();
// lower level of logging to prevent duplicate output
loggingManager.captureStandardOutput(LogLevel.WARN);
loggingManager.captureStandardError(LogLevel.WARN);
new proguard.ProGuard(config).execute();
}
}
unfuscateTask.dependsOn obfuscateTask
variant.dex.dependsOn unfuscateTask
}
}
Run Code Online (Sandbox Code Playgroud)
整体的另一部分是转变。我设法快速编写了一个全匹配的正则表达式模式,所以它非常简单。您可以安全地忽略类结构和重新映射方法。关键是processLine每行调用哪个。该行被分成几部分,混淆名称之前和之后的文本保持原样(两个substring),名称在中间更改。更改为return语句unfuscate以满足您的需要。
class Mapping {
private static java.util.regex.Pattern MAPPING_PATTERN =
~/^(?<member> )?(?<location>\d+:\d+:)?(?:(?<type>.*?) )?(?<name>.*?)(?:\((?<args>.*?)\))?(?: -> )(?<obfuscated>.*?)(?<class>:?)$/;
private static int MAPPING_PATTERN_OBFUSCATED_INDEX = 6;
private final File source
public Mapping(File source) {
this.source = source
}
public void remap(File target) {
target.withWriter { source.eachLine Mapping.&processLine.curry(it) }
}
private static void processLine(Writer out, String line, int num) {
java.util.regex.Matcher m = MAPPING_PATTERN.matcher(line)
if (!m.find()) {
throw new IllegalArgumentException("Line #${num} is not recognized: ${line}")
}
try {
def originalName = m.group("name")
def obfuscatedName = m.group("obfuscated")
def newName = originalName.equals(obfuscatedName) ? obfuscatedName : unfuscate(originalName, obfuscatedName)
out.write(line.substring(0, m.start(MAPPING_PATTERN_OBFUSCATED_INDEX)))
out.write(newName)
out.write(line.substring(m.end(MAPPING_PATTERN_OBFUSCATED_INDEX)))
out.write('\n')
} catch (Exception ex) {
StringBuilder sb = new StringBuilder("Line #${num} failed: ${line}\n");
0.upto(m.groupCount()) { sb.append("Group #${it}: '${m.group(it)}'\n") }
throw new IllegalArgumentException(sb.toString(), ex)
}
}
private static String unfuscate(String name, String obfuscated) {
int lastDot = name.lastIndexOf('.') + 1;
String pkgWithDot = 0 < lastDot ? name.substring(0, lastDot) : "";
name = 0 < lastDot ? name.substring(lastDot) : name;
// reassemble the names with something readable, but still breaking changes
// pkgWithDot will be empty for fields and methods
return pkgWithDot + '_' + name;
}
}
Run Code Online (Sandbox Code Playgroud)
您应该能够对包名称应用转换,但我没有对此进行测试。
// android.support.v4.a.a, that is the original obfuscated one
return obfuscated;
// android.support.v4.app._Fragment
return pkgWithDot + '_' + name;
// android.support.v4.app.Fragment_a17d4670
return pkgWithDot + name + '_' + Integer.toHexString(name.hashCode());
// android.support.v4.app.Fragment_a
return pkgWithDot + name + '_' + afterLastDot(obfuscated)
// android.support.v4.app.fRAGMENT
return pkgWithDot + org.apache.commons.lang.StringUtils.swapCase(name);
// needs the following in build.gradle:
buildscript {
repositories { jcenter() }
dependencies { classpath 'commons-lang:commons-lang:2.6' }
}
// android.support.v4.app.fragment
return pkgWithDot + name.toLowerCase();
Run Code Online (Sandbox Code Playgroud)
警告:不可逆的转换很容易出错。考虑以下:
class X {
private static final Factory FACTORY = ...;
...
public interface Factory {
}
}
// notice how both `X.Factory` and `X.FACTORY` become `X.factory` which is not allowed.
Run Code Online (Sandbox Code Playgroud)
当然,上述所有转换都可以以一种或另一种方式被欺骗,但对于不常见的前置后缀和文本转换来说,这种可能性较小。
| 归档时间: |
|
| 查看次数: |
3670 次 |
| 最近记录: |