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删除临时目录成功,统计完成!"
0 commit comments