Beispiel #1
0
class TestAPK(unittest.TestCase):
    def setUp(self):
        file_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
        self.apk = APK(file_path)

    def test_get_manifest(self):
        self.assertIn('com.example', self.apk.get_manifest().get('@package'))

    def test_get_strings(self):
        self.assertEqual(len(self.apk.get_strings()), 8594)

    def test_get_files(self):
        for item in self.apk.get_files():
            if item.get('crc') == 'FA974826':
                self.assertIn('AndroidManifest.xml', item.get('name'))
                break

    def test_get_opcodes(self):
        for item in self.apk.get_opcodes():
            class_name = item.get('class_name')
            method_name = item.get('method_name')
            opcodes = item.get('opcodes')

            if class_name == 'com/example/hellojni/MainActivity' and method_name == 'onCreate':
                self.assertEqual(opcodes, '6F156E0E')
                break
Beispiel #2
0
    def __init__(self, file_path):
        Cmd.__init__(self)
        self.prompt = 'saam > '

        self.shortcuts.remove(('@', 'load'))
        self.shortcuts.append(('@', 'adb_cmd'))
        self.shortcuts.remove(('@@', '_relative_load'))
        self.shortcuts.append(('$', 'adb_shell_cmd'))
        self.shortcuts.append(('qc', 'quit_and_clean'))

        self.apk_path = file_path
        self.apk = APK(self.apk_path)
        self.apk_out = os.path.basename(file_path) + ".out"
        self.smali_files = None
        self.smali_dir = None
        self.adb = None
        self.smali_method_descs = []
        self.java_files = []

        vsmali_action = CmdLineApp.vsmali_parser.add_argument(
            'sfile', help='smali file path')
        setattr(vsmali_action, argparse_completer.ACTION_ARG_CHOICES,
                ('delimiter_complete', {
                    'delimiter': '/',
                    'match_against': self.smali_method_descs
                }))

        vjava_action = CmdLineApp.vjava_parser.add_argument(
            'jfile', help='java file path')
        setattr(vjava_action, argparse_completer.ACTION_ARG_CHOICES,
                ('delimiter_complete', {
                    'delimiter': '/',
                    'match_against': self.java_files
                }))
Beispiel #3
0
def parse_apkfile(file):
    '''
    Args:
        - file: filename or file object
    Returns:
        Manifest(Class)
    '''
    apk = APK(file)
    return Manifest(apk.get_org_manifest())
Beispiel #4
0
 def test_kotlin_app(self):
     file_path = os.path.abspath(
         os.path.join(os.path.dirname(__file__), "..", 'data',
                      'kotlin-app.zip'))
     apk = APK(file_path)
     self.assertEqual([(
         'CN=Android Debug, O=Android, C=US',
         '299D8DE477962C781714EAAB76A90C287BB67123CD2909DE0F743838CAD264E4')
                       ], apk.get_certs('sha256'))
Beispiel #5
0
def retireve_info(file_in_check):

    proc = subprocess.Popen("{} d badging '{}'".format(AAPT, file_in_check), shell=True, stdin=subprocess.PIPE,
                            stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    r = (proc.communicate()[0]).decode()

    items = re.compile("package: name='(.*?)' versionCode='(.*?)' versionName='(.*?)'").findall(r)
    try:
        pkg, vercode, vername = items[0]
    except:
        pkg = vercode = vername = ''

    try:
        appname = re.compile("application-label:'(.*?)'").findall(r)[0]
    except:
        appname = ''

    try:
        dn,cert = APK(file_in_check).get_certs('sha1')[0]
    except:
        dn, cert = '', ''

    cert = cert.lower()

    #info = [os.path.basename(apk), sha1, pkg, vername, appname, size]
    # log_writer(sha1, pkg, cert, dn, vername, appname)

    # db_data = {"sha1": None, "pkg": None, "cert": None, "dn": None, "vername": None, "appname": None}
    db_data = {"sha1": getSHA1(file_in_check), "pkg": pkg, "cert": cert, "dn": dn, "vername": vername, "appname": appname}
    globalDB.insert_one("apk_info", db_data)
Beispiel #6
0
    def __init__(self, file_path):
        Cmd.__init__(self)
        self.prompt = 'saam > '

        self.shortcuts.remove(('@', 'load'))
        self.shortcuts.append(('@', 'adb_cmd'))
        self.shortcuts.remove(('@@', '_relative_load'))
        self.shortcuts.append(('$', 'adb_shell_cmd'))
        self.shortcuts.append(('qc', 'quit_and_clean'))

        self.apk_path = file_path
        self.apk = APK(self.apk_path)
        self.apk_out = os.path.basename(file_path) + ".out"
        self.smali_files = None
        self.smali_dir = None
        self.adb = None
Beispiel #7
0
class TestAPK(unittest.TestCase):
    def setUp(self):
        file_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", 'data', 'i15.zip'))
        self.apk = APK(file_path)

    def test_get_strings_refx(self):
        result = self.apk.get_strings_refx()
        self.assertEqual(len(result), 1477)
Beispiel #8
0
def parse_apkfile(file: str) -> Manifest:
    '''
    Args:
        - file: filename

    Returns:
        Manifest(Class)
    '''
    return Manifest(APK(file))
Beispiel #9
0
def main(args):
    apk = APK(args.p)

    if args.m:
        import json
        print(apk.get_mini_mani())
        if apk.get_manifest():
            print(json.dumps(apk.get_manifest(), indent=1))
        elif apk.get_org_manifest():
            print(apk.get_org_manifest())

    elif args.s:
        for item in apk.get_strings():
            print(binascii.unhexlify(item).decode(errors='ignore'))

    elif args.f:
        for item in apk.get_files():
            print(item)

    elif args.c:
        for item in apk.get_certs():
            print(item[0], item[1])
Beispiel #10
0
    def test_youtube(self):
        file_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", 'data',
                         'youtube.zip'))
        apk = APK(file_path)

        # Check that the default (md5) is correct
        self.assertEqual([(
            'C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
            'D046FC5D1FC3CD0E57C5444097CD5449')], apk.get_certs())

        # Check that sha1 is correct
        self.assertEqual([(
            'C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
            '24BB24C05E47E0AEFA68A58A766179D9B613A600')],
                         apk.get_certs('sha1'))

        # Check that sha256 is correct
        self.assertEqual([(
            'C=US, ST=CA, L=Mountain View, O=Google, Inc, OU=Google, Inc, CN=Unknown',
            '3D7A1223019AA39D9EA0E3436AB7C0896BFB4FB679F4DE5FE7C23F326C8F994A')
                          ], apk.get_certs('sha256'))
class TestAPK(unittest.TestCase):

    def setUp(self):
        file_path = os.path.abspath(os.path.join(
            os.path.dirname(__file__), "..", 'data', 'test'))
        self.apk = APK(file_path)

    def test_get_strings_refx(self):
        result = self.apk.get_strings_refx()
        for clsname in result:
            for mtdname in result[clsname]:
                if b'hellojni' in result[clsname][mtdname]:
                    print(clsname, mtdname, result[clsname][mtdname])
Beispiel #12
0
class TestAPK(unittest.TestCase):
    def setUp(self):
        file_path = os.path.abspath(
            os.path.join(os.path.dirname(__file__), "..", 'data',
                         'test_zip_fake_pwd'))
        self.apk = APK(file_path)

    def test_get_manifest(self):
        self.apk.get_manifest()

    def test_get_strings(self):
        self.apk.get_strings()

    def test_get_files(self):
        self.apk.get_files()

    def test_get_opcodes(self):
        self.apk.get_opcodes()
Beispiel #13
0
def scan_apk(apk_path, rules, timeout):
    td = None
    try:
        with zipfile.ZipFile(apk_path, 'r') as zf:
            for name in zf.namelist():
                td = tempfile.mkdtemp()
                zf.extract(name, td)

                file_path = os.path.join(td, name)
                key_path = '{}!{}'.format(apk_path, name)
                match_dict = do_yara(file_path, rules, timeout)
                if len(match_dict) > 0:
                    print_matches(key_path, match_dict)

    except Exception as e:
        print(e)

    from apkutils import APK
    txt = APK(apk_path).get_org_manifest()
    match_dict = scan_manifest(txt, rules, timeout)
    if len(match_dict) > 0:
        key_path = '{}!{}'.format(apk_path, 'AndroidManifest.xml')
        print_matches(key_path, match_dict)
Beispiel #14
0
def main(args):
    if os.path.isfile(args.file):
        if args.T:
            t = Magic(args.file).get_type()
            if t != 'apk':
                return
            trees = APK(args.file).get_trees()
            nodes = trees.get(args.T, [])
            for node in nodes:
                APK.pretty_print(node)
        else:
            return

    if not os.path.isdir(args.file):
        return

    apks = []
    for root, _, files in os.walk(args.file):
        for f in files:
            path = os.path.join(root, f)
            t = Magic(path).get_type()
            if t != 'apk':
                continue
            apks.append(APK(path))

    if not apks:
        return

    ai = APK_Intersection(apks)
    if args.m:
        ai.intersect_manifest()

    if args.s:
        ai.intersect_dex_string()  # TODO 相同的字符串太多了,反编译删除干扰的数据

    if args.t:
        ai.intersect_dex_tree()

    if args.p:
        ai.intersect_apis()

    if args.r:
        ai.intersect_resources()
Beispiel #15
0
 def setUp(self):
     file_path = os.path.abspath(
         os.path.join(os.path.dirname(__file__), "..", 'data', 'i15.zip'))
     self.apk = APK(file_path)
Beispiel #16
0
import os
import os
import unittest
import zipfile
from collections import OrderedDict

import xmltodict
from apkutils.axml.arscparser import ARSCParser

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'youtube.zip'))

# file_path = os.path.abspath(os.path.join(
#     os.path.dirname(__file__), "..", 'data', 'i15.zip'))
apk = APK(file_path)

arsc = apk.get_arsc()

# file_path = os.path.abspath(os.path.join(
#     os.path.dirname(__file__), "..", 'data', 'youtube.zip'))

# with zipfile.ZipFile(file_path, mode="r") as zf:
#     data = zf.read('resources.arsc')
#     arscobj = ARSCParser(data)

# apk = apkutils.APK(file_path)
# arsc = apk.get_asrc()
package = arsc.get_packages_names()[0]
print(package)
Beispiel #17
0
def main(apkPath):

    starttime = datetime.datetime.now()
    """
    :param apkPath: apk文件的最终路径
    :return: NULL
    """

    print("[?]Analyzing {}".format(apkPath))

    if "/" in apkPath:
        savePath = "./result/" + apkPath.split("/")[-1].split(".")[0]
    else:
        savePath = "./result/" + apkPath.split(".")[0]
    try:
        os.mkdir(savePath)
    except Exception as e:
        # print(e)
        pass

    if dex2jar(apkPath, savePath):
        print("[+]Dex2jar success!")

    # print(savePath)
    '''
    先判断这个安装包是否提取过,再开始处理。
    '''
    if len([
            lists for lists in os.listdir(savePath)
            if os.path.isfile(os.path.join(savePath, lists))
    ]) > 5:
        print(
            "[!]The information has been extracted to {}. Please delete it if you need to extract it again"
            .format(savePath))
    else:
        apk = APK(apkPath)

        #获取AndroidManifest.xml的字符串信息
        manifestInfo = getManifestInfo(apk)
        xml = open("{}/AndroidManifest.xml".format(savePath), "w")
        xml.write(manifestInfo)
        xml.close()

        #签名信息
        sign = getSignInfo(apk)

        print("[?]Extracting all strings in apk")

        stringList = getStrings(apk)

        try:
            urlList = []
            ipList = []
            hashlist = []
            forbidStrList = []
            accessKeyList = []

            for string in stringList:

                # 保存所有的字符串
                base = open("{}/strings.txt".format(savePath), "a")
                try:
                    base.write(str(string) + '\n')
                except Exception as e:
                    # print(e)
                    pass
                base.close()
                '''
                下面开始提取URLs
                '''
                # url = re.findall('https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+', string)

                urlStrList = ["https://", "http://"]  # 提取的特征
                for urlStr in urlStrList:
                    if urlStr in string:
                        # print(string)
                        if string not in urlList:
                            u = open("{}/urls.txt".format(savePath), "a")
                            u.write(str(string) + '\n')
                            u.close()
                            urlList.append(string)
                '''
                判断是否包含IP
                '''
                p = re.compile(
                    '(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)'
                )
                if p.match(string):
                    ip = open("{}/ips.txt".format(savePath), "a")
                    ip.write(str(string) + '\n')
                    ip.close()
                    if string not in ipList:
                        ipList.append(string)
                '''
                下面开始提取可能是base64编码以及hash的值
                '''
                '''
                下面开始匹配32位长度和15长度的hash
                '''
                if len(string) == 32 or len(string) == 16:
                    if re.match(r'^[a-z0-9]{16,32}$', string):
                        if string not in hashlist:
                            hashlist.append(string)
                            hashs = open("{}/hash.txt".format(savePath), "a")
                            hashs.write(str(string) + '\n')
                            hashs.close()
                '''
                下面提取可能存在的敏感字符串
                '''
                # forbidStr = ["accessKey", "database","ssh","rdp","smb","mysql","sqlserver","oracle",
                #              "ftp","mongodb","memcached","postgresql","telnet","smtp","pop3","imap",
                #              "vnc","redis","admin","root","config","jdbc",".properties","aliyuncs",
                #              "oss"]  # 特征字典
                forbidStr = ['accesskey', 'aliyuncs']
                for forbid in forbidStr:
                    if forbid in string:
                        # print(string)
                        if string not in forbidStrList:
                            fb = open("{}/forbidStr.txt".format(savePath), "a")
                            try:
                                fb.write(str(string) + '\n')
                            except Exception as e:
                                print(e)
                                pass
                            fb.close()
                            forbidStrList.append(string)
                '''
                下面开始匹配AccessKey,自己测试发现:        
                AccessKeyId 约为24位
                AccessKeySecret 约为30位
                '''

                if str(string).isalnum():
                    if re.match(
                            r'^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).{24,24}$',
                            string):
                        if string not in accessKeyList:
                            accessKeyList.append(string)
                            ak = open("{}/accessKey.txt".format(savePath), "a")
                            if string.startswith("LTAI"):
                                ak.write(
                                    "=============AccessKeyId================")
                            ak.write("Id:{}\n".format(str(string)))
                            ak.close()

                    if re.match(
                            r'^(?:(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])).{30,30}$',
                            string):
                        if string not in accessKeyList:
                            accessKeyList.append(string)
                            ak = open("{}/accessKey.txt".format(savePath), "a")
                            ak.write("Secret:{}\n".format(str(string)))
                            ak.close()
        except Exception as e:
            #print(e)
            pass

        print("""[*]Found:
        \tString:{}\tURL:{}
        \tIps:{}\tHash:{}\tForbidStr:{}
        \tMaybe it's accessKey:{}""".format(len(stringList), len(urlList),
                                            len(ipList), len(hashlist),
                                            len(forbidStrList),
                                            len(accessKeyList)))

    endtime = datetime.datetime.now()
    print("[+]Use time {}s\n".format((endtime - starttime).seconds))
Beispiel #18
0
 def __init__(self, apk: APK):
     content = apk.get_org_manifest()
     self._dom = minidom.parseString(content)
     self._permissions = None
     self._apk = apk
Beispiel #19
0
class CmdLineApp(Cmd):

    sdcard = '/storage/sdcard0/'
    maps = None

    def __init__(self, file_path):
        Cmd.__init__(self)
        self.prompt = 'saam > '

        self.shortcuts.remove(('@', 'load'))
        self.shortcuts.append(('@', 'adb_cmd'))
        self.shortcuts.remove(('@@', '_relative_load'))
        self.shortcuts.append(('$', 'adb_shell_cmd'))
        self.shortcuts.append(('qc', 'quit_and_clean'))

        self.apk_path = file_path
        self.apk = APK(self.apk_path)
        self.apk_out = os.path.basename(file_path) + ".out"
        self.smali_files = None
        self.smali_dir = None
        self.adb = None

    def do_quit_and_clean(self, arg):
        import shutil
        shutil.rmtree(self.apk_out)

        self._should_quit = True
        return self._STOP_AND_EXIT

    # ------------------- Hardware And System  ---------------------
    def do_devinfos(self, arg):
        '''
        显示设备硬件信息
        '''
        cmd = 'getprop ro.product.brand'
        print('Brand  :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.product.model'
        print('Model  :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.product.device'
        print('Device :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.product.cpu.abi'
        print('CPU    :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop persist.radio.imei'
        print('IMEI   :', self.adb.run_shell_cmd(cmd).decode())

    def do_osinfos(self):
        '''显示设备系统信息'''
        cmd = 'getprop ro.build.description'
        print('Build Desc    :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.build.date'
        print('Build Data    :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.build.version.release'
        print('Build Version :', self.adb.run_shell_cmd(cmd)[:-1].decode())
        cmd = 'getprop ro.build.version.sdk'
        print('SDK Version   :', self.adb.run_shell_cmd(cmd)[:-1].decode())

    # ---------------------- Manifest -------------------------
    @staticmethod
    def serialize_xml(org_xml):
        import xmlformatter
        org_xml = re.sub(r'>[^<]+<', '><', org_xml)

        formatter = xmlformatter.Formatter()
        return formatter.format_string(org_xml).decode('utf-8')

    def do_manifest(self, arg):
        '''显示清单信息'''
        org_xml = self.apk.get_org_manifest()
        if org_xml:
            print(self.serialize_xml(org_xml))

    def get_package(self):
        return self.apk.get_manifest()['@package']

    def get_main_activity(self):
        try:
            activities = self.apk.get_manifest()['application']['activity']
        except KeyError:
            return None

        if not isinstance(activities, list):
            activities = [activities]

        for item in activities:
            content = json.dumps(item)
            if 'android.intent.action.MAIN' in content\
                    and 'android.intent.category.LAUNCHER' in content:
                return item['@android:name']

        return None

    def do_receiver(self, arg):
        try:
            receivers = self.apk.get_manifest()['application']['receiver']
        except KeyError:
            return None
        print(json.dumps(receivers, indent=4, sort_keys=True))

    def do_activity(self, arg):
        try:
            receivers = self.apk.get_manifest()['application']['activity']
        except KeyError:
            return None
        print(json.dumps(receivers, indent=4, sort_keys=True))

    def do_service(self, arg):
        try:
            receivers = self.apk.get_manifest()['application']['service']
        except KeyError:
            return None
        print(json.dumps(receivers, indent=4, sort_keys=True))

    def show_risk_children(self, flag=False):
        result = ''
        pflag = True
        self.apk.get_files().sort(key=lambda k: (k.get('type'), k.get('name')))
        for item in self.apk.get_files():
            if flag:
                print(item.get('type'), item.get('name'))
                continue

            if item.get('type') not in ['dex', 'apk', 'elf']:
                continue

            if pflag:
                result = Color.red('\n===== Risk Files =====\n')
                pflag = False

            result += item.get('type') + ' ' + item.get('name') + '\n'

        return result

    children_parser = argparse.ArgumentParser()
    children_parser.add_argument('-a', '--all', action='store_true')

    @with_argparser(children_parser)
    def do_children(self, args):
        '''
        列出APK中的特殊文件
        '''
        self.apk.get_files().sort(key=lambda k: (k.get('type'), k.get('name')))
        for item in self.apk.get_files():
            if args.all:
                print(item.get('type'), item.get('name'))
                continue

            if item.get('type') in ['dex', 'apk', 'elf']:
                print(item.get('type'), item.get('name'))

    # ------------------- Static Analysis -------------------------
    def do_decompile(self, arg):
        '''
        使用apktool反编译apk, 默认初始化清单相关的包。

        '''
        pkgs = self.find_pkg_refx_manifest(self.apk.get_org_manifest())

        apktool.decode(None, self.apk_out, self.apk_path, True)
        for item in os.listdir(self.apk_out):
            if not item.startswith('smali'):
                continue

            self.smali_dir = SmaliDir(os.path.join(
                self.apk_out, item), include=pkgs)

    @staticmethod
    def find_pkg_refx_manifest(manifest):
        '''
        找出与清单相关的包
        '''
        if not manifest:
            return
        pkgs = set()
        ptn_s = r'android:name="([^\.]*?\.[^\.]*?)\..*?"'
        ptn = re.compile(ptn_s)

        for item in ptn.finditer(manifest):
            pkgs.add(item.groups()[0])

        pkgs.remove("android.intent")
        pkgs.remove("android.permission")

        return pkgs

    init_smali_argparser = argparse.ArgumentParser()
    init_smali_argparser.add_argument(
        '-m', '--manifest', action='store_true', help='根据manifest相关包初始化')
    init_smali_argparser.add_argument(
        '-f', '--filter', action='store_true', help='根据过滤列表初始化')

    @with_argparser(init_smali_argparser)
    def do_init_smali(self, args):
        '''
        初始化smali,默认初始化所有

        '''
        pkgs = None

        if args.manifest:
            pkgs = self.find_pkg_refx_manifest(self.apk.get_org_manifest())

        self.smali_dir = None
        for item in os.listdir(self.apk_out):
            if not item.startswith('smali'):
                continue

            sd = SmaliDir(os.path.join(self.apk_out, item), include=pkgs)
            if self.smali_dir:
                self.smali_dir[len(self.smali_dir):] = sd
            else:
                self.smali_dir = sd
                print('初始化{0}个smali文件'.format(len(self.smali_dir)))

        print('初始化{0}个smali文件'.format(len(self.smali_dir)))

    def do_build(self, arg):
        '''
        使用apktool回编译apk
        '''
        apktool.build(self.apk_out, force=True)

    def __init__smali_dir(self):
        pass

    def show_risk_perm(self):
        result = ''
        if not self.apk.get_manifest():
            return result
        risk_perms = [
            '_SMS',
            '_CALL',
            '_DEVICE_ADMIN',
            '_AUDIO',
            '_CONTACTS'
        ]
        ps = set()
        pflag = True
        for item in self.apk.get_manifest().get('uses-permission', []):
            for perm in risk_perms:
                if perm not in item.get('@android:name'):
                    continue

                if pflag:
                    result += Color.red('===== Risk Permissions =====\n')
                    pflag = False

                name = item.get('@android:name')
                if name in ps:
                    continue
                result += name + '\n'
                ps.add(name)

        app = self.apk.get_manifest().get('application')
        recs = app.get('receiver')

        def process_item(item):
            text = ''
            perm = item.get('@android:permission', '')
            if '_DEVICE' not in perm:
                return text
            if pflag:
                text += Color.red('===== Risk Permissions =====\n')
            text += perm + item.get('@android:name') + '\n'

        if isinstance(recs, dict):
            text = process_item(item)
            if text:
                result += text
                pflag = False
        elif isinstance(recs, list):
            for item in recs:
                text = process_item(item)
                if text:
                    result += text
                    pflag = False
                    break

        if not pflag:
            result += '\n'

        return result

    def show_risk_code(self, level):
        result = Color.red('===== Risk Codes =====\n')
        for k, v in RISKS.items():
            if v['lvl'] < level:
                continue
            kflag = True
            for sf in self.smali_dir:
                for mtd in sf.get_methods():
                    mflag = True
                    lines = re.split(r'\n\s*', mtd.get_body())
                    for idx, line in enumerate(lines):
                        for ptn in v['code']:
                            if re.search(ptn, line):
                                if kflag:
                                    result += Color.magenta('---' +
                                                            k + '---\n')
                                    kflag = False
                                if mflag:
                                    result += Color.green(' ' +
                                                          str(mtd)) + '\n'
                                    mflag = False
                                result += ' ' * 3 + str(idx) + line + '\n'
                                break

        return result

    risk_parser = argparse.ArgumentParser()
    risk_parser.add_argument(
        '-l', '--level', help='指定风险级别,0/1/2/3,0为最低级别,3为最高级别')
    risk_parser.add_argument(
        '-f', '--force', action='store_true', help='强制覆盖已有文件')

    @with_argparser(risk_parser)
    def do_risk(self, args):
        if os.path.exists(self.apk_path + '.risk.txt'):
            if not args.force:
                # TODO read file
                return
        text = ''
        text += self.show_risk_perm()

        level = 3
        if args.level:
            level = int(args.level)

        text += self.show_risk_code(level)
        text += self.show_risk_children()
        # TODO save to file
        self.ppaged(text)
        # so
        # 文本
        # 二进制文件
        # 图片

    def ref(self, desc):
        global notes
        global classes
        global recursion_times
        recursion_times += 1

        if recursion_times > recursion_limit:
            return

        body = None
        for smali_file in self.smali_dir:
            for mtd in smali_file.get_methods():
                if desc in str(mtd):
                    body = mtd.get_body()
                    break
        print(desc)

        if body:
            for smali_file in self.smali_dir:
                for mtd in smali_file.get_methods():
                    if desc in mtd.get_body():
                        if desc.startswith('L'):
                            tmp = desc[: desc.index(';')]
                            classes.add(tmp[: tmp.rindex('/')])
                        tmp = str(mtd)[: str(mtd).index(';')]
                        classes.add(tmp[: tmp.rindex('/')])
                        notes.append([desc, str(mtd)])
                        self.ref(str(mtd))

    def refs(self, desc):
        self.ref(desc)

    def do_xrefs(self, arg):
        pass

    ref_parser = argparse.ArgumentParser()
    ref_parser.add_argument('-l', '--limit', help='递归次数')

    @with_argparser(ref_parser)
    def do_ref(self, args):
        if len(args) != 1:
            return

        if args.limit:
            recursion_limit = args.l

        mtd = args[0]
        self.refs(mtd)

        dot = Digraph()
        if notes:
            dot.edges(notes)
            dot.render('refs.gv', view=True)

    def main_ref(self, arg):
        '''
            生成入口函数调用数,调用层数默认100。
            可以传入一个数值修改递归次数。
        '''
        args = arg.split()
        if len(args) == 1:
            times = int(args[0])
            global recursion_limit
            recursion_limit = times
        else:
            recursion_limit = 100

        dot = Digraph()
        dot.attr('node', style='filled', fillcolor='red')

        # global recursion_times
        # recursion_times = 0
        # if not self.smali_files:
        #     self.smali_files = parse_smali(self.apk_out + os.sep + 'smali')

        main_acitivity = self.get_main_activity()
        main_acitivity = main_acitivity.replace(
            '.', '/') + ';->onCreate'
        self.refs(main_acitivity)
        dot.node(main_acitivity)

        # recs = self.apk.get_receivers()
        # for item in recs:
        #     dot.node(item)
        #     self.refs(item)

        # dot.attr('node', shape='box', style='filled',
        #          fillcolor='white', color='black')

        # if notes:
        #     dot.edges(notes)
        #     dot.render('refs.gv', view=True)

        # for item in sorted(list(classes)):
        #     print(item)

    search_parser = argparse.ArgumentParser()
    search_parser.add_argument('txt', help='默认在Dex中搜索字符串')
    # <public type="string" name="failed_text" id="0x7f050002" />
    search_parser.add_argument(
        '-r', '--res', action='store_true', help='查找资源(id/name/图片名)')
    search_parser.add_argument(
        '-a', '--all', action='store_true', help='在所有文本文件中查找(不包含Dex)')

    @with_argparser(search_parser)
    def do_search(self, args):
        def find_in_the_layout_xml(txt):
            results = []
            layout_path = os.path.join(self.apk_out, 'res', 'layout')
            for root, _, names in os.walk(layout_path):
                for name in names:
                    xml_path = os.path.join(root, name)
                    with open(xml_path, mode='r') as f:
                        lines = f.readlines()
                        for line in lines:
                            if txt in line:
                                results.append(os.path.splitext(name)[0])
                                break
            return results

        def find_in_the_smali_dir(txt):
            for smali_file in self.smali_dir:
                if txt in smali_file.get_content():
                    print(smali_file)

        def find_in_the_txt(content):
            self.apk.get_files().sort(key=lambda k: (k.get('type'), k.get('name')))
            for item in self.apk.get_files():
                if item.get('type') != 'txt':
                    continue
                if 'META-INF' in item.get('name'):
                    continue
                txt_path = os.path.join(self.apk_out, item.get('name'))
                with open(txt_path, mode='r') as f:
                    if 'txt_path' in f.read():
                        print(item.get('name'))

        if args.res:
            public_xml = os.path.join(
                self.apk_out, 'res', 'values', 'public.xml')
            rtype = None
            rname = None
            with open(public_xml, mode='r') as f:
                lines = f.readlines()
                flag = False
                for line in lines:
                    if args.txt in line:
                        match = line.strip()
                        print(match)
                        g = re.search(
                            r'type="(.*?)" name="(.*?)"', match).groups()
                        if g:
                            rtype = g[0]
                            rname = g[1]
                        break
                else:
                    return

            if rtype in {'string', 'layout'}:
                find_in_the_smali_dir(rname)
            elif rtype in {'id', 'drawable'}:
                txts = find_in_the_layout_xml(rname)
                for txt in txts:
                    print(os.path.join('res', 'layout', txt + '.xml'))
                    find_in_the_smali_dir(txt)
                    print()
        elif args.all:
            find_in_the_txt(args.txt)
        else:
            find_in_the_smali_dir(args.txt)

    # ------------------- ADB -------------------------
    adb_parser = argparse.ArgumentParser()
    adb_parser.add_argument(
        '-s', '--serial', help='use device with given serial (overrides $ANDROID_SERIAL)')

    @with_argparser(adb_parser)
    def do_adb_ready(self, args):
        '''
        连接设备/模拟器,准备adb命令。
        '''
        serial = None
        if args.serial:
            serial = args.serial

        self.adb = pyadb3.ADB(device=serial)
        if len(self.adb.get_output().decode()) > 10:
            print('ADB ready.')
        else:
            print("unable to connect to device.")

    def do_adb(self, arg):
        '''
        执行adb命令
        '''
        if not self.adb:
            self.adb = pyadb3.ADB()
        self.adb.run_cmd(arg)
        print(self.adb.get_output().decode('utf-8', errors='ignore'))

    def do_adb_shell(self, arg):
        '''
        执行adb shell命令
        '''
        if not self.adb:
            self.adb = pyadb3.ADB()

        self.adb.run_shell_cmd(arg)
        print(self.adb.get_output().decode('utf-8', errors='ignore'))

    def do_topactivity(self, args):
        if not self.adb:
            self.adb = pyadb3.ADB()

        self.adb.run_shell_cmd(
            "dumpsys activity activities | grep mFocusedActivity")
        print(self.adb.get_output().decode(
            'utf-8', errors='ignore').split()[-2])

    def do_details(self, args):
        if not self.adb:
            self.adb = pyadb3.ADB()
        self.adb.run_shell_cmd("dumpsys package {}".format(self.get_package()))
        print(self.adb.get_output().decode('utf-8', errors='ignore'))

    # ------------------- 备份还原 -------------------------

    # ------------------- 应用管理 -------------------------
    lspkgs_parser = argparse.ArgumentParser()
    lspkgs_parser.add_argument(
        '-f', '--file', action='store_true', help='显示应用关联的 apk 文件')
    lspkgs_parser.add_argument(
        '-d', '--disabled', action='store_true', help='只显示 disabled 的应用')
    lspkgs_parser.add_argument('-e', '--enabled', action='store_true',
                               help='只显示 enabled 的应用')
    lspkgs_parser.add_argument(
        '-s', '--system', action='store_true', help='只显示系统应用')
    lspkgs_parser.add_argument(
        '-3', '--three', action='store_true', help='只显示第三方应用')
    lspkgs_parser.add_argument(
        '-i', '--installer', action='store_true', help='显示应用的 installer')
    lspkgs_parser.add_argument(
        '-u', '--uninstall', action='store_true', help='包含已卸载应用')
    lspkgs_parser.add_argument(
        'filter', type=str, nargs="?", help='包名包含 < FILTER > 字符串')

    @with_argparser(lspkgs_parser)
    def do_lspkgs(self, args):
        '''
        查看应用列表,默认所有应用
        '''
        cmd = 'pm list packages'
        if args.file:
            cmd += ' -f'

        if args.disabled:
            cmd += ' -d'
        elif args.enabled:
            cmd += ' -e'

        if args.system:
            cmd += ' -s'
        elif args.three:
            cmd += ' -3'

        if args.installer:
            cmd += ' -i'
        elif args.uninstall:
            cmd += ' -u'

        if args.filter:
            cmd += ' ' + args.filter

        self.adb.run_shell_cmd(cmd)
        print(self.adb.get_output().decode())

    def do_install(self, arg):
        '''
        安装应用到手机或模拟器
        '''
        self.adb.run_cmd('install -r -f %s' % self.apk_path)
        output = self.adb.get_output().decode().split()

        if output[-2] == 'Failure':
            print(output[-1])
        else:
            # TODO if the sdcard path doesn't exist.
            cmd = 'touch %s.now' % self.sdcard
            self.adb.run_shell_cmd(cmd)

    def do_uninstall(self, arg):
        '''
        卸载应用
        '''
        self.adb.run_cmd('uninstall %s' % self.get_package())
        self.clearsd()

    def clearsd(self):
        ''' pull the newer files from sdcard.
        '''
        cmd = 'find %s -path "%slost+found" -prune -o -type d -print -newer %s.now -delete' % (
            self.sdcard, self.sdcard, self.sdcard)
        self.adb.run_shell_cmd(cmd)

    startapp_parser = argparse.ArgumentParser()
    startapp_parser.add_argument('-d', '--debug', action='store_true')

    def do_strace(self, args):
        '''
        使用 strace 跟踪应用
        setenforce 0  # In Android 4.3 and later, if SELinux is enabled, strace will fail with "strace: wait: Permission denied"

        有两种方式:
        1. 使用调试的方式启动应用,strace -p $pid
        2. trace -p $zygote_pid,启动应用
        '''
        # cmd = "strace -f -p `ps | grep zygote | awk '{print $2}'`"
        print(self.sdcard)
        cmd = "set `ps | grep zygote`; strace -p $2 -f -tt -T -s 500 -o {}strace.txt".format(
            self.sdcard)
        self.adb.run_shell_cmd(cmd)

    @with_argparser(startapp_parser)
    def do_startapp(self, args):
        '''启动应用'''
        main_acitivity = self.get_main_activity()
        if not main_acitivity:
            print("It does not have main activity.")
            return

        cmd = 'am start -n %s/%s' % (self.get_package(), main_acitivity)
        if args.debug:
            cmd = 'am start -D -n %s/%s' % (self.get_package(), main_acitivity)

        self.adb.run_shell_cmd(cmd)

    def do_stopapp(self, arg):
        '''停止应用'''
        cmd = 'am force-stop %s' % self.get_package()
        self.adb.run_shell_cmd(cmd)

    kill_parser = argparse.ArgumentParser()
    kill_parser.add_argument('-a', '--all', action='store_true')

    @with_argparser(kill_parser)
    def do_kill(self, args):
        '''杀死应用'''
        cmd = 'am kill %s' % self.get_package()
        if args.all:
            cmd = 'am kill-all'

        self.adb.run_shell_cmd(cmd)

    def do_clear(self, args):
        cmd = 'pm clear {}'.format(self.get_package())
        self.adb.run_shell_cmd(cmd)

    def do_screencap(self, args):
        import time
        cmd = 'screencap -p /sdcard/{}.png'.format(time.time())
        if not self.adb:
            self.adb = pyadb3.ADB()

        self.adb.run_shell_cmd(cmd)

    monkey_parser = argparse.ArgumentParser()
    monkey_parser.add_argument('-v', '--verbose', action='store_true')
    monkey_parser.add_argument('count', type=int, help="Count")

    @with_argparser(monkey_parser)
    def do_monkey(self, args):
        if not self.adb:
            self.adb = pyadb3.ADB()

        cmd = "monkey -p {} ".format(self.get_package())
        if args.verbose:
            cmd += '-v '

        cmd += str(args.count)

        self.adb.run_shell_cmd(cmd)
        print(self.adb.get_output().decode())
    # --------------------------------------------------------

    def do_set_sdcard(self, arg):
        '''
        设置sdcard位置
        '''
        if len(arg.split()) != 1:
            print('Please give one argument to set sdcard path.')
            return
        self.sdcard = arg

    # @options([make_option('-d', '--debug', action='store_true'),
    #           make_option('-e', '--edit', action='store_true')])
    def do_test(self, args):
        '''
        测试应用(未支持)
        '''
        print(args)
        print(''.join(args))
        if args.debug:
            print('debug')

        # TODO 增加自动化测试
        # 获取Receiver, 启动
        # 获取Service,启动
        # 获取Acitivity,启动

    def do_pids(self, arg):
        '''
        显示应用进程
        '''
        print(self.adb.run_shell_cmd('ps | grep %s' % self.get_package()))

    def lsof(self):
        axml = self.apk.get_manifest()
        if axml:
            lines = self.adb.run_shell_cmd('ps | grep %s' %
                                           axml.getPackageName()).decode()
            if not lines:
                return
            pids = []
            for line in lines.strip().split('\r\r\n'):
                pids.append(line.split()[1])

            for pid in pids:
                self.adb.run_shell_cmd('lsof | grep %s' % pid)
                lines = self.adb.get_output().decode().split('\r\r\n').decode()
                for line in lines:
                    # print(line)
                    if not line.endswith('(deleted)'):
                        continue

                    tmps = line.split()
                    fdno = tmps[3]
                    if not fdno.isdecimal():
                        continue

                    print(line)
                    filename = tmps[8].replace('/', '_')
                    print('%s %s' % (fdno, filename))
                    self.adb.run_shell_cmd(
                        "cat /proc/%s/fd/%s > /storage/sdcard0/%s" % (pid, fdno, filename))
                    self.adb.run_cmd(
                        'pull -a -p /storage/sdcard0/%s' % filename)

    def do_lssd(self, arg):
        '''列出SDCARD新增的文件'''
        command = ('find /storage/sdcard0 -path "/storage/sdcard0/lost+found"'
                   ' -prune -o -type f -print -newer /storage/sdcard0/.now')
        self.adb.run_shell_cmd(command)
        print(self.adb.get_output().decode())

    def pulldata(self):
        '''
            pull /data/data/pkg
        '''
        pkg = self.get_package()
        self.adb.run_shell_cmd('cp -r /data/data/%s /storage/sdcard0' % pkg)
        # self.adb.run_cmd('pull -a -p /storage/sdcard0/%s %s' % (pkg, pkg))
        # self.adb.run_shell_cmd('rm -r /storage/sdcard0/%s' % pkg)

    def pullsd(self):
        '''
            pull the newer files from sdcard.
        '''
        command = (
            'find /storage/sdcard0 -path "/storage/sdcard0/lost+found"'
            ' -prune -o -type f -print -newer /storage/sdcard0/.now'
        )
        ret = self.adb.run_shell_cmd(command).decode()

        dir_set = set([])
        import os
        for line in ret.split('\r\r\n'):
            if line == '/storage/sdcard0/.now':
                continue
            if line:
                print('->', line)
                path = os.path.dirname(line)
                flag = 0
                skip_path = None
                for item in dir_set:
                    if item == path:
                        flag == 2
                        break

                    if item in path:
                        flag = 2
                        break

                    if path in item:
                        flag = 1
                        skip_path = item
                        break

                if flag == 1:
                    print(path, skip_path)
                    dir_set.add(path)
                    dir_set.remove(skip_path)

                elif flag == 0:
                    dir_set.add(path)

        for line in dir_set:
            print(line, )
            local_path = os.path.dirname(line)[1:]
            if not os.path.exists(local_path):
                os.makedirs(local_path)
            self.adb.run_cmd('pull -a %s %s' % (line, local_path))

    def do_pull(self, arg):
        '''导出样本的所有的运行生成的文件'''
        self.pulldata()
        self.pullsd()

    # ----------------------------- 内存操作 -----------------------------
    # 内存字符串查看?内存字符串修改?
    def do_memview(self, arg):
        '''查看内存分布'''
        if not self.maps:
            self.get_maps()

        print(self.maps)

    def get_maps(self):
        pkg = self.get_package()
        lines = self.adb.run_shell_cmd('ps | grep %s' % pkg).decode()
        pids = []
        for line in lines.strip().split('\r\r\n'):
            pids.append(line.split()[1])

        for pid in pids:
            lines = self.adb.run_shell_cmd('ls /proc/%s/task/' % pid)
            clone = lines.decode().split()[-1]
            cmd = 'cat /proc/%s/maps' % clone
            self.adb.run_shell_cmd(cmd).decode()
            self.maps = self.adb.get_output().decode()

    def memdump(self, arg):
        '''
        内存Dump(仍未支持,需要gdb)

        用法:
        memdump 内存起始地址 内存结束地址
        '''
        pass
Beispiel #20
0
import binascii
import os

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

org_strs = apk.get_org_strings()  # the strings from all of classes\d*.dex
for item in org_strs:
    if 'helloword' in item.decode('utf-8'):
        print(item)

strs = apk.get_strings()  # the strings from all of classes\d*.dex
for item in strs:
    s = binascii.unhexlify(item).decode('utf-8', errors='ignore')
    if 'helloword' in s:
        print(s)

result = apk.get_strings_refx()
for clsname in result:
    for mtdname in result[clsname]:
        if b'hellojni' in result[clsname][mtdname]:
            print(clsname, mtdname, result[clsname][mtdname])
Beispiel #21
0
import os
import unittest
import zipfile
from collections import OrderedDict

import xmltodict
from apkutils.axml.arscparser import ARSCParser

import os

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

package = apk.get_manifest().get('@package', None)
if not package:
    exit()

icon_id = apk.get_manifest().get('application', {}).get('@android:icon', None)
if not icon_id:
    exit()

icon_id = icon_id[1:].lower()
datas = xmltodict.parse(apk.get_arsc().get_public_resources(package))


def get_icon_path():
    for item in datas['resources']['public']:
        if icon_id not in item.get('@id'):
Beispiel #22
0
import binascii
import os

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

strs = apk.get_strings()  # the strings from all of classes\d*.dex
for item in strs:
    s = binascii.unhexlify(item).decode('utf-8', errors='ignore')
    if 'hello' in s:
        print(s)
Beispiel #23
0
 def setUp(self):
     file_path = os.path.abspath(
         os.path.join(os.path.dirname(__file__), "..", 'data',
                      'test_zip_fake_pwd'))
     self.apk = APK(file_path)
Beispiel #24
0
import json
import os

from apkutils import APK

file_path = os.path.abspath(os.path.join(
    os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

m_xml = apk.get_org_manifest()
print(m_xml)

m_dict = apk.get_manifest()
print(json.dumps(m_dict, indent=1))

# get any item you want from dict
print('package:', m_dict['@package'])
print('android:versionName:', m_dict['@android:versionName'])
Beispiel #25
0
    exit(-1)

if not os.path.exists("./result"):
    os.mkdir("./result")

target_path = sys.argv[1]

ns = "{http://schemas.android.com/apk/res/android}"

safeAttr = lambda x, y: x.attrib[y] if y in x.attrib else ""

for item in glob(f"{target_path}/*.apk"):
    filename = Path(item).stem
    manifest_uuid = str(uuid.uuid4())

    apk = APK(item)
    root = ET.fromstring(apk.get_org_manifest())
    app = root.find("application")
    result = {}
    for child in list(app):
        if child.tag not in result:
            result[child.tag] = []

        name = safeAttr(child, f"{ns}name")
        enabled = safeAttr(child, f"{ns}enabled") != "false"
        exported = safeAttr(child, f"{ns}exported") == "true"
        permission = safeAttr(child, f"{ns}permission")
        read_permission = safeAttr(child, f"{ns}readpermission")
        write_permission = safeAttr(child, f"{ns}writepermission")

        intents = []
Beispiel #26
0
import os

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

res = apk.get_methods_refx()
for item in res:
    print(item, res[item])
Beispiel #27
0
import os

from apkutils import APK

file_path = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", 'data', 'test'))
apk = APK(file_path)

files = apk.get_files()
for item in files:
    print(item['name'])