tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

convert_dex_profile_tests.py (9762B)


      1 #!/usr/bin/env python3
      2 # Copyright 2018 The Chromium Authors
      3 # Use of this source code is governed by a BSD-style license that can be
      4 # found in the LICENSE file.
      5 
      6 """Tests for convert_dex_profile.
      7 
      8 Can be run from build/android/:
      9  $ cd build/android
     10  $ python convert_dex_profile_tests.py
     11 """
     12 
     13 import os
     14 import sys
     15 import tempfile
     16 import unittest
     17 
     18 import convert_dex_profile as cp
     19 
     20 sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'gyp'))
     21 from util import build_utils
     22 
     23 cp.logging.disable(cp.logging.CRITICAL)
     24 
     25 # There are two obfuscations used in the tests below, each with the same
     26 # unobfuscated profile. The first, corresponding to DEX_DUMP, PROGUARD_MAPPING,
     27 # and OBFUSCATED_PROFILE, has an ambiguous method a() which is mapped to both
     28 # getInstance and initialize. The second, corresponding to DEX_DUMP_2,
     29 # PROGUARD_MAPPING_2 and OBFUSCATED_PROFILE_2, removes the ambiguity.
     30 
     31 DEX_DUMP = """
     32 
     33 Class descriptor  : 'La;'
     34  Direct methods    -
     35      #0              : (in La;)
     36        name          : '<clinit>'
     37        type          : '(Ljava/lang/String;)V'
     38        code          -
     39        catches       : 1
     40                0x000f - 0x001e
     41                  <any> -> 0x0093
     42        positions     :
     43                0x0001 line=310
     44                0x0057 line=313
     45        locals        :
     46      #1              : (in La;)
     47        name          : '<init>'
     48        type          : '()V'
     49        positions     :
     50        locals        :
     51  Virtual methods   -
     52      #0              : (in La;)
     53        name          : 'a'
     54        type          : '(Ljava/lang/String;)I'
     55        positions     :
     56          0x0000 line=2
     57          0x0003 line=3
     58          0x001b line=8
     59        locals        :
     60          0x0000 - 0x0021 reg=3 this La;
     61      #1              : (in La;)
     62        name          : 'a'
     63        type          : '(Ljava/lang/Object;)I'
     64        positions     :
     65          0x0000 line=8
     66          0x0003 line=9
     67        locals        :
     68          0x0000 - 0x0021 reg=3 this La;
     69      #2              : (in La;)
     70        name          : 'b'
     71        type          : '()La;'
     72        positions     :
     73          0x0000 line=1
     74        locals        :
     75 """
     76 
     77 # pylint: disable=line-too-long
     78 PROGUARD_MAPPING = \
     79 """org.chromium.Original -> a:
     80    org.chromium.Original sDisplayAndroidManager -> e
     81    org.chromium.Original another() -> b
     82    4:4:void inlined():237:237 -> a
     83    4:4:org.chromium.Original getInstance():203 -> a
     84    5:5:void org.chromium.Original$Subclass.<init>(org.chromium.Original,byte):130:130 -> a
     85    5:5:void initialize():237 -> a
     86    5:5:org.chromium.Original getInstance():203 -> a
     87    6:6:void initialize():237:237 -> a
     88    9:9:android.content.Context org.chromium.base.ContextUtils.getApplicationContext():49:49 -> a
     89    9:9:android.content.Context getContext():219 -> a
     90    9:9:void initialize():245 -> a
     91    9:9:org.chromium.Original getInstance():203 -> a"""
     92 
     93 OBFUSCATED_PROFILE = \
     94 """La;
     95 PLa;->b()La;
     96 SLa;->a(Ljava/lang/Object;)I
     97 HPLa;->a(Ljava/lang/String;)I"""
     98 
     99 DEX_DUMP_2 = """
    100 
    101 Class descriptor  : 'La;'
    102  Direct methods    -
    103      #0              : (in La;)
    104        name          : '<clinit>'
    105        type          : '(Ljava/lang/String;)V'
    106        code          -
    107        catches       : 1
    108                0x000f - 0x001e
    109                  <any> -> 0x0093
    110        positions     :
    111                0x0001 line=310
    112                0x0057 line=313
    113        locals        :
    114      #1              : (in La;)
    115        name          : '<init>'
    116        type          : '()V'
    117        positions     :
    118        locals        :
    119  Virtual methods   -
    120      #0              : (in La;)
    121        name          : 'a'
    122        type          : '(Ljava/lang/String;)I'
    123        positions     :
    124          0x0000 line=2
    125          0x0003 line=3
    126          0x001b line=8
    127        locals        :
    128          0x0000 - 0x0021 reg=3 this La;
    129      #1              : (in La;)
    130        name          : 'c'
    131        type          : '(Ljava/lang/Object;)I'
    132        positions     :
    133          0x0000 line=8
    134          0x0003 line=9
    135        locals        :
    136          0x0000 - 0x0021 reg=3 this La;
    137      #2              : (in La;)
    138        name          : 'b'
    139        type          : '()La;'
    140        positions     :
    141          0x0000 line=1
    142        locals        :
    143 """
    144 
    145 # pylint: disable=line-too-long
    146 PROGUARD_MAPPING_2 = \
    147 """org.chromium.Original -> a:
    148    org.chromium.Original sDisplayAndroidManager -> e
    149    org.chromium.Original another() -> b
    150    void initialize() -> c
    151    org.chromium.Original getInstance():203 -> a
    152    4:4:void inlined():237:237 -> a"""
    153 
    154 OBFUSCATED_PROFILE_2 = \
    155 """La;
    156 PLa;->b()La;
    157 HPSLa;->a()La;
    158 HPLa;->c()V"""
    159 
    160 UNOBFUSCATED_PROFILE = \
    161 """Lorg/chromium/Original;
    162 PLorg/chromium/Original;->another()Lorg/chromium/Original;
    163 HPSLorg/chromium/Original;->getInstance()Lorg/chromium/Original;
    164 HPLorg/chromium/Original;->initialize()V"""
    165 
    166 class GenerateProfileTests(unittest.TestCase):
    167  def testProcessDex(self):
    168    dex = cp.ProcessDex(DEX_DUMP.splitlines())
    169    self.assertIsNotNone(dex['a'])
    170 
    171    self.assertEqual(len(dex['a'].FindMethodsAtLine('<clinit>', 311, 313)), 1)
    172    self.assertEqual(len(dex['a'].FindMethodsAtLine('<clinit>', 309, 315)), 1)
    173    clinit = dex['a'].FindMethodsAtLine('<clinit>', 311, 313)[0]
    174    self.assertEqual(clinit.name, '<clinit>')
    175    self.assertEqual(clinit.return_type, 'V')
    176    self.assertEqual(clinit.param_types, 'Ljava/lang/String;')
    177 
    178    self.assertEqual(len(dex['a'].FindMethodsAtLine('a', 8, None)), 2)
    179    self.assertIsNone(dex['a'].FindMethodsAtLine('a', 100, None))
    180 
    181 # pylint: disable=protected-access
    182  def testProcessProguardMapping(self):
    183    dex = cp.ProcessDex(DEX_DUMP.splitlines())
    184    mapping, reverse = cp.ProcessProguardMapping(
    185        PROGUARD_MAPPING.splitlines(), dex)
    186 
    187    self.assertEqual('La;', reverse.GetClassMapping('Lorg/chromium/Original;'))
    188 
    189    getInstance = cp.Method(
    190        'getInstance', 'Lorg/chromium/Original;', '', 'Lorg/chromium/Original;')
    191    initialize = cp.Method('initialize', 'Lorg/chromium/Original;', '', 'V')
    192    another = cp.Method(
    193        'another', 'Lorg/chromium/Original;', '', 'Lorg/chromium/Original;')
    194    subclassInit = cp.Method(
    195        '<init>', 'Lorg/chromium/Original$Subclass;',
    196        'Lorg/chromium/Original;B', 'V')
    197 
    198    mapped = mapping.GetMethodMapping(
    199        cp.Method('a', 'La;', 'Ljava/lang/String;', 'I'))
    200    self.assertEqual(len(mapped), 2)
    201    self.assertIn(getInstance, mapped)
    202    self.assertNotIn(subclassInit, mapped)
    203    self.assertNotIn(
    204        cp.Method('inlined', 'Lorg/chromium/Original;', '', 'V'), mapped)
    205    self.assertIn(initialize, mapped)
    206 
    207    mapped = mapping.GetMethodMapping(
    208        cp.Method('a', 'La;', 'Ljava/lang/Object;', 'I'))
    209    self.assertEqual(len(mapped), 1)
    210    self.assertIn(getInstance, mapped)
    211 
    212    mapped = mapping.GetMethodMapping(cp.Method('b', 'La;', '', 'La;'))
    213    self.assertEqual(len(mapped), 1)
    214    self.assertIn(another, mapped)
    215 
    216    for from_method, to_methods in mapping._method_mapping.items():
    217      for to_method in to_methods:
    218        self.assertIn(from_method, reverse.GetMethodMapping(to_method))
    219    for from_class, to_class in mapping._class_mapping.items():
    220      self.assertEqual(from_class, reverse.GetClassMapping(to_class))
    221 
    222  def testProcessProfile(self):
    223    dex = cp.ProcessDex(DEX_DUMP.splitlines())
    224    mapping, _ = cp.ProcessProguardMapping(PROGUARD_MAPPING.splitlines(), dex)
    225    profile = cp.ProcessProfile(OBFUSCATED_PROFILE.splitlines(), mapping)
    226 
    227    getInstance = cp.Method(
    228        'getInstance', 'Lorg/chromium/Original;', '', 'Lorg/chromium/Original;')
    229    initialize = cp.Method('initialize', 'Lorg/chromium/Original;', '', 'V')
    230    another = cp.Method(
    231        'another', 'Lorg/chromium/Original;', '', 'Lorg/chromium/Original;')
    232 
    233    self.assertIn('Lorg/chromium/Original;', profile._classes)
    234    self.assertIn(getInstance, profile._methods)
    235    self.assertIn(initialize, profile._methods)
    236    self.assertIn(another, profile._methods)
    237 
    238    self.assertEqual(profile._methods[getInstance], set(['H', 'S', 'P']))
    239    self.assertEqual(profile._methods[initialize], set(['H', 'P']))
    240    self.assertEqual(profile._methods[another], set(['P']))
    241 
    242  def testEndToEnd(self):
    243    dex = cp.ProcessDex(DEX_DUMP.splitlines())
    244    mapping, _ = cp.ProcessProguardMapping(PROGUARD_MAPPING.splitlines(), dex)
    245 
    246    profile = cp.ProcessProfile(OBFUSCATED_PROFILE.splitlines(), mapping)
    247    with tempfile.NamedTemporaryFile() as temp:
    248      profile.WriteToFile(temp.name)
    249      with open(temp.name, 'r') as f:
    250        for a, b in zip(sorted(f), sorted(UNOBFUSCATED_PROFILE.splitlines())):
    251          self.assertEqual(a.strip(), b.strip())
    252 
    253  def testObfuscateProfile(self):
    254    with build_utils.TempDir() as temp_dir:
    255      # The dex dump is used as the dexfile, by passing /bin/cat as the dexdump
    256      # program.
    257      dex_path = os.path.join(temp_dir, 'dexdump')
    258      with open(dex_path, 'w') as dex_file:
    259        dex_file.write(DEX_DUMP_2)
    260      mapping_path = os.path.join(temp_dir, 'mapping')
    261      with open(mapping_path, 'w') as mapping_file:
    262        mapping_file.write(PROGUARD_MAPPING_2)
    263      unobfuscated_path = os.path.join(temp_dir, 'unobfuscated')
    264      with open(unobfuscated_path, 'w') as unobfuscated_file:
    265        unobfuscated_file.write(UNOBFUSCATED_PROFILE)
    266      obfuscated_path = os.path.join(temp_dir, 'obfuscated')
    267      cp.ObfuscateProfile(unobfuscated_path, dex_path, mapping_path, '/bin/cat',
    268                          obfuscated_path)
    269      with open(obfuscated_path) as obfuscated_file:
    270        obfuscated_profile = sorted(obfuscated_file.readlines())
    271      for a, b in zip(
    272          sorted(OBFUSCATED_PROFILE_2.splitlines()), obfuscated_profile):
    273        self.assertEqual(a.strip(), b.strip())
    274 
    275 
    276 if __name__ == '__main__':
    277  unittest.main()