360加固+美團walle多渠道自動化打包

久見仙發表於2019-03-08

背景介紹

為了防止安卓應用程式被惡意破解,植入黑客病毒或修改程式碼用於商業競爭等,對應用程式進行加固必不可少。接下來,本篇文章會主要講加固的過程以及一些注意事項。

前期準備

首先,瞭解一下何為加固,加固的原理是怎樣的,這有利於後面分析問題。

簡單來說,加固就是對源Apk進行加密,然後再套上一層殼。用加密演算法對源Apk進行加密,再將殼Apk進行合併得到新的Dex檔案,最後替換殼程式中的dex檔案得到新的Apk,這個新的Apk已經不是一個完整意義上的Apk程式了,它的主要工作是負責解密源Apk,然後載入Apk,讓其正常執行起來。

目前,各大網際網路公司都會自己的應用程式進行加固保護,像360公司,騰訊都有對外開放自己的服務。另外,市場上還有一些專門加固的產品,比如愛加密和梆梆加固等。好好利用這些“輪子”,專注於業務開發,來提高工作效率。

加固工具的選擇:此次使用的是360加固

第一,從調研加固結果可見,360加固在相容性、啟動速度、體積變化上都佔有優勢,整體上加固效果比較好;

第二,360公司是一家安全起家的公司,在業界的影響力也很大,加固技術還是值得信賴的!

第三,看了很多加固工具的官網,加固的過程都是上傳簽名的APK包到官網頁面或使用相應的桌面程式進行上傳,這個過程需要人工進行上傳,而360加固提供了一個加固工具包,我們可以編寫指令碼來呼叫其中的加固程式進行自動化加固。現在很多公司都是用Jenkins線上自動化打包,加固也是打包過程的一部分,最好也能是自動化的,這樣整個打包流程是“一條龍”,沒有人工干預,相當於在黑匣子中進行,程式設計師不用關心打包過程,也減少人工成本和出錯機率。

期望目標

使用gradle指令碼實現自動化加固和多渠道打包

實現

整個過程分成三個步驟:加固——重簽名——多渠道打包

加固

加固過程: 瀏覽了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);這個方法

。。。

Q&A

1)網上傳聞,360加固後無法獲取到walle打包的渠道號?

是的,360加固過程會抹去已簽名release包的簽名資訊,假如在加固前用walle打渠道包就會造成渠道號丟失,所以我們採用的方法是先加固再多渠道打包,由於加固會破壞掉原有的簽名資訊,所以加固後需要重新簽名。

歡迎關注公眾號——久見先

(新開的公眾號,請大家多多支援!)

360加固+美團walle多渠道自動化打包

如果覺得對你有幫助,麻煩點個贊,謝謝!同時,歡迎大家評論,互相討論問題。

相關文章