首页 > 代码库 > Bash玩转脚本4之搞一套完整的Android反编译与分包工具

Bash玩转脚本4之搞一套完整的Android反编译与分包工具

一、前言


正在搞IOS的微信支付和支付宝支付,焦头烂额之时,天上掉下来一个Android分包工具的需求,觉得还蛮有意思,其实之前一直想搞一个类似的东西,正好趁着这次机会实践一下。

[原文地址]
(http://blog.csdn.net/yang8456211/article/details/52513354 )

(先说清楚需求,这个分包工具要干什么)

从产品角度

拿到一个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。

    <meta-data android:name="SDK_CHANNEL" android:value="天降正义" />

运行时截图

技术分享

可以看到现在的包名是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对字符串的替换,可以看我的这篇文章)
Bash玩转脚本3之几个指令有趣的筛选京东评价

指令为:

sed -i ‘‘ "s~^.*<meta-data android:name=\"SDK_CHANNEL\".*~        <meta-data android:name=\"SDK_CHANNEL\" android:value=http://www.mamicode.com/""$game_channel"\"/>~g" $manifest

在此我稍作解释,如果有兴趣的朋友可以去google或者百度才能系统的学习。

1)sed "s~原字符串~新字符串~g" 文件路径 这是sed最基本的指令, “~”符号可以换成很多符号,三个”~”对应更换即可.

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

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

3) ^.*<meta-data android:name=\"SDK_CHANNEL\".*
这一段是正则匹配,用于匹配到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

杨光(atany)原创,转载请注明博主与博文链接,未经博主允许,禁止任何商业用途。
博文地址:http://blog.csdn.net/yang8456211/article/details/52513354
博客地址:http://blog.csdn.net/yang8456211
本文遵循“署名-非商业用途-保持一致”创作公用协议

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Bash玩转脚本4之搞一套完整的Android反编译与分包工具