在之前的文章“【工具】统计jar包和apk中的java方法数”中,给大家分享了两个统计工具,分别是统计jar包种的java方法数工具jarmethod,和统计apk文件中所有的java方法数工具apkmethod。但在项目中,我发现这个还不够,因为我还想要去统计每个package中的java方法数,当然,这个工具就比前面两个要复杂一点点了。
首先,我们可以利用之前提到的工具,可以很轻松从dex文件中统计出java方法数,所以,只要我们能想办法从apk文件中分离出需要统计的package,问题就迎刃而解了!借助 baksmali 和 smali 两个工具,实现的思路就会变得清晰很多了,可以按照这个逻辑去实现:
1、解压apk包,得到classes.dex;这一步zip命令即可完成!
2、用baksmali分解classes.dex文件,得到和原工程目录结构一致的文件列表;这一步的命令示例:
# 将classes.dex分解到classes_dir目录下 java -jar baksmali-2.0.3.jar -o classes_dir/ classes.dex
3、用smali将需要统计方法数的package再转换为dex包,即可完成;这一步的命令示例:
# 将com.baidu.tieba.pb包转换为同名的dex文件 java -jar smali-2.0.3.jar classes_dir/com/baidu/tieba/pb -o com.baidu.tieba.pb.dex
4、利用之前文章中提到的方法,直接从dex文件中获得java方法数;这一步的命令示例:
# 统计com.baidu.tieba.pb.dex中的方法数 method=`dexdump -f com.baidu.tieba.pb.dex | grep method_ids_size` echo $method
5、当然,上面命令得到的结果不是一个纯数字,而是类似这样的:
method_ids_size : 2063
所以,为了让结果变得更干净,可以用很简单的shell脚本将数字提取出来:
# 替换掉method_ids_size关键字 method_num=${method_num//method_ids_size/} # 替换掉冒号 method_num=${method_num//:/} # 对结果进行trim处理,得到纯数字 method_num=`echo $method_num | sed -e 's/\(^ *\)//' -e 's/\( *$\)//'`
OK,几个核心的部分已经完成,接下来组装shell脚本即可,完整版的工具如下:pkgmethod.sh
#!/bin/bash # 得到命令行的所有参数 tool_name="$0" apk_path=$1 pkg_list=$2 # 检验参数的合法性 if [ x"$apk_path" == x ] || [ x"$pkg_list" == x ] ;then echo -e "\n\t用法:" echo -e "\tpkgmethod your_apk_path \"your_package_list\"" echo -e "\n\t例:" echo -e "\tpkgmethod ../../tieba.apk \"com.baidu.tieba.frs com.baidu.tieba.pb\"\n" exit 1; fi # 得到工具的原始目录 prog=$tool_name while [ -h "${prog}" ]; do newProg=`/bin/ls -ld "${prog}"` newProg=`expr "${newProg}" : ".* -> \(.*\)$"` if expr "x${newProg}" : 'x/' >/dev/null; then prog="${newProg}" else progdir=`dirname "${prog}"` prog="${progdir}/${newProg}" fi done # 这就是工具的目录了 tool_dir=`dirname "${prog}"` # 以此得到两个jar文件的完整路径 baksmali_jarfile=$tool_dir/baksmali-2.0.3.jar smali_jarfile=$tool_dir/smali-2.0.3.jar # 下面要做的事情是:在当前目录下创建临时文件夹,将目标apk文件拷贝进来并解压 # 创建一个临时目录,来解压这个apk文件 rm -rf apk_temp mkdir apk_temp cp $1 apk_temp/ cd apk_temp echo "创建临时目录成功..." # 获得apk的名称 apk_name="$(basename *.apk)" # 重命名为zip mv $apk_name $apk.zip # 解压apk,得到classes.dex包 unzip -x $APK_NAME.zip > /dev/null echo "解压apk文件并提取dex文件成功..." # 在当前目录下,就可以得到classes.dex文件了 # 接下来要做的事情就是: # 1、使用baksmali将classes.dex中的class导出(smali文件) java -jar $baksmali_jarfile -o classes_dir/ classes.dex echo "反编译dex文件成功..." echo -e "开始进行package下方法数量的统计...\n" # 2、用smali对各个package进行转换:smali to dex for pkg_item in $pkg_list ;do target_pkg_name=$pkg_item # 将.替换成/得到路径,比如:com.baidu.tieba.pb替换为com/baidu/tieba/pb target_pkg_path="classes_dir/${pkg_item//.//}" if [ -d "$target_pkg_path" ];then # 编译得到以包名命名的dex文件,如:com.baidu.tieba.pb.dex java -jar $smali_jarfile $target_pkg_path/ -o $target_pkg_name.dex # 从dex文件中统计方法数 method_num=`dexdump -f $target_pkg_name.dex | grep method_ids_size` # 为了得到纯数字部分,咱们吧不相干的东西删掉 method_num=${method_num//method_ids_size/} method_num=${method_num//:/} method_num=`echo $method_num | sed -e 's/\(^ *\)//' -e 's/\( *$\)//'` echo -e " $target_pkg_name \t: $method_num" else echo -e " $target_pkg_name \t: 包不存在" fi done # 删除临时目录,结束 # cd ../ && rm -rf apk_temp echo -e "\n删除临时目录成功,方法数量统计结束!"
和之前的工具一样,chmod一下,然后建一个软链接到/usr/bin下,以后就可以直接通过pkgmethod命令在任意目录直接使用了,比如,我们来统计一下tieba.apk中com.baidu.tieba.frs、com.baidu.tieba.pb、以及protobuf几个目录下的java方法数量,命令就可以这样了:
# apk可以在任意目录,package也可以是单个、多个 pkgmethod tieba.apk "com.baidu.tieba.frs com.baidu.tieba.pb protobuf"
得到的结果是:
如果你的项目中也需要统计方法数,不妨把工具拿去试一试!