使用python脚本自动对apk进行签名
直接执行sign_apk函数,输入参数就可以运行。
if __name__ == "__main__":
path1 = "D:\\apk\\demo.apk"
name = os.path.basename(path1)
sign_dirs = "D:\\sign_apk""
sign_path = os.path.join(sign_dirs, name)
sign_apk(path1, sign_path)
1.sign_apk函数
若apk中已经带有签名,则使用jarsigner对apk签名会签名失败
所以先判断该apk中是否带有签名信息,若有签名,需要删除,若无签名,可以直接签名。
参数path:指apk的全路径,比如D:\apk\demo.apk
参数sign_path:指重新签名后的apk的全路径,比如D:\sign_apk\demo.apk
参数back_apk_path:值将原apk拷贝到指定的目录,比如D:\back_apk\
import os
import subprocess
import shutil
import zipfile
# 对android apk签名
def sign_apk(path, sign_path):
name = os.path.basename(path)
back_apk_path = "D:\\back_apk\\"
pro_app_path = os.path.join(back_apk_path, name)
# 将原程序拷贝到其他指定路径下,防止和原程序有冲突
shutil.copyfile(path, pro_app_path)
# 参考meta_inf_check函数的代码
value = meta_inf_check(pro_app_path)
if value is True:
print("程序带有签名,需要删除清单,才能重新签名")
ext = os.path.splitext(name)
if ext[1] == '.apk':
new_name = ext[0] + '.zip'
new_name_path = os.path.join(back_apk_path, new_name)
if not os.path.exists(new_name_path):
shutil.copyfile(pro_app_path, new_name_path)
os.remove(pro_app_path)
# 参考change_zip_apk函数的代码
new_apk = change_zip_apk(new_name_path)
# 参考jarsigner_cmd函数的代码
jarsigner_cmd(new_apk, sign_path)
else:
new_apk = change_zip_apk(new_name_path)
jarsigner_cmd(new_apk, sign_path)
else:
print("程序没有清单未签名,对其进行签名")
jarsigner_cmd(pro_app_path, sign_path)
2.meta_inf_check函数
该函数为判断应用是否签名,若有签名,返回True,若无签名返回False。
# 判断应用是否签名,参数file_path指apk的全路径
def meta_inf_check(file_path):
"""
apkFile = zipfile.ZipFile(file_path, 'r')
sign_message = []
for fileName in apkFile.namelist():
if fileName.split("/")[0] == "META-INF":
sign_message.append(fileName.split("/")[1])
"""
# 判断apk是否有签名信息
shell = "jarsigner -verify " + file_path
p = subprocess.Popen(shell, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
std_out = p.stdout.readlines()
for outs in std_out:
out = outs.decode('gbk').strip()
if out.find('jar 已验证') == 0:
return True
if out.find('没有清单') == 0:
return False
3.change_zip_apk函数
该函数是将删除签名文件后将文件夹压缩并改成apk,其中删除签名文件的代码参考unzip_apk函数的代码
思路:获取得到删除签名文件的文件夹路径,并将其压缩成zip文件,并将.zip文件改为.apk文件,最后返回apk的全路径(此时apk里没有签名文件了)
参数path:指将.apk后缀名修改为的.zip文件路径
def change_zip_apk(path):
# 参考unzip_apk函数的代码
new_extra_name = unzip_apk(path)
ext_name = os.path.basename(new_extra_name) + '.zip'
ext_dir = os.path.dirname(new_extra_name)
# 获取压缩后zip的全路径
ext_path = os.path.join(ext_dir, ext_name)
# 将文件夹压缩为zip
z = zipfile.ZipFile(ext_path, 'w', zipfile.ZIP_DEFLATED)
for dir_path, dir_names, file_names in os.walk(new_extra_name):
f_path = dir_path.replace(new_extra_name, '')
# 实现当前文件夹以及包含的所有文件的压缩
f_path = f_path and f_path + os.sep or ''
for filename in file_names:
z.write(os.path.join(dir_path, filename), f_path + filename)
z.close()
# 压缩成zip成功后删除文件夹
shutil.rmtree(new_extra_name)
suffix = os.path.splitext(ext_path)
if suffix[1] == '.zip':
new_suffix = suffix[0] + '.apk'
print("new_suffix", new_suffix)
shutil.copyfile(ext_path, new_suffix)
# 拷贝完成后删除zip文件
os.remove(ext_path)
return new_suffix
4.unzip_apk函数
该函数是解压apk并删除三个签名文件。
参数path:指将.apk后缀名修改为的.zip文件路径
思路:先将zip文件解压, 文件不存在,则创建和apk一样的文件名,在进行解压,对文件夹里的签名文件删除后,返回文件夹的全路径。
def unzip_apk(path):
z = zipfile.ZipFile(path, 'r')
extra_name = path.replace('.zip', '')
if not os.path.exists(extra_name):
os.makedirs(extra_name)
z.extractall(extra_name)
z.close()
else:
shutil.rmtree(extra_name)
os.makedirs(extra_name)
z.extractall(extra_name)
z.close()
os.remove(path)
# 删除三个签名文件
for root, dirs, files in os.walk(extra_name):
for file in files:
file_path = os.path.join(root, file)
ext = os.path.splitext(file_path)
if ext[1] == '.RSA':
os.remove(file_path)
elif ext[1] == '.SF':
os.remove(file_path)
elif ext[1] == '.MF':
os.remove(file_path)
else:
continue
return extra_name
5.jarsigner_cmd函数
# 签名的命令
# jarsigner -verbose -keystore <签名文件> -storepass <密码> -keypass <密码> -signedjar <签名后apk> <未签名apk> <别名>
def jarsigner_cmd(path, sign_path):
key_file, alias, password = keystore()
cmd = "jarsigner -verbose -keystore " + key_file
shell = cmd + " -storepass " + password + " -keypass " +password +" -signedjar " + sign_path + " " + path + " " + alias
p = subprocess.Popen(shell, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
print("正在签名:", shell)
p.communicate()
# 签名文件、别名和密码的信息都写入config.ini文件里了,读取就行
def keystore():
cf = configparser.ConfigParser()
cf.read('config.ini', encoding="utf-8")
key_file = cf.get("key", "key_file")
alias = cf.get("key", "alias")
password = cf.get("key", "password")
return key_file, alias, password
获取包名/包活动名,用于使用adb启动安装成功后的应用
1.pa_la_name函数
参数apk_file:指apk的全路径
# 获取包名/包活动名
def pa_la_name(apk_file):
# adb shell monkey -p 包名 -v -v -v 1 获取包名/包活动名
# 参考package_name函数的代码
z2 = package_name(apk_file)
try:
pa_and_la = z2[0][0] + '/' + z2[0][1]
return pa_and_la
except:
print("无数据")
2.package_name函数
参数apk_file:指apk的全路径
# 判断应用中的包名和activity名
def package_name(apk_file):
pa_name_list = []
la_name_list = []
shell = deviceConfig.appt_path() + ' dump badging ' + apk_file
p = subprocess.Popen(shell, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
pa_na = p.stdout.readlines()
for na in pa_na:
lines = na.decode('utf8', 'ignore').strip()
if lines.find('package: name') == 0:
pa_name_list.append(lines.split("'")[1])
if lines.find('launchable-activity: name') == 0 or lines.find('leanback-launchable-activity: name') == 0:
la_name_list.append(lines.split("'")[1])
z = list(zip(pa_name_list, la_name_list))
return z
本文来自投稿,不代表本站立场,如若转载,请注明出处:https://typecho.firshare.cn/archives/1036.html
免责声明:文章内容不代表本站立场,本站不对其内容的真实性、完整性、准确性给予任何担保、暗示和承诺,仅供读者参考,文章版权归原作者所有。避免网络欺诈,本站不倡导任何交易行为。如您私自与本站转载自公开互联网中的资讯内容中提及到的个人或平台产生交易,则需自行承担后果。本站在注明来源的前提下推荐原文至此,仅作为优良公众、公开信息分享阅读,不进行商业发布、发表及从事营利性活动。如本文内容影响到您的合法权益(内容、图片等),请及时联系本站,我们会及时删除处理。