1.APK 打包流程
apk 中代碼混淆主要集中在以下三個任務中
- transformClassesWithDexBuilderForDebug → 將class打包成dex
- transformDexArchiveWithExternalLibsDexMergerForDebug → 打包第三庫的dex
- transformDexArchiveWithDexMergerForDebug → 打包最終的dex
三個Task最終都是通過DX或D8來打dex丑婿,跟下第一個Task:DexArchiveBuilderTransform.transform()
@Override
public void convert(
@NonNull Stream<ClassFileEntry> input, @NonNull Path output, boolean isIncremental)
throws DexArchiveBuilderException {
D8DiagnosticsHandler d8DiagnosticsHandler = new InterceptingDiagnosticsHandler();
try {
D8Command.Builder builder = D8Command.builder(d8DiagnosticsHandler);
AtomicInteger entryCount = new AtomicInteger();
input.forEach(
entry -> {
builder.addClassProgramData(
readAllBytes(entry), D8DiagnosticsHandler.getOrigin(entry));
entryCount.incrementAndGet();
});
if (entryCount.get() == 0) {
// nothing to do here, just return
return;
}
OutputMode outputMode =
isIncremental ? OutputMode.DexFilePerClassFile : OutputMode.DexIndexed;
builder.setMode(compilationMode)
.setMinApiLevel(minSdkVersion)
.setIntermediate(true)
.setOutput(output, outputMode)
.setIncludeClassesChecksum(compilationMode == compilationMode.DEBUG);
if (desugaring) {
builder.addLibraryResourceProvider(bootClasspath.getOrderedProvider());
builder.addClasspathResourceProvider(classpath.getOrderedProvider());
if (libConfiguration != null) {
builder.addSpecialLibraryConfiguration(libConfiguration);
}
} else {
builder.setDisableDesugaring(true);
}
D8.run(builder.build(), MoreExecutors.newDirectExecutorService());
} catch (Throwable e) {
throw getExceptionToRethrow(e, d8DiagnosticsHandler);
}
}
R8又干了啥早芭?
// 前面獲取dex的文件列表贪绘、混淆列表等急凰,初始化R8Transform實例時傳入
R8Transform transform =
new R8Transform(
variantScope,
userMainDexListFiles,
userMainDexListProguardRules,
inputProguardMapping,
variantScope.getOutputProguardMappingFile());
// 處理混淆規(guī)則蓄喇,callback用于在混淆后執(zhí)行后續(xù)操作
return applyProguardRules(
variantScope,
inputProguardMapping,
variantScope.getOutputProguardMappingFile(),
testedVariantData,
transform,
callback);
具體的邏輯,可以追溯到 R8.class → run()明场,做了這些事:
- 代碼刪除:通過語法樹靜態(tài)分析技術躏精、發(fā)現(xiàn)并刪除未使用的代碼,如未實例化的Class等破花;
- 代碼優(yōu)化:對運行時代碼進行優(yōu)化谦趣,刪除死代碼、未使用的參數(shù)座每,選擇性內聯(lián)前鹅、類合并等;
- 代碼混淆:優(yōu)化標識符名字峭梳,減少代碼量舰绘,會判斷混淆規(guī)則中是否允許修改標識符名字;
2.自定義混淆字典
之前在反編譯人家的APP時看到標識符竟然不是abcd葱椭,而是中文和特殊字符捂寿,怎么做到的呢?其實不難孵运,自定義一個混淆字典就好秦陋,在app的proguard-rules的同級目錄創(chuàng)建一個文件,比如 dictionary治笨,內容示例如下:
﹢
﹣
×
÷
...太長省略
接著在 proguard-rules 添加下述配置:
-obfuscationdictionary ./dictionary
-classobfuscationdictionary ./dictionary
-packageobfuscationdictionary ./dictionary