-
Notifications
You must be signed in to change notification settings - Fork 0
Diffing APKs
A large part of this project proposes a diffing tool which allows to list a set of differences between two APKs. Here is how to run the differences computation between two versions:
- Disassemble both APKs
- Load two
SmaliProject
as decribed previously; - Invoke the
differences()
methods to get a list of changes.
>>> from smalanalysis.smali.SmaliProject import SmaliProject
>>> v1 = SmaliProject()
>>> v1.parseProject('/Users/vince/facebook-1-8-3.apk.smali')
>>> v2 = SmaliProject()
>>> v2.parseProject('/Users/vince/facebook-1-8-4.apk.smali')
>>> diffs = v1.differences(v2, [])
>>> for diff in diffs:
... print(diff)
The diff output contains a list of list which is built such:
[[class_v1, class_v2], [change_list]]
- If
class_v1
is None, it means thatclass_v2
has been added inv2
; - If
class_v2
is None, it means thatclass_v1
has been removed inv2
; - Otherwise, it means that class exists in both version and
change_list
contain a set of list describing the changes.
>>> changes = list(filter(lambda x: x[1] != [], diffs))
>>> for diff in changes:
... print(diff)
[[<smali.SmaliObject.SmaliClass object at 0x1089b6c88>, <smali.SmaliObject.SmaliClass object at 0x10c7a4a90>], [[<smali.SmaliObject.SmaliMethod object at 0x1089de9b0>, <smali.SmaliObject.SmaliMethod object at 0x10c7c9b00>, 'REVISED_METHOD'], [<smali.SmaliObject.SmaliMethod object at 0x1089ded30>, <smali.SmaliObject.SmaliMethod object at 0x10c7c9e80>, 'REVISED_METHOD'], [None, <smali.SmaliObject.SmaliMethod object at 0x10c7c9f28>, 'NOT_FOUND'], [<smali.SmaliObject.SmaliField object at 0x1089de5f8>, None, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c7c96d8>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c7c9630>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c7c9588>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c7abc88>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c7ab978>, 'NOT_FOUND']]]
[[<smali.SmaliObject.SmaliClass object at 0x1084deac8>, <smali.SmaliObject.SmaliClass object at 0x10c2c9668>], [[<smali.SmaliObject.SmaliMethod object at 0x1085de048>, <smali.SmaliObject.SmaliMethod object at 0x10c371cf8>, 'REVISED_METHOD'], [<smali.SmaliObject.SmaliMethod object at 0x1085de1d0>, <smali.SmaliObject.SmaliMethod object at 0x10c371f98>, 'REVISED_METHOD'], [<smali.SmaliObject.SmaliMethod object at 0x1085de240>, <smali.SmaliObject.SmaliMethod object at 0x10c3c9048>, 'REVISED_METHOD'], [None, <smali.SmaliObject.SmaliMethod object at 0x10c371e10>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c371c18>, 'NOT_FOUND'], [None, <smali.SmaliObject.SmaliField object at 0x10c371ac8>, 'NOT_FOUND']]]
[[<smali.SmaliObject.SmaliClass object at 0x107ba3588>, <smali.SmaliObject.SmaliClass object at 0x10b991128>], [[<smali.SmaliObject.SmaliMethod object at 0x107bcdf60>, <smali.SmaliObject.SmaliMethod object at 0x10b9b0b00>, 'REVISED_METHOD']]]
[[<smali.SmaliObject.SmaliClass object at 0x107b64748>, <smali.SmaliObject.SmaliClass object at 0x10b9502e8>], [[<smali.SmaliObject.SmaliMethod object at 0x107b715c0>, <smali.SmaliObject.SmaliMethod object at 0x10b95a160>, 'REVISED_METHOD']]]
[[<smali.SmaliObject.SmaliClass object at 0x1068bc630>, <smali.SmaliObject.SmaliClass object at 0x10a7b50f0>], [[<smali.SmaliObject.SmaliMethod object at 0x1068cfa90>, <smali.SmaliObject.SmaliMethod object at 0x10a7c35f8>, 'SAME_NAME'], [None, <smali.SmaliObject.SmaliMethod object at 0x10a7c3668>, 'NOT_FOUND'], [<smali.SmaliObject.SmaliField object at 0x1068bcac8>, <smali.SmaliObject.SmaliField object at 0x10a7b5630>, 'FIELD_CHANGED', ['NOT_SAME_INIT_VALUE']], [<smali.SmaliObject.SmaliField object at 0x1068bce10>, <smali.SmaliObject.SmaliField object at 0x10a7b5978>, 'FIELD_CHANGED', ['NOT_SAME_INIT_VALUE']]]]
[[<smali.SmaliObject.SmaliClass object at 0x1067b9208>, <smali.SmaliObject.SmaliClass object at 0x10a688c88>], [[<smali.SmaliObject.SmaliMethod object at 0x1067da978>, <smali.SmaliObject.SmaliMethod object at 0x10a6b3438>, 'REVISED_METHOD']]]
[[<smali.SmaliObject.SmaliClass object at 0x10671eb38>, <smali.SmaliObject.SmaliClass object at 0x10a61c5f8>], [[<smali.SmaliObject.SmaliField object at 0x1067289e8>, <smali.SmaliObject.SmaliField object at 0x10a6244a8>, 'FIELD_CHANGED', ['NOT_SAME_INIT_VALUE']]]]
[[<smali.SmaliObject.SmaliClass object at 0x1061b0588>, None], None]
[[None, <smali.SmaliObject.SmaliClass object at 0x10a582080>], None]
[(<smali.SmaliObject.SmaliClass object at 0x10684f358>, <smali.SmaliObject.SmaliClass object at 0x10a724dd8>), [[<smali.SmaliObject.SmaliMethod object at 0x10685dcf8>, <smali.SmaliObject.SmaliMethod object at 0x10a7607b8>, 'REVISED_METHOD']]]
[[<smali.SmaliObject.SmaliClass object at 0x10621bc18>, None], None]
[[<smali.SmaliObject.SmaliClass object at 0x10627c780>, None], None]
An example of class deletion:
>>> changes[7]
[[<smali.SmaliObject.SmaliClass object at 0x1061b0588>, None], None]
>>> changes[7][0][0].getName()
'Lcustom/android/ListActivity;'
An example of class addition:
>>> changes[8]
[[None, <smali.SmaliObject.SmaliClass object at 0x10a582080>], None]
>>> changes[8][0][1].getName()
'Lcom/facebook/katana/UserAgent$AppForegroundMode;'
>>>
An example of class changes:
>>> for c in changes[0][1]:
... print(c)
...
[<smali.SmaliObject.SmaliMethod object at 0x1089de9b0>, <smali.SmaliObject.SmaliMethod object at 0x10c7c9b00>, 'REVISED_METHOD']
[<smali.SmaliObject.SmaliMethod object at 0x1089ded30>, <smali.SmaliObject.SmaliMethod object at 0x10c7c9e80>, 'REVISED_METHOD']
[None, <smali.SmaliObject.SmaliMethod object at 0x10c7c9f28>, 'NOT_FOUND']
[<smali.SmaliObject.SmaliField object at 0x1089de5f8>, None, 'NOT_FOUND']
[None, <smali.SmaliObject.SmaliField object at 0x10c7c96d8>, 'NOT_FOUND']
[None, <smali.SmaliObject.SmaliField object at 0x10c7c9630>, 'NOT_FOUND']
[None, <smali.SmaliObject.SmaliField object at 0x10c7c9588>, 'NOT_FOUND']
[None, <smali.SmaliObject.SmaliField object at 0x10c7abc88>, 'NOT_FOUND']
[None, <smali.SmaliObject.SmaliField object at 0x10c7ab978>, 'NOT_FOUND']
If we look deeply on the first change:
>>> changes[0][1][0][0].getFullSignature()
'com.facebook.katana.service.method.HttpOperation.<clinit>()V'
>>> changes[0][1][0][1].getFullSignature()
'com.facebook.katana.service.method.HttpOperation.<clinit>()V'
>>> changes[0][1][0][2]
'REVISED_METHOD'
We see that the method com.facebook.katana.service.method.HttpOperation.<clinit>()V
has been revised from v1 to v2.
Here we observe the third change:
>>> changes[0][1][2]
[None, <smali.SmaliObject.SmaliMethod object at 0x10c7c9f28>, 'NOT_FOUND']
>>> changes[0][1][2][1].getFullSignature()
'com.facebook.katana.service.method.HttpOperation.getUserAgentWithBGField(Landroid/content/Context;)Ljava/lang/String;'
>>> changes[0][1][2][2]
'NOT_FOUND'
We cas see that on v1, com.facebook.katana.service.method.HttpOperation.getUserAgentWithBGField(Landroid/content/Context;)Ljava/lang/String;
was NOT_FOUND
which means that this method has been added.
myProject.differences(self, otherProject, ignores, process_inner_classes=True)
The difference
method takes as a first parameter a list of ignored items which can include constants from the ComparisonIgnores
file (located in smali
folder):
CLASS_NAME = "CLASS_NAME"
CLASS_SUPER = "CLASS_SUPER"
CLASS_IMPLEMENTS = "CLASS_IMPLEMENTS"
CLASS_METHODS = "CLASS_METHODS"
CLASS_FIELDS = "CLASS_FIELDS"
METHOD_PARAMS = "METHOD_PARAMS"
METHOD_RETURN = "METHOD_RETURN"
FIELD_NAME = "FIELD_NAME"
FIELD_TYPE = "FIELD_TYPE"
FIELD_INIT = "FIELD_INIT"
WITHLINES_SOURCECODE = "WITHLINES_SOURCECODE"
WITHLINES_NAME = "WITHLINES_NAME"
ANOT_MOD_MODIFIERS = "ANOT_MOD_MODIFIERS"
ANOT_MOD_ANNOTATIONS = "ANOT_MOD_ANNOTATIONS"
Moreover, the second parameter is a boolean which instruct if inner classes
should be proceeded or not (default True
).