diff --git a/binaries/classsearch.py b/binaries/classsearch.py deleted file mode 100755 index bb28249..0000000 --- a/binaries/classsearch.py +++ /dev/null @@ -1,174 +0,0 @@ -#!/usr/bin/env python -# DTF Core Content -# Copyright 2013-2015 Jake Valletta (@jake_valletta) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# Class searching support -from sys import argv -from os import listdir -from pydtf import dtfconfig -import sqlite3 -import argparse - -def searchDb(db_name, config): - - search_class_name = config['class_name'] - search_method_name = config['method_name'] - show_fields = config['show_fields'] - show_methods = config['show_methods'] - field_contains = config['field_contains'] - file_mode = config['file_mode'] - exact = config['exact'] - - method_search = False - - #print "doing : %s" % (db_name) - class_list = list() - - #print search_class_name, search_method_name - - if search_method_name is not None: - method_search = True - - conn = sqlite3.connect(db_name) - - c = conn.cursor() - - sql = ('SELECT id, name, access_flags, superclass ' - 'FROM classes') - - for class_id, class_name, access_flags, superclass in c.execute(sql): - - if method_search: - - method_matches = list() - - mc = conn.cursor() - msql = ("SELECT name FROM methods where class_id=%d" % class_id) - - for method_name in mc.execute(msql): - - if method_name[0] == search_method_name: - method_matches.append([class_name, method_name[0]]) - - if len(method_matches) > 0: - print "Match(es) in '%s':" % db_name - for match in method_matches: - print " %s->%s" % (match[0], match[1]) - - else: - if exact: - if class_name == search_class_name: - class_list.append( class_name ) - else: - if class_name.find(search_class_name) != -1: - class_list.append( class_name ) - - if not method_search and len(class_list) != 0: - if not file_mode: print "[+] In database: %s" % (db_name) - for n in class_list: - if file_mode: - framework_file = db_name.replace(".dbs/frameworkdexdbs/","").replace(".db","") - unframework_dir = dtfconfig.get_prop("Local", "unframework-dir") - dotted_path = n.replace(".","/") - print "%s/%s/%s.smali" % (unframework_dir, framework_file, dotted_path) - else: - print "\t%s" % (n) - if show_fields: - fsql = ("SELECT sf.name FROM static_fields sf JOIN classes c ON c.id=sf.class_id WHERE c.name='%s'" % n) - - for field_name in c.execute(fsql): - - if field_name[0].find(field_contains) != -1: - print "\t +%s" % field_name[0] - - - - - -config = dict() -class_name = None -method_name = None - -parser = argparse.ArgumentParser(description='Search for a class name.') -parser.add_argument('search_class', type=str, help='Class to search for.', nargs='?', - default=None) -parser.add_argument('--frameworks', dest='fw', action='store_const', - const=True, default=False, - help='Search framework files.') -parser.add_argument('--apps', dest='app', action='store_const', - const=True, default=False, - help='Search application files.') -parser.add_argument('-e', dest='exact', action='store_const', const=True, default=False, - help='Match exact name.') -parser.add_argument('-f', dest='file_mode', action='store_const', const=True, default=False, - help='Print path to file instead.') -parser.add_argument('--hasMethod', dest='has_method', help='Search by method name.') -parser.add_argument('--implements', dest='implements', default=None, - help='Search by implemented class') -parser.add_argument('--fields', dest='show_fields', action='store_const', const=1, default=0, - help='Display fields for matching class') -parser.add_argument('--methods', dest='show_methods', action='store_const', const=1, default=0, - help='Display methods for matching class') -parser.add_argument('--fieldContains', dest='field_contains', default=None, - help='Filter fields') - -args = parser.parse_args() - -search_frameworks = args.fw -search_apps = args.app - -if search_frameworks == False and search_apps == False: - print "[ERROR] You need to specify either '--frameworks' or '--apps'!" - exit(-2) - -method_name = args.has_method -class_name = args.search_class -show_methods = args.show_methods -show_fields = args.show_fields -field_contains = args.field_contains -exact = args.exact -file_mode = args.file_mode - -config['method_name'] = method_name -config['class_name'] = class_name -config['show_methods'] = show_methods -config['show_fields'] = show_fields -config['field_contains'] = field_contains -config['file_mode'] = file_mode -config['exact'] = exact - -if class_name is None and method_name is None: - print "You need to specify a class_name to search for!" - exit(2) - -db_dir = dtfconfig.get_prop("Local", "db-dir") - -if search_frameworks: - # For file in frameworkdexdbs - try: - for db in listdir(db_dir+"/frameworkdexdbs/"): - searchDb(db_dir+"/frameworkdexdbs/"+db, config) - except OSError: - print "[ERROR] Error listing framework DEX databases, do they exist?" - exit(-4) - -if search_apps: - # For file in appdexdbs - try: - for db in listdir(db_dir+"/appdexdbs/"): - searchDb(db_dir+"/appdexdbs/"+db, config) - except OSError: - print "[ERROR] Error listing app DEX databases, do they exist?" - exit(-4) diff --git a/binaries/platform-diff.py b/binaries/platform-diff.py deleted file mode 100755 index a4c2936..0000000 --- a/binaries/platform-diff.py +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env python -# DTF Core Content -# Copyright 2013-2015 Jake Valletta (@jake_valletta) -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# "platform.xml" diffing support -import sqlite3 -from lxml import etree -from pydtf import dtfconfig, dtfglobals -from pydtf import dtflog as log -from os.path import isfile - -def safeOpenXML(file_name): - - try: - f_handle = open(file_name) - except IOError: - return -1 - - root = etree.XML(f_handle.read()) - - f_handle.close() - - return root - -def safeSqlite3Connect(db): - - if isfile(db): - return sqlite3.connect(db) - - else: - raise IOError("Target Sqlite3 file not found!") - -def getProtectionLevel(db, permission_name): - - c = db.cursor() - - sql = ('SELECT p.protection_level ' - 'FROM permissions p ' - 'WHERE p.name="%s" ' - 'LIMIT 1' % (permission_name)) - - rtn = c.execute(sql) - - try: - return c.fetchone()[0] - except TypeError: - #log.e(TAG, "Unable to find permission?? \"%s\"!" % permission_name) - return "????" - -def parseXML(db, root): - - group_mappings = {} - account_perms = {} - - # First lets do the mapping - for element in root.findall(".//permission"): - - permission_name = element.attrib['name'] - - for group in element.xpath("group"): - - gid_name = group.attrib['gid'] - - if permission_name not in group_mappings: - group_mappings[permission_name] = list() - - group_mappings[permission_name].append(gid_name) - - # Now the permission assigns - for element in root.findall(".//assign-permission"): - - permission_name = element.attrib['name'] - - uid = element.attrib['uid'] - - if uid in account_perms.keys(): - account_perms[uid].append(permission_name) - else: - account_perms[uid] = [ permission_name ] - - - return group_mappings, account_perms - -TAG = "platformdiff" - -try: - project_db = safeSqlite3Connect('.dbs/sysapps.db') -except IOError: - print "[Error] The local \"sysapps.db\" does not exist, did you run app2db yet?" - exit(-1) - -aosp_platform_file = dtfglobals.DTF_PACKAGES + "/aosp-data-" + dtfconfig.get_prop("Info", "sdk") + "/platform.xml" - -aosp_platform_root = safeOpenXML(aosp_platform_file) - -if aosp_platform_root == -1: - print "Unable to open AOSP platform details. Are you sure you have this API level installed? Exiting!" - exit(-1) - -project_platform_root = safeOpenXML(dtfconfig.get_prop("Local", "permissions-dir")+"/platform.xml") - -if project_platform_root == -1: - print "Unable to open local platform.xml file. Did you pull it down? Exiting!" - exit(-1) - -project_group_mappings, project_account_perms = parseXML(project_db, project_platform_root) -aosp_group_mappings, aosp_account_perms = parseXML(project_db, aosp_platform_root) - -print "[+] OEM Added mappings:" -for name, gids in project_group_mappings.iteritems(): - - # Name is completely new. - if name not in aosp_group_mappings: - - protection_level = getProtectionLevel(project_db, name) - - for gid in gids: - print "\t%s [%s] ---> %s" % (name,protection_level, gid) - - # Name might still be there - else: - aosp_gids = aosp_group_mappings[name] - - protection_level = getProtectionLevel(project_db, name) - - for gid in gids: - if gid not in aosp_gids: - print "\t%s [%s] ---> %s" % (name,protection_level, gid) - -print "" -print "[+] OEM Added tags:" -for uid, permissions in project_account_perms.iteritems(): - - if uid not in aosp_account_perms: - print "\tUser %s [OEM]:" % uid - for name in project_account_perms[uid]: - protection_level = getProtectionLevel(project_db, name) - print "\t\t+%s [%s]" % (name, protection_level) - else: - print "\tUser %s:" % uid - for name in project_account_perms[uid]: - if name not in aosp_account_perms[uid]: - protection_level = getProtectionLevel(project_db, name) - print "\t\t+%s [%s]" % (name, protection_level) diff --git a/modules/bindiff b/modules/bindiff index fe5f09b..d182c2f 100755 --- a/modules/bindiff +++ b/modules/bindiff @@ -19,8 +19,7 @@ #@Health: broken #@Version: 1.0 -# Imports -. dtf_core.sh +. $DTF_CORE make_csv() { @@ -32,7 +31,6 @@ make_csv() csv_file=${reports_dir}/oembins.csv - echo "Binary Name,Permissions,Owner,Group,Size,\"File Detail\",Notes" > ${csv_file} IFS=$' \t\n' @@ -64,8 +62,6 @@ binlist_file=${DTF_PACKAGES}/aosp-data-${sdk}/bindirlist IFS=$'\n' aosp_binlist=($(cat ${binlist_file})) -echo ${aosp_binlist[*]} - if [ "$1" == "--pull" ]; then echo "Pulling OEM bins to \"${pull_dir}\"" mkdir ${pull_dir} 2> /dev/null diff --git a/modules/classsearch b/modules/classsearch index a55aac9..2e8323e 100755 --- a/modules/classsearch +++ b/modules/classsearch @@ -1,24 +1,213 @@ -#!/usr/bin/env bash -# DTF Core Content +#!/usr/bin/env python +# Android Device Testing Framework ("dtf") # Copyright 2013-2015 Jake Valletta (@jake_valletta) -# +# # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at -# +# # http://www.apache.org/licenses/LICENSE-2.0 -# +# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# +"""Search across framework or application DEX databases""" -#@About: Search class files for classname (requires DEX databases). -#@Author: Jake Valletta (jakev) -#@Health: stable -#@Version: 1.0 +import dtf.properties as prop +import dtf.logging as log +from dtf.module import Module -. dtf_core.sh +import sqlite3 +from argparse import ArgumentParser +from os import listdir + +TAG = 'classsearch' + + +class classsearch(Module): + + """Module class for searching DEX databses""" + + about = 'Search across DEX databases (requires processed DEX DBs).' + author = 'Jake Valletta (jakev)' + health = 'stable' + name = 'classsearch' + version = '1.0' + + @classmethod + def search_db(cls, db_name, config): + + """Perform search""" + + search_class_name = config['class_name'] + search_method_name = config['method_name'] + show_fields = config['show_fields'] + show_methods = config['show_methods'] + field_contains = config['field_contains'] + file_mode = config['file_mode'] + exact = config['exact'] + + method_search = False + + log.d(TAG, "Doing : %s" % (db_name)) + class_list = list() + + if search_method_name is not None: + method_search = True + + conn = sqlite3.connect(db_name) + + cursor = conn.cursor() + + sql = ('SELECT id, name, access_flags, superclass ' + 'FROM classes') + + for cls_id, cls_name, access_flags, superclass in cursor.execute(sql): + + if method_search: + + method_matches = list() + + m_cursor = conn.cursor() + msql = ('SELECT name ' + 'FROM methods ' + "WHERE class_id=%d" % cls_id) + + for method_name in m_cursor.execute(msql): + + if method_name[0] == search_method_name: + method_matches.append([cls_name, method_name[0]]) + + if len(method_matches) > 0: + print "Match(es) in '%s':" % db_name + + for match in method_matches: + print " %s->%s" % (match[0], match[1]) + + else: + if exact: + if cls_name == search_class_name: + class_list.append(cls_name) + else: + if cls_name.find(search_class_name) != -1: + class_list.append(cls_name) + + if not method_search and len(class_list) != 0: + if not file_mode: + print "[+] In database: %s" % (db_name) + for n_class in class_list: + if file_mode: + + dex_db_dir = '.dbs/frameworkdexdbs/' + tmp_fw_file = db_name.replace(dex_db_dir, "") + framework_file = tmp_fw_file.replace(".db", "") + + unframework_dir = prop.get_prop("Local", "unframework-dir") + dotted_path = n_class.replace(".", "/") + + print ("%s/%s/%s.smali" + % (unframework_dir, framework_file, dotted_path)) + else: + print "\t%s" % (n_class) + if show_fields: + fsql = ('SELECT sf.name ' + 'FROM static_fields sf ' + 'JOIN classes c ' + 'ON c.id=sf.class_id ' + "WHERE c.name='%s'" % n_class) + + for field_name in cursor.execute(fsql): + + if field_name[0].find(field_contains) != -1: + print "\t +%s" % field_name[0] + + def execute(self, args): + + """Main executor method""" + + config = dict() + class_name = None + method_name = None + + parser = ArgumentParser(prog=self.name, + description='Search for a class name.') + parser.add_argument('search_class', help='Class to search for.', + default=None) + parser.add_argument('--frameworks', dest='fw', action='store_const', + const=True, default=False, + help='Search framework files.') + parser.add_argument('--apps', dest='app', action='store_const', + const=True, default=False, + help='Search application files.') + parser.add_argument('-e', dest='exact', action='store_const', + const=True, default=False, + help='Match exact name.') + parser.add_argument('-f', dest='file_mode', action='store_const', + const=True, default=False, + help='Print path to file instead.') + parser.add_argument('--hasMethod', dest='has_method', + help='Search by method name.') + parser.add_argument('--implements', dest='implements', default=None, + help='Search by implemented class') + parser.add_argument('--fields', dest='show_fields', + action='store_const', const=1, default=0, + help='Display fields for matching class') + parser.add_argument('--methods', dest='show_methods', + action='store_const', const=1, default=0, + help='Display methods for matching class') + parser.add_argument('--fieldContains', dest='field_contains', + default=None, help='Filter fields') + + parsed_args = parser.parse_args(args) + + search_frameworks = parsed_args.fw + search_apps = parsed_args.app + + if search_frameworks == False and search_apps == False: + log.e(TAG, "You must specify either '--frameworks' or '--apps'!") + return -2 + + method_name = parsed_args.has_method + class_name = parsed_args.search_class + show_methods = parsed_args.show_methods + show_fields = parsed_args.show_fields + field_contains = parsed_args.field_contains + exact = parsed_args.exact + file_mode = parsed_args.file_mode + + config['method_name'] = method_name + config['class_name'] = class_name + config['show_methods'] = show_methods + config['show_fields'] = show_fields + config['field_contains'] = field_contains + config['file_mode'] = file_mode + config['exact'] = exact + + if class_name is None and method_name is None: + log.e(TAG, "You need to specify a class_name to search for!") + exit(2) + + db_dir = prop.get_prop("Local", "db-dir") + + if search_frameworks: + # For file in frameworkdexdbs + try: + for db_file in listdir(db_dir + "/frameworkdexdbs/"): + self.search_db(db_dir + "/frameworkdexdbs/" + db_file, + config) + except OSError: + log.e(TAG, "Error listing framework DEX DBs, do they exist?") + return -4 + + if search_apps: + # For file in appdexdbs + try: + for db_file in listdir(db_dir + "/appdexdbs/"): + self.search_db(db_dir + "/appdexdbs/" + db_file, config) + except OSError: + log.e(TAG, "Error listing app DEX DBs, do they exist?") + return -4 -${DTF_BINS}/classsearch.py $@ diff --git a/modules/frameworkres b/modules/frameworkres index fd5b0f6..b6271b4 100755 --- a/modules/frameworkres +++ b/modules/frameworkres @@ -19,8 +19,8 @@ #@Health: stable #@Version: 1.1 -. dtf_core.sh -. dtf_log.sh +. $DTF_CORE +. $DTF_LOG fwres_dir=.fwres framework_dir=$(dtf prop get Local framework-dir) diff --git a/modules/platformdiff b/modules/platformdiff index 9fc7b23..6110c9d 100644 --- a/modules/platformdiff +++ b/modules/platformdiff @@ -35,7 +35,7 @@ PLATFORM_XML_NAME = 'platform.xml' class platformdiff(Module): - """Module class for archiving a project""" + """Module class for diffing platform.xml""" about = 'Parse the /system/etc/permissions/platform.xml file.' author = 'Jake Valletta (jakev)'