export to ipython will probably not go into this release...

This commit is contained in:
Sebastian Bachmann 2019-02-12 15:34:04 +01:00
parent c22ccf799d
commit f723a57d75
3 changed files with 69 additions and 15 deletions

View File

@ -459,8 +459,19 @@ class MethodClassAnalysis:
@property
def name(self):
"""Returns the name of this method"""
return self.method.get_name()
@property
def descriptor(self):
"""Returns the type descriptor for this method"""
return self.method.get_descriptor()
@property
def access(self):
"""Returns the access flags to the method as a string"""
return self.method.get_access_flags_string()
def AddXrefTo(self, classobj, methodobj, offset):
"""
Add a crossreference to another method
@ -1433,48 +1444,47 @@ class Analysis:
def create_ipython_exports(self):
"""
Creates attributes for all classes, methods, fields and strings on the Analysis object itself.
.. warning:: this feature is experimental and is currently not enabled by default! Use with caution!
Creates attributes for all classes, methods and fields on the Analysis object itself.
This makes it easier to work with Analysis module in an iPython shell.
Classes can be search by typing :code:`dx.CLASS_<tab>`, as each class is added via this attribute name.
Each class will have all methods attached to it via :code:`dx.CLASS_Foobar.METHOD_<tab>`.
Fields have a similar syntax: :code:`dx.CLASS_Foobar.FIELD_<tab>`.
Strings are added directly to the dx object, similar to classes: :code:`dx.STR_<tab>`.
As strings might be very long, they are truncated to 64 characters.
.. todo:: If strings do contain the same first 64 chars, a number is appended to the string.
As Strings can contain nearly anything, use :meth:`find_strings` instead.
* Each `CLASS_` item will return a :class:`~ClassAnalysis`
* Each `METHOD_` item will return a :class:`~MethodClassAnalysis`
* Each `FIELD_` item will return a :class:`~FieldClassAnalysis`
* Each `STR_` item will return a :class:`~StringAnalysis`
"""
# TODO: it would be fun to have the classes organized like the packages. I.e. you could do dx.CLASS_xx.yyy.zzz
for cls in self.get_classes():
name = "CLASS_" + bytecode.FormatClassToPython(cls.name)
if hasattr(self, name):
log.warning("Already existing!")
log.warning("Already existing class {}!".format(name))
setattr(self, name, cls)
for meth in cls.get_methods():
mname = "METH_" + bytecode.FormatNameToPython(meth.name)
method_name = meth.name
if method_name in ["<init>", "<clinit>"]:
_, method_name = bytecode.get_package_class_name(cls.name)
# FIXME this naming schema is not very good... but to describe a method uniquely, we need all of it
mname = "METH_" + method_name + "_" + bytecode.FormatDescriptorToPython(meth.access) + "_" + bytecode.FormatDescriptorToPython(meth.descriptor)
if hasattr(cls, mname):
log.warning("already existing method: {} at class {}".format(mname, name))
setattr(cls, mname, meth)
# FIXME: syntetic classes produce problems here.
# If the field name is the same in the parent as in the syntetic one, we can only add one!
for field in cls.get_fields():
mname = "FIELD_" + bytecode.FormatNameToPython(field.name)
if hasattr(cls, mname):
log.warning("already existing field: {} at class {}".format(mname, name))
setattr(cls, mname, field)
for s in self.get_strings():
name = "STR_" + bytecode.FormatNameToPython(s[:64])
if hasattr(self, mname):
log.warning("already existing string: {}".format(name))
setattr(self, name, s)
def is_ascii_obfuscation(vm):
"""

View File

@ -894,6 +894,34 @@ def FormatClassToPython(i):
return i
def get_package_class_name(name):
"""
Return package and class name in a java variant from a typed variant name.
If no package could be found, the package is an empty string.
example::
>>> get_package_class_name('Ljava/lang/Object;')
('java.lang', 'Object')
:param name: the name
:rtype: tuple
:return:
"""
if name[0] != 'L' and name[-1] != ';':
raise ValueError("The name '{}' does not look like a typed name!".format(name))
name = name[1:-1]
if '/' not in name:
return '', name
package, clsname = name.rsplit('/', 1)
package = package.replace('/', '.')
return package, clsname
def FormatNameToPython(i):
"""
Transform a (method) name into a form which can be used as a python

View File

@ -20,6 +20,22 @@ class MiscTest(unittest.TestCase):
with tempfile.NamedTemporaryFile() as fp:
self.assertEqual(fp.name + "_0", clean_file_name(fp.name, unique=True))
def testClassNameFormatting(self):
from androguard.core.bytecode import get_package_class_name
self.assertEqual(get_package_class_name('Ljava/lang/Object;'), ('java.lang', 'Object'))
self.assertEqual(get_package_class_name('LFoobar;'), ('', 'Foobar'))
self.assertEqual(get_package_class_name('Lsdflkjdsklfjsdkjfklsdjfkljsdkflsd/shdfjksdhkjfhsdkjfsh;'),
('sdflkjdsklfjsdkjfklsdjfkljsdkflsd', 'shdfjksdhkjfhsdkjfsh'))
self.assertEqual(get_package_class_name('L;'), ('', ''))
with self.assertRaises(ValueError):
get_package_class_name('Foobar')
with self.assertRaises(ValueError):
get_package_class_name('java.lang.Object')
if __name__ == '__main__':
unittest.main()