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 it2.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="https://www.songbingjia.com/android/天降正义" />

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

文章图片

可以看到现在的包名是com.example.testmultipac,渠道是天降正义~
接下来我们就开始着手一个一个解决问题
三、开始处理我们的包3.1 怎么去生成这个n个包?想要生成多个包,必须涉及到需要把apk进行反编译,然后重新生成apk包的过程,因此我使用了apktool这个工具。
注:apktool 有1和2两个版本,两者语法有些许不同,在这里使用了apktool_2.1.0这个jar,当然你也可以使用apktool1;window可以直接调用apktool.bat的批处理,事实上也是调用了apktool.jar。
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

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=https://www.songbingjia.com/""$game_channel"\"/> ~g" $manifest

在此我稍作解释,如果有兴趣的朋友可以去google或者百度才能系统的学习。
1)sed "s~原字符串~新字符串~g" 文件路径 这是sed最基本的指令, “~”符号可以换成很多符号,三个”~”对应更换即可.
2)sed -i
通过 man sed 可以查到sed的基本指令(OSX 系统)
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

意思是不备份,直接在原文件上面进行操作。
注:在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这个变量的值。
【Bash玩转脚本4之搞一套完整的Android反编译与分包工具】至此我们已经弄清楚了其中的知识点,对于一些难点也有了一定处理办法,接下来我们便要开始做一个脚本去自动化实现Android的反编译和分包了。
3.4 来看看我们的流程Created with Rapha?l 2.1.0开始获得参数apk解包修改包内参数和包名apk组包签名结束可以发现,在这个流程的最后一步,还有个签名的过程,那怎么进行签名呢?
3.5 签名过程我们是通过jarsigner这个工具对apk进行签名的,如果不签名的应用可是无法安装的~
那个这个jarsigner是什么呢?其实这个是jdk自带的对jar包进行签名的工具,我们可以在安装的java指令的同级目录找到它。
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

通过直接输入jarsigner指令,可以得到提示
用法:
#显示信息签名文件签名密码生成apk未签名apkalias 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)执行效果:
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

看到已经生成了4个包:
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

4)安装效果:
如图生成了5个包
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

随便选取一个包的效果
Bash玩转脚本4之搞一套完整的Android反编译与分包工具

文章图片

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





































    推荐阅读