static DecryptMaterial parse_backup_file_module_info(XmlDocument xml_entry) { string aString = xml_entry.FirstChild.Attributes["table"].Value; DecryptMaterial decm = new DecryptMaterial(aString); XmlNodeList elemList = xml_entry.GetElementsByTagName("column"); foreach (XmlNode entry in elemList) { string name = entry.Attributes["name"].Value; if (name == "encMsgV3") { decm.encMsgV3 = xml_get_column_value(entry) as string; } else if (name == "checkMsgV3") { // TBR: reverse this double sized checkMsgV3. } else if (name == "name") { decm.name = xml_get_column_value(entry) as string; } } if (decm.do_check() == false) { return(null); } return(decm); }
static HybridDictionary parse_xml(string filepath, HybridDictionary decrypt_material_dict) { string filename = Path.GetFileName(filepath); Console.WriteLine("parsing xml file " + filename); // Create the XmlDocument. XmlDocument xml_dom = new XmlDocument(); xml_dom.Load(filepath); string fullpath = Directory.GetParent(filepath).FullName; string parent = Path.Combine(fullpath, Path.GetFileNameWithoutExtension(filepath)); XmlNodeList elemList = xml_dom.GetElementsByTagName("File"); foreach (XmlNode node in elemList) { string path = node.SelectSingleNode("Path").InnerText; string iv = node.SelectSingleNode("Iv").InnerText; if (!String.IsNullOrEmpty(path) && !String.IsNullOrEmpty(iv)) { DecryptMaterial dec_material = new DecryptMaterial(Path.GetFileNameWithoutExtension(filepath)); // XML files use Windows style path separator, backslash. dec_material.path = path; dec_material.iv = iv; string dkey = Path.Combine(parent, path.TrimStart(new char[] { '\\' })); decrypt_material_dict[dkey] = dec_material; } } return(decrypt_material_dict); }
internal string decrypt_package(DecryptMaterial dec_material, byte[] data) { if (this.good == false) { Console.WriteLine("well, it is hard to decrypt with a wrong key."); } if (String.IsNullOrEmpty(dec_material.encMsgV3)) { Console.WriteLine("cannot decrypt with an empty encMsgV3!"); return(null); } byte[] salt = dec_material._encMsgV3Bytes.Take(32).ToArray(); byte[] counter_iv = dec_material._encMsgV3Bytes.Skip(32).ToArray(); byte[] key = PBKDF2_SHA256_GetBytes(Encoding.UTF8.GetBytes(this._bkey), salt, Decryptor.dklen, Decryptor.count); string filename = @"./__temp__/" + Guid.NewGuid().ToString(); FileStream output = new FileStream(filename, FileMode.CreateNew, FileAccess.Write); MemoryStream input = new MemoryStream(data); AES_CTR_Transform(key, counter_iv, input, output); output.Dispose(); output.Close(); return(filename); }
internal byte[] decrypt_package(DecryptMaterial dec_material, byte[] data) { if (this.good == false) { Console.WriteLine("well, it is hard to decrypt with a wrong key."); } if (String.IsNullOrEmpty(dec_material.encMsgV3)) { Console.WriteLine("cannot decrypt with an empty encMsgV3!"); return(null); } byte[] salt = dec_material._encMsgV3Bytes.Take(32).ToArray(); byte[] counter_iv = dec_material._encMsgV3Bytes.Skip(32).ToArray(); byte[] key = PBKDF2_SHA256_GetBytes(Encoding.UTF8.GetBytes(this._bkey), salt, Decryptor.dklen, Decryptor.count); MemoryStream output = new MemoryStream(); MemoryStream input = new MemoryStream(data); AES_CTR_Transform(key, counter_iv, input, output); return(output.ToArray()); }
static HybridDictionary parse_info_xml(string filepath, ref Decryptor decryptor, HybridDictionary decrypt_material_dict) { // Create the XmlDocument. XmlDocument info_dom = new XmlDocument(); info_dom.Load(filepath); if (info_dom.GetElementsByTagName("info.xml").Count != 1) { Console.WriteLine("First tag should be 'info.xml', not '{0}'", info_dom.FirstChild.Name); decryptor = null; return(null); } string parent = Directory.GetParent(filepath).FullName; XmlDocument doc; XmlNodeList elemList = info_dom.GetElementsByTagName("row"); foreach (XmlNode entry in elemList) { string title = entry.Attributes["table"].Value; switch (title) { case "BackupFilesTypeInfo": doc = new XmlDocument(); doc.LoadXml(entry.OuterXml); parse_backup_files_type_info(ref decryptor, doc); break; case "HeaderInfo": case "BackupFilePhoneInfo": case "BackupFileVersionInfo": ignore_entry(entry); break; case "BackupFileModuleInfo": case "BackupFileModuleInfo_Contact": case "BackupFileModuleInfo_Media": case "BackupFileModuleInfo_SystemData": doc = new XmlDocument(); doc.LoadXml(entry.OuterXml); DecryptMaterial dec_material = parse_backup_file_module_info(doc); if (dec_material != null) { string dkey = Path.Combine(parent, dec_material.name); decrypt_material_dict[dkey] = dec_material; } break; default: Console.WriteLine("Unknown entry in 'info.xml': {0}", title); break; } } return(decrypt_material_dict); }
internal void crypto_init() { if (this.good == true) { Console.WriteLine("crypto_init: already done with success!"); return; } if (this.type_attch != 3) { Console.WriteLine("crypto_init: type_attch *should be* 3!"); return; } if (!String.IsNullOrEmpty(this.e_perbackupkey) && !String.IsNullOrEmpty(this.pwkey_salt)) { Console.WriteLine("crypto_init: using version 4"); __decrypt_bkey_v4(); } else { Console.WriteLine("crypto_init: using version 3"); this._bkey = this._upwd; } var passwordBytes = Encoding.UTF8.GetBytes(this._bkey); passwordBytes = SHA256.Create().ComputeHash(passwordBytes); this._bkey_sha256 = passwordBytes.Take(16).ToArray(); Console.WriteLine("SHA256(BKEY)[{0}] = {1}", this._bkey_sha256.Length, DecryptMaterial.hexlify(this._bkey_sha256)); byte[] salt = this._checkMsgBytes.Skip(32).ToArray(); Console.WriteLine("SALT[{0}] = {1}", salt.Length, DecryptMaterial.hexlify(salt)); byte[] res = PBKDF2_SHA256_GetBytes(Encoding.UTF8.GetBytes(this._bkey), salt, Decryptor.dklen, Decryptor.count); Console.WriteLine("KEY check expected = {0}", DecryptMaterial.hexlify(this._checkMsgBytes.Take(32).ToArray())); Console.WriteLine("RESULT = {0}", DecryptMaterial.hexlify(res)); if (res.SequenceEqual(this._checkMsgBytes.Take(32)) == true) { Console.WriteLine("OK, backup key is correct!"); this.good = true; } else { Console.WriteLine("KO, backup key is wrong!"); this.good = false; } }
internal byte[] decrypt_file(DecryptMaterial dec_material, byte[] data) { if (this.good == false) { Console.WriteLine("well, it is hard to decrypt with a wrong key."); } if (String.IsNullOrEmpty(dec_material.iv)) { Console.WriteLine("cannot decrypt with an empty iv!"); return(null); } MemoryStream output = new MemoryStream(); MemoryStream input = new MemoryStream(data); AES_CTR_Transform(this._bkey_sha256, dec_material._ivBytes, input, output); return(output.ToArray()); }
internal static void decrypt(string user_password, string backup_path_in, string dest_path_out) { Console.WriteLine("getting files and folders from: " + backup_path_in); if (!Directory.Exists(backup_path_in)) { Console.WriteLine("input backup folder does not exist!"); return; } Console.WriteLine("using output folder: " + dest_path_out); if (Directory.Exists(dest_path_out)) { List <string> files = Directory.GetFiles(dest_path_out, "*.*", SearchOption.AllDirectories).ToList(); if (files.Count > 0) { Console.WriteLine("output folder contains {0} files, cannot overwrite them!", files.Count); return; } } List <string> backup_all_files = Directory.GetFiles(backup_path_in, "*.*", SearchOption.AllDirectories).ToList(); List <string> xml_files = new List <string>(); List <string> apk_files = new List <string>(); List <string> tar_files = new List <string>(); List <string> db_files = new List <string>(); List <string> enc_files = new List <string>(); List <string> unk_files = new List <string>(); List <string> done_list; foreach (string entry in backup_all_files) { string extension = Path.GetExtension(entry).ToLower(); switch (extension) { case ".xml": xml_files.Add(entry); break; case ".apk": apk_files.Add(entry); break; case ".tar": tar_files.Add(entry); break; case ".db": db_files.Add(entry); break; case ".enc": enc_files.Add(entry); break; default: unk_files.Add(entry); break; } } HybridDictionary decrypt_material_dict = new HybridDictionary(); Decryptor decryptor = new Decryptor(user_password); Console.WriteLine("parsing XML files..."); foreach (string entry in xml_files) { string filename = Path.GetFileName(entry); Console.WriteLine("parsing xml " + filename); if (filename.ToLower() == "info.xml") { decrypt_material_dict = parse_info_xml(entry, ref decryptor, decrypt_material_dict); } else { decrypt_material_dict = parse_xml(entry, decrypt_material_dict); } } decryptor.crypto_init(); if (decryptor.good == false) { Console.WriteLine("decryption key is not good..."); return; } if (apk_files.Count > 0) { Console.WriteLine("copying APK to destination..."); string data_apk_dir = Path.Combine(dest_path_out, @"data\app"); Directory.CreateDirectory(data_apk_dir); done_list = new List <string>(); foreach (string entry in apk_files) { string filename = Path.GetFileName(entry); Console.WriteLine("working on " + filename); string dest_file = Path.Combine(data_apk_dir, filename + "-1"); Directory.CreateDirectory(dest_file); dest_file = Path.Combine(dest_file, "base.apk"); File.Copy(entry, dest_file); done_list.Add(entry); } foreach (string entry in done_list) { apk_files.Remove(entry); } } if (tar_files.Count > 0) { Console.WriteLine("decrypting and un-TAR-ing packages to destination..."); string data_app_dir = Path.Combine(dest_path_out, @"data\data"); Directory.CreateDirectory(data_app_dir); done_list = new List <string>(); foreach (string entry in tar_files) { Console.WriteLine("working on " + Path.GetFileName(entry)); string cleartext = null; string directory = Path.GetDirectoryName(entry); string filename = Path.GetFileNameWithoutExtension(entry); string skey = Path.Combine(directory, filename); if (decrypt_material_dict.Contains(skey)) { done_list.Add(entry); DecryptMaterial dec_material = (DecryptMaterial)decrypt_material_dict[skey]; cleartext = decryptor.decrypt_package(dec_material, File.ReadAllBytes(entry)); } else { Console.WriteLine("entry '{0}' has no decrypt material!", skey); } if (cleartext != null) { using (FileStream ms = new FileStream(cleartext, FileMode.Open, FileAccess.Read)) { TAR_Extract(ms, data_app_dir); } } else if (File.Exists(entry)) { using (StreamReader sr = new StreamReader(entry)) { TAR_Extract(sr.BaseStream, data_app_dir); } } } foreach (string entry in done_list) { tar_files.Remove(entry); } } if (db_files.Count > 0) { Console.WriteLine("decrypting database DB files to destination..."); string data_app_dir = Path.Combine(dest_path_out, "db"); Directory.CreateDirectory(data_app_dir); done_list = new List <string>(); foreach (string entry in db_files) { Console.WriteLine("working on " + Path.GetFileName(entry)); string cleartext = null; string directory = Path.GetDirectoryName(entry); string filename = Path.GetFileNameWithoutExtension(entry); string skey = Path.Combine(directory, filename); if (decrypt_material_dict.Contains(skey)) { done_list.Add(entry); DecryptMaterial dec_material = (DecryptMaterial)decrypt_material_dict[skey]; cleartext = decryptor.decrypt_package(dec_material, File.ReadAllBytes(entry)); } else { Console.WriteLine("entry '{0}' has no decrypt material!", skey); } if (cleartext != null) { string dest_file = Path.Combine(data_app_dir, Path.GetFileName(entry)); File.Copy(cleartext, dest_file); //File.WriteAllBytes(dest_file, cleartext); } } foreach (string entry in done_list) { db_files.Remove(entry); } } if (enc_files.Count > 0) { Console.WriteLine("decrypting multimedia ENC files to destination..."); string asterisk = @"|/-\-"; done_list = new List <string>(); for (int i = 1; i <= enc_files.Count; i++) { string entry = enc_files[i - 1]; byte[] cleartext = null; DecryptMaterial dec_material = null; string directory = Path.GetDirectoryName(entry); string filename = Path.GetFileNameWithoutExtension(entry); string skey = Path.Combine(directory, filename); if (decrypt_material_dict.Contains(skey)) { done_list.Add(entry); dec_material = (DecryptMaterial)decrypt_material_dict[skey]; string aString = String.Format("{0} of {1}: {2}", i, enc_files.Count, Path.GetFileName(dec_material.path)); aString = aString.PadRight(Console.WindowWidth - 2).Substring(0, Console.WindowWidth - 2); Console.Write("\r{0}{1}", aString, asterisk[i % asterisk.Length]); cleartext = decryptor.decrypt_file(dec_material, File.ReadAllBytes(entry)); } else { Console.WriteLine("entry '{0}' has no decrypt material!", skey); } if (cleartext != null && dec_material != null) { string dest_file = dest_path_out; string tmp_path = dec_material.path.TrimStart(new char[] { '\\', '/' }); dest_file = Path.Combine(dest_file, tmp_path); string dest_dir = Directory.GetParent(dest_file).FullName; Directory.CreateDirectory(dest_dir); File.WriteAllBytes(dest_file, cleartext); } } if (enc_files.Count > 0) { Console.Write("\r"); } foreach (string entry in done_list) { enc_files.Remove(entry); } } if (unk_files.Count > 0) { Console.WriteLine("copying unmanaged files to destination..."); string data_unk_dir = Path.Combine(dest_path_out, "misc"); Directory.CreateDirectory(data_unk_dir); done_list = new List <string>(); foreach (string entry in unk_files) { string common_path = FindCommonPath(new List <string>() { entry, backup_path_in }); string relative_path = entry.Replace(common_path, ""); relative_path = relative_path.TrimStart(new char[] { '\\', '/' }); string dest_file = Path.Combine(data_unk_dir, relative_path); Directory.CreateDirectory(Directory.GetParent(dest_file).FullName); File.Copy(entry, dest_file); done_list.Add(entry); } foreach (string entry in done_list) { unk_files.Remove(entry); } } foreach (string entry in apk_files) { string filename = Path.GetFileName(entry); Console.WriteLine("APK file not handled: " + filename); } foreach (string entry in tar_files) { string filename = Path.GetFileName(entry); Console.WriteLine("TAR file not handled: " + filename); } foreach (string entry in db_files) { string filename = Path.GetFileName(entry); Console.WriteLine("DB file not handled: " + filename); } foreach (string entry in enc_files) { string filename = Path.GetFileName(entry); Console.WriteLine("ENC file not handled: " + filename); } foreach (string entry in unk_files) { string filename = Path.GetFileName(entry); Console.WriteLine("UNK file not handled: " + filename); } Console.WriteLine("DONE!"); }