How to parse the Manifest.mbdb file in an iOS 4.0 iTunes Backup

后端 未结 8 1100
小蘑菇
小蘑菇 2020-12-02 03:45

In iOS 4.0 Apple has redesigned the backup process.

iTunes used to store a list of filenames associated with backup files in the Manifest.plist file, but in iOS 4.

8条回答
  •  长情又很酷
    2020-12-02 04:21

    This python script is awesome.

    Here's my Ruby version of it (with minor improvement) and search capabilities. (for iOS 5)

    # encoding: utf-8
    require 'fileutils'
    require 'digest/sha1'
    
    class ManifestParser
      def initialize(mbdb_filename, verbose = false)
        @verbose = verbose
        process_mbdb_file(mbdb_filename)
      end
    
      # Returns the numbers of records in the Manifest files.
      def record_number
        @mbdb.size
      end
    
      # Returns a huge string containing the parsing of the Manifest files.
      def to_s
        s = ''
        @mbdb.each do |v|
          s += "#{fileinfo_str(v)}\n"
        end
        s
      end
    
      def to_file(filename)
        File.open(filename, 'w') do |f|
          @mbdb.each do |v|
            f.puts fileinfo_str(v)
          end
        end
      end
    
      # Copy the backup files to their real path/name.
      # * domain_match Can be a regexp to restrict the files to copy.
      # * filename_match Can be a regexp to restrict the files to copy.
      def rename_files(domain_match = nil, filename_match = nil)
        @mbdb.each do |v|
          if v[:type] == '-' # Only rename files.
            if (domain_match.nil? or v[:domain] =~ domain_match) and (filename_match.nil? or v[:filename] =~ filename_match)
              dst = "#{v[:domain]}/#{v[:filename]}"
              puts "Creating: #{dst}"
              FileUtils.mkdir_p(File.dirname(dst))
              FileUtils.cp(v[:fileID], dst)
            end
          end
        end
      end
    
      # Return the filename that math the given regexp.
      def search(regexp)
        result = Array.new
        @mbdb.each do |v|
          if "#{v[:domain]}::#{v[:filename]}" =~ regexp
            result << v
          end
        end
        result
      end
    
      private
      # Retrieve an integer (big-endian) and new offset from the current offset
      def getint(data, offset, intsize)
        value = 0
        while intsize > 0
          value = (value<<8) + data[offset].ord
          offset += 1
          intsize -= 1
        end
        return value, offset
      end
    
      # Retrieve a string and new offset from the current offset into the data
      def getstring(data, offset)
        return '', offset + 2 if data[offset] == 0xFF.chr and data[offset + 1] == 0xFF.chr # Blank string
        length, offset = getint(data, offset, 2) # 2-byte length
        value = data[offset...(offset + length)]
        return value, (offset + length)
      end
    
      def process_mbdb_file(filename)
        @mbdb = Array.new
        data = File.open(filename, 'rb') { |f| f.read }
        puts "MBDB file read. Size: #{data.size}"
        raise 'This does not look like an MBDB file' if data[0...4] != 'mbdb'
        offset = 4
        offset += 2 # value x05 x00, not sure what this is
        while offset < data.size
          fileinfo = Hash.new
          fileinfo[:start_offset] = offset
          fileinfo[:domain], offset = getstring(data, offset)
          fileinfo[:filename], offset = getstring(data, offset)
          fileinfo[:linktarget], offset = getstring(data, offset)
          fileinfo[:datahash], offset = getstring(data, offset)
          fileinfo[:unknown1], offset = getstring(data, offset)
          fileinfo[:mode], offset = getint(data, offset, 2)
          if (fileinfo[:mode] & 0xE000) == 0xA000 # Symlink
            fileinfo[:type] = 'l'
          elsif (fileinfo[:mode] & 0xE000) == 0x8000 # File
            fileinfo[:type] = '-'
          elsif (fileinfo[:mode] & 0xE000) == 0x4000 # Dir
            fileinfo[:type] = 'd'
          else
            # $stderr.puts "Unknown file type %04x for #{fileinfo_str(f, false)}" % f['mode']
            fileinfo[:type] = '?'
          end
          fileinfo[:unknown2], offset = getint(data, offset, 4)
          fileinfo[:unknown3], offset = getint(data, offset, 4)
          fileinfo[:userid], offset = getint(data, offset, 4)
          fileinfo[:groupid], offset = getint(data, offset, 4)
          fileinfo[:mtime], offset = getint(data, offset, 4)
          fileinfo[:atime], offset = getint(data, offset, 4)
          fileinfo[:ctime], offset = getint(data, offset, 4)
          fileinfo[:filelen], offset = getint(data, offset, 8)
          fileinfo[:flag], offset = getint(data, offset, 1)
          fileinfo[:numprops], offset = getint(data, offset, 1)
          fileinfo[:properties] = Hash.new
          (0...(fileinfo[:numprops])).each do |ii|
            propname, offset = getstring(data, offset)
            propval, offset = getstring(data, offset)
            fileinfo[:properties][propname] = propval
          end
          # Compute the ID of the file.
          fullpath = fileinfo[:domain] + '-' + fileinfo[:filename]
          fileinfo[:fileID] = Digest::SHA1.hexdigest(fullpath)
          # We add the file to the list of files.
          @mbdb << fileinfo
        end
        @mbdb
      end
    
      def modestr(val)
        def mode(val)
          r = (val & 0x4) ? 'r' : '-'
          w = (val & 0x2) ? 'w' : '-'
          x = (val & 0x1) ? 'x' : '-'
          r + w + x
        end
        mode(val >> 6) + mode(val >> 3) + mode(val)
      end
    
      def fileinfo_str(f)
        return "(#{f[:fileID]})#{f[:domain]}::#{f[:filename]}" unless @verbose
        data = [f[:type], modestr(f[:mode]), f[:userid], f[:groupid], f[:filelen], f[:mtime], f[:atime], f[:ctime], f[:fileID], f[:domain], f[:filename]]
        info = "%s%s %08x %08x %7d %10d %10d %10d (%s)%s::%s" % data
        info += ' -> ' + f[:linktarget] if f[:type] == 'l' # Symlink destination
        f[:properties].each do |k, v|
          info += " #{k}=#{v.inspect}"
        end
        info
      end
    end
    
    if __FILE__ == $0
      mp = ManifestParser.new 'Manifest.mbdb', true
      mp.to_file 'filenames.txt'
    end
    

提交回复
热议问题