def testReadPlistCanParseXMLPlistAtOffset(self): for _ in range(5): self.min_xml.seek(0, os.SEEK_SET) rand_int = random.randint(0, 2048) padding = "A" * rand_int padded_xml_plist = padding + self.min_xml.read() fd = StringIO.StringIO(padded_xml_plist) fd.seek(rand_int, os.SEEK_SET) binplist.readPlist(fd)
def testReadPlist(self): blank_file = StringIO.StringIO() self.assertRaises(binplist.FormatError, binplist.readPlist, blank_file) bplist15 = StringIO.StringIO("bplist15") self.assertRaises(binplist.FormatError, binplist.readPlist, bplist15) # Incomplete XML does not parse (expat error from plistlib is caught) xml_file = StringIO.StringIO("<xml") self.assertRaises(binplist.FormatError, binplist.readPlist, xml_file) # Check that a minimal XML plist does parse. # We're not testing full XML parsing per se as that's plistlib's tests job. binplist.readPlist(self.min_xml)
def Run(self, args): self.context = args.context self.filter_query = args.query with vfs.VFSOpen(args.pathspec, progress_callback=self.Progress) as fd: data = fd.Read(self.MAX_PLIST_SIZE) plist = binplist.readPlist(cStringIO.StringIO(data)) # Create the query parser parser = plist_lib.PlistFilterParser(self.filter_query).Parse() filter_imp = plist_lib.PlistFilterImplementation matcher = parser.Compile(filter_imp) if self.context: # Obtain the values for the context using the value expander value_expander = filter_imp.FILTERS["ValueExpander"] iterator = value_expander().Expand(plist, self.context) else: # If we didn't get a context, the context is the whole plist iterator = [plist] reply = protodict.RDFValueArray() for item in iterator: # As we're setting the context manually, we need to account for types if isinstance(item, types.ListType): for sub_item in item: partial_plist = plist_lib.PlistValueToPlainValue(sub_item) if matcher.Matches(partial_plist): reply.Append(sub_item) else: partial_plist = plist_lib.PlistValueToPlainValue(item) if matcher.Matches(partial_plist): reply.Append(partial_plist) self.SendReply(reply)
def Run(self, args): self.context = args.context self.filter_query = args.query with vfs.VFSOpen(args.pathspec, progress_callback=self.Progress) as fd: data = fd.Read(self.MAX_PLIST_SIZE) plist = binplist.readPlist(cStringIO.StringIO(data)) # Create the query parser parser = plist_lib.PlistFilterParser(self.filter_query).Parse() filter_imp = plist_lib.PlistFilterImplementation matcher = parser.Compile(filter_imp) if self.context: # Obtain the values for the context using the value expander value_expander = filter_imp.FILTERS["ValueExpander"] iterator = value_expander().Expand(plist, self.context) else: # If we didn't get a context, the context is the whole plist iterator = [plist] reply = rdfvalue.RDFValueArray() for item in iterator: # As we're setting the context manually, we need to account for types if isinstance(item, types.ListType): for sub_item in item: partial_plist = plist_lib.PlistValueToPlainValue(sub_item) if matcher.Matches(partial_plist): reply.Append(sub_item) else: partial_plist = plist_lib.PlistValueToPlainValue(item) if matcher.Matches(partial_plist): reply.Append(partial_plist) self.SendReply(reply)
def GetTopLevel(self, file_object, file_name=u''): """Returns the deserialized content of a plist as a dictionary object. Args: file_object: A file-like object to parse. file_name: The name of the file-like object. Returns: A dictionary object representing the contents of the plist. Raises: UnableToParseFile: when the file cannot be parsed. """ # Note that binplist.readPlist does not seek to offset 0. try: top_level_object = binplist.readPlist(file_object) except binplist.FormatError as exception: if not isinstance(exception, py2to3.BYTES_TYPE): error_string = str(exception).decode(u'utf8', errors=u'replace') else: error_string = exception raise errors.UnableToParseFile( u'File is not a plist file: {0:s}'.format(error_string)) except (AttributeError, LookupError, ValueError, binascii.Error) as exception: raise errors.UnableToParseFile( u'Unable to parse XML file, reason: {0:s}'.format(exception)) except OverflowError as exception: raise errors.UnableToParseFile( u'Unable to parse: {0:s} with error: {1:s}'.format( file_name, exception)) if not top_level_object: raise errors.UnableToParseFile( u'File is not a plist: missing top level object') # Since we are using readPlist from binplist now instead of manually # opening the binary plist file we loose this option. Keep it commented # out for now but this needs to be tested a bit more. # TODO: Re-evaluate if we can delete this or still require it. #if bpl.is_corrupt: # logging.warning( # u'Corruption detected in binary plist: {0:s}'.format(file_name)) return top_level_object
def GetTopLevel(self, file_object, file_name=u''): """Returns the deserialized content of a plist as a dictionary object. Args: file_object: A file-like object to parse. file_name: The name of the file-like object. Returns: A dictionary object representing the contents of the plist. Raises: UnableToParseFile: when the file cannot be parsed. """ # Note that binplist.readPlist does not seek to offset 0. try: top_level_object = binplist.readPlist(file_object) except binplist.FormatError as exception: if not isinstance(exception, py2to3.BYTES_TYPE): error_string = str(exception).decode(u'utf8', errors=u'replace') else: error_string = exception raise errors.UnableToParseFile( u'File is not a plist file: {0:s}'.format(error_string)) except ( AttributeError, LookupError, ValueError, binascii.Error) as exception: raise errors.UnableToParseFile( u'Unable to parse XML file, reason: {0:s}'.format(exception)) except OverflowError as exception: raise errors.UnableToParseFile( u'Unable to parse: {0:s} with error: {1:s}'.format( file_name, exception)) if not top_level_object: raise errors.UnableToParseFile( u'File is not a plist: missing top level object') # Since we are using readPlist from binplist now instead of manually # opening the binary plist file we loose this option. Keep it commented # out for now but this needs to be tested a bit more. # TODO: Re-evaluate if we can delete this or still require it. #if bpl.is_corrupt: # logging.warning( # u'Corruption detected in binary plist: {0:s}'.format(file_name)) return top_level_object
def Parse(self, cmd, args, stdout, stderr, return_val, time_taken, knowledge_base): """Parse the system profiler output. We get it in the form of a plist.""" _ = stderr, time_taken, args, knowledge_base # Unused self.CheckReturn(cmd, return_val) serial_number = [] hardware_list = [] plist = binplist.readPlist(cStringIO.StringIO(stdout)) if len(plist) > 1: raise parsers.ParseError("SPHardwareDataType plist has too many items.") hardware_list = plist[0]["_items"][0] serial_number = hardware_list["serial_number"] yield rdf_client.HardwareInfo(serial_number=serial_number)
def Parse(self, cmd, args, stdout, stderr, return_val, time_taken, knowledge_base): """Parse the system profiler output. We get it in the form of a plist.""" _ = stderr, time_taken, args, knowledge_base # Unused self.CheckReturn(cmd, return_val) serial_number = [] hardware_list = [] plist = binplist.readPlist(cStringIO.StringIO(stdout)) if len(plist) > 1: raise parsers.ParseError("SPHardwareDataType plist has too many items.") hardware_list = plist[0]["_items"][0] serial_number = hardware_list["serial_number"] yield rdfvalue.HardwareInfo(serial_number=serial_number)
def Read(self, file_object): """Reads a plist from a file-like object. Args: file_object: the file-like object. Raises: IOError: if the plist file-like object cannot be read. """ try: file_object.seek(0, os.SEEK_SET) # Note that binplist.readPlist does not seek to offset 0. self.root_key = binplist.readPlist(file_object) except (AttributeError, binascii.Error, binplist.FormatError, LookupError, OverflowError, ValueError) as exception: raise IOError(exception)
def Read(self, file_object): """Reads a plist from a file-like object. Args: file_object: the file-like object. Raises: IOError: if the plist file-like object cannot be read. """ try: file_object.seek(0, os.SEEK_SET) # Note that binplist.readPlist does not seek to offset 0. self.root_key = binplist.readPlist(file_object) except ( AttributeError, binascii.Error, binplist.FormatError, LookupError, OverflowError, ValueError) as exception: raise IOError(exception)
def Parse(self, statentry, file_object, knowledge_base): """Parse the Plist file.""" plist = binplist.readPlist(file_object) if not isinstance(plist, list): raise parser.ParseError( "InstallHistory plist is a '%s', expecting a list" % type(plist)) for sw in plist: yield rdf_client.SoftwarePackage( name=sw.get("displayName"), version=sw.get("displayVersion"), description=",".join(sw.get("packageIdentifiers")), # TODO(hanuszczak): make installed_on an RDFDatetime installed_on=_DateToEpoch(sw.get("date")), install_state=rdf_client.SoftwarePackage.InstallState.INSTALLED )
def GetTopLevel(self, file_object, file_name=u''): """Returns the deserialized content of a plist as a dictionary object. Args: file_object: A file-like object to parse. file_name: The name of the file-like object. Returns: A dictionary object representing the contents of the plist. """ # Note that binplist.readPlist does not seek to offset 0. try: top_level_object = binplist.readPlist(file_object) except binplist.FormatError as exception: raise errors.UnableToParseFile( u'[{0:s}] File is not a plist file: {1:s}'.format( self.NAME, utils.GetUnicodeString(exception))) except ( LookupError, binascii.Error, ValueError, AttributeError) as exception: raise errors.UnableToParseFile( u'[{0:s}] Unable to parse XML file, reason: {1:s}'.format( self.NAME, exception)) except OverflowError as exception: raise errors.UnableToParseFile( u'[{0:s}] Unable to parse: {1:s} with error: {2:s}'.format( self.NAME, file_name, exception)) if not top_level_object: raise errors.UnableToParseFile( u'[{0:s}] File is not a plist: {1:s}'.format( self.NAME, utils.GetUnicodeString(file_name))) # Since we are using readPlist from binplist now instead of manually # opening the binary plist file we loose this option. Keep it commented # out for now but this needs to be tested a bit more. # TODO: Re-evaluate if we can delete this or still require it. #if bpl.is_corrupt: # logging.warning( # u'[{0:s}] corruption detected in binary plist: {1:s}'.format( # self.NAME, file_name)) return top_level_object
def GetTopLevel(self, file_object, file_name=u''): """Returns the deserialized content of a plist as a dictionary object. Args: file_object: A file-like object to parse. file_name: The name of the file-like object. Returns: A dictionary object representing the contents of the plist. """ # Note that binplist.readPlist does not seek to offset 0. try: top_level_object = binplist.readPlist(file_object) except binplist.FormatError as exception: raise errors.UnableToParseFile( u'[{0:s}] File is not a plist file: {1:s}'.format( self.NAME, utils.GetUnicodeString(exception))) except (LookupError, binascii.Error, ValueError, AttributeError) as exception: raise errors.UnableToParseFile( u'[{0:s}] Unable to parse XML file, reason: {1:s}'.format( self.NAME, exception)) except OverflowError as exception: raise errors.UnableToParseFile( u'[{0:s}] Unable to parse: {1:s} with error: {2:s}'.format( self.NAME, file_name, exception)) if not top_level_object: raise errors.UnableToParseFile( u'[{0:s}] File is not a plist: {1:s}'.format( self.NAME, utils.GetUnicodeString(file_name))) # Since we are using readPlist from binplist now instead of manually # opening the binary plist file we loose this option. Keep it commented # out for now but this needs to be tested a bit more. # TODO: Re-evaluate if we can delete this or still require it. #if bpl.is_corrupt: # logging.warning( # u'[{0:s}] corruption detected in binary plist: {1:s}'.format( # self.NAME, file_name)) return top_level_object
def Parse(self, cmd, args, stdout, stderr, return_val, time_taken, knowledge_base): """Parse the system profiler output. We get it in the form of a plist.""" _ = stderr, time_taken, args, knowledge_base # Unused self.CheckReturn(cmd, return_val) plist = binplist.readPlist(io.BytesIO(stdout)) if len(plist) > 1: raise parser.ParseError( "SPHardwareDataType plist has too many items.") hardware_list = plist[0]["_items"][0] serial_number = getattr(hardware_list, "serial_number", None) system_product_name = getattr(hardware_list, "machine_model", None) bios_version = getattr(hardware_list, "boot_rom_version", None) yield rdf_client.HardwareInfo(serial_number=serial_number, bios_version=bios_version, system_product_name=system_product_name)
def Parse(self, cmd, args, stdout, stderr, return_val, time_taken, knowledge_base): """Parse the system profiler output. We get it in the form of a plist.""" _ = stderr, time_taken, args, knowledge_base # Unused self.CheckReturn(cmd, return_val) plist = binplist.readPlist(io.BytesIO(stdout)) if len(plist) > 1: raise parser.ParseError("SPHardwareDataType plist has too many items.") hardware_list = plist[0]["_items"][0] serial_number = getattr(hardware_list, "serial_number", None) system_product_name = getattr(hardware_list, "machine_model", None) bios_version = getattr(hardware_list, "boot_rom_version", None) yield rdf_client.HardwareInfo( serial_number=serial_number, bios_version=bios_version, system_product_name=system_product_name)
def Parse(self, statentry, file_object, knowledge_base): """Parse the Plist file.""" _ = knowledge_base kwargs = {} try: kwargs["aff4path"] = file_object.urn except AttributeError: pass direct_copy_items = [ "Label", "Disabled", "UserName", "GroupName", "Program", "StandardInPath", "StandardOutPath", "StandardErrorPath", "LimitLoadToSessionType", "EnableGlobbing", "EnableTransactions", "OnDemand", "RunAtLoad", "RootDirectory", "WorkingDirectory", "Umask", "TimeOut", "ExitTimeOut", "ThrottleInterval", "InitGroups", "StartOnMount", "StartInterval", "Debug", "WaitForDebugger", "Nice", "ProcessType", "AbandonProcessGroup", "LowPriorityIO", "LaunchOnlyOnce" ] string_array_items = [ "LimitLoadToHosts", "LimitLoadFromHosts", "LimitLoadToSessionType", "ProgramArguments", "WatchPaths", "QueueDirectories" ] flag_only_items = ["SoftResourceLimits", "HardResourceLimits", "Sockets"] plist = {} try: plist = binplist.readPlist(file_object) except (binplist.FormatError, ValueError, IOError) as e: plist["Label"] = "Could not parse plist: %s" % e # These are items that can be directly copied for key in direct_copy_items: kwargs[key] = plist.get(key) # These could be a string, they could be an array, we don't know and neither # does Apple so we check. for key in string_array_items: elements = plist.get(key) if isinstance(elements, basestring): kwargs[key] = [elements] else: kwargs[key] = elements # These are complex items that can appear in multiple data structures # so we only flag on their existence for key in flag_only_items: if plist.get(key): kwargs[key] = True if plist.get("inetdCompatability") is not None: kwargs["inetdCompatabilityWait"] = plist.get("inetdCompatability").get( "Wait") keepalive = plist.get("KeepAlive") if isinstance(keepalive, bool) or keepalive is None: kwargs["KeepAlive"] = keepalive else: keepalivedict = {} keepalivedict["SuccessfulExit"] = keepalive.get("SuccessfulExit") keepalivedict["NetworkState"] = keepalive.get("NetworkState") pathstates = keepalive.get("PathState") if pathstates is not None: keepalivedict["PathState"] = [] for pathstate in pathstates: keepalivedict["PathState"].append( rdf_plist.PlistBoolDictEntry( name=pathstate, value=pathstates[pathstate])) otherjobs = keepalive.get("OtherJobEnabled") if otherjobs is not None: keepalivedict["OtherJobEnabled"] = [] for otherjob in otherjobs: keepalivedict["OtherJobEnabled"].append( rdf_plist.PlistBoolDictEntry( name=otherjob, value=otherjobs[otherjob])) kwargs["KeepAliveDict"] = rdf_plist.LaunchdKeepAlive(**keepalivedict) envvars = plist.get("EnvironmentVariables") if envvars is not None: kwargs["EnvironmentVariables"] = [] for envvar in envvars: kwargs["EnvironmentVariables"].append( rdf_plist.PlistStringDictEntry(name=envvar, value=envvars[envvar])) startcalendarinterval = plist.get("StartCalendarInterval") if startcalendarinterval is not None: if isinstance(startcalendarinterval, dict): kwargs["StartCalendarInterval"] = [ rdf_plist.LaunchdStartCalendarIntervalEntry( Minute=startcalendarinterval.get("Minute"), Hour=startcalendarinterval.get("Hour"), Day=startcalendarinterval.get("Day"), Weekday=startcalendarinterval.get("Weekday"), Month=startcalendarinterval.get("Month")) ] else: kwargs["StartCalendarInterval"] = [] for entry in startcalendarinterval: kwargs["StartCalendarInterval"].append( rdf_plist.LaunchdStartCalendarIntervalEntry( Minute=entry.get("Minute"), Hour=entry.get("Hour"), Day=entry.get("Day"), Weekday=entry.get("Weekday"), Month=entry.get("Month"))) yield rdf_plist.LaunchdPlist(**kwargs)
def Parse(self, statentry, file_object, knowledge_base): """Parse the Plist file.""" _ = knowledge_base kwargs = {} try: kwargs["aff4path"] = file_object.urn except AttributeError: pass direct_copy_items = [ "Label", "Disabled", "UserName", "GroupName", "Program", "StandardInPath", "StandardOutPath", "StandardErrorPath", "LimitLoadToSessionType", "EnableGlobbing", "EnableTransactions", "OnDemand", "RunAtLoad", "RootDirectory", "WorkingDirectory", "Umask", "TimeOut", "ExitTimeOut", "ThrottleInterval", "InitGroups", "StartOnMount", "StartInterval", "Debug", "WaitForDebugger", "Nice", "ProcessType", "AbandonProcessGroup", "LowPriorityIO", "LaunchOnlyOnce" ] string_array_items = [ "LimitLoadToHosts", "LimitLoadFromHosts", "LimitLoadToSessionType", "ProgramArguments", "WatchPaths", "QueueDirectories" ] flag_only_items = [ "SoftResourceLimits", "HardResourceLimits", "Sockets" ] plist = {} try: plist = binplist.readPlist(file_object) except (binplist.FormatError, ValueError, IOError) as e: plist["Label"] = "Could not parse plist: %s" % e # These are items that can be directly copied for key in direct_copy_items: kwargs[key] = plist.get(key) # These could be a string, they could be an array, we don't know and neither # does Apple so we check. for key in string_array_items: elements = plist.get(key) if isinstance(elements, basestring): kwargs[key] = [elements] else: kwargs[key] = elements # These are complex items that can appear in multiple data structures # so we only flag on their existence for key in flag_only_items: if plist.get(key): kwargs[key] = True if plist.get("inetdCompatability") is not None: kwargs["inetdCompatabilityWait"] = plist.get( "inetdCompatability").get("Wait") keepalive = plist.get("KeepAlive") if isinstance(keepalive, bool) or keepalive is None: kwargs["KeepAlive"] = keepalive else: keepalivedict = {} keepalivedict["SuccessfulExit"] = keepalive.get("SuccessfulExit") keepalivedict["NetworkState"] = keepalive.get("NetworkState") pathstates = keepalive.get("PathState") if pathstates is not None: keepalivedict["PathState"] = [] for pathstate in pathstates: keepalivedict["PathState"].append( rdf_plist.PlistBoolDictEntry( name=pathstate, value=pathstates[pathstate])) otherjobs = keepalive.get("OtherJobEnabled") if otherjobs is not None: keepalivedict["OtherJobEnabled"] = [] for otherjob in otherjobs: keepalivedict["OtherJobEnabled"].append( rdf_plist.PlistBoolDictEntry( name=otherjob, value=otherjobs[otherjob])) kwargs["KeepAliveDict"] = rdf_plist.LaunchdKeepAlive( **keepalivedict) envvars = plist.get("EnvironmentVariables") if envvars is not None: kwargs["EnvironmentVariables"] = [] for envvar in envvars: kwargs["EnvironmentVariables"].append( rdf_plist.PlistStringDictEntry(name=envvar, value=envvars[envvar])) startcalendarinterval = plist.get("StartCalendarInterval") if startcalendarinterval is not None: if isinstance(startcalendarinterval, dict): kwargs["StartCalendarInterval"] = [ rdf_plist.LaunchdStartCalendarIntervalEntry( Minute=startcalendarinterval.get("Minute"), Hour=startcalendarinterval.get("Hour"), Day=startcalendarinterval.get("Day"), Weekday=startcalendarinterval.get("Weekday"), Month=startcalendarinterval.get("Month")) ] else: kwargs["StartCalendarInterval"] = [] for entry in startcalendarinterval: kwargs["StartCalendarInterval"].append( rdf_plist.LaunchdStartCalendarIntervalEntry( Minute=entry.get("Minute"), Hour=entry.get("Hour"), Day=entry.get("Day"), Weekday=entry.get("Weekday"), Month=entry.get("Month"))) yield rdf_plist.LaunchdPlist(**kwargs)