Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Bash玩轉腳本4之搞一套完整的Android反編譯與分包工具

Bash玩轉腳本4之搞一套完整的Android反編譯與分包工具

編輯:關於Android編程

一、前言


正在搞IOS的微信支付和支付寶支付,焦頭爛額之時,天上掉下來一個Android分包工具的需求,覺得還蠻有意思,其實之前一直想搞一個類似的東西,正好趁著這次機會實踐一下。

(先說清楚需求,這個分包工具要干什麼)

從產品角度

拿到一個apk安裝包,然後用這個包去生成n個包,這n個包需要有特定的標示,能夠根據包的標示去收集信息,而且這個n個包彼此不能覆蓋安裝。

從技術角度

對於這個需求,關鍵點在於三個點
1. 怎麼去生成n個包?
2. 怎麼修改apk的標示?
3. 怎麼使得這n個包不能覆蓋安裝?

二、Just do it

2.1 首先我們自己制作一個簡單的apk包

這個apk包包含兩個功能點:

獲取一些包的基本信息,例如應用包名 獲取一些Meta信息,用來區分我們所打的包

因為這篇文章主要在講bash和apk打包,對於Android代碼就不贅述了,貼出來參考。

(獲取應用包名)

        PackageInfo info = null;
        try {
            // 獲取包名
            info = this.getPackageManager()
                    .getPackageInfo(this.getPackageName(), 0);
        } catch (NameNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        // 當前版本的包名
        pgName = info.packageName;

(獲取Meta信息)

        PackageManager pm = this.getPackageManager();

        ApplicationInfo appInfo = null;
        try {
            appInfo = pm.getApplicationInfo(this.getPackageName(),
                    PackageManager.GET_META_DATA);
        } catch (NameNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        // 讀取meta的內容
        msg.append(appInfo.metaData.getString("SDK_CHANNEL"));

這裡我們讀取了SDK_CHANNEL這個信息作為包的標示,需要在Android Manifest中配置好相關的Meta data。

可以看到現在的包名是com.example.testmultipac,渠道是天降正義~

接下來我們就開始著手一個一個解決問題

三、開始處理我們的包

3.1 怎麼去生成這個n個包?

想要生成多個包,必須涉及到需要把apk進行反編譯,然後重新生成apk包的過程,因此我使用了apktool這個工具。

注:apktool 有1和2兩個版本,兩者語法有些許不同,在這裡使用了apktool_2.1.0這個jar,當然你也可以使用apktool1;window可以直接調用apktool.bat的批處理,事實上也是調用了apktool.jar。

apktool指令:

解開apk包:
java -jar apktool d -f 輸入的apk路徑 -o 輸出的文件夾路徑

重新生成apk包:
java -jar apktool b 上一步解出的文件夾路徑 -o 輸出apk路徑

注:這裡使用的apktool2,因此有-o這個參數,apktool是沒有的,請注意。

了解了apktool的指令後,我們便可以方便的實現apk的解包和組包,在組包的會後通過修改包的名稱,就可以生成多個不同名的包了。

例如:

do_repac(){

    outapk=$apkpacpath"/"$game_package_name".apk"

    # apktool重新回包 以免apktool的一些臨時改動
    java -jar ./tool/apktool/$APKTOOL_JAR b $unpacpath -o $outapk
}

上面的outapk即為 $apkpacpath”/”$game_package_name”.apk”

game_package_name為當前包的包名。(如果能做到包名不同,則生成的apk的名字便不同)

3.2 怎麼修改apk的標示?

目前例子來說,我們apk的標示是”SDK_CHANNEL”這個Meta字段,我們可以通過修改這個字段來實現我們對包的區分。當客戶端對服務器發起請求的時候,帶上這個字段,服務器便可以方便的知道是哪個包進行的請求了。

那我們要怎麼修改這個字段呢?

在此我使用了sed,一個方便替換的文本的指令。

指令為:

sed -i '' "s~^.*~g" $manifest

在此我稍作解釋,如果有興趣的朋友可以去google或者百度才能系統的學習。

1)sed "s~原字符串~新字符串~g" 文件路徑 這是sed最基本的指令, “~”符號可以換成很多符號,三個”~”對應更換即可.

2)sed -i
通過 man sed 可以查到sed的基本指令(OSX 系統)

意思是不備份,直接在原文件上面進行操作。
注:在linux上可以直接使用 sed -i,而在Unix上需要sed -i ""

3) ^.* 這一段是正則匹配,用於匹配到SDK_CHANNEL那一行,sed是一行一行進行掃描的,如果遇到能夠匹配的就會進行替換。

4)"$game_channel"這個是bash的取值,相當於一個變量,這裡我是取game_channel這個變量

到現在,我們已經能夠把包解出來,然後使用sed去修改裡面的標示,看樣子已經成功了一半了。

3.3 怎麼使得這n個包不能覆蓋安裝?

涉及到這個問題,我們需要對Android的基本知識進行一個簡單回顧。

Android通過什麼來保證應用的唯一性?
答案是包名和簽名。

如果兩個apk,包名相同,簽名也相同,則會根據versionCode的值來決定是否會覆蓋,如果後一個apk的versionCode比較大,才能夠覆蓋安裝。 如果包名相同,簽名不同,則會被識別為不安全的應用,會給予提示,安裝的結果會是後一個apk會刪掉前一個apk,然後進行安裝。 如果包名不同,簽名相同,則代表著同一個開發者的應用,被識別為不同的應用。(看來這樣就可以實現我們的不覆蓋安裝了)

在修改包名的時候,我同樣是用了一系列指令。(下意識寫的指令,並沒有考慮到是否最好,如果有更好的指令可以給我留言)

    old_pacname=`cat $manifest | grep "package=" | head -n 1 | awk -F 'package=\"' '{print $2}' | awk -F '\"' '{print $1}' |  xargs echo `
    echo "==>"$old_pacname
    sed -i '' "s~package=\""$old_pacname\""~package="\"$game_package_name\""~g" $manifest

講一下思路:
1. 先把”package=”關鍵字的那一行抓出來(這裡是為了抓包名)
2. 使用兩次awk取出 package=後面的包名(得到當前的包名)
3. 當前的包名賦值給old_pacname
4 .使用sed替換舊的包名為新包名,新的包名為game_package_name這個變量的值。

至此我們已經弄清楚了其中的知識點,對於一些難點也有了一定處理辦法,接下來我們便要開始做一個腳本去自動化實現Android的反編譯和分包了。

3.4 來看看我們的流程

Created with Rapha?l 2.1.0開始獲得參數apk解包修改包內參數和包名apk組包簽名結束

可以發現,在這個流程的最後一步,還有個簽名的過程,那怎麼進行簽名呢?

3.5 簽名過程

我們是通過jarsigner這個工具對apk進行簽名的,如果不簽名的應用可是無法安裝的~

那個這個jarsigner是什麼呢?其實這個是jdk自帶的對jar包進行簽名的工具,我們可以在安裝的java指令的同級目錄找到它。


通過直接輸入jarsigner指令,可以得到提示

用法:

    #         顯示信息              簽名文件                   簽名密碼                生成apk                                            未簽名apk        alias   
    jarsigner -verbose -keystore $keystore_name -storepass $SIGN_PASS -signedjar $sign_apkname -digestalg SHA1 -sigalg MD5withRSA $unsign_apkname $SIGN_ALIAS 

每個參數的意思注釋都標志的很清楚了~

四、make Auto

下面講講腳本的思路

4.1 實現兩個調用模式

第一種)所有的參數從外部傳遞調用,Mode為1時
第二種)讀取本地的配置文件,Mode為0時,可以實現批處理生成多個包

功能說明:

help_info() {
cat << ENTER
     ============= Auto pac For game =============
     Version: 1.0
     Date: 20160907
     Usage: Auto repackage For the game, modify package name and subchannel
     e.g.: 
        Mode0: sh autopac.sh inputApkPath gamename
        Mode1: sh autopac.sh inputApkPath gamename channel outputApkPath
            inputApkPath: 待分包的apk路徑
            gamename: 游戲名稱英文首字母小寫
            channel: 渠道號
            outputApkPath: 完成輸出apk的路徑
     ============= Auto pac For game =============
ENTER
}

參數由外部輸入就不贅述了,簡單的賦值即可。
對於Mode=0時的讀取配置文件一般是通過awk來實現的。

4.2 使用awk讀取配置文件

例如配置文件的內容是這樣

[package]
package1=d101
package2=d102

我們通過awk取出對應的d101,和102出來

read_config() {
    inifile=$1 #$1為配置文件的位置
    _readIni=`awk -F '=' '$1~/'package[d]*'/{print $2}' $inifile`
    echo $_readIni
}

稍微解釋一下:
1. awk -F ‘=’ 為按照’=’,進行字符串分割
2. package[d]*為正則表達式,用來匹配package1,package2這種類型的一行,所以配置文件中可以有很多個package
3. {print $2} 就是打印出用’=’分割的第二個字符串,即’=’號後面的內容。

(假如我的標示不止1個怎麼辦?)
例如我的標示有三個,channel、subchannel、payway分別對應著渠道號、子渠道、支付渠道,那這個時候怎麼辦呢?

我們可以把配置文件寫成這樣:

package1=d101:subchannel101:1
package1=d102:subchannel102:2

然後在按照上面的做法,我們取出了 ‘=’ 右邊的值,例如d101:subchannel101:1

我們便可以通過bash的字符串分割來做:

    game_payway=${1##*:}
    temp=${1%:*}
    game_subchannel=${temp##*:}
    game_channel=${temp%:*}

這樣便可以取出payway等值了,關於bash的字符串分割,可以自行百度

基本的東西都講完了,我們來看看最終的效果:

五、實現效果

以mode為1的批處理為例子:
1)配置文件配置了4個不同channel的包:

[package]

package1=yingxiongbuxiu:英雄不朽
package2=wushiyidao:午時已到
package3=laidianyinyue:來點音樂
package4=ronghuohexin:熔火核心

2)執行腳本:

sh autopac.sh ./TestMultiPac.apk mszl

TestMutiPac.apk就是我們最開始生成的一個apk,mszl為游戲的名稱

3)執行效果:
看到已經生成了4個包:

4)安裝效果:
如圖生成了5個包
隨便選取一個包的效果

源碼地址:
https://github.com/yang8456211/AutoPacAndroid

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved