Skip to content

Commit 944abd2

Browse files
committed
第一个版本
0 parents  commit 944abd2

File tree

4 files changed

+291
-0
lines changed

4 files changed

+291
-0
lines changed

README.md

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
apkcal(apk中方法数统计工具)
2+
===================================
3+
4+
### 1、工具用途?
5+
对Android APK包中的如下类型进行统计:
6+
1)、class:类数
7+
2)、field:字段数
8+
3)、method:方法数
9+
4)、string:字符串数
10+
11+
12+
### 2、统计的目的?
13+
因为在Android Dex File Format中,这些东西都有一个65536大小的限制;即:单个dex文件中,方法数量(等)不能超过这个数值。
14+
15+
16+
### 3、如何配置?
17+
#### 1)、配置ADT的platform-tools和tools环境变量
18+
19+
#### 2)、下载工具,解压后放到一个目录,并保证脚本可执行,比如:
20+
```shell
21+
cp -r apkcal ~/Document/Tool/apkcal
22+
chmod -R 0755 ~/Document/Tool/apkcal/
23+
```
24+
25+
#### 3)、为apkcal.sh建立软链接:
26+
```shell
27+
cd /usr/local/bin
28+
ln -s ~/Document/Tool/apkcal/apkcal.sh apkcal
29+
```
30+
31+
#### 4)、切换到任意目录,apkcal命令已可用
32+
33+
34+
### 4、如何使用?
35+
#### 1)、查看帮助
36+
```shell
37+
apkcal -h
38+
```
39+
以上命令将输出:
40+
用法:
41+
apkcal type=[type] deep=[deep] your_apk_path "your_package_list"
42+
43+
type:统计类型,可选:[class|field|method|string]
44+
deep:是否进行package深度扫描统计,可选:[0|1] 默认:0
45+
46+
例:
47+
apkcal type=method ../tieba.apk "com.baidu.tieba.frs com.baidu.tieba.pb"
48+
49+
#### 2)、统计tiebaAll.apk文件中"com.baidu.tieba.account"包下的方法数
50+
```shell
51+
apkcal type=method tiebaAll/tiebaAll.apk "com.baidu.tieba.account"
52+
```
53+
以上命令将输出:
54+
开始进行apk文件中【 method 数】的统计...
55+
创建临时目录成功...
56+
解压apk文件并提取dex文件成功...
57+
反编译dex文件成功...
58+
59+
com.baidu.tieba.account : 872
60+
61+
删除临时目录成功,统计完成!
62+
63+
#### 3)、深度统计tiebaAll.apk文件中"com.baidu.tieba.account"包下的方法数
64+
```shell
65+
apkcal type=method deep=1 tiebaAll/tiebaAll.apk "com.baidu.tieba.account"
66+
```
67+
以上命令将输出:
68+
开始进行apk文件中【 method 数】的统计...
69+
创建临时目录成功...
70+
解压apk文件并提取dex文件成功...
71+
反编译dex文件成功...
72+
73+
com/baidu/tieba/account : 872
74+
com/baidu/tieba/account/appeal : 126
75+
com/baidu/tieba/account/forbid : 144
76+
77+
删除临时目录成功,统计完成!
78+
79+
#### 4)、同时统计多个包,包名之间用空格分开即可
80+
```shell
81+
apkcal type=method tiebaAll/tiebaAll.apk "com.baidu.tieba.account com.baidu.tieba.frs com.baidu.tieba.pb"
82+
```
83+
84+
#### 5)、统计class的数量
85+
```shell
86+
apkcal type=class deep=1 tiebaAll/tiebaAll.apk "com.baidu.tieba.account"
87+
```
88+
89+
#### 6)、不输入包名的情况下,则会对整个apk中所有的package进行深度遍历
90+
```shell
91+
apkcal type=class deep=1 tiebaAll/tiebaAll.apk
92+
```
93+
注意!!!:机器性能不好,请别轻易这样玩儿,尤其是apk中package数量特别多的情况。
94+
95+
### 5、意见反馈
96+
Author:zhaoxianlie
97+
Blog:http://www.baidufe.com
98+

apkcal.sh

+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/bin/bash
2+
3+
# 得到命令行的所有参数
4+
tool_name="$0"
5+
the_type=$1
6+
deep_scan=$2
7+
apk_path=$3
8+
pkg_list=$4
9+
10+
# 检查是否需要深度扫描
11+
flag=`echo $deep_scan | grep "^deep=1"`
12+
if [ "$flag" == "" ];then
13+
deep_scan=0
14+
apk_path=$2
15+
pkg_list=$3
16+
else
17+
# 提取深度扫描package的标示,0 | 1
18+
deep_scan=${deep_scan//deep=/}
19+
fi
20+
21+
# 检验参数的合法性,必须指定apk的路径
22+
if [ x"$apk_path" == x ] || [ ! -f "$apk_path" ] || [ x"$the_type" == x ] || [ "$1" == "-h" ] ;then
23+
echo -e "\n\t用法:"
24+
echo -e "\tapkcal type=[type] deep=[deep] your_apk_path \"your_package_list\"\n"
25+
echo -e "\t type:统计类型,可选:[class|field|method|string]"
26+
echo -e "\t deep:是否进行package深度扫描统计,可选:[0|1] 默认:0"
27+
echo -e "\n\t例:"
28+
echo -e "\tapkcal type=method ../tieba.apk \"com.baidu.tieba.frs com.baidu.tieba.pb\"\n"
29+
exit 1;
30+
fi
31+
32+
#从参数 type=* 中提取统计类型:method | field | type | string
33+
the_type=${the_type//type=/}
34+
35+
# 打印调试信息
36+
echo "开始进行apk文件中【 "$the_type" 数】的统计..."
37+
38+
# 得到工具的原始目录
39+
prog=$tool_name
40+
while [ -h "${prog}" ]; do
41+
newProg=`/bin/ls -ld "${prog}"`
42+
newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
43+
if expr "x${newProg}" : 'x/' >/dev/null; then
44+
prog="${newProg}"
45+
else
46+
progdir=`dirname "${prog}"`
47+
prog="${progdir}/${newProg}"
48+
fi
49+
done
50+
51+
# 这就是工具的目录了
52+
tool_dir=`dirname "${prog}"`
53+
54+
# 以此得到两个jar文件的完整路径
55+
baksmali_jarfile=$tool_dir/baksmali-2.0.3.jar
56+
smali_jarfile=$tool_dir/smali-2.0.3.jar
57+
58+
# 下面要做的事情是:在当前目录下创建临时文件夹,将目标apk文件拷贝进来并解压
59+
# 创建一个临时目录,来解压这个apk文件
60+
rm -rf apk_temp
61+
mkdir apk_temp
62+
cp $apk_path apk_temp/
63+
cd apk_temp
64+
# package list dir path
65+
classes_dir_path="`pwd`/classes_dir"
66+
67+
echo "创建临时目录成功..."
68+
69+
# 获得apk的名称
70+
apk_name="$(basename *.apk)"
71+
72+
# 重命名为zip
73+
mv $apk_name $apk.zip
74+
75+
# 解压apk,得到classes.dex包
76+
unzip -x $APK_NAME.zip > /dev/null
77+
echo "解压apk文件并提取dex文件成功..."
78+
79+
# 在当前目录下,就可以得到classes.dex文件了
80+
# 接下来要做的事情就是:
81+
# 1、使用baksmali将classes.dex中的class导出(smali文件)
82+
java -jar $baksmali_jarfile -o $classes_dir_path classes.dex
83+
echo -e "反编译dex文件成功...\n"
84+
85+
# 按照package维度进行统计
86+
# 用smali对各个package进行转换:smali to dex
87+
# 使用方法: cal_package com.baidu.tieba method
88+
function cal_package() {
89+
local cal_type pkg_item;
90+
pkg_item=$1;
91+
cal_type=$2;
92+
93+
# 对type进行修正
94+
if [ "$cal_type" == "class" ];then
95+
cal_type="class_defs_size"
96+
else
97+
cal_type=$cal_type"_ids_size"
98+
fi
99+
100+
target_pkg_name=$pkg_item
101+
# 将.替换成/得到路径,比如:com.baidu.tieba.pb替换为com/baidu/tieba/pb
102+
target_pkg_path="${pkg_item//.//}"
103+
104+
if [ -d "$target_pkg_path" ];then
105+
# 编译得到以包名命名的dex文件,如:com.baidu.tieba.pb.dex
106+
java -jar $smali_jarfile $target_pkg_path/ -o $target_pkg_name.dex
107+
108+
# 从dex文件中统计
109+
the_num=`dexdump -f $target_pkg_name.dex | grep $cal_type`
110+
# 为了得到纯数字部分,咱们吧不相干的东西删掉
111+
the_num=${the_num//$cal_type/}
112+
the_num=${the_num//:/}
113+
the_num=`echo $the_num | sed -e 's/\(^ *\)//' -e 's/\( *$\)//'`
114+
echo -e " $target_pkg_name \t: $the_num"
115+
# 删除临时文件
116+
rm -rf $target_pkg_name.dex
117+
else
118+
echo -e " $target_pkg_name \t: 包不存在"
119+
fi
120+
}
121+
122+
# 遍历整个文件夹
123+
# 使用方法:scan_package ~/dirname
124+
function scan_package() {
125+
local root_dir package_name cur_dir parent_dir;
126+
cd $1;
127+
cur_dir=`pwd`;
128+
root_dir=$2;
129+
if [ x"$root_dir" == x ];then
130+
root_dir="$cur_dir";
131+
fi
132+
133+
# 要的就是这个相对路径
134+
package_name=${cur_dir//$root_dir/}
135+
if [ x"$package_name" != x ];then
136+
package_name="$package_name/"
137+
fi
138+
139+
# 遍历所有子目录
140+
for dir in `ls $cur_dir` ; do
141+
# 如果是目录,则说明是一个package,对当前package下的内容进行统计
142+
if [ -d $dir ] && [ "$dir" != "android" ]; then
143+
flag=0;
144+
# 过滤重复的
145+
for pkg_item in $pkg_list ;do
146+
if [ "$pkg_item" == "$package_name$the_dir" ];then
147+
flag=1;
148+
break;
149+
fi
150+
done
151+
152+
# 包名累加
153+
if [ $flag == 0 ];then
154+
the_dir=`echo $package_name$dir | sed -e 's/\(^\/\)//'`
155+
# 得到package完整路径
156+
pkg_list="$pkg_list $the_dir";
157+
fi
158+
159+
cd $dir;
160+
# 递归遍历下一级子目录
161+
scan_package $cur_dir/$dir $root_dir;
162+
cd ..;
163+
fi
164+
done
165+
}
166+
167+
168+
# 没有输入package list的情况下,就深度遍历apk中的所有包
169+
if [ "$pkg_list" == "" ] ;then
170+
scan_package $classes_dir_path
171+
else
172+
# 如果是指定了package list,则判断是否需要进行深度遍历
173+
if [ $deep_scan == 1 ];then
174+
# 先把package list缓存起来,对这个list中的每一个package进行深度遍历
175+
tmp_pkg_list=$pkg_list
176+
pkg_list=""
177+
for pkg_item in $tmp_pkg_list ;do
178+
target_pkg_path="${pkg_item//.//}"
179+
pkg_list="$pkg_list $target_pkg_path"
180+
scan_package $classes_dir_path/$target_pkg_path $classes_dir_path/
181+
done
182+
fi
183+
fi
184+
185+
# 进入package list目录,按照package维度进行统计
186+
cd $classes_dir_path
187+
for pkg_item in $pkg_list ;do
188+
cal_package $pkg_item "$the_type"
189+
done
190+
191+
# 删除临时目录,结束
192+
cd ../../ && rm -rf apk_temp
193+
echo -e "\n删除临时目录成功,统计完成!"

baksmali-2.0.3.jar

999 KB
Binary file not shown.

smali-2.0.3.jar

847 KB
Binary file not shown.

0 commit comments

Comments
 (0)