每次发给测试的apk,测试都需要校验AndroidManifest.xml中的版本号,渠道号,apk的签名和代码是否混淆。每次用人力去做太麻烦,我们希望用自动化脚本将这些数据展示到文本中,然后人力核对本文。这对一次打上百个包来说是唯一的可行方案。
实现环境
操作系统:linux
语言:python
在校验过程中,我们只需要三个文件如下
- AndroidManifest.xml:获取版本名,版本号和渠道号
- META-INF/CERT.RSA:获取签名信息
- classes.dex:获取java类信息
这里不需要将manifest全部解压
fileArray=['AndroidManifest.xml','META-INF/CERT.RSA','classes.dex']
zfile = zipfile.ZipFile(fname,'r')
for filename in zfile.namelist():
if filename in fileArray:
data = zfile.read(filename)
if '/' in filename:
filename=filename.split('/')[1]
wfile = open(filename,'w+b')
wfile.write(data)
wfile.close()
获取apk签名用java自带工具keytool。keytool会将CERT.RSA中的相应信息打印出来,如下所示。
Serial number: 12345678
Valid from: Wed Sep 04 12:21:54 CST 2013 until: Tue Jan 05 12:21:54 CST 3013
Certificate fingerprints:
MD5: 00:00:00:00:DC:FE:D1:F5:A0:FB:21:E1:12:34:56:78
SHA1: 00:00:00:00:1A:FF:7F:16:A8:3F:46:D0:06:EF:95:82:12:34:56:78
Signature algorithm name: SHA256withRSA
Version: 3
这里我们需要从输出的文本中提取触MD5和SHA1
signature = os.popen('keytool -printcert -file CERT.RSA').readlines()
md5=''
sha1=''
for line in signature:
if 'MD5' in line:
md5=line[line.rindex(' ')+1:].replace('\n','')
elif 'SHA1' in line:
sha1=line[line.rindex(' ')+1:].replace('\n','')
解压出来的AndroidManifest.xml是二进制文件,我们需要先用工具将其转换成文本,再提取信息。
以下是axml.sh的代码,用网上提供的axml.jar进行反编译
#!/bin/bash
java -jar /opt/android/reverse/axml.jar $1
反编译之后会出现如下信息:
<manifest
android:versionCode="<0x4F, type 0x10>"
android:versionName="3.5.9"
>
...
<meta-data
android:name="UMENG_CHANNEL"
android:value="default"/>
...
<manifest>
其中versionName可以直接获取字符串,而versionCode是十六进制,所以需要进行转换。另外由于UMENG_CHANNEL的name和value不在一行,所以不能用for line in lineArray的方式,只会用下标的方式取下一行。
name=''
code=''
channel=''
xml = os.popen('axml.sh AndroidManifest.xml').readlines()
for i in range(len(xml)):
line=xml[i]
#versionName
if 'versionName' in line:
name=line.split('"')[1]
#versionCode
if 'versionCode' in line:
code=line[line.index('<')+1:line.index(',')]
#十六进制转十进制
code=str(int(code,16))
#channel
if 'UMENG_CHANNEL' in line:
channel = xml[i+1].split('"')[1]
dex转jar同样需要用网上的工具dex2jar。转换成jar包之后,就可以像读取zip文件一样读取内部文件的名称了。
os.popen('dex2jar.sh classes.dex')
jfile = zipfile.ZipFile('classes_dex2jar.jar','r')
count=0
for filename in jfile.namelist():
#要过滤包名,不管匿名内部类
if 'com/daniel/android' in filename and '$' not in filename:
#获取类名
className=filename[filename.rindex('/')+1:filename.index('.')]
#如何类名长度小于3,同时都是小写字母,就认为是混淆之后的类
if len(className)<3 and className.islower():
count+=1
赶脚最方便的方式是做成由逗号分割的csv文件,然后用excel显示。将元组转换成由逗号分割的字符串就很简单了。需要注意的是,元组的每个元素都得是字符串
outfile.write(','.join(item)+'\n')