@@ -32,6 +32,7 @@
Loading
32 32
DEX_FILE_MAGIC_36 = b'dex\n036\x00'
33 33
DEX_FILE_MAGIC_37 = b'dex\n037\x00'
34 34
DEX_FILE_MAGIC_38 = b'dex\n038\x00'
35 +
DEX_FILE_MAGIC_39 = b'dex\n039\x00'
35 36
36 37
ODEX_FILE_MAGIC_35 = b'dey\n035\x00'
37 38
ODEX_FILE_MAGIC_36 = b'dey\n036\x00'
@@ -1142,6 +1143,109 @@
Loading
1142 1143
        return length
1143 1144
1144 1145
1146 +
class HiddenApiClassDataItem:
1147 +
    """
1148 +
    This class can parse an hiddenapi_class_data_item of a dex file (from Android 10 dex version 039)
1149 +
1150 +
    :param buff: a string which represents a Buff object of the hiddenapi_class_data_item
1151 +
    :type buff: Buff object
1152 +
    :param cm: a ClassManager object
1153 +
    :type cm: :class:`ClassManager`
1154 +
    """
1155 +
1156 +
    class RestrictionApiFlag(IntEnum):
1157 +
        WHITELIST = 0
1158 +
        GREYLIST = 1
1159 +
        BLACKLIST = 2
1160 +
        GREYLIST_MAX_O = 3
1161 +
        GREYLIST_MAX_P = 4
1162 +
        GREYLIST_MAX_Q = 5
1163 +
        GREYLIST_MAX_R = 6
1164 +
1165 +
    class DomapiApiFlag(IntEnum):
1166 +
        NONE = 0
1167 +
        CORE_PLATFORM_API = 1
1168 +
        TEST_API = 2
1169 +
1170 +
    def __init__(self, buff, cm):
1171 +
        self.CM = cm
1172 +
1173 +
        self.offset = buff.get_idx()
1174 +
1175 +
        self.section_size, = cm.packer["I"].unpack(buff.read(4))
1176 +
1177 +
        # Find the end of the offsets array (first non-zero offset entry is the start of `flags`)
1178 +
        offsets_size = 0
1179 +
        i = 0
1180 +
        while buff.get_idx() - self.offset < self.section_size:
1181 +
            if offsets_size != 0 and i >= offsets_size:
1182 +
                break
1183 +
            offset, = cm.packer["I"].unpack(buff.read(4))
1184 +
            if offset != 0 and offsets_size == 0:
1185 +
                offsets_size = (offset - 4) // 4
1186 +
            i += 1
1187 +
1188 +
        self.flags = []
1189 +
        for i in range(offsets_size):
1190 +
            flag = readuleb128(cm, buff)
1191 +
            self.flags.append((
1192 +
                self.RestrictionApiFlag(flag & 0b111),
1193 +
                self.DomapiApiFlag(flag >> 3)))
1194 +
1195 +
    def get_section_size(self):
1196 +
        """
1197 +
        Return the total size of this section
1198 +
1199 +
        :rtype: int
1200 +
        """
1201 +
        return self.section_size
1202 +
1203 +
    def get_flags(self, idx):
1204 +
        """
1205 +
        Return a tuple of the flags per class
1206 +
1207 +
        :param idx: The index to return the flags of (index of the class)
1208 +
        :type idx: int
1209 +
1210 +
        :rtype: Tuple[RestrictionApiFlag, DomainApiFlag]
1211 +
        """
1212 +
        return self.flags[idx]
1213 +
1214 +
    def set_off(self, off):
1215 +
        self.offset = off
1216 +
1217 +
    def get_off(self):
1218 +
        return self.offset
1219 +
1220 +
    def show(self):
1221 +
        bytecode._PrintSubBanner("HiddenApi Class Data Item")
1222 +
        bytecode._PrintDefault(
1223 +
            "section_size=0x%x\n"
1224 +
            % (self.section_size,))
1225 +
1226 +
        for i, (rf, df) in enumerate(self.flags):
1227 +
            bytecode._PrintDefault(
1228 +
                "[%u] %s, %s\n"
1229 +
                % (i, rf, df))
1230 +
1231 +
    def get_obj(self):
1232 +
        base = 4 + len(self.flags)
1233 +
        raw_offsets = b''
1234 +
        raw_flags = b''
1235 +
        for rf, df in self.flags:
1236 +
            raw_offsets += self.CM.packer["I"].pack(base + len(raw_flags))
1237 +
            raw_flags += writeuleb128(self.CM, (df.value << 3) | rf.value)
1238 +
1239 +
        return (self.CM.packer["I"].pack(self.section_size) +
1240 +
                raw_offsets + raw_flags)
1241 +
1242 +
    def get_raw(self):
1243 +
        return self.get_obj()
1244 +
1245 +
    def get_length(self):
1246 +
        return self.section_size
1247 +
1248 +
1145 1249
class TypeItem:
1146 1250
    """
1147 1251
    This class can parse a type_item of a dex file
@@ -6956,6 +7060,11 @@
Loading
6956 7060
            buff.set_idx(self.offset + (self.offset % 4))
6957 7061
            self.item = [AnnotationsDirectoryItem(buff, cm) for _ in range(self.size)]
6958 7062
7063 +
        elif TypeMapItem.HIDDENAPI_CLASS_DATA_ITEM == self.type:
7064 +
            # Byte aligned
7065 +
            buff.set_idx(self.offset)
7066 +
            self.item = HiddenApiClassDataItem(buff, cm)
7067 +
6959 7068
        elif TypeMapItem.ANNOTATION_SET_REF_LIST == self.type:
6960 7069
            # 4-byte aligned
6961 7070
            buff.set_idx(self.offset + (self.offset % 4))
@@ -7208,12 +7317,17 @@
Loading
7208 7317
        for i in self.__manage_item[TypeMapItem.ANNOTATION_OFF_ITEM]:
7209 7318
            if i.get_off() == off:
7210 7319
                return i
7211 -
    
7320 +
7212 7321
    def get_annotation_item(self, off):
7213 7322
        for i in self.__manage_item[TypeMapItem.ANNOTATION_ITEM]:
7214 7323
            if i.get_off() == off:
7215 7324
                return i
7216 7325
7326 +
    def get_hiddenapi_class_data_item(self, off):
7327 +
        for i in self.__manage_item[TypeMapItem.HIDDENAPI_CLASS_DATA_ITEM]:
7328 +
            if i.get_off() == off:
7329 +
                return i
7330 +
7217 7331
    def get_string(self, idx):
7218 7332
        """
7219 7333
        Return a string from the string table at index `idx`
@@ -7577,6 +7691,7 @@
Loading
7577 7691
            self.codes = self.map_list.get_item_type(TypeMapItem.CODE_ITEM)
7578 7692
            self.strings = self.map_list.get_item_type(TypeMapItem.STRING_DATA_ITEM)
7579 7693
            self.debug = self.map_list.get_item_type(TypeMapItem.DEBUG_INFO_ITEM)
7694 +
            self.hidden_api = self.map_list.get_item_type(TypeMapItem.HIDDENAPI_CLASS_DATA_ITEM)
7580 7695
7581 7696
        self._flush()
7582 7697
@@ -7696,6 +7811,14 @@
Loading
7696 7811
        """
7697 7812
        return self.header
7698 7813
7814 +
    def get_hidden_api(self):
7815 +
        """
7816 +
        This function returns the hidden api item (from Android 10)
7817 +
7818 +
        :rtype: :class:`HiddenApiClassDataItem` object
7819 +
        """
7820 +
        return self.hidden_api
7821 +
7699 7822
    def get_class_manager(self):
7700 7823
        """
7701 7824
        This function returns a ClassManager object which allow you to get

@@ -325,6 +325,17 @@
Loading
325 325
        meths = [x.name for x in dx.get_permission_usage('android.permission.ACCESS_NETWORK_STATE')]
326 326
        self.assertListEqual(sorted(meths), sorted(network_meths))
327 327
328 +
    def testHiddenAnnotation(self):
329 +
        a, d, dx = AnalyzeAPK("examples/android/TestsAnnotation/OPCommonTelephony.jar")
330 +
331 +
        class1 = dx.classes["Lvendor/mediatek/hardware/radio_op/V1_2/IRadioIndicationOp$Stub;"]
332 +
        self.assertEqual(class1.restriction_flag, dvm.HiddenApiClassDataItem.RestrictionApiFlag.WHITELIST)
333 +
        self.assertEqual(class1.domain_flag, dvm.HiddenApiClassDataItem.DomapiApiFlag.CORE_PLATFORM_API)
334 +
335 +
        class1 = dx.classes["Lcom/mediatek/opcommon/telephony/MtkRILConstantsOp;"]
336 +
        self.assertEqual(class1.restriction_flag, dvm.HiddenApiClassDataItem.RestrictionApiFlag.BLACKLIST)
337 +
        self.assertEqual(class1.domain_flag, dvm.HiddenApiClassDataItem.DomapiApiFlag.NONE)
338 +
328 339
329 340
if __name__ == '__main__':
330 341
    unittest.main()

@@ -353,6 +353,10 @@
Loading
353 353
        self.xrefnewinstance = set()
354 354
        self.xrefconstclass = set()
355 355
356 +
        # For Android 10+
357 +
        self.restriction_flag = None
358 +
        self.domain_flag = None
359 +
356 360
        # Reserved for further use
357 361
        self.apilist = None
358 362
@@ -1301,6 +1305,34 @@
Loading
1301 1305
        """
1302 1306
        return self.orig_class
1303 1307
1308 +
    def set_restriction_flag(self, flag):
1309 +
        """
1310 +
        Set the level of restriction for this class (hidden level, from Android 10)
1311 +
        (only applicable to internal classes)
1312 +
1313 +
        :param flag: The flag to set to
1314 +
        :type flag: androguard.core.bytecodes.dvm.HiddenApiClassDataItem.RestrictionApiFlag
1315 +
        """
1316 +
        if self.is_external():
1317 +
            raise RuntimeError(
1318 +
                "Can\'t set restriction flag for external class: %s"
1319 +
                % (self.orig_class.name,))
1320 +
        self.restriction_flag = flag
1321 +
1322 +
    def set_domain_flag(self, flag):
1323 +
        """
1324 +
        Set the api domain for this class (hidden level, from Android 10)
1325 +
        (only applicable to internal classes)
1326 +
1327 +
        :param flag: The flag to set to
1328 +
        :type flag: androguard.core.bytecodes.dvm.HiddenApiClassDataItem.DomainApiFlag
1329 +
        """
1330 +
        if self.is_external():
1331 +
            raise RuntimeError(
1332 +
                "Can\'t set domain flag for external class: %s"
1333 +
                % (self.orig_class.name,))
1334 +
        self.domain_flag = flag
1335 +
1304 1336
    # Alias
1305 1337
    get_class = get_vm_class
1306 1338
@@ -1397,12 +1429,20 @@
Loading
1397 1429
        log.info("Adding DEX file version {}".format(vm.version))
1398 1430
        # TODO: This step can easily be multithreaded, as there is no dependecy between the objects at this stage
1399 1431
        tic = time.time()
1400 -
        for current_class in vm.get_classes():
1432 +
        for i, current_class in enumerate(vm.get_classes()):
1401 1433
            self.classes[current_class.get_name()] = ClassAnalysis(current_class)
1434 +
            new_class = self.classes[current_class.get_name()]
1435 +
            # Fix up the hidden api annotations (Android 10)
1436 +
            hidden_api = vm.get_hidden_api()
1437 +
            if hidden_api:
1438 +
                rf, df = hidden_api.get_flags(i)
1439 +
                new_class.set_restriction_flag(rf)
1440 +
                new_class.set_domain_flag(df)
1441 +
1402 1442
            for method in current_class.get_methods():
1403 1443
                self.methods[method] = MethodAnalysis(vm, method)
1404 1444
1405 -
                self.classes[current_class.get_name()].add_method(self.methods[method])
1445 +
                new_class.add_method(self.methods[method])
1406 1446
1407 1447
                # Store for faster lookup during create_xrefs
1408 1448
                m_hash = (current_class.get_name(), method.get_name(), str(method.get_descriptor()))

@@ -72,6 +72,7 @@
Loading
72 72
    ANNOTATION_ITEM = 0x2004
73 73
    ENCODED_ARRAY_ITEM = 0x2005
74 74
    ANNOTATIONS_DIRECTORY_ITEM = 0x2006
75 +
    HIDDENAPI_CLASS_DATA_ITEM = 0xf000
75 76
76 77
    @staticmethod
77 78
    def _get_dependencies():
@@ -106,7 +107,8 @@
Loading
106 107
             {TypeMapItem.PROTO_ID_ITEM, TypeMapItem.STRING_ID_ITEM, TypeMapItem.TYPE_ID_ITEM,
107 108
              TypeMapItem.FIELD_ID_ITEM, TypeMapItem.METHOD_ID_ITEM}),
108 109
            (TypeMapItem.ANNOTATIONS_DIRECTORY_ITEM,
109 -
             {TypeMapItem.FIELD_ID_ITEM, TypeMapItem.METHOD_ID_ITEM, TypeMapItem.ANNOTATION_SET_ITEM})
110 +
             {TypeMapItem.FIELD_ID_ITEM, TypeMapItem.METHOD_ID_ITEM, TypeMapItem.ANNOTATION_SET_ITEM}),
111 +
            (TypeMapItem.HIDDENAPI_CLASS_DATA_ITEM, set()),
110 112
        ])
111 113
112 114
    @staticmethod
Files Coverage
androguard 73.56%
tests 96.96%
Project Totals (51 files) 76.85%
1088.1
TRAVIS_PYTHON_VERSION=3.6
TRAVIS_OS_NAME=linux
1088.2
TRAVIS_PYTHON_VERSION=3.7
TRAVIS_OS_NAME=linux
1088.3
TRAVIS_PYTHON_VERSION=3.5
TRAVIS_OS_NAME=linux
1088.4
TRAVIS_PYTHON_VERSION=3.8
TRAVIS_OS_NAME=linux

No yaml found.

Create your codecov.yml to customize your Codecov experience

Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading