adding simple is_android_api methods

This commit is contained in:
Sebastian Bachmann 2018-04-25 09:10:52 +02:00
parent b1ccfe11a4
commit 861a62139b
2 changed files with 140 additions and 63 deletions

View File

@ -453,6 +453,9 @@ class MethodClassAnalysis:
self.xrefto = set()
self.xreffrom = set()
# Reserved for further use
self.apilist = None
def AddXrefTo(self, classobj, methodobj, offset):
"""
Add a crossreference to another method
@ -507,6 +510,34 @@ class MethodClassAnalysis:
"""
return isinstance(self.method, ExternalMethod)
def is_android_api(self):
"""
Returns True if the method seems to be an Android API method.
This method might be not very precise unless an list of known API methods
is given.
:return: boolean
"""
if not self.is_external():
# Method must be external to be an API
return False
# Packages found at https://developer.android.com/reference/packages.html
api_candidates = ["Landroid/", "Lcom/android/internal/util", "Ldalvik/", "Ljava/", "Ljavax/", "Lorg/apache/",
"Lorg/json/", "Lorg/w3c/dom/", "Lorg/xml/sax", "Lorg/xmlpull/v1/", "Ljunit/"]
if self.apilist:
# FIXME: This will not work... need to introduce a name for lookup (like EncodedMethod.__str__ but without
# the offsert! Such a name is also needed for the lookup in permissions
return self.method.get_name() in self.apilist
else:
for candidate in api_candidates:
if self.method.get_class_name().startswith(candidate):
return True
return False
def get_method(self):
"""
Return the `EncodedMethod` object that relates to this object
@ -529,7 +560,7 @@ class MethodClassAnalysis:
def __repr__(self):
return "<analysis.MethodClassAnalysis {}{}>".format(self.method,
" EXTERNAL" if isinstance(self.method, ExternalMethod) else "")
" EXTERNAL" if isinstance(self.method, ExternalMethod) else "")
class FieldClassAnalysis:
@ -666,7 +697,7 @@ class ClassAnalysis:
:param classobj: class:`~androguard.core.bytecode.dvm.ClassDefItem` or :class:`ExternalClass`
"""
# Automaticallt decide if the class is external or not
# Automatically decide if the class is external or not
self.external = isinstance(classobj, ExternalClass)
self.orig_class = classobj
@ -677,6 +708,9 @@ class ClassAnalysis:
self.xrefto = collections.defaultdict(set)
self.xreffrom = collections.defaultdict(set)
# Reserved for further use
self.apilist = None
def is_external(self):
"""
Tests wheather this class is an external class
@ -685,6 +719,34 @@ class ClassAnalysis:
"""
return self.external
def is_android_api(self):
"""
Tries to guess if the current class is an Android API class.
This might be not very precise unless an apilist is given, with classes that
are in fact known APIs.
Such a list might be generated by using the android.jar files.
:return: boolean
"""
# Packages found at https://developer.android.com/reference/packages.html
api_candidates = ["Landroid/", "Lcom/android/internal/util", "Ldalvik/", "Ljava/", "Ljavax/", "Lorg/apache/",
"Lorg/json/", "Lorg/w3c/dom/", "Lorg/xml/sax", "Lorg/xmlpull/v1/", "Ljunit/"]
if not self.is_external():
# API must be external
return False
if self.apilist:
return self.orig_class.get_name() in self.apilist
else:
for candidate in api_candidates:
if self.orig_class.get_name().startswith(candidate):
return True
return False
def get_methods(self):
"""
Return all `MethodClassAnalysis` objects of this class

View File

@ -176,36 +176,37 @@ def PrettyShow(m_a, basic_blocks, notes={}):
print_fct("\n")
def method2dot(mx, colors={}):
def method2dot(mx, colors=None):
"""
Export analysis method to dot format
Export analysis method to dot format
@param mx : MethodAnalysis object
@param colors : MethodAnalysis object
:param mx: :class:`~androguard.core.analysis.analysis.MethodAnalysis`
:param colors: dict of colors to use, if colors is None the default colors are used
@rtype : dot format buffer (it is a subgraph (dict))
:returns: a string which contains the dot graph
"""
colors = colors or {
"true_branch": "green",
"false_branch": "red",
"default_branch": "purple",
"jump_branch": "blue",
"bg_idx": "lightgray",
"idx": "blue",
"bg_start_idx": "yellow",
"bg_instruction": "lightgray",
"instruction_name": "black",
"instructions_operands": "yellow",
"raw": "red",
"string": "red",
"literal": "green",
"offset": "#4000FF",
"method": "#DF3A01",
"field": "#088A08",
"type": "#0000FF",
"registers_range": ("#999933", "#6666FF")
}
if not colors:
colors = {
"true_branch": "green",
"false_branch": "red",
"default_branch": "purple",
"jump_branch": "blue",
"bg_idx": "lightgray",
"idx": "blue",
"bg_start_idx": "yellow",
"bg_instruction": "lightgray",
"instruction_name": "black",
"instructions_operands": "yellow",
"raw": "red",
"string": "red",
"literal": "green",
"offset": "#4000FF",
"method": "#DF3A01",
"field": "#088A08",
"type": "#0000FF",
"registers_range": ("#999933", "#6666FF")
}
node_tpl = "\nstruct_%s [label=<\n<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"3\">\n%s</TABLE>>];\n"
label_tpl = "<TR><TD ALIGN=\"LEFT\" BGCOLOR=\"%s\"> <FONT FACE=\"Times-Bold\" color=\"%s\">%x</FONT> </TD><TD ALIGN=\"LEFT\" BGCOLOR=\"%s\"> <FONT FACE=\"Times-Bold\" color=\"%s\">%s </FONT> %s </TD></TR>\n"
@ -243,8 +244,7 @@ def method2dot(mx, colors={}):
for DVMBasicMethodBlock in mx.basic_blocks.gets():
ins_idx = DVMBasicMethodBlock.start
block_id = hashlib.md5(bytearray(sha256 + DVMBasicMethodBlock.get_name(
), "UTF-8")).hexdigest()
block_id = hashlib.md5(bytearray(sha256 + DVMBasicMethodBlock.get_name(), "UTF-8")).hexdigest()
content = link_tpl % 'header'
@ -252,12 +252,10 @@ def method2dot(mx, colors={}):
if DVMBasicMethodBlockInstruction.get_op_value(
) == 0x2b or DVMBasicMethodBlockInstruction.get_op_value() == 0x2c:
new_links.append((DVMBasicMethodBlock, ins_idx,
DVMBasicMethodBlockInstruction.get_ref_off(
) * 2 + ins_idx))
DVMBasicMethodBlockInstruction.get_ref_off() * 2 + ins_idx))
elif DVMBasicMethodBlockInstruction.get_op_value() == 0x26:
new_links.append((DVMBasicMethodBlock, ins_idx,
DVMBasicMethodBlockInstruction.get_ref_off(
) * 2 + ins_idx))
DVMBasicMethodBlockInstruction.get_ref_off() * 2 + ins_idx))
operands = DVMBasicMethodBlockInstruction.get_operands(ins_idx)
output = ", ".join(mx.get_vm().get_operand_html(
@ -359,12 +357,12 @@ def method2dot(mx, colors={}):
def method2format(output, _format="png", mx=None, raw=None):
"""
Export method to a specific file format
Export method to a specific file format
@param output : output filename
@param _format : format type (png, jpg ...) (default : png)
@param mx : specify the MethodAnalysis object
@param raw : use directly a dot raw buffer if None
@param output : output filename
@param _format : format type (png, jpg ...) (default : png)
@param mx : specify the MethodAnalysis object
@param raw : use directly a dot raw buffer if None
"""
# pydot is optional!
import pydot
@ -379,8 +377,7 @@ def method2format(output, _format="png", mx=None, raw=None):
data = method2dot(mx)
# subgraphs cluster
buff += "subgraph cluster_" + hashlib.md5(bytearray(output, "UTF-8")).hexdigest(
) + " {\nlabel=\"%s\"\n" % data['name']
buff += "subgraph cluster_" + hashlib.md5(bytearray(output, "UTF-8")).hexdigest() + " {\nlabel=\"%s\"\n" % data['name']
buff += data['nodes']
buff += "}\n"
@ -395,14 +392,14 @@ def method2format(output, _format="png", mx=None, raw=None):
def method2png(output, mx, raw=False):
"""
Export method to a png file format
Export method to a png file format
:param output: output filename
:type output: string
:param mx: specify the MethodAnalysis object
:type mx: :class:`MethodAnalysis` object
:param raw: use directly a dot raw buffer
:type raw: string
:param output: output filename
:type output: string
:param mx: specify the MethodAnalysis object
:type mx: :class:`MethodAnalysis` object
:param raw: use directly a dot raw buffer
:type raw: string
"""
buff = raw
if not raw:
@ -413,14 +410,14 @@ def method2png(output, mx, raw=False):
def method2jpg(output, mx, raw=False):
"""
Export method to a jpg file format
Export method to a jpg file format
:param output: output filename
:type output: string
:param mx: specify the MethodAnalysis object
:type mx: :class:`MethodAnalysis` object
:param raw: use directly a dot raw buffer (optional)
:type raw: string
:param output: output filename
:type output: string
:param mx: specify the MethodAnalysis object
:type mx: :class:`MethodAnalysis` object
:param raw: use directly a dot raw buffer (optional)
:type raw: string
"""
buff = raw
if not raw:
@ -430,6 +427,12 @@ def method2jpg(output, mx, raw=False):
def vm2json(vm):
"""
Get a JSON representation of a DEX file
:param vm: :class:`~androguard.core.bytecodes.dvm.DalvikVMFormat`
:return:
"""
d = {"name": "root", "children": []}
for _class in vm.get_classes():
@ -455,12 +458,24 @@ class TmpBlock(object):
def method2json(mx, directed_graph=False):
"""
Create directed or undirected graph in the json format.
:param mx: :class:`~androguard.core.analysis.analysis.MethodAnalysis`
:param directed_graph: True if a directed graph should be created (default: False)
:return:
"""
if directed_graph:
return method2json_direct(mx)
return method2json_undirect(mx)
def method2json_undirect(mx):
"""
:param mx: :class:`~androguard.core.analysis.analysis.MethodAnalysis`
:return:
"""
d = {}
reports = []
d["reports"] = reports
@ -488,6 +503,11 @@ def method2json_undirect(mx):
def method2json_direct(mx):
"""
:param mx: :class:`~androguard.core.analysis.analysis.MethodAnalysis`
:return:
"""
d = {}
reports = []
d["reports"] = reports
@ -496,10 +516,8 @@ def method2json_direct(mx):
l = []
for DVMBasicMethodBlock in mx.basic_blocks.gets():
for index, DVMBasicMethodBlockChild in enumerate(
DVMBasicMethodBlock.childs):
if DVMBasicMethodBlock.get_name(
) == DVMBasicMethodBlockChild[-1].get_name():
for index, DVMBasicMethodBlockChild in enumerate(DVMBasicMethodBlock.childs):
if DVMBasicMethodBlock.get_name() == DVMBasicMethodBlockChild[-1].get_name():
preblock = TmpBlock(DVMBasicMethodBlock.get_name() + "-pre")
@ -560,12 +578,9 @@ def method2json_direct(mx):
for DVMBasicMethodBlockChild in DVMBasicMethodBlock.childs:
ok = False
if DVMBasicMethodBlock.get_name() in hooks:
if DVMBasicMethodBlockChild[-1] in hooks[
DVMBasicMethodBlock.get_name()
]:
if DVMBasicMethodBlockChild[-1] in hooks[DVMBasicMethodBlock.get_name()]:
ok = True
cblock["Edge"].append(hooks[DVMBasicMethodBlock.get_name(
)][0].get_name())
cblock["Edge"].append(hooks[DVMBasicMethodBlock.get_name()][0].get_name())
if not ok:
cblock["Edge"].append(DVMBasicMethodBlockChild[-1].get_name())