IOS 裝置備份檔案詳解 (二)
http://www.cnblogs.com/ios8/p/ios-store2.html
這篇主要講解如何解析Manifest.mbdb檔案。
使用二進位制工具開啟這個檔案,檔案的頭6個位元組是固定的,相當於是檔案的一種標識
後面的內容是一個一個的項,可以使用一個迴圈來讀取檔案,一個一個解析。
這裡有一個概念要先說一下,就是域,域是用來定位一個檔案在手機上的全路徑。比如上圖的 AppDomain-cairot,這個域就表示檔案在手機上的目錄為 /var/mobile/Applications/,不同的域對應的手機的目錄是不一樣的,下面給出所有域和目錄的一個對應關係,下面是自己的一段程式碼, 看懂這段程式碼就知道域和路徑的關係了。
if (item.Domain == "WirelessDomain")
{
item.PathOnPhone.Format("/var/wireless/%s", item.Path);
}
else if (item.Domain == "ManagedPreferencesDomain")
{
item.PathOnPhone.Format("/var/Managed Preferences/%s", item.Path);
}
else if (item.Domain == "MediaDomain")
{
item.PathOnPhone.Format("/var/mobile/%s", item.Path);
}
else if (item.Domain == "SystemPreferencesDomain")
{
item.PathOnPhone.Format("/var/preferences/%s", item.Path);
}
else if (item.Domain == "CameraRollDomain")
{
item.PathOnPhone.Format("/var/mobile/%s", item.Path);
}
else if (item.Domain == "RootDomain")
{
item.PathOnPhone.Format("/var/root/%s", item.Path);
}
else if (item.Domain == "MobileDeviceDomain")
{
item.PathOnPhone.Format("/var/MobileDevice/%s", item.Path);
}
else if (item.Domain == "KeychainDomain")
{
item.PathOnPhone.Format("/var/Keychains/%s", item.Path);
}
else if (item.Domain == "HomeDomain")
{
item.PathOnPhone.Format("/var/mobile/%s", item.Path);
}
else if (item.Domain == "DatabaseDomain")
{
item.PathOnPhone.Format("/var/db/%s", item.Path);
}
else if (item.Domain.Find("AppDomain-") == 0)
{
CAtlStringA strTmp(item.Domain);
strTmp.Replace("AppDomain-", "");
item.PathOnPhone.Format("/var/mobile/Applications/%s/%s",strTmp , item.Path);
}
1) 獲取域。 頭6個位元組之後的2個位元組,標識域的長度,但是2位元組的內容並不是直接標識長度,看下面的程式碼。先讀出一個位元組,然後再讀出一個位元組,進行運算之後得出的一個長度。下圖就是 AppDomain-cairot
std::string CBackupMbdb::ReadNextStringBy2bytesLen(CBinaryReader& reader)
{
std::string strResult;
byte num = reader.ReadByte();
byte num2 = reader.ReadByte();
if ((num == 0xff) && (num2 == 0xff))
{
return strResult;
}
int num3 = (num * 0x100) + num2;
strResult = reader.ReadString(num3);
return strResult;
}
2) 之後就是手機上路徑。這個路徑和域組合之後就可以得出檔案在iphone 上的全路徑了。第一個圖中的item.path 就是這個路徑,item.Domain 就是域。 根據上面的程式碼就可以組合出全路徑了。這個路徑獲取方法和域是一樣的。先讀取檔案的2個位元組,然後根據這兩個位元組的大小讀取內容。從上圖看先讀的兩個為0000,所以路徑就是空字串。
3)連結路徑。這個我也不是很清楚,貌似像是windows 的快捷方式一樣,會指向一個其他的路徑。反正我沒用上這個東西。 讀取的方式和前面一樣
4) 緊接著資料的一段雜湊。先讀出2個位元組,然後根據這兩個位元組計算出一個大小,進行一些判斷之後把16 進位制的資料轉換成字串。
std::string CBackupMbdb::smethod10(CBinaryReader& reader)
{
std::string strResult ;
byte num = reader.ReadByte();
byte num2 = reader.ReadByte();
if ((num == 0xff) && (num2 == 0xff))
{
return strResult;
}
int num6 = (num * 0x100) + num2;
CAutoVectorPtr<byte> pBuffer;
pBuffer.Allocate(num6);
reader.Read(pBuffer.m_p,num6);
int index = 0;
index = 0;
while (index < num6)
{
if ((pBuffer.m_p[index] < 0x20) || (pBuffer.m_p[index] >= 0x80))
{
break;
}
index++;
}
if (index == num6)
{
strResult = bytes_to_hex_string(pBuffer.m_p,num6);
}
return strResult;
}
5) 第五個內容的讀取方法和第四個一樣,但是所有的讀出來的都是空字串。
6) 讀出固定的40個位元組,這40個位元組裡面包含的資訊很多,不過大部分都是不需要的,只有一個欄位是重要的,這個欄位會影響後面的檔案讀取。
CAutoVectorPtr<byte> pRecordInfo;
pRecordInfo.Allocate(40);
reader.Read(pRecordInfo.m_p,40);
//0x27 也就是最後的位置儲存了這個項的屬性個數,要用這個數字迴圈讀出屬性來。
itemInfo.PropertyCount = pRecordInfo[0x27];
for (int i=0;i<itemInfo.PropertyCount;i++)
{
CAtlStringA key = ReadNextStringBy2bytesLen(reader).c_str();
CAtlStringA value = smethod10(reader).c_str();
itemInfo.Properties[key] = value;
}
7) 根據第一步和第二步得出來的域和路徑計算SHA1值,這個雜湊值也就是本地的路徑。
std::string strHash1Src;
if (itemInfo.Path.IsEmpty())
{
strHash1Src = itemInfo.Domain;
}
else
{
strHash1Src = itemInfo.Domain + "-" + itemInfo.Path;
}
std::string strTmp;
CAppUtilis::EncrypBySHA1(strHash1Src,strTmp);
然後一直迴圈讀取檔案,直到檔案讀完就可以解析出所有的檔案路徑了