public static MBFileRecord[] ReadMBDB(string BackupPath, bool dump, bool checks) { try { MBFileRecord[] files; MBFileRecord rec = new MBFileRecord(); byte[] signature = new byte[6]; // buffer signature byte[] buf = new byte[26]; // buffer for .mbdx record StringBuilder sb = new StringBuilder(40); // stringbuilder for the Key byte[] data = new byte[40]; // buffer for the fixed part of .mbdb record System.DateTime unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); // open the index FileStream mbdx = new FileStream(Path.Combine(BackupPath, "Manifest.mbdx"), FileMode.Open, FileAccess.Read); // skip signature mbdx.Read(signature, 0, 6); if (BitConverter.ToString(signature, 0) != "6D-62-64-78-02-00") // "mbdx\2\0" { throw new Exception("bad .mbdx file"); } // open the database FileStream mbdb = new FileStream(Path.Combine(BackupPath, "Manifest.mbdb"), FileMode.Open, FileAccess.Read); // skip signature mbdb.Read(signature, 0, 6); if (BitConverter.ToString(signature, 0) != "6D-62-64-62-05-00") // "mbdb\5\0" { throw new Exception("bad .mbdb file"); } // number of records in .mbdx if (mbdx.Read(buf, 0, 4) != 4) { throw new Exception("altered .mbdx file"); } int records = BigEndianBitConverter.ToInt32(buf, 0); files = new MBFileRecord[records]; // loop through the records for (int i = 0; i < records; ++i) { // get the fixed size .mbdx record if (mbdx.Read(buf, 0, 26) != 26) { break; } // convert key to text, it's the filename in the backup directory // in previous versions of iTunes, it was the file part of .mddata/.mdinfo sb.Clear(); for (int j = 0; j < 20; ++j) { byte b = buf[j]; sb.Append(toHexLow(b >> 4)); sb.Append(toHexLow(b & 15)); } rec.key = sb.ToString(); rec.offset = BigEndianBitConverter.ToInt32(buf, 20); rec.Mode = BigEndianBitConverter.ToUInt16(buf, 24); // read the record in the .mbdb mbdb.Seek(6 + rec.offset, SeekOrigin.Begin); rec.Domain = getS(mbdb); rec.Path = getS(mbdb); rec.LinkTarget = getS(mbdb); rec.DataHash = getD(mbdb); rec.alwaysNA = getD(mbdb); mbdb.Read(data, 0, 40); rec.data = toHex(data, 2, 4, 4, 4, 4, 4, 4, 4, 8, 1, 1); //rec.ModeBis = BigEndianBitConverter.ToUInt16(data, 0); rec.alwaysZero = BigEndianBitConverter.ToInt32(data, 2); rec.unknown = BigEndianBitConverter.ToInt32(data, 6); rec.UserId = BigEndianBitConverter.ToInt32(data, 10); // or maybe GroupId (don't care...) rec.GroupId = BigEndianBitConverter.ToInt32(data, 14); // or maybe UserId rec.aTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 18)); rec.bTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 22)); rec.cTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 26)); rec.FileLength = BigEndianBitConverter.ToInt64(data, 30); rec.flag = data[38]; rec.PropertyCount = data[39]; rec.Properties = new MBFileRecord.Property[rec.PropertyCount]; for (int j = 0; j < rec.PropertyCount; ++j) { rec.Properties[j].Name = getS(mbdb); rec.Properties[j].Value = getD(mbdb); } files[i] = rec; // debug print if (dump) { Console.WriteLine(""); Console.WriteLine("record {0} (mbdb offset {1})", i, rec.offset + 6); Console.WriteLine(" key {0}", rec.key); Console.WriteLine(" domain {0}", rec.Domain); Console.WriteLine(" path {0}", rec.Path); if (rec.LinkTarget != "n/a") { Console.WriteLine(" target {0}", rec.LinkTarget); } if (rec.DataHash != "n/a") { Console.WriteLine(" hash {0}", rec.DataHash); } if (rec.alwaysNA != "n/a") { Console.WriteLine(" unk3 {0}", rec.alwaysNA); } string l = "?"; switch ((rec.Mode & 0xF000) >> 12) { case 0xA: l = "link"; break; case 0x4: l = "dir"; break; case 0x8: l = "file"; break; } Console.WriteLine(" mode {1} ({0})", rec.Mode & 0xFFF, l); Console.WriteLine(" time {0}", rec.aTime); // length is unsignificant if link or dir if ((rec.Mode & 0xF000) == 0x8000) { Console.WriteLine(" length {0}", rec.FileLength); } Console.WriteLine(" data {0}", rec.data); for (int j = 0; j < rec.PropertyCount; ++j) { Console.WriteLine(" pn[{0}] {1}", j, rec.Properties[j].Name); Console.WriteLine(" pv[{0}] {1}", j, rec.Properties[j].Value); } } // some assertions... if (checks) { //Debug.Assert(rec.Mode == rec.ModeBis); Debug.Assert(rec.alwaysZero == 0); if (rec.LinkTarget != "n/a") { Debug.Assert((rec.Mode & 0xF000) == 0xA000); } if (rec.DataHash != "n/a") { Debug.Assert(rec.DataHash.Length == 40); } Debug.Assert(rec.alwaysNA == "n/a"); if (rec.Domain.StartsWith("AppDomain-")) { Debug.Assert(rec.GroupId == 501 && rec.UserId == 501); } if (rec.FileLength != 0) { Debug.Assert((rec.Mode & 0xF000) == 0x8000); } if ((rec.Mode & 0xF000) == 0x8000) { Debug.Assert(rec.flag != 0); } if ((rec.Mode & 0xF000) == 0xA000) { Debug.Assert(rec.flag == 0 && rec.FileLength == 0); } if ((rec.Mode & 0xF000) == 0x4000) { Debug.Assert(rec.flag == 0 && rec.FileLength == 0); } } } return(files); } catch (Exception e) { Console.WriteLine("exception: {0}", e.Message); } return(null); }
public static List <MBFileRecord> ReadMBDB(string BackupPath) { try { List <MBFileRecord> files; byte[] signature = new byte[6]; // buffer signature byte[] buf = new byte[26]; // buffer for .mbdx record StringBuilder sb = new StringBuilder(40); // stringbuilder for the Key byte[] data = new byte[40]; // buffer for the fixed part of .mbdb record SHA1CryptoServiceProvider hasher = new SHA1CryptoServiceProvider(); System.DateTime unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); // open the database FileStream mbdb = new FileStream(Path.Combine(BackupPath, "Manifest.mbdb"), FileMode.Open, FileAccess.Read); // skip signature mbdb.Read(signature, 0, 6); if (BitConverter.ToString(signature, 0) != "6D-62-64-62-05-00") // "mbdb\5\0" { throw new Exception("bad .mbdb file"); } files = new List <MBFileRecord>(); // loop through the records for (int i = 0; mbdb.Position < mbdb.Length; ++i) { MBFileRecord rec = new MBFileRecord(); rec.Domain = getS(mbdb); rec.Path = getS(mbdb); rec.LinkTarget = getS(mbdb); rec.DataHash = getD(mbdb); rec.alwaysNull = getD(mbdb); mbdb.Read(data, 0, 40); rec.data = toHex(data, 2, 4, 4, 4, 4, 4, 4, 4, 8, 1, 1); rec.Mode = BigEndianBitConverter.ToUInt16(data, 0); rec.alwaysZero = BigEndianBitConverter.ToInt32(data, 2); rec.inode = BigEndianBitConverter.ToUInt32(data, 6); rec.UserId = BigEndianBitConverter.ToUInt32(data, 10); // or maybe GroupId (don't care...) rec.GroupId = BigEndianBitConverter.ToUInt32(data, 14); // or maybe UserId rec.aTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 18)); rec.bTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 22)); rec.cTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 26)); rec.FileLength = BigEndianBitConverter.ToInt64(data, 30); rec.flag = data[38]; rec.PropertyCount = data[39]; rec.Properties = new MBFileRecord.Property[rec.PropertyCount]; for (int j = 0; j < rec.PropertyCount; ++j) { rec.Properties[j].Name = getS(mbdb); rec.Properties[j].Value = getD(mbdb); } StringBuilder fileName = new StringBuilder(); byte[] fb = hasher.ComputeHash(ASCIIEncoding.UTF8.GetBytes(rec.Domain + "-" + rec.Path)); for (int k = 0; k < fb.Length; k++) { fileName.Append(fb[k].ToString("x2")); } rec.key = fileName.ToString(); files.Add(rec); } return(files); } catch (Exception e) { Console.WriteLine("exception: {0}", e.Message); } return(null); }