From 4426a1b7f219a4868be0ab5a057a15fd5a3afb12 Mon Sep 17 00:00:00 2001 From: HD Moore Date: Sun, 24 Apr 2011 18:21:13 +0000 Subject: [PATCH] Port of the python Manifest.mbdb parser git-svn-id: file:///home/svn/framework3/trunk@12424 4d416f70-5f16-0410-b530-b9f4589650da --- lib/rex/parser/apple_backup_manifestdb.rb | 125 ++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 lib/rex/parser/apple_backup_manifestdb.rb diff --git a/lib/rex/parser/apple_backup_manifestdb.rb b/lib/rex/parser/apple_backup_manifestdb.rb new file mode 100644 index 0000000000..612570e313 --- /dev/null +++ b/lib/rex/parser/apple_backup_manifestdb.rb @@ -0,0 +1,125 @@ +# +# This is a Ruby port of the Python manifest parsing code posted to: +# http://stackoverflow.com/questions/3085153/how-to-parse-the-manifest-mbdb-file-in-an-ios-4-0-itunes-backup/3130860#3130860 +# + +class AppleBackupManifestDB + + attr_accessor :entry_offsets + attr_accessor :entries + attr_accessor :mbdb, :mbdx + attr_accessor :mbdb_data, :mbdx_data + attr_accessor :mbdb_offset, :mbdx_offset + + def initialize(mbdb_data, mbdx_data) + self.entries = {} + self.entry_offsets = {} + self.mbdb_data = mbdb_data + self.mbdx_data = mbdx_data + parse_mbdb + parse_mbdx + end + + def self.from_files(mbdb_file, mbdx_file) + mbdb_data = "" + ::File.open(mbdb_file, "rb") {|fd| mbdb_data = fd.read(fd.stat.size) } + mbdx_data = "" + ::File.open(mbdx_file, "rb") {|fd| mbdx_data = fd.read(fd.stat.size) } + + self.new(mbdb_data, mbdx_data) + end + + def parse_mbdb + raise ArgumentError, "Not valid MBDB data" if self.mbdb_data[0,4] != "mbdb" + self.mbdb_offset = 4 + self.mbdb_offset = self.mbdb_offset + 2 # Maps to \x05 \x00 (unknown) + + while self.mbdb_offset < self.mbdb_data.length + info = {} + info[:start_offset] = self.mbdb_offset + info[:domain] = mbdb_read_string + info[:filename] = mbdb_read_string + info[:linktarget] = mbdb_read_string + info[:datahash] = mbdb_read_string + info[:unknown1] = mbdb_read_string + info[:mode] = mbdb_read_int(2) + info[:unknown2] = mbdb_read_int(4) + info[:unknown3] = mbdb_read_int(4) + info[:uid] = mbdb_read_int(4) + info[:gid] = mbdb_read_int(4) + info[:mtime] = Time.at(mbdb_read_int(4)) + info[:atime] = Time.at(mbdb_read_int(4)) + info[:ctime] = Time.at(mbdb_read_int(4)) + info[:length] = mbdb_read_int(8) + info[:flag] = mbdb_read_int(1) + property_count = mbdb_read_int(1) + info[:properties] = {} + 1.upto(property_count) do |i| + k = mbdb_read_string + v = mbdb_read_string + info[:properties][k] = v + end + self.entry_offsets[ info[:start_offset] ] = info + end + self.mbdb_data = "" + end + + def parse_mbdx + raise ArgumentError, "Not a valid MBDX file" if self.mbdx_data[0,4] != "mbdx" + + self.mbdx_offset = 4 + self.mbdx_offset = self.mbdx_offset + 2 # Maps to \x02 \x00 (unknown) + + file_count = mbdx_read_int(4) + + while self.mbdx_offset < self.mbdx_data.length + file_id = self.mbdx_data[self.mbdx_offset, 20].unpack("C*").map{|c| "%02x" % c}.join + self.mbdx_offset += 20 + entry_offset = mbdx_read_int(4) + 6 + mode = mbdx_read_int(2) + entry = entry_offsets[ entry_offset ] + # May be corrupted if there is no matching entry, but what to do about it? + next if not entry + self.entries[file_id] = entry.merge({:mbdx_mode => mode, :file_id => file_id}) + end + self.mbdx_data = "" + end + + def mbdb_read_string + raise RuntimeError, "Corrupted MBDB file" if self.mbdb_offset > self.mbdb_data.length + len = self.mbdb_data[self.mbdb_offset, 2].unpack("n")[0] + self.mbdb_offset += 2 + return '' if len == 65535 + val = self.mbdb_data[self.mbdb_offset, len] + self.mbdb_offset += len + return val + end + + def mbdb_read_int(size) + val = 0 + size.downto(1) do |i| + val = (val << 8) + self.mbdb_data[self.mbdb_offset, 1].unpack("C")[0] + self.mbdb_offset += 1 + end + val + end + + def mbdx_read_string + raise RuntimeError, "Corrupted MBDX file" if self.mbdx_offset > self.mbdx_data.length + len = self.mbdx_data[self.mbdx_offset, 2].unpack("n")[0] + self.mbdx_offset += 2 + return '' if len == 65535 + val = self.mbdx_data[self.mbdx_offset, len] + self.mbdx_offset += len + return val + end + + def mbdx_read_int(size) + val = 0 + size.downto(1) do |i| + val = (val << 8) + self.mbdx_data[self.mbdx_offset, 1].unpack("C")[0] + self.mbdx_offset += 1 + end + val + end +end