Skip to content

Diffing APKs

Vincenzo Musco edited this page Oct 9, 2018 · 1 revision

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)

Interpreting the output

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 that class_v2 has been added in v2;
  • If class_v2 is None, it means that class_v1 has been removed in v2;
  • 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.

Ignoring some comparisons

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).

Clone this wiki locally