python 语言初学

0. 打印 log

print '[+] Apktool decompiled Target.apk successfully!' + string
#print 后直接加单引号字符串

条件语句:
    if not applicationName.startswith(packageName) and applicationName.startswith('.'):  #语句格式
        print '[+] Target app\'s Application: ', applicationName
    else:
        print '[+] Target app\'s Application: ', applicationName

1. 获取当前工作路径

retvall = os.getcwd()   #查看修改后的工作目录
print 'current work dir :' + retvall

2. 删除当前工作路径下的文件、文件夹

import shutil       #导入这个库

remove_without_exception('Target', 'd')     #调用

def remove_without_exception(item, type):   #删除方法
    if type == 'd':
        try:
            shutil.rmtree(item)
        except Exception as e:
            pass
    else:
        try:
            os.remove(item)
        except Exception as e:
            pass

3. 在当前工作路径下执行命令

import subprocess   #导入库

subprocess.Popen(["gradlew","clean"], shell=True, stdout=subprocess.PIPE).stdout.read()
# 这句就是在 CMD 下执行 $:gradlew clean 命令

out = subprocess.Popen(["gradlew","build"], shell=True, stdout=subprocess.PIPE).stdout.read()
out.find('BUILD SUCCESSFUL')  #判断返回值是否包含标识字符串
#这个返回值是执行命令后的返回参数

4. 改变工作路径

os.chdir('TuokeApk/')   #改变工作路径到指定的路径
#这句意思就是进入 TuokeApk 文件夹,命令等同 cd TuokeApk

os.chdir('../')  #意思是返回上一级
#命令等同: cd ..

5. 拷贝文件为目标文件

shutil.copyfile(input_filename, 'Target.apk')  #拷贝 input_filename 到 Target.apk 文件
#就是将传入的文件、复制为 Target.apk 文件
shutil.copyfile(extracted_dir + '/classes.dex', 'classes.dex')  #跨目录拷贝

6. 解析 xml 文件,并获取对应元素的值

from  xml.dom import  minidom       #导入库
import codecs   #导入

doc = minidom.parse('Target/AndroidManifest.xml')   #解析文件
root = doc.documentElement      #获取文件
application_node = root.getElementsByTagName('application')[0]      #获取对应的参数条目
applicationName = application_node.getAttribute('android:name')     #根据条目访问对应的属性、参数
packageName = root.getAttribute('package')  #获取 package 条目

application_node.setAttribute('android:name', 'org.hackcode.ProxyApplication')  #往条目中写入属性值

file_handle = codecs.open('Target/AndroidManifest.xml','w', 'utf-8')    #codecs 新建,打开 xml
root.writexml(file_handle)      #重写 xml
file_handle.close()     #这里是保存完毕了

7. 根据字符串路径打开文件

import re   #导入模块

file_path = '/hackcode/ProxyApplication.java'
new_file_path = '/hackcode/ProxyApplication2.java'
file_in = open(file_path)           #打开文件
file_out = open(new_file_path, 'w') #新建文件并赋予可写权限

while 1:
    line = file_in.readline()  #逐行读取
    if not line:
        break
    pattern = re.compile(r'.*String.*appClassName.*=.*\".*\";.*')       #正则匹配字符串
    if re.search(pattern, line):        #找到了要更改的行
        print '[+] Find \"String appClassName = ...\", replace it with \"' + application_name + '\"'
        file_out.write('\t\t\tString appClassName = \"' + application_name + '\";\n')   #重写这一行
    else:
        file_out.write(line)        #是从 in 逐行写到 out 内
file_in.close()
file_out.close()
os.remove(file_path)
os.rename(new_file_path, file_path)     #更改文件名字,完成字符串匹配更改

8. zip 解压

import zipfile  #导入

extracted_dir = un_zip('Target.modified.apk')   #调用函数

f = zipfile.ZipFile('TargetApk.zip', 'w', zipfile.ZIP_DEFLATED)     #新建可写 zip 文件
f.write('classes.dex')      #写入 dex 文件
f.close()       #写入完毕

def un_zip(file_name):  
    """unzip zip file"""        #注释
    zip_file = zipfile.ZipFile(file_name)       #获取压缩文件,创建 zipfile 对象
    if os.path.isdir(file_name + "_files"):
        pass  
    else:  
        os.mkdir(file_name + "_files")  
    for names in zip_file.namelist():  
        zip_file.extract(names,file_name + "_files/")       #解压到某个文件夹内,解压全部文件
    zip_file.close() 
    return file_name + "_files"     #返回解压后的文件夹名字
    
8.1 zip 压缩
zip_dir(extracted_dir, 'Target.modified.2.apk') #将 extracted_dir 目录压缩为 Target.modified.2.apk 文件

def zip_dir(dirname,zipfilename):   #压缩为 zipfilename
    filelist = []
    if os.path.isfile(dirname):
        filelist.append(dirname)
    else :
        for root, dirs, files in os.walk(dirname):
            for name in files:
                filelist.append(os.path.join(root, name))

    zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED)
    for tar in filelist:
        arcname = tar[len(dirname):]
        zf.write(tar,arcname)
    zf.close()


9. 拷贝对应 so 文件

if not os.path.exists(extracted_dir + '/lib/'):     #是否存在 lib 文件夹
    os.mkdir(extracted_dir + '/lib/')       #创建
    for item in os.listdir('TuokeApk\app\src\main\libs'):       #遍历 libs 下的文件夹目录
        if not os.path.exists(extracted_dir + '/lib/' + item):
            shutil.copytree('TuokeApk/app/src/main/libs/' + item, extracted_dir + '/lib/' + item)   #拷贝树
        else:
            shutil.copyfile('TuokeApk/app/src/main/libs/' + item + '/libhackcodejiagu.so', extracted_dir + '/lib/' + item + '/libhackcodejiagu.so')
else:
    for item in os.listdir(extracted_dir + '/lib/'):
        print '[+] item ...' + item
        shutil.copyfile('TuokeApk/app/src/main/libs/' + item + '/libhackcodejiagu.so', extracted_dir + '/lib/' + item + '/libhackcodejiagu.so')
.
#!/usr/bin/env python
#coding:utf-8
import subprocess
import shutil
from  xml.dom import  minidom
import zipfile 
import os
import re
import glob
import sys
import codecs
import random
import string
import time

from elf_header import ELF


'''
一、针对目标app不存在自定义application的情况
    1.反编译目标app
    apktool.bat d Target.apk
    2.检测manifest文件是否有自定义的Application,并假设没有自定义Application
    3.如果没有自定义Application,则复制smali文件夹,跟反编译后的app下的smali合并: cp -rf smali Target/
    4.修改manifest文件,将自定义Application设定为“org.hackcode.ProxyApplication”
    5.重打包目标app
    6.提取重打包后的apk中的classes.dex文件,并压缩为TargetApk.zip文件,并将重打包的app命名为Target.modified.apk
    7.合并TuokeApk项目下的classes.dex和TargetApk.zip(加固),生成classes.dex
    8.将合并生成的新classes.dex文件与Target.modified.apk中的classes.dex替换
    9.复制TuokeApk项目下的lib目录下的所有文件和文件夹到目标app中
    10.将修改后的app重压缩成zip文件
    11.签名
'''

def un_zip(file_name):  
    """unzip zip file"""
    zip_file = zipfile.ZipFile(file_name)  
    if os.path.isdir(file_name + "_files"):
        pass  
    else:  
        os.mkdir(file_name + "_files")  
    for names in zip_file.namelist():  
        zip_file.extract(names,file_name + "_files/")  
    zip_file.close() 
    return file_name + "_files"

def zip_dir(dirname,zipfilename):
    filelist = []
    if os.path.isfile(dirname):
        filelist.append(dirname)
    else :
        for root, dirs, files in os.walk(dirname):
            for name in files:
                filelist.append(os.path.join(root, name))

    zf = zipfile.ZipFile(zipfilename, "w", zipfile.zlib.DEFLATED)
    for tar in filelist:
        arcname = tar[len(dirname):]
        zf.write(tar,arcname)
    zf.close()

def recompile_TuokeApk_Project(application_name):
    '''
    1.修改 String appClassName = "com.targetapk.MyApplication";
    2.重新编译
    '''
    file_path = 'TuokeApk/app/src/main/java/org/hackcode/ProxyApplication.java'
    new_file_path = 'TuokeApk/app/src/main/java/org/hackcode/ProxyApplication2.java'
    file_in = open(file_path)
    file_out = open(new_file_path, 'w')
    print 'rename ApplicationName start'
    while 1:
        line = file_in.readline()
        if not line:
            break
        pattern = re.compile(r'.*String.*appClassName.*=.*\".*\";.*')
        if re.search(pattern, line):
            print '[+] Find \"String appClassName = ...\", replace it with \"' + application_name + '\"'
            file_out.write('\t\t\tString appClassName = \"' + application_name + '\";\n')
        else:
            file_out.write(line)
    file_in.close()
    file_out.close()

    os.remove(file_path)
    os.rename(new_file_path, file_path)

    # 重新编译TuokeApk工程
    os.chdir('TuokeApk/')
    
    subprocess.Popen(["gradlew","clean"], shell=True, stdout=subprocess.PIPE).stdout.read()
    out = subprocess.Popen(["gradlew","build"], shell=True, stdout=subprocess.PIPE).stdout.read()
    if out.find('BUILD SUCCESSFUL') < 0:
        print 'Build error!'
        return False
    print '[+] Rebuild TuokeApk project successfully!'
    os.chdir('../')
    return True

def remove_without_exception(item, type):
    if type == 'd':
        try:
            shutil.rmtree(item)
        except Exception as e:
            pass
    else:
        try:
            os.remove(item)
        except Exception as e:
            pass

def clean():
    print 'clean start'
    remove_without_exception('Target', 'd')
    remove_without_exception('Target.modified.apk_files', 'd')
    remove_without_exception('Target.apk', 'f')
    remove_without_exception('Target.modified.apk', 'f')
    remove_without_exception('Target.modified.2.apk', 'f')
    remove_without_exception('classes.dex', 'f')
    remove_without_exception('TargetApk.zip', 'f')
    remove_without_exception('tuoke.dex', 'f')

    
    os.chdir('TuokeApk/')   #改变工作路径到指定的路径
    retval = os.getcwd()    #查看修改后的工作目录
    subprocess.Popen(["gradlew","clean"], shell=True, stdout=subprocess.PIPE).stdout.read()
    os.chdir('../')

def genRandomStr(length):
    chars=string.ascii_letters+string.digits
    return ''.join([random.choice(chars) for i in range(length)])#得出的结果中字符会有重复的

def modify_ehdr_and_delete_shdr(apk_dir):
    '''
    修改ELF header(e_shoff和e_shnum属性)和删除section header table
    TODO: 指定目标so文件
    '''
    for root, dirs, files in os.walk(apk_dir):
        for name in files:
            filepath = root + os.path.sep + name
            if filepath.endswith('libhackcodejiagu.so'):
                print '    - Modifying \"', filepath, '\" ELF header...'
                dex = ELF(filepath)
                file_size = os.path.getsize(filepath)
                shdr_offset = dex.elf32_Ehdr.e_shoff
                shdr_size = dex.elf32_Ehdr.e_shnum * dex.elf32_Ehdr.e_shentsize

                src_file = file(filepath, 'rb')
                dst_file = file(filepath + '2', 'wb')
                # 1.破坏ELF Header
                dst_file.write(src_file.read(32)) # 保存e_shoff之前的内容
                src_file.read(4)
                dst_file.write(genRandomStr(4)) # 修复e_shoff

                dst_file.write(src_file.read(12)) # 保存e_shoff到e_shnum之间的内容
                src_file.read(2)
                dst_file.write(genRandomStr(2)) # 修复e_shnum

                # 2.删除section header table
                #读取section header table之前的内容
                dst_file.write(src_file.read(shdr_offset - 50))

                #读取section header table之后的内容
                src_file.seek(shdr_offset + shdr_size, 0)
                dst_file.write(src_file.read())

                src_file.close()
                dst_file.close()

                shutil.move(filepath + '2', filepath)

def main(filepath = None):
    clean()
    if filepath:
        input_filename = filepath
    else:
        input_filename = sys.argv[1]
    print 'copy apk :' + input_filename
    shutil.copyfile(input_filename, 'Target.apk')

    retvall = os.getcwd()   #查看修改后的工作目录
    print 'current work dir :' + retvall
    # Step1: 反编译目标app
    out = subprocess.Popen('apktool.bat d Target.apk', stdout=subprocess.PIPE).stdout.read()

    if out.find('error') > 0 or out.find('exception') > 0:
        print '[Error] apktool decompiled error!'
        return
    print '[+] Apktool decompiled Target.apk successfully!'


    # Step2: 检测manifest文件是否有自定义的Application
    doc = minidom.parse('Target/AndroidManifest.xml')
    root = doc.documentElement
    application_node = root.getElementsByTagName('application')[0]
    applicationName = application_node.getAttribute('android:name')

    packageName = root.getAttribute('package')
    if applicationName:
        if not applicationName.startswith(packageName) and applicationName.startswith('.'):
            applicationName = packageName + applicationName
        print '[+] Target app\'s Application: ', applicationName
        # Step3: 修改JiguApk工程中ProxyApplication中的applicationName变量为目标app的Application名称
        recompile_TuokeApk_Project(applicationName)
    else:
        print '[+] Target.apk has no self-defined Application!'
        applicationName = 'com.targetapk.MyApplication'
        recompile_TuokeApk_Project(applicationName)
        # Step3: 复制smali文件夹,跟反编译后的app下的smali合并
        print '[+] Copy smali folder into Target folder...'
        out = subprocess.Popen('cp -rf smali Target/', stdout=subprocess.PIPE).stdout.read()

    # Step4: 修改manifest文件,将自定义Application设定为“org.hackcode.ProxyApplication”
    print '[+] Modified AndroidManifest.xml...'
    application_node.setAttribute('android:name', 'org.hackcode.ProxyApplication')
    file_handle = codecs.open('Target/AndroidManifest.xml','w', 'utf-8')
    root.writexml(file_handle)
    file_handle.close()

    # Step5: 重打包目标app
    out = subprocess.Popen('apktool.bat b Target', stdout=subprocess.PIPE).stdout.read()
    if out.find('error') > 0 or out.find('exception') > 0:
        print '[Error] apktool recompiled error!'
        return
    print '[+] Apktool recompiled Target successfully!'

    # Step6: 将重打包的app命名为Target.modified.apk,并提取重打包后的apk中的classes.dex文件,并压缩为TargetApk.zip文件
    print '[+] Rename target app: \"Target.modified.apk\"'
    shutil.copyfile('Target/dist/Target.apk', 'Target.modified.apk')
    extracted_dir = un_zip('Target.modified.apk')

    print '[+] Extracted classes.dex from Target.modifed.apk into TargetApk.zip'
    shutil.copyfile(extracted_dir + '/classes.dex', 'classes.dex')
    print '>>> copyfile :' + extracted_dir
    
    if os.path.exists(extracted_dir + '/classes2.dex'):     #过滤 multiDEX 型的 app
        shutil.copyfile('Target.modified.apk', 'TargetApk.zip')
    else:
        #写入classes.dex
        f = zipfile.ZipFile('TargetApk.zip', 'w', zipfile.ZIP_DEFLATED)
        f.write('classes.dex')
        f.close()
    os.remove('classes.dex')
    
    # Step7: 合并TuokeApk/bin/classes.dex和TargetApk.zip(加固),生成classes.dex
    shutil.copyfile('TuokeApk/app/build/intermediates/transforms/dex/release/0/classes.dex', 'tuoke.dex')
    subprocess.Popen('java -jar dexjoint.jar tuoke.dex TargetApk.zip', stdout=subprocess.PIPE).stdout.read()

    # Step8: 将合并生成的新classes.dex文件与Target.modified.apk中的classes.dex 替换
    print '[+] Replace \"%s\" with \"classes_joint.dex\"' % (extracted_dir + '/classes.dex', )
    shutil.copyfile('classes_joint.dex', extracted_dir + '/classes.dex')

    # Step9: 复制TuokeApk/libs目录下的所有文件和文件夹到目标app中
    print '[+] Copying TuokeApk\app\src\main\libs...'
    if not os.path.exists(extracted_dir + '/lib/'):
        os.mkdir(extracted_dir + '/lib/')
        for item in os.listdir('TuokeApk\app\src\main\libs'):
            if not os.path.exists(extracted_dir + '/lib/' + item):
                shutil.copytree('TuokeApk/app/src/main/libs/' + item, extracted_dir + '/lib/' + item)
            else:
                shutil.copyfile('TuokeApk/app/src/main/libs/' + item + '/libhackcodejiagu.so', extracted_dir + '/lib/' + item + '/libhackcodejiagu.so')
    else:
        for item in os.listdir(extracted_dir + '/lib/'):
            print '[+] item ...' + item
            shutil.copyfile('TuokeApk/app/src/main/libs/' + item + '/libhackcodejiagu.so', extracted_dir + '/lib/' + item + '/libhackcodejiagu.so')

    # 破坏SO文件的ELF头部(删除 ELF header)
    modify_ehdr_and_delete_shdr(extracted_dir)

    # Step10: 将修改后的app重压缩成zip文件
    print '[+] Compress %s folder into Target.modified.2.apk' % extracted_dir
    zip_dir(extracted_dir, 'Target.modified.2.apk')

    # Step11: 签名
    print '[+] Signning...'
    output_filename = input_filename[:input_filename.rfind('apk')] + 'signed.apk'
    retvall = os.getcwd()   #查看修改后的工作目录
    print '[+] Signning file name : ' + output_filename + ', work : ' + retvall
    out = subprocess.Popen('java -jar sign/signapk.jar sign/testkey.x509.pem sign/testkey.pk8 Target.modified.2.apk ' + output_filename, stdout=subprocess.PIPE).stdout.read()
    #clean()

if __name__ == '__main__':
    start = time.time()
    main()
    end = time.time()
    print "Total time running %s seconds" %(str(end - start))

这段代码的意思:一键加固 APK 脚本

你可能感兴趣的:(python 语言初学)