如何解密加密的 Apple iTunes iPhone 备份?

2024-04-05

许多不幸的 iPhone 用户请求我帮助他们从 iTunes 备份中恢复数据。当它们未加密时,这很容易,但当它们加密时,无论密码是否已知,都不容易。

因此,我试图找出加密时 mddata 和 mdinfo 文件使用的加密方案。否则,我在阅读这些文件时没有任何问题,并且为此构建了一些强大的 C# 库。 (如果你能帮忙,我不在乎你使用哪种语言。这是我在这里追求的原则!)

Apple《iPhone OS 企业部署指南》指出,“通过选择加密 iPhone,可以以加密格式存储设备备份” iTunes 设备摘要窗格中的备份选项。文件使用 AES128 和 256 位密钥进行加密。密钥安全地存储在 iPhone 钥匙串中。”

这是一个很好的线索,iPhone 上的 Stackoverflow 上有一些很好的信息AES/Rijndael 互操作性 https://stackoverflow.com/questions/538435/aes-interoperability-between-net-and-iphone建议可以使用密钥大小 128 和 CBC 模式。

除了任何其他混淆之外,还需要密钥和初始化向量 (IV)/盐。

人们可能会认为该密钥是对“备份密码”的操纵,iTunes 会提示用户输入该密码并将其传递给“AppleMobileBackup.exe http://keepmurmuring.blogspot.com/2009/06/iphone-backup-informationnote.html”,以 CBC 规定的方式填充。但是,考虑到 iPhone 钥匙串的参考,我想知道“备份密码”是否不能用作 X509 证书或对称私钥的密码,并且证书或私钥密钥本身可以用作密钥。(AES http://en.wikipedia.org/wiki/Advanced_Encryption_Standard并且 iTunes 加密/解密过程是对称的。)

IV 是另一回事,可能有几件事。也许这是关键之一硬编码的 http://hcsoftware.sourceforge.net/jason-rohrer/itms4all/进入 iTunes,或进入设备本身 http://wikee.iphwn.org/s5l8900:encryption_keys.

尽管苹果的上述评论表明密钥存在于设备的钥匙串上,但我认为这并不那么重要。人们可以将加密的备份恢复到不同的设备,这表明与解密相关的所有信息都存在于备份和iTunes配置中,并且在这种情况下,仅在设备上的任何内容都是不相关且可替换的。那么关键可能在哪里呢?

我在下面列出了来自 Windows 计算机的路径,但无论我们使用哪种操作系统,它都非常重要。

“\appdata\Roaming\Apple Computer\iTunes\itunesprefs.xml”包含一个 PList,其中包含“Keychain”字典条目。 “\programdata\apple\Lockdown\09037027da8f4bdefdea97d706703ca034c88bab.plist”包含一个带有“DeviceCertificate”、“HostCertificate”和“RootCertificate”的 PList,所有这些似乎都是有效的 X509 证书。同一文件似乎还包含非对称密钥“RootPrivateKey”和“HostPrivateKey”(我的阅读表明这些可能是 PKCS #7 封装的)。此外,在每个备份中,Manifest.plist 文件中都有“AuthSignature”和“AuthData”值,尽管这些值似乎随着每个文件的增量备份而轮换,表明它们作为密钥没有那么有用,除非确实有什么东西正在进行中。

有很多误导性的内容表明从加密备份中获取数据很容易。事实并非如此,据我所知,这还没有完成。绕过或禁用 http://iphoneinsecurity.com/备份加密完全是另一回事,也不是我想要做的。

这并不是要破解 iPhone 或类似的东西。我所追求的只是一种从加密的 iTunes 备份中提取数据(照片、联系人等)的方法,就像我可以提取未加密的数据一样。我已经根据上面列出的信息尝试了各种排列,但一无所获。我很感激我可能错过的任何想法或技术。


安全研究员 Jean-Baptiste Bédrune 和 Jean Sigwald at Hack-in-the-box 阿姆斯特丹 2011 https://conference.hitb.org/hitbsecconf2011ams/.

此后,苹果发布了iOS 安全白皮书 https://www.apple.com/ca/business-docs/iOS_Security_Guide.pdf有关密钥和算法的更多详细信息,以及 Charlie Miller 等人。有 发布了iOS 黑客手册 https://rads.stackoverflow.com/amzn/click/com/1118204123,其中涵盖了一些相同的 以指导方式接地。 iOS 10 首次发布时发生了一些变化 苹果一开始并没有公开的备份格式,但是各种 人们对格式更改进行逆向工程 https://github.com/horrorho/InflatableDonkey/issues/41#issuecomment-261927890.

加密备份很棒

加密 iPhone 备份的伟大之处在于它们包含一些内容 例如不在常规未加密备份中的 WiFi 密码。作为 中讨论的iOS 安全白皮书 https://www.apple.com/ca/business-docs/iOS_Security_Guide.pdf, 加密备份 被认为更“安全”,因此苹果认为可以包含更多 其中的敏感信息。

重要警告:显然,解密你的 iOS 设备的备份 删除其加密。为了保护您的隐私和安全,你应该 仅在具有全磁盘加密的计算机上运行这些脚本。虽然它 安全专家可以编写保护密钥的软件 记忆,例如通过使用类似的函数VirtualLock() https://msdn.microsoft.com/en-us/library/windows/desktop/aa366895(v=vs.85).aspx and SecureZeroMemory() https://msdn.microsoft.com/en-us/library/windows/desktop/aa366877(v=vs.85).aspx除许多其他事项外,这些 Python 脚本会将您的加密密钥和密码存储在字符串中 被Python垃圾收集。这意味着您的密钥和密码 会在 RAM 中存在一段时间,从那时起它们就会泄漏到你的交换区中 文件并保存到您的磁盘上,对手可以在其中恢复它们。这 完全失去了加密备份的意义。

如何解密备份:理论上

The iOS 安全白皮书 https://www.apple.com/ca/business-docs/iOS_Security_Guide.pdf解释基本概念 每个文件密钥、保护类别、保护类别密钥和密钥包 比我能做的更好。如果您还不熟悉这些,请看一些 分钟阅读相关部分。

现在你知道 iOS 中的每个文件都是用自己的随机数加密的 每个文件的加密密钥,属于保护类别,并且每个文件 加密密钥存储在文件系统元数据中,包装在 保护等级密钥。

解密:

  1. 解码存储在BackupKeyBag的条目Manifest.plist。该结构的高级概述在 这白皮书 https://www.apple.com/ca/business-docs/iOS_Security_Guide.pdf. The iPhone 维基 http://theiphonewiki.com/wiki/index.php?title=ITunes_Backup#BackupKeyBag描述二进制格式:一个4字节的字符串类型字段,一个4字节 大端长度字段,然后是值本身。

    重要的值是 PBKDF2ITER化和SALT, 双 保护盐DPSL和迭代次数DPIC,然后对于每个 保护CLS, the WPKY包裹的钥匙。

  2. 使用备份密码使用正确的 PBKDF2 派生 32 字节密钥 盐和迭代次数。首先使用 SHA256 轮DPSL and DPIC,然后进行 SHA1 轮ITER and SALT.

    根据以下方式解开每个包装好的密钥RFC 3394 http://www.apps.ietf.org/rfc/rfc3394.html.

  3. 通过从清单数据库中提取 4 字节保护类和更长的密钥来解密清单数据库ManifestKey in Manifest.plist,然后打开它。你现在有一个 包含所有文件元数据的 SQLite 数据库。

  4. 对于每个感兴趣的文件,获取类加密的每个文件加密 密钥和保护等级代码通过查看Files.file数据库 包含以下内容的二进制 plist 的列EncryptionKey and ProtectionClass条目。剥离初始的四字节长度标签EncryptionKey使用前。

    然后,通过用类解包来导出最终的解密密钥 使用备份密码解开的密钥。然后解密文件 在 CBC 模式下使用 AES,IV 为零。

如何解密备份:实践

首先,您需要一些库依赖项。如果您在 Mac 上使用自制安装的 Python 2.7 或 3.7,则可以使用以下命令安装依赖项:

CFLAGS="-I$(brew --prefix)/opt/openssl/include" \
LDFLAGS="-L$(brew --prefix)/opt/openssl/lib" \    
    pip install biplist fastpbkdf2 pycrypto

以可运行的源代码形式,以下是如何解密单个 来自加密 iPhone 备份的首选项文件:

#!/usr/bin/env python3.7
# coding: UTF-8

from __future__ import print_function
from __future__ import division

import argparse
import getpass
import os.path
import pprint
import random
import shutil
import sqlite3
import string
import struct
import tempfile
from binascii import hexlify

import Crypto.Cipher.AES # https://www.dlitz.net/software/pycrypto/
import biplist
import fastpbkdf2
from biplist import InvalidPlistException


def main():
    ## Parse options
    parser = argparse.ArgumentParser()
    parser.add_argument('--backup-directory', dest='backup_directory',
                    default='testdata/encrypted')
    parser.add_argument('--password-pipe', dest='password_pipe',
                        help="""\
Keeps password from being visible in system process list.
Typical use: --password-pipe=<(echo -n foo)
""")
    parser.add_argument('--no-anonymize-output', dest='anonymize',
                        action='store_false')
    args = parser.parse_args()
    global ANONYMIZE_OUTPUT
    ANONYMIZE_OUTPUT = args.anonymize
    if ANONYMIZE_OUTPUT:
        print('Warning: All output keys are FAKE to protect your privacy')

    manifest_file = os.path.join(args.backup_directory, 'Manifest.plist')
    with open(manifest_file, 'rb') as infile:
        manifest_plist = biplist.readPlist(infile)
    keybag = Keybag(manifest_plist['BackupKeyBag'])
    # the actual keys are unknown, but the wrapped keys are known
    keybag.printClassKeys()

    if args.password_pipe:
        password = readpipe(args.password_pipe)
        if password.endswith(b'\n'):
            password = password[:-1]
    else:
        password = getpass.getpass('Backup password: ').encode('utf-8')

    ## Unlock keybag with password
    if not keybag.unlockWithPasscode(password):
        raise Exception('Could not unlock keybag; bad password?')
    # now the keys are known too
    keybag.printClassKeys()

    ## Decrypt metadata DB
    manifest_key = manifest_plist['ManifestKey'][4:]
    with open(os.path.join(args.backup_directory, 'Manifest.db'), 'rb') as db:
        encrypted_db = db.read()

    manifest_class = struct.unpack('<l', manifest_plist['ManifestKey'][:4])[0]
    key = keybag.unwrapKeyForClass(manifest_class, manifest_key)
    decrypted_data = AESdecryptCBC(encrypted_db, key)

    temp_dir = tempfile.mkdtemp()
    try:
        # Does anyone know how to get Python’s SQLite module to open some
        # bytes in memory as a database?
        db_filename = os.path.join(temp_dir, 'db.sqlite3')
        with open(db_filename, 'wb') as db_file:
            db_file.write(decrypted_data)
        conn = sqlite3.connect(db_filename)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        # c.execute("select * from Files limit 1");
        # r = c.fetchone()
        c.execute("""
            SELECT fileID, domain, relativePath, file
            FROM Files
            WHERE relativePath LIKE 'Media/PhotoData/MISC/DCIM_APPLE.plist'
            ORDER BY domain, relativePath""")
        results = c.fetchall()
    finally:
        shutil.rmtree(temp_dir)

    for item in results:
        fileID, domain, relativePath, file_bplist = item

        plist = biplist.readPlistFromString(file_bplist)
        file_data = plist['$objects'][plist['$top']['root'].integer]
        size = file_data['Size']

        protection_class = file_data['ProtectionClass']
        encryption_key = plist['$objects'][
            file_data['EncryptionKey'].integer]['NS.data'][4:]

        backup_filename = os.path.join(args.backup_directory,
                                    fileID[:2], fileID)
        with open(backup_filename, 'rb') as infile:
            data = infile.read()
            key = keybag.unwrapKeyForClass(protection_class, encryption_key)
            # truncate to actual length, as encryption may introduce padding
            decrypted_data = AESdecryptCBC(data, key)[:size]

        print('== decrypted data:')
        print(wrap(decrypted_data))
        print()

        print('== pretty-printed plist')
        pprint.pprint(biplist.readPlistFromString(decrypted_data))

##
# this section is mostly copied from parts of iphone-dataprotection
# http://code.google.com/p/iphone-dataprotection/

CLASSKEY_TAGS = [b"CLAS",b"WRAP",b"WPKY", b"KTYP", b"PBKY"]  #UUID
KEYBAG_TYPES = ["System", "Backup", "Escrow", "OTA (icloud)"]
KEY_TYPES = ["AES", "Curve25519"]
PROTECTION_CLASSES={
    1:"NSFileProtectionComplete",
    2:"NSFileProtectionCompleteUnlessOpen",
    3:"NSFileProtectionCompleteUntilFirstUserAuthentication",
    4:"NSFileProtectionNone",
    5:"NSFileProtectionRecovery?",

    6: "kSecAttrAccessibleWhenUnlocked",
    7: "kSecAttrAccessibleAfterFirstUnlock",
    8: "kSecAttrAccessibleAlways",
    9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly",
    10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly",
    11: "kSecAttrAccessibleAlwaysThisDeviceOnly"
}
WRAP_DEVICE = 1
WRAP_PASSCODE = 2

class Keybag(object):
    def __init__(self, data):
        self.type = None
        self.uuid = None
        self.wrap = None
        self.deviceKey = None
        self.attrs = {}
        self.classKeys = {}
        self.KeyBagKeys = None #DATASIGN blob
        self.parseBinaryBlob(data)

    def parseBinaryBlob(self, data):
        currentClassKey = None

        for tag, data in loopTLVBlocks(data):
            if len(data) == 4:
                data = struct.unpack(">L", data)[0]
            if tag == b"TYPE":
                self.type = data
                if self.type > 3:
                    print("FAIL: keybag type > 3 : %d" % self.type)
            elif tag == b"UUID" and self.uuid is None:
                self.uuid = data
            elif tag == b"WRAP" and self.wrap is None:
                self.wrap = data
            elif tag == b"UUID":
                if currentClassKey:
                    self.classKeys[currentClassKey[b"CLAS"]] = currentClassKey
                currentClassKey = {b"UUID": data}
            elif tag in CLASSKEY_TAGS:
                currentClassKey[tag] = data
            else:
                self.attrs[tag] = data
        if currentClassKey:
            self.classKeys[currentClassKey[b"CLAS"]] = currentClassKey

    def unlockWithPasscode(self, passcode):
        passcode1 = fastpbkdf2.pbkdf2_hmac('sha256', passcode,
                                        self.attrs[b"DPSL"],
                                        self.attrs[b"DPIC"], 32)
        passcode_key = fastpbkdf2.pbkdf2_hmac('sha1', passcode1,
                                            self.attrs[b"SALT"],
                                            self.attrs[b"ITER"], 32)
        print('== Passcode key')
        print(anonymize(hexlify(passcode_key)))
        for classkey in self.classKeys.values():
            if b"WPKY" not in classkey:
                continue
            k = classkey[b"WPKY"]
            if classkey[b"WRAP"] & WRAP_PASSCODE:
                k = AESUnwrap(passcode_key, classkey[b"WPKY"])
                if not k:
                    return False
                classkey[b"KEY"] = k
        return True

    def unwrapKeyForClass(self, protection_class, persistent_key):
        ck = self.classKeys[protection_class][b"KEY"]
        if len(persistent_key) != 0x28:
            raise Exception("Invalid key length")
        return AESUnwrap(ck, persistent_key)

    def printClassKeys(self):
        print("== Keybag")
        print("Keybag type: %s keybag (%d)" % (KEYBAG_TYPES[self.type], self.type))
        print("Keybag version: %d" % self.attrs[b"VERS"])
        print("Keybag UUID: %s" % anonymize(hexlify(self.uuid)))
        print("-"*209)
        print("".join(["Class".ljust(53),
                    "WRAP".ljust(5),
                    "Type".ljust(11),
                    "Key".ljust(65),
                    "WPKY".ljust(65),
                    "Public key"]))
        print("-"*208)
        for k, ck in self.classKeys.items():
            if k == 6:print("")

            print("".join(
                [PROTECTION_CLASSES.get(k).ljust(53),
                str(ck.get(b"WRAP","")).ljust(5),
                KEY_TYPES[ck.get(b"KTYP",0)].ljust(11),
                anonymize(hexlify(ck.get(b"KEY", b""))).ljust(65),
                anonymize(hexlify(ck.get(b"WPKY", b""))).ljust(65),
            ]))
        print()

def loopTLVBlocks(blob):
    i = 0
    while i + 8 <= len(blob):
        tag = blob[i:i+4]
        length = struct.unpack(">L",blob[i+4:i+8])[0]
        data = blob[i+8:i+8+length]
        yield (tag,data)
        i += 8 + length

def unpack64bit(s):
    return struct.unpack(">Q",s)[0]
def pack64bit(s):
    return struct.pack(">Q",s)

def AESUnwrap(kek, wrapped):
    C = []
    for i in range(len(wrapped)//8):
        C.append(unpack64bit(wrapped[i*8:i*8+8]))
    n = len(C) - 1
    R = [0] * (n+1)
    A = C[0]

    for i in range(1,n+1):
        R[i] = C[i]

    for j in reversed(range(0,6)):
        for i in reversed(range(1,n+1)):
            todec = pack64bit(A ^ (n*j+i))
            todec += pack64bit(R[i])
            B = Crypto.Cipher.AES.new(kek).decrypt(todec)
            A = unpack64bit(B[:8])
            R[i] = unpack64bit(B[8:])

    if A != 0xa6a6a6a6a6a6a6a6:
        return None
    res = b"".join(map(pack64bit, R[1:]))
    return res

ZEROIV = "\x00"*16
def AESdecryptCBC(data, key, iv=ZEROIV, padding=False):
    if len(data) % 16:
        print("AESdecryptCBC: data length not /16, truncating")
        data = data[0:(len(data)/16) * 16]
    data = Crypto.Cipher.AES.new(key, Crypto.Cipher.AES.MODE_CBC, iv).decrypt(data)
    if padding:
        return removePadding(16, data)
    return data

##
# here are some utility functions, one making sure I don’t leak my
# secret keys when posting the output on Stack Exchange

anon_random = random.Random(0)
memo = {}
def anonymize(s):
    if type(s) == str:
        s = s.encode('utf-8')
    global anon_random, memo
    if ANONYMIZE_OUTPUT:
        if s in memo:
            return memo[s]
        possible_alphabets = [
            string.digits,
            string.digits + 'abcdef',
            string.ascii_letters,
            "".join(chr(x) for x in range(0, 256)),
        ]
        for a in possible_alphabets:
            if all((chr(c) if type(c) == int else c) in a for c in s):
                alphabet = a
                break
        ret = "".join([anon_random.choice(alphabet) for i in range(len(s))])
        memo[s] = ret
        return ret
    else:
        return s

def wrap(s, width=78):
    "Return a width-wrapped repr(s)-like string without breaking on \’s"
    s = repr(s)
    quote = s[0]
    s = s[1:-1]
    ret = []
    while len(s):
        i = s.rfind('\\', 0, width)
        if i <= width - 4: # "\x??" is four characters
            i = width
        ret.append(s[:i])
        s = s[i:]
    return '\n'.join("%s%s%s" % (quote, line ,quote) for line in ret)

def readpipe(path):
    if stat.S_ISFIFO(os.stat(path).st_mode):
        with open(path, 'rb') as pipe:
            return pipe.read()
    else:
        raise Exception("Not a pipe: {!r}".format(path))

if __name__ == '__main__':
    main()

然后打印以下输出:

Warning: All output keys are FAKE to protect your privacy
== Keybag
Keybag type: Backup keybag (1)
Keybag version: 3
Keybag UUID: dc6486c479e84c94efce4bea7169ef7d
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class                                                WRAP Type       Key                                                              WPKY                                                             Public key
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NSFileProtectionComplete                             2    AES                                                                         4c80b6da07d35d393fc7158e18b8d8f9979694329a71ceedee86b4cde9f97afec197ad3b13c5d12b
NSFileProtectionCompleteUnlessOpen                   2    AES                                                                         09e8a0a9965f00f213ce06143a52801f35bde2af0ad54972769845d480b5043f545fa9b66a0353a6
NSFileProtectionCompleteUntilFirstUserAuthentication 2    AES                                                                         e966b6a0742878ce747cec3fa1bf6a53b0d811ad4f1d6147cd28a5d400a8ffe0bbabea5839025cb5
NSFileProtectionNone                                 2    AES                                                                         902f46847302816561e7df57b64beea6fa11b0068779a65f4c651dbe7a1630f323682ff26ae7e577
NSFileProtectionRecovery?                            3    AES                                                                         a3935fed024cd9bc11d0300d522af8e89accfbe389d7c69dca02841df46c0a24d0067dba2f696072

kSecAttrAccessibleWhenUnlocked                       2    AES                                                                         09a1856c7e97a51a9c2ecedac8c3c7c7c10e7efa931decb64169ee61cb07a0efb115050fd1e33af1
kSecAttrAccessibleAfterFirstUnlock                   2    AES                                                                         0509d215f2f574efa2f192efc53c460201168b26a175f066b5347fc48bc76c637e27a730b904ca82
kSecAttrAccessibleAlways                             2    AES                                                                         b7ac3c4f1e04896144ce90c4583e26489a86a6cc45a2b692a5767b5a04b0907e081daba009fdbb3c
kSecAttrAccessibleWhenUnlockedThisDeviceOnly         3    AES                                                                         417526e67b82e7c6c633f9063120a299b84e57a8ffee97b34020a2caf6e751ec5750053833ab4d45
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly     3    AES                                                                         b0e17b0cf7111c6e716cd0272de5684834798431c1b34bab8d1a1b5aba3d38a3a42c859026f81ccc
kSecAttrAccessibleAlwaysThisDeviceOnly               3    AES                                                                         9b3bdc59ae1d85703aa7f75d49bdc600bf57ba4a458b20a003a10f6e36525fb6648ba70e6602d8b2

== Passcode key
ee34f5bb635830d698074b1e3e268059c590973b0f1138f1954a2a4e1069e612

== Keybag
Keybag type: Backup keybag (1)
Keybag version: 3
Keybag UUID: dc6486c479e84c94efce4bea7169ef7d
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Class                                                WRAP Type       Key                                                              WPKY                                                             Public key
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
NSFileProtectionComplete                             2    AES        64e8fc94a7b670b0a9c4a385ff395fe9ba5ee5b0d9f5a5c9f0202ef7fdcb386f 4c80b6da07d35d393fc7158e18b8d8f9979694329a71ceedee86b4cde9f97afec197ad3b13c5d12b
NSFileProtectionCompleteUnlessOpen                   2    AES        22a218c9c446fbf88f3ccdc2ae95f869c308faaa7b3e4fe17b78cbf2eeaf4ec9 09e8a0a9965f00f213ce06143a52801f35bde2af0ad54972769845d480b5043f545fa9b66a0353a6
NSFileProtectionCompleteUntilFirstUserAuthentication 2    AES        1004c6ca6e07d2b507809503180edf5efc4a9640227ac0d08baf5918d34b44ef e966b6a0742878ce747cec3fa1bf6a53b0d811ad4f1d6147cd28a5d400a8ffe0bbabea5839025cb5
NSFileProtectionNone                                 2    AES        2e809a0cd1a73725a788d5d1657d8fd150b0e360460cb5d105eca9c60c365152 902f46847302816561e7df57b64beea6fa11b0068779a65f4c651dbe7a1630f323682ff26ae7e577
NSFileProtectionRecovery?                            3    AES        9a078d710dcd4a1d5f70ea4062822ea3e9f7ea034233e7e290e06cf0d80c19ca a3935fed024cd9bc11d0300d522af8e89accfbe389d7c69dca02841df46c0a24d0067dba2f696072

kSecAttrAccessibleWhenUnlocked                       2    AES        606e5328816af66736a69dfe5097305cf1e0b06d6eb92569f48e5acac3f294a4 09a1856c7e97a51a9c2ecedac8c3c7c7c10e7efa931decb64169ee61cb07a0efb115050fd1e33af1
kSecAttrAccessibleAfterFirstUnlock                   2    AES        6a4b5292661bac882338d5ebb51fd6de585befb4ef5f8ffda209be8ba3af1b96 0509d215f2f574efa2f192efc53c460201168b26a175f066b5347fc48bc76c637e27a730b904ca82
kSecAttrAccessibleAlways                             2    AES        c0ed717947ce8d1de2dde893b6026e9ee1958771d7a7282dd2116f84312c2dd2 b7ac3c4f1e04896144ce90c4583e26489a86a6cc45a2b692a5767b5a04b0907e081daba009fdbb3c
kSecAttrAccessibleWhenUnlockedThisDeviceOnly         3    AES        80d8c7be8d5103d437f8519356c3eb7e562c687a5e656cfd747532f71668ff99 417526e67b82e7c6c633f9063120a299b84e57a8ffee97b34020a2caf6e751ec5750053833ab4d45
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly     3    AES        a875a15e3ff901351c5306019e3b30ed123e6c66c949bdaa91fb4b9a69a3811e b0e17b0cf7111c6e716cd0272de5684834798431c1b34bab8d1a1b5aba3d38a3a42c859026f81ccc
kSecAttrAccessibleAlwaysThisDeviceOnly               3    AES        1e7756695d337e0b06c764734a9ef8148af20dcc7a636ccfea8b2eb96a9e9373 9b3bdc59ae1d85703aa7f75d49bdc600bf57ba4a458b20a003a10f6e36525fb6648ba70e6602d8b2

== decrypted data:
'<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD '
'PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist versi'
'on="1.0">\n<dict>\n\t<key>DCIMLastDirectoryNumber</key>\n\t<integer>100</integ'
'er>\n\t<key>DCIMLastFileNumber</key>\n\t<integer>3</integer>\n</dict>\n</plist'
'>\n'

== pretty-printed plist
{'DCIMLastDirectoryNumber': 100, 'DCIMLastFileNumber': 3}

额外学分

The iPhone 数据保护代码 https://code.google.com/archive/p/iphone-dataprotection/source由 Bédrune 和 Sigwald 发布 解密keychain从备份中获取,包括保存的 wifi 等有趣的内容 和网站密码:

$ python iphone-dataprotection/python_scripts/keychain_tool.py ...

--------------------------------------------------------------------------------------
|                              Passwords                                             |
--------------------------------------------------------------------------------------
|Service           |Account          |Data           |Access group  |Protection class|
--------------------------------------------------------------------------------------
|AirPort           |Ed’s Coffee Shop |<3FrenchRoast  |apple         |AfterFirstUnlock|
...

该代码不再适用于使用最新 iOS 的手机的备份,但有一些golang https://github.com/dunhamsteve/ios ports https://github.com/chiefbrain/ios已保持最新状态 允许访问keychain https://github.com/gwatts/pinfinder.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何解密加密的 Apple iTunes iPhone 备份? 的相关文章

  • 使用 UIWebview 导航 Pdf 在 IOS 5 中不起作用

    我最近升级到 Xcode 4 我发现一个运行良好一年多的应用程序现在无法在 Ios 5 下运行 该应用程序在 UIWebview 中导航 Pdf 并使用以下代码移动到其中的任何页面pdf self pdfNavigateController
  • UISlider 可捕捉到固定的步数(如 iOS 7 设置应用中的文本大小)

    我正在尝试创建一个UISlider让您可以从一组数字中进行选择 每个滑块位置应等距 并且滑块应卡入每个位置 而不是在它们之间平滑滑动 这是滑块的行为Settings gt General gt Text Size 这是在 iOS 7 中引入
  • iphone总是返回UIInterfaceOrientationPortrait

    我需要确保当我的UIViewController负载 它根据需要旋转 我已经实施了shouldAutorotateToInterfaceOrientation方法及其所有工作正常 除非应用程序首次加载时 当 iphone 处于横向模式时 或
  • 为 UILabel 设置不同字体的问题

    我想将字体大小和姓氏设置为 titleLabel Helvetica Neue UltraLight titleLabel setFont UIFont fontWithName Helvetica Neue UltraLight size
  • Interface Builder 中的多个视图状态

    我正在处理一个有 3 种状态的屏幕 证实 Loading Error 前两个非常简单 因为只更改了标签文本 第三个比较棘手 因为我需要显示一条错误消息并在其上有一个重试按钮 另外 我希望将所有这些都放在一个控制器下 我想这是最简单的部分 问
  • 以编程方式创建 UILabel

    我通过代码执行了以下操作 UILabel label UILabel alloc initWithFrame CGRectMake 40 70 300 50 label backgroundColor UIColor clearColor
  • 以编程方式添加 TabBarController

    我想以编程方式制作标签栏控制器和导航控制器 到目前为止 我的代码有效 它在底部显示了一个选项卡栏 但 OptionViewController 在第二个选项卡栏的按钮上没有说任何内容 没有标题 有趣的是 当我单击没有任何内容的按钮时 标题出
  • 无法向引导服务器注册 com.XXXXX.deviceapp

    我在设备中运行时遇到此错误 请确实需要全面的帮助来解决此问题 无法向引导服务器注册 com XXXXX deviceapp 错误 未知错误代码 这通常意味着该进程的另一个实例已经在运行或挂在调试器中 程序收到信号 SIGABRT 我就遇到过
  • UITableView:以编程方式滚动内容视图

    您好 我正在尝试转发从 UITableView 前面的 UIView 收到的触摸 但这样做我不能再让桌子滚动了 see here https stackoverflow com questions 3417808 uitableview c
  • MPMoviePlayerController 添加自定义播放按钮

    目前我正在开发一个显示视频的 iPhone 应用程序 我使用 MPMoviePlayerController 从本地文件夹加载视频 我们可以定制标准媒体播放器吗 我想要做的是隐藏所有标准元素 播放 前进 倒带 完成 并仅使用带有自定义图像的
  • 如何使用 Google Direction api 或 iPhone 应用程序的其他一些 api 比较两条路线

    我想比较两条路线以检查它们在我的 iPhone 应用程序中是否相同 有一个人X想要从A点到B点 另一个人想要从A1点到B1点 我可以使用谷歌的方向 API 获取 A 到 B 之间的路线 http maps googleapis com ma
  • iOS后台Location不发送http请求

    我的应用程序需要在后台跟踪用户位置 但无法发送 获取 请求 当应用程序到达前台时 http 请求会立即发送 我正在使用 RestKit 来处理所有网络请求 并且遵循本教程 http www mindsizzlers com 2011 07
  • @iPhone:如何在我们的应用程序中创建 twitter + OAuth 自定义登录

    我正在制作iphone应用程序 我正在使用 OAuth 集成 Twiiter 我的问题是我想要自己的登录页面 而不是像 twitter 提供的新 OAuth 集成那样由 twitter 提供 通过在新的 OAuth 中创建自定义登录页面 是
  • 如何在iPhone SDK中使用标签栏控制器设置视图出口

    我在 Xcode 中创建了一个简单的选项卡栏应用程序 默认选项卡栏有 2 个选项卡栏项目 我添加了第三个选项卡栏项目 并将其视图控制器属性设置为我创建并随后保存的名为 ThirdView xib 的视图 当我尝试运行时 前两个默认选项卡工作
  • 即使只有单个项目,也强制 uiscrollview 弹跳滚动

    我有一个 UIScrollView 其中包含不同数量的项目 子视图 当有多个项目时 滚动弹跳起作用 然而 有时滚动视图应该只有一项 我想向用户提供他们的滚动被识别的反馈 从而产生反弹效果 但是 UIScrollView 仅禁用一项滚动 答案
  • 推送通知发送成功,但设备收不到(偶尔)

    从昨天开始 我一直遇到一些设备无法收到推送通知的问题 证书 设备令牌似乎是正确的 直到昨天 该设备才能成功接收推送通知 在服务器端 没有错误或连接拒绝 并且推送通知似乎每次都成功发送 但仍然有很多情况下设备无法正确接收推送 一些周边信息 我
  • “试图弹出到不存在的视图控制器。”

    当我调用我的方法 DismissView 时 我收到此错误 这是方法存根 IBAction dismissView RootViewController rootController RootViewController alloc ini
  • 如何使我的网络应用程序兼容 iPhone 5?

    我有一个使用 iOS safari 优化为 Web 应用程序的网站 因此添加到主屏幕会在单独的 Web 视图中打开该应用程序 然而 它总是以 3 5 信箱模式打开 而不是拉伸以填充屏幕 我知道使用本机应用程序 您只需添加 iPhone 5
  • Apple 应用程序加载器 - 如何更改正在使用的 Apple ID?

    我想切换 Apple App Loader 使用的 Apple ID 我找不到更改应用程序本身使用的帐户的方法 谷歌搜索没有带来任何有用的信息 当我启动加载程序应用程序时 它给我一个错误 您的 Apple ID 或密码输入不正确 20101
  • VoiceOver 的 UISlider 可访问性特征?

    使 UISlider 在启用 VoiceOver 的情况下可用所需的正确 UIAccessibility 特征和处理程序是什么 是否有关于用户如何使用启用 VoiceOver 的 UISlider 的描述 以下是我最终添加到 UISlide

随机推荐