public void OpenFile(string nbufilename, bool bruteForceScan) { this.Text = Path.GetFileName(nbufilename) + " - " + appTitle; currentFileName = nbufilename; textBoxLog.Clear(); StreamUtils.Counter = 0; DataSetNbuExplorer.DefaultInstance.Clear(); treeViewMsgFilter.Nodes[0].Nodes.Clear(); treeViewMsgFilter.Nodes[1].Nodes.Clear(); treeViewMsgFilter.Nodes[2].Nodes.Clear(); treeViewDirs.Nodes.Clear(); listViewFiles.Items.Clear(); listViewFiles_SelectedIndexChanged(this, EventArgs.Empty); recountTotal(); statusLabelSelected.Text = "-"; this.menuStripMain.Enabled = false; this.treeViewDirs.Enabled = false; this.Cursor = Cursors.AppStarting; this.analyzeRequest = false; FileStream fs = null; try { fs = File.OpenRead(currentFileName); string fileext = Path.GetExtension(currentFileName).ToLower(); #region nfb & nfc format if (!bruteForceScan && (fileext == ".nfb" || fileext == ".nfc")) { fs.Seek(4, SeekOrigin.Begin); addLine("Phone model:\t" + StreamUtils.ReadString2(fs)); // RM addLine("Phone name:\t" + StreamUtils.ReadString2(fs)); // nazev addLine(""); int count = StreamUtils.ReadUInt32asInt(fs); addLine(string.Format("{0} items found", count)); addLine(""); string name; try { for (int i = 0; i < count; i++) { uint type = StreamUtils.ReadUInt32(fs); switch (type) { case 2: // folder name = StreamUtils.ReadString2(fs); addLine(string.Format("Folder '{0}' found", name)); break; case 1: // file name = StreamUtils.ReadString2(fs).TrimStart('\\'); UInt32 len = StreamUtils.ReadUInt32(fs); if (name.EndsWith("FolderIndex")) { fs.Seek(len + 4, SeekOrigin.Current); } else if (name.EndsWith("InfoIndex")) { long indexEndAddr = fs.Position + len + 4; fs.Seek(5, SeekOrigin.Current); StreamReader sr = new StreamReader(fs, System.Text.Encoding.Unicode, false, 32); List<string> fileNames = new List<string>(); string homeDir = Path.GetDirectoryName(name); List<FileInfo> list = findOrCreateFileInfoList(homeDir); for (int j = list.Count - 1; j > -1; j--) { try { int index = int.Parse(list[j].Filename); while (index > fileNames.Count - 1 && !sr.EndOfStream) { fileNames.Add(sr.ReadLine().Replace("\\v", "\\")); while (!string.IsNullOrEmpty(sr.ReadLine())) ; // move to next filename } List<FileInfo> l2 = findOrCreateFileInfoList(homeDir + "\\" + Path.GetDirectoryName(fileNames[index])); l2.Add(new FileInfo(Path.GetFileName(fileNames[index]), list[j].Start, list[j].FileSize)); list.RemoveAt(j); addLine(fileNames[index]); } catch { } } fs.Seek(indexEndAddr, SeekOrigin.Begin); } else { if (name.StartsWith("MPAPI")) name += ".csv"; if (name.IndexOf('\\') == -1) name = "root\\" + name + ".csv"; List<FileInfo> list = findOrCreateFileInfoList(Path.GetDirectoryName(name)); list.Add(new FileInfo(Path.GetFileName(name), fs.Position, len)); fs.Seek(len + 4, SeekOrigin.Current); } StreamUtils.Counter += len; break; default: throw new ApplicationException(string.Format("Unknown item type {0}", type)); } } } catch (Exception exc) { analyzeRequest = true; addLine(exc.Message); } if (fs.Length - fs.Position > 4) addLine("End of file not reached"); } #endregion #region nbu format else if (!bruteForceScan && fileext == ".nbu") { fs.Seek(0x14, SeekOrigin.Begin); fs.Seek(StreamUtils.ReadUInt64asLong(fs), SeekOrigin.Begin); fs.Seek(0x14, SeekOrigin.Current); addLine("Backup time:\t" + StreamUtils.ReadNokiaDateTime(fs).ToString()); // datetime addLine("Phone IMEI:\t" + StreamUtils.ReadString(fs)); addLine("Phone model:\t" + StreamUtils.ReadString(fs)); addLine("Phone name:\t" + StreamUtils.ReadString(fs)); addLine("Phone firmware:\t" + StreamUtils.ReadString(fs)); addLine("Phone language:\t" + StreamUtils.ReadString(fs)); fs.Seek(0x14, SeekOrigin.Current); // ? int partcount = StreamUtils.ReadUInt32asInt(fs); long partPos, partStartAddr;//, partLength; int count; List<FileInfo> contactList = new List<FileInfo>(); while (partcount > 0) { byte[] partGuid = new byte[16]; fs.Read(partGuid, 0, partGuid.Length); partStartAddr = StreamUtils.ReadUInt64asLong(fs); fs.Seek(8, SeekOrigin.Current); //partLength = StreamUtils.ReadUInt64asLong(fs); addLine(""); addLine("BEGIN " + numToAddr(partStartAddr)); Section sect; try { sect = NokiaConstants.FindSectById(partGuid); } catch (ApplicationException) { addLine("Unknown section type - BREAK !!!"); addLine("TOC addr: " + numToAddr(fs.Position - 32)); if (MessageBox.Show("Unknown structure type found, process cannot continue. Please consider providing this backup to the author of application for analyzing and improving application.\r\n\r\nIf you need to get your contacts, messages and calendar items, you can try brute force scan mode. Would you like to do it now?", this.appTitle, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes) { OpenFile(nbufilename, true); return; } break; } #if DEBUG System.Diagnostics.Debug.WriteLine(sect.name + " " + sect.name2); #endif if (string.IsNullOrEmpty(sect.name2)) { addLine(sect.name); } else { addLine(string.Format("{0} - {1}", sect.name, sect.name2)); } addLine("================="); List<FileInfo> partFiles; string foldername, filename; int count2; switch (sect.type) { #region filesystem case ProcessType.FileSystem: count = StreamUtils.ReadUInt32asInt(fs); addLine(count.ToString() + " files found"); fs.Seek(0x14, SeekOrigin.Current); // ? long dirStartAddr = StreamUtils.ReadUInt64asLong(fs); long fileStartAddr = partStartAddr + 0x30; partPos = fs.Position; // zapamatovani pro dalsi process fs.Seek(dirStartAddr + 20, SeekOrigin.Begin); for (int i = 0; i < count; i++) { foldername = StreamUtils.ReadString(fs).TrimStart('\\'); filename = StreamUtils.ReadString(fs); fs.Seek(4, SeekOrigin.Current); // ??? long fileSize = StreamUtils.ReadUInt32(fs); DateTime fileTime = StreamUtils.ReadNokiaDateTime(fs); addLine(foldername + filename + " - " + fileSize + " Bytes, " + fileTime.ToString()); fs.Seek(12, SeekOrigin.Current); // ??? partFiles = findOrCreateFileInfoList(sect.name + "/" + foldername); partFiles.Add(new FileInfo(filename, fileStartAddr, fileSize, fileTime)); fileStartAddr += fileSize; StreamUtils.Counter += fileSize; } fs.Seek(partPos, SeekOrigin.Begin); break; #endregion #region vcards case ProcessType.Vcards: count = StreamUtils.ReadUInt32asInt(fs); // files count2 = StreamUtils.ReadUInt32asInt(fs); // folders if (count2 > 0) { addLine(count2.ToString() + " folders found"); for (int i = 0; i < count2; i++) { fs.Seek(4, SeekOrigin.Current); //folder id long folderAddr = StreamUtils.ReadUInt64asLong(fs); partPos = fs.Position; try { fs.Seek(folderAddr + 4, SeekOrigin.Begin); foldername = StreamUtils.ReadString(fs); addLine(string.Format("\r\nFolder BEGIN {0}, name: '{1}'", numToAddr(folderAddr), foldername)); parseFolderVcard(fs, contactList, sect.name, sect.name + "\\" + foldername); } catch (Exception exc) { addLine(exc.Message); analyzeRequest = true; } fs.Seek(partPos, SeekOrigin.Begin); } } else { partPos = fs.Position; try { fs.Seek(partStartAddr + 0x2C, SeekOrigin.Begin); parseFolderVcard(fs, contactList, sect.name, sect.name); } catch (Exception exc) { addLine(exc.Message); analyzeRequest = true; } fs.Seek(partPos, SeekOrigin.Begin); } break; #endregion #region memos case ProcessType.Memos: count = StreamUtils.ReadUInt32asInt(fs); addLine(count.ToString() + " memos found"); partPos = fs.Position + 4; fs.Seek(partStartAddr + 0x30, SeekOrigin.Begin); partFiles = findOrCreateFileInfoList(sect.name); for (int i = 0; i < count; i++) { fs.Seek(4, SeekOrigin.Current); int len = StreamUtils.ReadUInt16(fs) * 2; partFiles.Add(new FileInfo(string.Format("{0}.txt", numToName(i + 1)), fs.Position, len)); fs.Seek(len, SeekOrigin.Current); StreamUtils.Counter += len; } fs.Seek(partPos, SeekOrigin.Begin); break; #endregion #region groups case ProcessType.Groups: count2 = StreamUtils.ReadUInt32asInt(fs); count = StreamUtils.ReadUInt32asInt(fs); addLine(string.Format("{0} groups found.", count)); for (int i = 0; i < count; i++) { fs.Seek(4, SeekOrigin.Current); long start = StreamUtils.ReadUInt64asLong(fs); addLine("Folder BEGIN " + numToAddr(start)); partPos = fs.Position; fs.Seek(start + 4, SeekOrigin.Begin); foldername = StreamUtils.ReadString(fs); count2 = StreamUtils.ReadUInt32asInt(fs); addLine(string.Format("{0} - {1} contacts", foldername, count2)); partFiles = findOrCreateFileInfoList(sect.name + "\\" + foldername); if (contactList != null) { for (int j = 0; j < count2; j++) { int ix = StreamUtils.ReadUInt32asInt(fs); if (contactList.Count >= ix) partFiles.Add(contactList[ix - 1]); else addLine("Invalid index: " + ix); } } fs.Seek(partPos, SeekOrigin.Begin); } break; #endregion #region folders case ProcessType.GeneralFolders: count2 = StreamUtils.ReadUInt32asInt(fs); count = StreamUtils.ReadUInt32asInt(fs); if (sect.name2 == "Messages" && count == 0) { partPos = fs.Position; fs.Seek(partStartAddr, SeekOrigin.Begin); parseBinaryMessages(fs); fs.Seek(partPos, SeekOrigin.Begin); } else { addLine(string.Format("{0} folders found", count)); for (int i = 0; i < count; i++) { fs.Seek(4, SeekOrigin.Current); long start = StreamUtils.ReadUInt64asLong(fs); addLine(""); addLine("Folder BEGIN " + numToAddr(start)); partPos = fs.Position; parseFolder(fs, start, sect.name); fs.Seek(partPos, SeekOrigin.Begin); } } break; #endregion case ProcessType.Sbackup: analyzeRequest = true; fs.Seek(8, SeekOrigin.Current); // just skip break; } partcount--; } } #endregion #region arc else if (!bruteForceScan && fileext == ".arc") { UInt32 test0 = StreamUtils.ReadUInt32(fs); if (test0 == 0x101F4667) { List<FileInfo> compFr = findOrCreateFileInfoList("compressed fragments"); fs.Seek(0x1C, SeekOrigin.Begin); UInt32 test1 = StreamUtils.ReadUInt32(fs); fs.Seek(4, SeekOrigin.Current); UInt32 test2 = StreamUtils.ReadUInt32(fs); bool startsWithFiles = (test1 == 0 && test2 == 0) || // Backup.arc (test1 == 0x1ea367a4 && test2 == 0xb00d58ae) || // UserFiles.arc (test1 == 0x53fb0d19 && test2 == 0xef7ac531); // Settings.arc fs.Seek(0x3C, SeekOrigin.Begin); addLine("Phone model: " + StreamUtils.ReadShortString(fs)); addLine(""); long startAddr = fs.Position; long lenComp; long lenUncomp; byte[] seq = new byte[] { 0, 0, 0, 1, 0, 0, 0, 0 }; byte[] buff = new byte[8]; if (startsWithFiles && StreamUtils.SeekTo(seq, fs)) { string filename = ""; string dir = ""; long recoveryAddr; do { recoveryAddr = fs.Position; fs.Seek(12, SeekOrigin.Current); filename = StreamUtils.ReadStringTo(fs, 0, 0x80); fs.Seek(11, SeekOrigin.Current); lenUncomp = StreamUtils.ReadUInt32(fs); fs.Seek(12, SeekOrigin.Current); lenComp = StreamUtils.ReadUInt64asLong(fs); addLine(filename + " - size: " + lenComp + " / " + lenUncomp); try { dir = Path.GetDirectoryName(filename); filename = Path.GetFileName(filename); } catch (Exception exc) { addLine(exc.Message); analyzeRequest = true; fs.Seek(recoveryAddr, SeekOrigin.Begin); if (StreamUtils.SeekTo(NokiaConstants.compHead, fs)) { fs.Seek(-22, SeekOrigin.Current); } break; } List<FileInfo> list = findOrCreateFileInfoList(dir); if (filename.Length > 0 && lenComp > 8) { list.Add(new FileinfoCf(filename, startAddr, lenComp, lenUncomp, DateTime.MinValue)); } StreamUtils.Counter += lenComp; startAddr += lenComp; fs.Seek(1, SeekOrigin.Current); fs.Read(buff, 0, buff.Length); } while (NokiaConstants.CompareByteArr(seq, buff)); addLine(""); // end of first section } seq[3] = 0; long lastRecoveryPosition = 0; while (true) { fs.Read(buff, 0, buff.Length); if (NokiaConstants.CompareByteArr(seq, buff)) { fs.Seek(4, SeekOrigin.Current); do { lenComp = StreamUtils.ReadUInt32(fs); lenUncomp = StreamUtils.ReadUInt32(fs); FileinfoCf fi = new FileinfoCf(numToAddr(fs.Position), fs.Position, lenComp, lenUncomp, DateTime.MinValue); compFr.Add(fi); addLine(fi.Filename + " - compressed fragment"); parseCompressedFragment("", fi, lenUncomp); fs.Seek(lenComp, SeekOrigin.Current); } while (lenUncomp == 65536); } else { if (StreamUtils.SeekTo(NokiaConstants.compHead, fs)) { if (lastRecoveryPosition != fs.Position) { lastRecoveryPosition = fs.Position; fs.Seek(-22, SeekOrigin.Current); } } else break; } } } else { byte[] pathStartSequence = new byte[] { 0x00, 0x5c, 0x00, 0x53, 0x00 }; if (StreamUtils.SeekTo(pathStartSequence, fs)) { fs.Seek(-9, SeekOrigin.Current); while (true) { try { int len = fs.ReadByte() / 2; string filename = StreamUtils.ReadString(fs, len); string dir = Path.GetDirectoryName(filename); bool canBeMessage = ExpectMessage(dir); fs.Seek(10, SeekOrigin.Current); // ?? datetime? UInt32 lenUncomp = StreamUtils.ReadUInt32(fs); UInt32 lenComp = StreamUtils.ReadUInt32(fs); StreamUtils.Counter += lenComp; addLine(filename + " - size: " + lenComp + " / " + lenUncomp); filename = Path.GetFileName(filename); List<FileInfo> list = findOrCreateFileInfoList(dir); FileinfoCf fic = new FileinfoCf(filename, fs.Position, lenComp, lenUncomp, DateTime.MinValue); if (canBeMessage) parseSymbianMessage(fic); list.Add(fic); fs.Seek(lenComp, SeekOrigin.Current); } catch { if (StreamUtils.SeekTo(pathStartSequence, fs)) { fs.Seek(-9, SeekOrigin.Current); } else { break; } } } } } foreach (TreeNode tn in treeViewDirs.Nodes) { tn.Expand(); } } #endregion #region bruteforce else { addLine("Scanning for vcards (contacts, messages, calendar items, bookmarks)..."); addLine(""); List<FileInfo> files; string filename; int current; int pr = 0; int pr2 = 0; while ((current = fs.ReadByte()) > -1) { if (fs.Position % 2048 == 0) { pr = (int)(20 * fs.Position / fs.Length); if (pr != pr2) { addLine(string.Format("{0:00}%", pr * 5)); pr2 = pr; } Application.DoEvents(); } if (Pattern.Msg.Step((byte)current, fs.Position)) { Vcard msg = new Vcard(Pattern.Msg.GetCaptureAsString(fs)); string box = msg["X-MESSAGE-TYPE"]; if (box.Length == 0) box = msg["X-IRMC-BOX"]; files = findOrCreateFileInfoList(NokiaConstants.ptMessages + "\\" + box); filename = numToName(files.Count + 1); try { filename = msg.PhoneNumbers[0]; filename = DataSetNbuExplorer.FindPhoneBookEntry(filename).name; } catch { } files.Add(new FileInfo(filename + ".vmg", Pattern.Msg.StartIndex, Pattern.Msg.Length, msg.MessageTime)); StreamUtils.Counter += Pattern.Msg.Length; addLine(numToProgressAndAddr(Pattern.Msg.StartIndex, fs.Length) + "\tmessage: " + filename); DataSetNbuExplorer.AddMessageFromVmg(msg); } if (Pattern.Msg.Active) continue; if (Pattern.Contact.Step((byte)current, fs.Position)) { Vcard contact = new Vcard(Pattern.Contact.GetCaptureAsString(fs)); string name = contact.Name; DateTime time = contact.GetDateTime("REV"); foreach (string number in contact.PhoneNumbers) { DataSetNbuExplorer.AddPhonebookEntry(number, name); } files = findOrCreateFileInfoList(NokiaConstants.ptContacts); filename = name; if (filename.Length == 0) filename = numToName(files.Count + 1); files.Add(new FileInfo(filename + ".vcf", Pattern.Contact.StartIndex, Pattern.Contact.Length, time)); if (contact.Photo != null) { files.Add(new FileInfoMemory(filename + "." + contact.PhotoExtension, contact.Photo, time)); } StreamUtils.Counter += Pattern.Contact.Length; addLine(numToProgressAndAddr(Pattern.Contact.StartIndex, fs.Length) + "\tcontact: " + filename); } if (Pattern.Contact.Active) continue; if (Pattern.Calendar.Step((byte)current, fs.Position)) { Vcard calendar = new Vcard(Pattern.Calendar.GetCaptureAsString(fs)); files = findOrCreateFileInfoList("Calendar\\" + calendar["X-EPOCAGENDAENTRYTYPE"]); filename = calendar["SUMMARY"]; if (filename.Length == 0) filename = numToName(files.Count + 1); files.Add(new FileInfo(filename + ".vcs", Pattern.Calendar.StartIndex, Pattern.Calendar.Length, calendar.GetDateTime("DTSTART"))); StreamUtils.Counter += Pattern.Calendar.Length; addLine(numToProgressAndAddr(Pattern.Calendar.StartIndex, fs.Length) + "\tcalendar: " + filename); } if (Pattern.Calendar.Active) continue; if (Pattern.Bookmark.Step((byte)current, fs.Position)) { Vcard calendar = new Vcard(Pattern.Bookmark.GetCaptureAsString(fs)); files = findOrCreateFileInfoList(NokiaConstants.ptBookmarks); filename = calendar["TITLE"]; if (filename.Length == 0) filename = numToName(files.Count + 1); files.Add(new FileInfo(filename + ".url", Pattern.Bookmark.StartIndex, Pattern.Bookmark.Length)); StreamUtils.Counter += Pattern.Bookmark.Length; addLine(numToProgressAndAddr(Pattern.Bookmark.StartIndex, fs.Length) + "\tbookmark: " + filename); } } } #endregion addLine(""); addLine(string.Format("Done, file coverage: {0:0.##}%", 100 * (float)StreamUtils.Counter / fs.Length)); addLine(""); recursiveRenameDuplicates(treeViewDirs.Nodes); #region Prepare message filtering by numbers treeViewMsgFilter.AfterCheck -= new TreeViewEventHandler(treeViewMsgFilter_AfterCheck); buildFilterSubNodes(treeViewMsgFilter.Nodes[0], "I"); buildFilterSubNodes(treeViewMsgFilter.Nodes[1], "O"); buildFilterSubNodes(treeViewMsgFilter.Nodes[2], "U"); treeViewMsgFilter_AfterCheck(this, new TreeViewEventArgs(null)); #endregion } catch (Exception exc) { MessageBox.Show(string.Format("Following error occured during parse:\r\n{0}\r\nPlease consider providing this backup to the author of application for analyzing and improving application.", exc.Message), this.appTitle, MessageBoxButtons.OK, MessageBoxIcon.Error); analyzeRequest = false; } finally { if (fs != null) fs.Close(); menuStripMain.Enabled = true; treeViewDirs.Enabled = true; saveParsingLogToolStripMenuItem.Enabled = (textBoxLog.Text.Trim().Length > 0); exportAllToolStripMenuItem.Enabled = (treeViewDirs.Nodes.Count > 0); exportToolStripMenuItem.Enabled = exportSelectedFolderToolStripMenuItem.Enabled = (treeViewDirs.SelectedNode != null); recountTotal(); this.Cursor = Cursors.Default; } if (analyzeRequest) { MessageBox.Show("Unknown structure type found. Please consider providing this backup to the author of application for analyzing and improving application.", this.appTitle, MessageBoxButtons.OK, MessageBoxIcon.Information); } }
private void parseFolderVcard(FileStream fs, List<FileInfo> contactList, string sectName, string rootFolderPath) { int count = StreamUtils.ReadUInt32asInt(fs); List<FileInfo> partFiles = findOrCreateFileInfoList(rootFolderPath); string filenameTemplate; if (sectName == NokiaConstants.ptContacts) { filenameTemplate = "{0}.vcf"; addLine(count.ToString() + " contacts found"); } else if (sectName == NokiaConstants.ptBookmarks) { filenameTemplate = "{0}.url"; addLine(count.ToString() + " bookmarks found"); } else { filenameTemplate = "{0}.vcs"; addLine(count.ToString() + " calendar items found"); } for (int i = 0; i < count; i++) { uint test = StreamUtils.ReadUInt32(fs); if (test == 0x10) { test = StreamUtils.ReadUInt32(fs); if (test > 1) { addLine("test 2 greater than 0x01:" + test.ToString("X")); } } else { addLine("test 1 different than 0x10:" + test.ToString("X")); } int vclen = StreamUtils.ReadUInt32asInt(fs); long start = fs.Position; byte[] buff = StreamUtils.ReadBuff(fs, vclen); StreamUtils.Counter += buff.Length; Vcard crd = new Vcard(System.Text.Encoding.UTF8.GetString(buff)); string name; DateTime time = DateTime.MinValue; if (sectName == NokiaConstants.ptContacts) { name = crd.Name; foreach (string number in crd.PhoneNumbers) { DataSetNbuExplorer.AddPhonebookEntry(number, name); } time = crd.GetDateTime("REV"); } else if (sectName == NokiaConstants.ptBookmarks) { name = crd["TITLE"]; } else { partFiles = findOrCreateFileInfoList(rootFolderPath + "\\" + crd["X-EPOCAGENDAENTRYTYPE"]); name = crd["SUMMARY"]; time = crd.GetDateTime("DTSTART"); } if (string.IsNullOrEmpty(name)) name = numToName(i + 1); FileInfo fi = new FileInfo(string.Format(filenameTemplate, name), start, vclen, time); partFiles.Add(fi); if (sectName == NokiaConstants.ptContacts) { contactList.Add(fi); } if (crd.Photo != null) { partFiles.Add(new FileInfoMemory(name + "." + crd.PhotoExtension, crd.Photo, time)); } } }