Guest

iPhone .mdbd content dump

By: a guest on Feb 7th, 2011  |  syntax: C++  |  size: 14.59 KB  |  hits: 146  |  expires: Never
download  |  raw  |  embed  |  report abuse
Copied
  1. // MBDB/MBDX decoder
  2. // René DEVICHI 2010
  3. // Mirror from http://code.google.com/p/iphonebackupbrowser/ in case it goes down
  4.  
  5. using System;
  6. using System.Text;
  7. using System.IO;
  8. using System.Diagnostics;
  9.  
  10.  
  11. namespace mbdbdump
  12. {
  13.  
  14.     #region BigEndianBitConverter class
  15.     class BigEndianBitConverter
  16.     {
  17.         private static byte[] ReverseBytes(byte[] inArray, int offset, int count)
  18.         {
  19.             int j = count;
  20.             byte[] ret = new byte[count];
  21.  
  22.             for (int i = offset; i < offset + count; ++i)
  23.                 ret[--j] = inArray[i];
  24.             return ret;
  25.         }
  26.  
  27.         public static short ToInt16(byte[] value, int startIndex)
  28.         {
  29.             if (BitConverter.IsLittleEndian)
  30.             {
  31.                 return BitConverter.ToInt16(ReverseBytes(value, startIndex, 2), 0);
  32.             }
  33.             else
  34.             {
  35.                 return BitConverter.ToInt16(value, startIndex);
  36.             }
  37.         }
  38.  
  39.         public static ushort ToUInt16(byte[] value, int startIndex)
  40.         {
  41.             if (BitConverter.IsLittleEndian)
  42.             {
  43.                 return BitConverter.ToUInt16(ReverseBytes(value, startIndex, 2), 0);
  44.             }
  45.             else
  46.             {
  47.                 return BitConverter.ToUInt16(value, startIndex);
  48.             }
  49.         }
  50.  
  51.         public static int ToInt32(byte[] value, int startIndex)
  52.         {
  53.             if (BitConverter.IsLittleEndian)
  54.             {
  55.                 return BitConverter.ToInt32(ReverseBytes(value, startIndex, 4), 0);
  56.             }
  57.             else
  58.             {
  59.                 return BitConverter.ToInt32(value, startIndex);
  60.             }
  61.         }
  62.  
  63.         public static uint ToUInt32(byte[] value, int startIndex)
  64.         {
  65.             if (BitConverter.IsLittleEndian)
  66.             {
  67.                 return BitConverter.ToUInt32(ReverseBytes(value, startIndex, 4), 0);
  68.             }
  69.             else
  70.             {
  71.                 return BitConverter.ToUInt32(value, startIndex);
  72.             }
  73.         }
  74.  
  75.         public static long ToInt64(byte[] value, int startIndex)
  76.         {
  77.             if (BitConverter.IsLittleEndian)
  78.             {
  79.                 return BitConverter.ToInt64(ReverseBytes(value, startIndex, 8), 0);
  80.             }
  81.             return BitConverter.ToInt64(value, startIndex);
  82.  
  83.         }
  84.     }
  85.     #endregion
  86.  
  87.  
  88.     class mbdb
  89.     {
  90.         private static string getS(Stream fs)
  91.         {
  92.             int b0 = fs.ReadByte();
  93.             int b1 = fs.ReadByte();
  94.  
  95.             if (b0 == 255 && b1 == 255)
  96.                 return "n/a";
  97.  
  98.             int length = b0 * 256 + b1;
  99.  
  100.             byte[] buf = new byte[length];
  101.             fs.Read(buf, 0, length);
  102.  
  103.             // We need to do a "Unicode normalization form C" (see Unicode 4.0 TR#15)
  104.             // since some applications don't like the canonical decomposition (NormalizationD)...
  105.  
  106.             // More information: http://msdn.microsoft.com/en-us/library/dd319093(VS.85).aspx
  107.             // or http://msdn.microsoft.com/en-us/library/8eaxk1x2.aspx
  108.  
  109.             string s = Encoding.UTF8.GetString(buf, 0, length);
  110.  
  111.             return s.Normalize(NormalizationForm.FormC);
  112.         }
  113.  
  114.  
  115.         private static char toHex(int value)
  116.         {
  117.             value &= 0xF;
  118.             if (value >= 0 && value <= 9) return (char)('0' + value);
  119.             else return (char)('A' + (value - 10));
  120.         }
  121.  
  122.  
  123.         private static char toHexLow(int value)
  124.         {
  125.             value &= 0xF;
  126.             if (value >= 0 && value <= 9) return (char)('0' + value);
  127.             else return (char)('a' + (value - 10));
  128.         }
  129.  
  130.  
  131.         private static string toHex(byte[] data, params int[] spaces)
  132.         {
  133.             StringBuilder sb = new StringBuilder(data.Length * 3);
  134.  
  135.             int n = 0;
  136.             int p = 0;
  137.  
  138.             for (int i = 0; i < data.Length; ++i)
  139.             {
  140.                 if (n < spaces.Length && i == p + spaces[n])
  141.                 {
  142.                     sb.Append(' ');
  143.                     p += spaces[n];
  144.                     n++;
  145.                 }
  146.                 sb.Append(toHex(data[i] >> 4));
  147.                 sb.Append(toHex(data[i] & 15));
  148.             }
  149.  
  150.             return sb.ToString();
  151.         }
  152.  
  153.  
  154.         private static int fromHex(char c)
  155.         {
  156.             if (c >= '0' && c <= '9')
  157.                 return (int)(c - '0');
  158.  
  159.             if (c >= 'A' && c <= 'F')
  160.                 return (int)(c - 'A' + 10);
  161.  
  162.             if (c >= 'a' && c <= 'f')
  163.                 return (int)(c - 'a' + 10);
  164.  
  165.             return 0;
  166.         }
  167.  
  168.  
  169.         private static string getD(Stream fs)
  170.         {
  171.             int b0 = fs.ReadByte();
  172.             int b1 = fs.ReadByte();
  173.  
  174.             if (b0 == 255 && b1 == 255)
  175.                 return "n/a";
  176.  
  177.             int length = b0 * 256 + b1;
  178.  
  179.             byte[] buf = new byte[length];
  180.             fs.Read(buf, 0, length);
  181.  
  182.             // if we have only ASCII printable characters, we return the string
  183.             int i;
  184.             for (i = 0; i < length; ++i)
  185.             {
  186.                 if (buf[i] < 32 || buf[i] >= 128)
  187.                     break;
  188.             }
  189.             if (i == length)
  190.                 return Encoding.ASCII.GetString(buf, 0, length);
  191.  
  192.             // otherwise the hexadecimal dump
  193.             StringBuilder sb = new StringBuilder(length * 2);
  194.  
  195.             for (i = 0; i < length; ++i)
  196.             {
  197.                 sb.Append(toHex(buf[i] >> 4));
  198.                 sb.Append(toHex(buf[i] & 15));
  199.             }
  200.  
  201.             return sb.ToString();
  202.         }
  203.  
  204.  
  205.         public struct MBFileRecord
  206.         {
  207.             // from .mbdx
  208.             public string key;              // filename if the directory
  209.             public int offset;              // offset of record in the .mbdb file
  210.             public ushort Mode;             // 8xxx=dir, 4xxx=file, Axxx=symlink
  211.  
  212.             // from .mbdb
  213.             public string Domain;
  214.             public string Path;
  215.             public string LinkTarget;
  216.             public string DataHash;         // SHA.1 for 'important' files
  217.             public string alwaysNA;
  218.  
  219.             public string data;             // the 40-byte block (some fields still need to be explained)
  220.  
  221.             //public ushort ModeBis;        // same as .mbdx field
  222.             public int alwaysZero;
  223.             public int unknown;
  224.             public int UserId;
  225.             public int GroupId;
  226.             public DateTime aTime;          // aTime or bTime is the former ModificationTime
  227.             public DateTime bTime;
  228.             public DateTime cTime;
  229.             public long FileLength;         // always 0 for link or directory
  230.             public byte flag;               // 0 if special (link, directory), otherwise values unknown
  231.             public byte PropertyCount;
  232.  
  233.             public struct Property
  234.             {
  235.                 public string Name;
  236.                 public string Value;
  237.             };
  238.             public Property[] Properties;
  239.         }
  240.  
  241.  
  242.         public static MBFileRecord[] ReadMBDB(string BackupPath, bool dump, bool checks)
  243.         {
  244.             try
  245.             {
  246.                 MBFileRecord[] files;
  247.                 MBFileRecord rec = new MBFileRecord();
  248.                 byte[] signature = new byte[6];                     // buffer signature
  249.                 byte[] buf = new byte[26];                          // buffer for .mbdx record
  250.                 StringBuilder sb = new StringBuilder(40);           // stringbuilder for the Key
  251.                 byte[] data = new byte[40];                         // buffer for the fixed part of .mbdb record
  252.  
  253.                 System.DateTime unixEpoch = new System.DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
  254.  
  255.  
  256.                 // open the index
  257.                 FileStream mbdx = new FileStream(Path.Combine(BackupPath, "Manifest.mbdx"), FileMode.Open, FileAccess.Read);
  258.  
  259.                 // skip signature
  260.                 mbdx.Read(signature, 0, 6);
  261.                 if (BitConverter.ToString(signature, 0) != "6D-62-64-78-02-00")     // "mbdx\2\0"
  262.                 {
  263.                     throw new Exception("bad .mbdx file");
  264.                 }
  265.  
  266.  
  267.                 // open the database
  268.                 FileStream mbdb = new FileStream(Path.Combine(BackupPath, "Manifest.mbdb"), FileMode.Open, FileAccess.Read);
  269.  
  270.                 // skip signature
  271.                 mbdb.Read(signature, 0, 6);
  272.                 if (BitConverter.ToString(signature, 0) != "6D-62-64-62-05-00")     // "mbdb\5\0"
  273.                 {
  274.                     throw new Exception("bad .mbdb file");
  275.                 }
  276.  
  277.                 // number of records in .mbdx
  278.                 if (mbdx.Read(buf, 0, 4) != 4)
  279.                 {
  280.                     throw new Exception("altered .mbdx file");
  281.                 }
  282.                 int records = BigEndianBitConverter.ToInt32(buf, 0);
  283.                 files = new MBFileRecord[records];
  284.  
  285.                 // loop through the records
  286.                 for (int i = 0; i < records; ++i)
  287.                 {
  288.                     // get the fixed size .mbdx record
  289.                     if (mbdx.Read(buf, 0, 26) != 26)
  290.                         break;
  291.  
  292.                     // convert key to text, it's the filename in the backup directory
  293.                     // in previous versions of iTunes, it was the file part of .mddata/.mdinfo
  294.                     sb.Clear();
  295.                     for (int j = 0; j < 20; ++j)
  296.                     {
  297.                         byte b = buf[j];
  298.                         sb.Append(toHexLow(b >> 4));
  299.                         sb.Append(toHexLow(b & 15));
  300.                     }
  301.  
  302.                     rec.key = sb.ToString();
  303.                     rec.offset = BigEndianBitConverter.ToInt32(buf, 20);
  304.                     rec.Mode = BigEndianBitConverter.ToUInt16(buf, 24);
  305.  
  306.  
  307.                     // read the record in the .mbdb
  308.                     mbdb.Seek(6 + rec.offset, SeekOrigin.Begin);
  309.  
  310.                     rec.Domain = getS(mbdb);
  311.                     rec.Path = getS(mbdb);
  312.                     rec.LinkTarget = getS(mbdb);
  313.                     rec.DataHash = getD(mbdb);
  314.                     rec.alwaysNA = getD(mbdb);
  315.  
  316.                     mbdb.Read(data, 0, 40);
  317.  
  318.                     rec.data = toHex(data, 2, 4, 4, 4, 4, 4, 4, 4, 8, 1, 1);
  319.  
  320.                     //rec.ModeBis = BigEndianBitConverter.ToUInt16(data, 0);
  321.                     rec.alwaysZero = BigEndianBitConverter.ToInt32(data, 2);
  322.                     rec.unknown = BigEndianBitConverter.ToInt32(data, 6);
  323.                     rec.UserId = BigEndianBitConverter.ToInt32(data, 10);       // or maybe GroupId (don't care...)
  324.                     rec.GroupId = BigEndianBitConverter.ToInt32(data, 14);      // or maybe UserId
  325.  
  326.                     rec.aTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 18));
  327.                     rec.bTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 22));
  328.                     rec.cTime = unixEpoch.AddSeconds(BigEndianBitConverter.ToUInt32(data, 26));
  329.  
  330.                     rec.FileLength = BigEndianBitConverter.ToInt64(data, 30);
  331.  
  332.                     rec.flag = data[38];
  333.                     rec.PropertyCount = data[39];
  334.  
  335.                     rec.Properties = new MBFileRecord.Property[rec.PropertyCount];
  336.                     for (int j = 0; j < rec.PropertyCount; ++j)
  337.                     {
  338.                         rec.Properties[j].Name = getS(mbdb);
  339.                         rec.Properties[j].Value = getD(mbdb);
  340.                     }
  341.  
  342.                     files[i] = rec;
  343.  
  344.  
  345.                     // debug print
  346.                     if (dump)
  347.                     {
  348.                         Console.WriteLine("");
  349.                         Console.WriteLine("record {0} (mbdb offset {1})", i, rec.offset + 6);
  350.  
  351.                         Console.WriteLine("  key    {0}", rec.key);
  352.                         Console.WriteLine("  domain {0}", rec.Domain);
  353.                         Console.WriteLine("  path   {0}", rec.Path);
  354.                         if (rec.LinkTarget != "n/a") Console.WriteLine("  target {0}", rec.LinkTarget);
  355.                         if (rec.DataHash != "n/a") Console.WriteLine("  hash   {0}", rec.DataHash);
  356.                         if (rec.alwaysNA != "n/a") Console.WriteLine("  unk3   {0}", rec.alwaysNA);
  357.  
  358.                         string l = "?";
  359.                         switch ((rec.Mode & 0xF000) >> 12)
  360.                         {
  361.                             case 0xA: l = "link"; break;
  362.                             case 0x4: l = "dir"; break;
  363.                             case 0x8: l = "file"; break;
  364.                         }
  365.                         Console.WriteLine("  mode   {1} ({0})", rec.Mode & 0xFFF, l);
  366.  
  367.                         Console.WriteLine("  time   {0}", rec.aTime);
  368.  
  369.                         // length is unsignificant if link or dir
  370.                         if ((rec.Mode & 0xF000) == 0x8000) Console.WriteLine("  length {0}", rec.FileLength);
  371.  
  372.                         Console.WriteLine("  data   {0}", rec.data);
  373.                         for (int j = 0; j < rec.PropertyCount; ++j)
  374.                         {
  375.                             Console.WriteLine("  pn[{0}]  {1}", j, rec.Properties[j].Name);
  376.                             Console.WriteLine("  pv[{0}]  {1}", j, rec.Properties[j].Value);
  377.                         }
  378.                     }
  379.  
  380.  
  381.                     // some assertions...
  382.                     if (checks)
  383.                     {
  384.                         //Debug.Assert(rec.Mode == rec.ModeBis);
  385.                         Debug.Assert(rec.alwaysZero == 0);
  386.                         if (rec.LinkTarget != "n/a") Debug.Assert((rec.Mode & 0xF000) == 0xA000);
  387.                         if (rec.DataHash != "n/a") Debug.Assert(rec.DataHash.Length == 40);
  388.                         Debug.Assert(rec.alwaysNA == "n/a");
  389.                         if (rec.Domain.StartsWith("AppDomain-")) Debug.Assert(rec.GroupId == 501 && rec.UserId == 501);
  390.                         if (rec.FileLength != 0) Debug.Assert((rec.Mode & 0xF000) == 0x8000);
  391.                         if ((rec.Mode & 0xF000) == 0x8000) Debug.Assert(rec.flag != 0);
  392.                         if ((rec.Mode & 0xF000) == 0xA000) Debug.Assert(rec.flag == 0 && rec.FileLength == 0);
  393.                         if ((rec.Mode & 0xF000) == 0x4000) Debug.Assert(rec.flag == 0 && rec.FileLength == 0);
  394.                     }
  395.  
  396.                 }
  397.  
  398.                 return files;
  399.             }
  400.             catch (Exception e)
  401.             {
  402.                 Console.WriteLine("exception: {0}", e.Message);
  403.             }
  404.  
  405.             return null;
  406.         }
  407.  
  408.     }
  409. }