爲了防止安卓應用程序被惡意破解,植入黑客病毒或修改代碼用於商業競爭等,對應用程序進行加固必不可少。接下來,本篇文章會主要講加固的過程以及一些注意事項。html
首先,瞭解一下何爲加固,加固的原理是怎樣的,這有利於後面分析問題。java
簡單來講,加固就是對源Apk進行加密,而後再套上一層殼。用加密算法對源Apk進行加密,再將殼Apk進行合併獲得新的Dex文件,最後替換殼程序中的dex文件獲得新的Apk,這個新的Apk已經不是一個完整意義上的Apk程序了,它的主要工做是負責解密源Apk,而後加載Apk,讓其正常運行起來。git
目前,各大互聯網公司都會本身的應用程序進行加固保護,像360公司,騰訊都有對外開放本身的服務。另外,市場上還有一些專門加固的產品,好比愛加密和梆梆加固等。好好利用這些「輪子」,專一於業務開發,來提升工做效率。程序員
加固工具的選擇:這次使用的是360加固github
第一,從調研加固結果可見,360加固在兼容性、啓動速度、體積變化上都佔有優點,總體上加固效果比較好;算法
第二,360公司是一家安全起家的公司,在業界的影響力也很大,加固技術仍是值得信賴的!shell
第三,看了不少加固工具的官網,加固的過程都是上傳簽名的APK包到官網頁面或使用相應的桌面程序進行上傳,這個過程須要人工進行上傳,而360加固提供了一個加固工具包,咱們能夠編寫腳本來調用其中的加固程序進行自動化加固。如今不少公司都是用Jenkins線上自動化打包,加固也是打包過程的一部分,最好也能是自動化的,這樣整個打包流程是「一條龍」,沒有人工干預,至關於在黑匣子中進行,程序員不用關心打包過程,也減小人工成本和出錯概率。windows
使用gradle腳本實現自動化加固和多渠道打包安全
整個過程分紅三個步驟:加固——重簽名——多渠道打包bash
加固過程: 瀏覽了360加固官網,整個加固過程其實很簡單,主要有如下的三個步驟:
1)輸入360加固平臺的賬號、密碼
2)將簽名文件上傳到加固平臺
3)上傳須要加固的apk文件進行加固
關鍵加固命令行代碼以下:
commandLine "{命令執行符號}", "-c" ,"java -jar {加固jar包的位置} -login {360加固平臺賬號} {360加固平臺密碼}"
commandLine "{命令執行符號}", "-c" ,"java -jar {加固jar包的位置} -importsign {簽名文件的位置} {簽名文件存儲的密碼} {alias別名} {alias密碼}"
commandLine "{命令執行符號}", "-c" ,"java -jar {加固jar包的位置} -jiagu {所要加固的apk文件路徑} {加固後的apk輸出路徑} -autosign"
複製代碼
說明:
1)系統環境不一樣,命令執行符號也會不一樣(Linux系統:sh ;Mac系統:bash ;windows系統:powershell);
2)第二行上傳簽名文件信息是非必要的,加固平臺加固後能夠進行自動從新簽名,而自動簽名所須要的信息正是以前上傳的簽名信息。爲了保證簽名文件的保密性和安全性,不對第三方加固平臺公開,那麼不能執行第二行代碼便可,由於加固時將原簽名抹除,而第三方此時沒辦法獲取到咱們的簽名信息,因此加固後須要咱們本地從新簽名,下文將會介紹對加固包重簽名;
3)當選擇本地加固時,第三行代碼不須要加上參數-autosign,由於加固平臺沒辦法獲取到簽名信息進行加固;
4)更多有關加固的命令行,請參考360官網.官網介紹中,還有關於加固後導入渠道信息的功能,這次多渠道打包並無使用該功能,第一,項目中原先使用多渠道打包方式的是美團walle;第二,暫時不知道如何獲取到360加固打包後的渠道信息,而該渠道信息會在項目中普遍被使用到,好比數據埋點,渠道統計等。
基於上面的說明和項目的具體狀況,整理一下代碼,以Linux系統爲例:
/** * 360加固 * @param apk 加固的原始apk File * @param outputPath 輸出目錄 */
def reinforceApk(File apk,outputPath) {
println "--- 360 reinforceApk start! ---"
println "reinforce apk:" + apk
if(apk == null || !apk.exists()) {
throw new FileNotFoundException('apk is not exists and cannot reinforce')
println "---360 reinforceApk throw exception and forced stop!---"
}
exec {
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -login ${REINFORCE_NAME} ${REINFORCE_PASSWORD}"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -showsign"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -jiagu ${apk} ${outputPath}"
}
println "--- 360 reinforce end! ---"
}
複製代碼
加固工做已經完成差很少了,剩下的工做就是對加固包從新簽名
重簽名的方法主要是調用AndroidSDK中的build-tools,使用工具包中對齊工具和簽名工具完成簽名。具體步驟以下:
1)對齊,對Apk文件進行存檔對齊優化,確保全部的未壓縮數據都從文件的開始位置以指定的對齊方式排列
2)簽名,選擇Signature V2
commandLine "{命令執行符號}","-c", "{zipalign工具的文件路徑} -v -p 4 {已加固的apk文件路徑} {對齊後輸出的apk文件路徑}"
commandLine "{命令執行符號}", "-c", "{apksigner工具的文件路徑} sign --ks {簽名文件的位置} --ks-key-alias {alias別名} --ks-pass pass:{簽名文件存儲的密碼} --key-pass pass:{alias密碼} --out {簽名後輸出的apk文件} {對齊後輸出的apk文件路徑}"
複製代碼
最後,使用walle美團的多渠道打包工具
平時使用walle多渠道打包,只須要在app/build.gradle下配置插件,指定渠道包的輸出路徑和渠道配置文件便可,最後在Android studio的Terminal中輸入./gradlew assembleReleaseChannels,任務執行完成後在指定的輸出路徑下生成多個對應的渠道包。具體的流程和細節可參考官方介紹。
這種多渠道打包方式是全自動化構建,很難去幹涉到構建流程,不符合咱們的需求:
1)在app/build.gradle配置插件時,在官方介紹中並無找到指定源APK輸入路徑的方式,估計打包插件默認使用的是app/build/outputs/apk/release下的apk文件,這樣就沒辦法對不一樣文件路徑下的已加固apk包進行多渠道打包。
2)打包任務設置在assembleRelease以後執行,這個執行依賴封裝在插件內部,外部很難修改打包任務依賴於加固任務,在加固任務以後執行。
除了上面的多渠道打包方式以後,walle還提供了另一種多渠道打包方式,用命令行執行walle提供的walle-cli-all.jar執行打包操做,只須要一條打包命令便可完成打包。
commandLine "sh", "-c", "java -jar {walle-cli-all.jar文件路徑} batch -f {渠道文件路徑} {要加渠道的apk文件路徑} {渠道包的輸出路徑}"
複製代碼
walle-cli-all.jar文件下載地址:walle-cli-all.jar
至此,360加固+walle多渠道打包的基本工做完成了!剩下就是構建總體流程和優化代碼。
首先,將加固和打包操做封裝成自動化操做,利用gradle腳本構建加固任務。爲了代碼解耦,咱們不在app/build.gradle裏面實現加固任務,而是從新建一個gradle文件來實現具體的加固和多渠道打包過程,在app/build.gradle只須要經過apply from: '×××.gradle'
引用這個gradle文件便可,當須要修改加固的一些代碼邏輯時,只須要在這個gradle文件裏面修改。
引入工具包。根據本身的系統環境,在加固助手網頁選擇對應的加固助手工具,下載後將裏面的jiagu文件夾拷貝到本身項目的根目錄下;在walle-cli-jar下載連接下載jar包到本身項目中。
肯定加固任務的時機。加固任務時機應該在release包生成以後,那麼加固任務應該依賴於assembleRelease這個任務,而且設置在這個任務以後執行。
接下來就是咱們的基本流程了
1)找到release包,通常在app/build/outputs/apk/release/路徑下
2)執行加固命令,將release包路徑設置到命令中,並指定加固apk文件的輸出路徑
3)找到已加固的apk文件,對已加固apk文件進行對齊、重簽名。(360已加固的apk文件會在原有的release文件名後面加上"_jiagu")
4)找到從新簽名的apk文件,執行多渠道打包命令。(重簽名後的文件名是在原有文件名後面加上"_sign")
/** * 360加固 + 美團walle渠道打包 */
task assembleReinforceRelease() {
group '360reinforce'
dependsOn("assembleRelease")
doLast {
cleanFilesPath(CHANNEL_APKS_PATH) //清空上一次生成的渠道包
def releaseApkFile = findApkFile(SOURCE_APK_PATH,"release") //遍歷文件,尋找release包
if(releaseApkFile != null) {
reinforceApk(releaseApkFile, DEFAULT_APK_PATH) //執行加固
def reinforceApk = findApkFile(DEFAULT_APK_PATH, "_jiagu") //尋找已加固的apk包
if(reinforceApk != null) {
signApkV2(reinforceApk) //使用V2重簽名
def signatureApk = findApkFile(DEFAULT_APK_PATH, "sign")
if(signatureApk != null) {
buildChannelApks(signatureApk,CHANNEL_APKS_PATH) //執行多渠道打包
renameChannelApkFiles(CHANNEL_APKS_PATH) //重命名渠道包
}
}
}
}
}
複製代碼
整個流程肯定後,差很少接近尾聲了。
代碼優化:
1)將流程中每一個步驟封裝成一個方法,使代碼更加簡潔易懂;
2)任務中涉及360加固平臺賬號密碼等敏感信息,能夠將這部分信息放到簽名信息所在的文件(eg:keystore.properties)中統一管理,而後將這些信息加載到gradle文件中;
3)各類輸入輸出的文件路徑定義爲常量,便於修改和管理;
加固方法,重命名和渠道打包的方法相似:
/** * 360加固 * @param apk 加固的原始apk File * @param outputPath 輸出目錄 */
def reinforceApk(File apk,outputPath) {
println "--- 360 reinforceApk start! ---"
println "reinforce apk:" + apk
if(apk == null || !apk.exists()) {
throw new FileNotFoundException('apk is not exists and cannot reinforce')
println "---360 reinforceApk throw exception and forced stop!---"
}
exec {
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -login ${REINFORCE_NAME} ${REINFORCE_PASSWORD}"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -showsign"
commandLine "sh", "-c", "java -jar ${REINFORCE_JAR} -jiagu ${apk} ${outputPath}"
}
println "--- 360 reinforce end! ---"
}
複製代碼
任務中涉及到的各類常量,各類密鑰名、路徑都要根據本身的實際狀況修改:
/*加載keystore.properties信息到該gradle文件中*/
def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
ext {
/*加固*/
REINFORCE_JAR = "${project.rootDir}/jiagu/jiagu.jar"
REINFORCE_NAME = keystoreProperties['360_NAME'] //360加固帳號
REINFORCE_PASSWORD = keystoreProperties['360_PASSWORD'] //360加固密碼
KEY_PATH = keystoreProperties['storeFile'] //密鑰路徑
KEY_PASSWORD = keystoreProperties['storePassword'] //密鑰密碼
ALIAS = keystoreProperties['keyAlias'] //密鑰別名
ALIAS_PASSWORD = keystoreProperties['keyPassword'] //別名密碼
SOURCE_APK_PATH = "${project.buildDir}/bakApk" //源apk文件路徑
DEFAULT_APK_PATH = "${project.buildDir}/outputs/apk/release" //默認release文件路徑
/*多渠道打包*/
WALLE_JAR = "${project.rootDir}/walle-cli-all.jar"
WALLE_CHANNELS_CONFIG = "../app/channel" //渠道配置文件
CHANNEL_APKS_PATH = "${project.buildDir}/outputs/channels" //渠道Apk輸出路徑
}
複製代碼
1)對比加固前release包的簽名和加固後apk的簽名是否一致,二者相同說明新apk可以覆蓋安裝
2)用反編譯工具對加固包進行反編譯,看可否看到Activity這些類
3)驗證是否能夠獲取到渠道包,代碼中獲取渠道號是經過WalleChannelReader.getChannel(application);這個方法
。。。
1)網上傳聞,360加固後沒法獲取到walle打包的渠道號?
是的,360加固過程會抹去已簽名release包的簽名信息,假如在加固前用walle打渠道包就會形成渠道號丟失,因此咱們採用的方法是先加固再多渠道打包,因爲加固會破壞掉原有的簽名信息,因此加固後須要從新簽名。
(新開的公衆號,請你們多多支持!)
若是以爲對你有幫助,麻煩點個贊,謝謝!同時,歡迎你們評論,互相討論問題。