1.背景
為了實現(xiàn)apk字節(jié)碼加密技術(shù),我在CI上通過插入了腳本重新打包任務(wù)顽冶。然而采驻,這改動導(dǎo)致apktool回編譯apk包變大,文件經(jīng)調(diào)研和分析發(fā)現(xiàn):在apktool 2.0.3之后為了快速解壓和打包视事,加入了反編譯文件回編譯不壓縮
機制胆萧。該配置文件位于apktool.yml文件中。
幾乎所有的二進制和非二進制文件打包時都未采用壓縮俐东,導(dǎo)致生成的apk變大(即使未簽名依然比原apk大)跌穗,最后在回編過程進行分析订晌。
在反編譯文件中查看yml文件,該文件記錄了apk反編譯信息(見apktool
源碼brut/androlib/meta/MetaInfo
)
compressionType
:記錄arsc的是否壓縮
doNotCompress
:記錄不壓縮的文件類型和路徑
2.解法
由于最新版本的apktool回編采用了不壓縮的策略蚌吸,導(dǎo)致我們的重編譯apk變大锈拨。
可以想到的有兩種方法:
1.通過apktool高版本來釋放1.apk(framworks apk見上次分享),低版本處理反編譯和打包的問題套利。
2.通過腳本將apktool.yml文件進行修改和定制推励,可以使得我們的release包更小。
3.YAML格式
我們需要對yaml格式的文件進行解析肉迫,有一個支持yaml格式的著名開源庫snakeyaml
(用java實現(xiàn))验辞。
我采用jar的形式對這一部分代碼(解析YAML和寫入文件)進行實現(xiàn)。通過shell腳本插入該任務(wù)喊衫,從而達到了自動化過程跌造。
snakeyaml
的使用參考:
snakeyaml項目地址:https://bitbucket.org/asomov/snakeyaml
snakeyaml java使用:http://www.reibang.com/p/d8136c913e52
4.CI上打包結(jié)果
通過對反編譯腳本的優(yōu)化使得回編譯簽名包和zipalign包顯著減小。
原始apk | 反編譯簽名apk | 反編譯zipalign-apk |
---|---|---|
23.90M | 22.48M | 22.48M |
5.腳本實現(xiàn)
#定義out文件的路徑
rebApkFile=$cache_dir/rebuildApp.apk
echo "rebuildTools> 修改yml打包的配置文件"
#定義jar的路徑族购,用來提取和修改apktool.yml文件
yamlFileCovert_jar=./yamlFileCovert.jar
#執(zhí)行yamlFileCovert_jar
java -jar $yamlFileCovert_jar ${apkDecodeDir}/apktool.yml ${apkDecodeDir}/apktool.yml
echo "rebuildTools> 開始回編譯apk并替換目標文件..."
apktool b -o ${rebApkFile} ${apkDecodeDir}
echo "rebuildTools> 開始簽名新apk..."
...
java部分:
public class YamlFileParse {
public static void main(String[] args) {
String fileName;
String outFile;
if (args != null && args.length >= 2) {
fileName = args[0];
outFile = args[1];
} else {
System.out.print("請輸入yml路徑!\n" +
"java -jar yamlFileCovert.jar [in] [out] ");
return;
}
try {
MetaInfo metaInfo = loadYml(fileName);
saveYml(outFile, metaInfo);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void saveYml(String outFile, MetaInfo ret) throws IOException {
FileOutputStream fos = new FileOutputStream(new File(outFile));
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
Writer writer = new BufferedWriter(outputStreamWriter);
save(ret, writer);
outputStreamWriter.close();
}
private static MetaInfo loadYml(String fileName) throws IOException {
FileInputStream yamlInput = new FileInputStream(new File(fileName));
MetaInfo ret = new Yaml().loadAs(yamlInput, MetaInfo.class);
yamlInput.close();
ret.doNotCompress = null;
return ret;
}
private static void save(MetaInfo info, Writer output) {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
new Yaml().dump(info, output);
}
}
java的源碼部分包括:yaml
格式文件的讀寫和MetaInfo
(用于apktool.yml序列化指定壳贪,原因見apktool.yml首行)類。
以下是apktool.yml文件寝杖,由于文件中包含了MetaInfo類违施,所以必須指定yml序列化需要的類(這一部分拷貝了apktool源碼中部分MetaInfo部分)。
!!brut.androlib.meta.MetaInfo
apkFileName: Rong360-release.apk
compressionType: false
doNotCompress: null
isFrameworkApk: false
packageInfo: {forcedPackageId: '127', renameManifestPackage: null}
sdkInfo: {minSdkVersion: '14', targetSdkVersion: '21'}
sharedLibrary: false
sparseResources: false
usesFramework:
ids: [1]
tag: null
version: 2.3.3
versionInfo: {versionCode: '316', versionName: 3.1.6}
運行java -jar $yamlFileCovert_jar ${apkDecodeDir}/apktool.yml
對yml進行修改后瑟幕,回編apk磕蒲,從而產(chǎn)物的apk大小達到了預(yù)期。