天天看点

Android如何自动校验版本号,渠道号,签名和混淆为什么做什么怎么做后续

为什么

每次发给测试的apk,测试都需要校验AndroidManifest.xml中的版本号,渠道号,apk的签名和代码是否混淆。每次用人力去做太麻烦,我们希望用自动化脚本将这些数据展示到文本中,然后人力核对本文。这对一次打上百个包来说是唯一的可行方案。

做什么

  • 获取apk签名的md5和sha1
  • 收集AndroidManifest.xml中的版本号、版本名、渠道号
  • 获取apk中class文件中被混淆的class的个数
  • 将上述内容用表格显示出来

怎么做

实现环境

操作系统:linux

语言:python

1. 将apk中的指定文件解压

在校验过程中,我们只需要三个文件如下

- 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('/')[]
        wfile = open(filename,'w+b')
        wfile.write(data)
        wfile.close()
           

2. 获取apk的签名信息

获取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(' ')+:].replace('\n','')
    elif 'SHA1' in line:
        sha1=line[line.rindex(' ')+:].replace('\n','')
           

3. 将AndroidManifest.xml由二进制转换成文本,并提取触相应信息

解压出来的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('"')[]
#versionCode
    if 'versionCode' in line:
        code=line[line.index('<')+:line.index(',')]
        #十六进制转十进制
        code=str(int(code,))
#channel
    if 'UMENG_CHANNEL' in line:
        channel = xml[i+].split('"')[]
           

4. 将dex转换成jar,获取其中的class文件名称

dex转jar同样需要用网上的工具dex2jar。转换成jar包之后,就可以像读取zip文件一样读取内部文件的名称了。

os.popen('dex2jar.sh classes.dex')
jfile = zipfile.ZipFile('classes_dex2jar.jar','r')

count=
for filename in jfile.namelist():
    #要过滤包名,不管匿名内部类
    if 'com/daniel/android' in filename and '$' not in filename:
        #获取类名
        className=filename[filename.rindex('/')+:filename.index('.')]
        #如何类名长度小于3,同时都是小写字母,就认为是混淆之后的类
        if len(className)< and className.islower():
            count+=
           

5. 显示出来

赶脚最方便的方式是做成由逗号分割的csv文件,然后用excel显示。将元组转换成由逗号分割的字符串就很简单了。需要注意的是,元组的每个元素都得是字符串

outfile.write(','.join(item)+'\n')
           

后续

还可以和jenkins结合,部署在服务器上。

Android如何自动校验版本号,渠道号,签名和混淆为什么做什么怎么做后续