Binary plist to text?

1.2k Views Asked by At

I have been trying forever to crack this so I hope someone here can help me. I have a plist file which from my understandig is written in base64 encode binary plist. How can I covert this so it is actually readable. Here is a snippet of some of the data in the file:

            <key>classKeyIdx</key>
            <integer>11</integer>
            <key>data</key>
            <dict>
                <key>ciphertext</key>
                <data>
                    YnBsaXN0MDDUAQIDBAUGBwpYJHZlcnNpb25ZJGFyY2hpdmVyVCR0
                    b3BYJG9iamVjdHMSAAGGoF8QD05TS2V5ZWRBcmNoaXZlctEICVRy
                    b290gAGmCwwVFhcYVSRudWxs1A0ODxAREhMUViRjbGFzc18QFlNG
                    SW5pdGlhbGl6YXRpb25WZWN0b3JcU0ZDaXBoZXJ0ZXh0XxAUU0ZB
                    dXRoZW50aWNhdGlvbkNvZGWABYAEgAKAA08RAUBAvCvsM96NqKlU
                    9ia5p3bzbxtssls26FA6Vf5LQryDagfVaoSyDdDuHGdMQmMtKwls
                    +AoVjEqhmnT9G7km7c5fScPBHysrCazJCT71Z/5TZBwYMhjcVS3U
                    weH7/u588wYigSjWf2odJnvY+fv5M19OJP9ldgz43W19yyXJrmqt
                    uzMWbDnj1OXpEa1AdC7D3ld30mt5tO2Ie9uQGs9ggwJMbvomVTnX
                    Q2bLI5NT7esS/0w3AqCm3h0pDEjZ8uOcb37DDm4eB8ppZhyi7YBR
                    hnlii/10mYYnqbxgXsGpwVOOEbhwJboYcOxGQYEBNgHXSFLgGF6f
                    BID53iPU/Iic1AuJNyepkLke3+ohp8MxxB8583ws422FcU7OMZia
                    kQWsvi2keGZ3qFbh5TA9nNqfRIoZ0xk36X6wcnrmODLy+4mRCk8Q
                    EDbi8aFoWsYO6vlAUAU/j1VPECBn5T3qX+uYursdxPEZFW5wbtOg
                    s2InhowRPlIA3Z4FCdIZGhscWiRjbGFzc25hbWVYJGNsYXNzZXNf
                    EBpfU0ZBdXRoZW50aWNhdGVkQ2lwaGVydGV4dKMdHh9fEBpfU0ZB
                    dXRoZW50aWNhdGVkQ2lwaGVydGV4dF1fU0ZDaXBoZXJ0ZXh0WE5T
                    T2JqZWN0AAgAEQAaACQAKQAyADcASQBMAFEAUwBaAGAAaQBwAIkA
                    lgCtAK8AsQCzALUB+QIMAi8CNAI/AkgCZQJpAoYClAAAAAAAAAIB
                    AAAAAAAAACAAAAAAAAAAAAAAAAAAAAKd
                    </data>
                <key>tamperCheck</key>
                <string>38FBD9A8-6BFE-41A3-A228-EACBA03663E1</string>
                <key>unwrappedKey</key>
                <data>
                    gvpDnO1bm662KrtkzGa4xoL9jqWIm1kJBWYlnAQKZuU=
                    </data>

As shown in the picture I am trying to get hold of the data in the marked area.

[What I I'm trying to find](https://i.stack.imgur.com/6RVN4.png)

Thanks for any responses!

(This is for a school project and this is data from a test phone)

I have tried tools like plutil on mac. I have also tried a base64 convertion online but the text did not make any sens.

1

There are 1 best solutions below

1
Larme On

There is no langage stated, so I'd go with Swift solutions:

It's the steps explained by @vadian's comment.

You can use PropertyListDecoder to decode the main plist, if you know its structure.

Let's imagine you have just:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>classKeyIdx</key>
        <integer>11</integer>
        <key>data</key>
        <dict>
            <key>ciphertext</key>
            <data>
                    YnBsa...
                    </data>
            <key>tamperCheck</key>
            <string>38FBD9A8-6BFE-41A3-A228-EACBA03663E1</string>
            <key>unwrappedKey</key>
            <data>gvpDnO1bm662KrtkzGa4xoL9jqWIm1kJBWYlnAQKZuU=</data>
        </dict>
    </dict>
</plist>

Then, the corresponding class could be:

struct Obj: Codable {
    let classKeyIdx: Int
    let data: Cipher

    struct Cipher: Codable {
        let ciphertext: Data
        let tamperCheck: String
        let unwrappedKey: Data
    }
}

Decoding:

do {
    let obj = try decoder.decode(Obj.self, from: thePlistData)
} catch {
    print("Error: \(error)")
}

You should be able to retrieve ciphertext value then.

If you don't know the format, you can use PropertyListSerialization that will gives you a Dictionary back:

var format = PropertyListSerialization.PropertyListFormat.xml
let obj = try PropertyListSerialization.propertyList(from: thePlistData, options: [], format: &format)
print(obj)

Now, you should be able to access the Base64 data.

I have this two external extensions that I find nice when I don't know what encoding I'm dealing with:

extension String.Encoding {
    static func allEncodings() -> [String.Encoding] {
        return [.ascii, .iso2022JP, .isoLatin1, .isoLatin2, .japaneseEUC, .macOSRoman, .nextstep, .nonLossyASCII, .nextstep, .nonLossyASCII, .shiftJIS, .symbol, .unicode, .utf16, .utf16BigEndian, .utf16LittleEndian, .utf32, .utf32BigEndian, .utf32LittleEndian, .windowsCP1250, .windowsCP1251, .windowsCP1252, .windowsCP1253, .windowsCP1254]
    }
}

extension String {
    static func printAllStringDecoding(data: Data) {
        String.Encoding.allEncodings().forEach { anEncoding in
            if let str = String(data: data, encoding: anEncoding) {
                print("Success for: \(anEncoding):\n\(str)")
            }
        }
    }

    static func successEncodings(for data: Data) -> [String.Encoding] {
        String.Encoding.allEncodings().filter { String(data: data, encoding: $0) != nil }
    }
}

So if you just want the bplist00Ô... part, it should be enough then:

I never remember which string encoding is valid for this kind of data, so I use the loop, that's why.

let encodings = String.successEncodings(for: data)
if let first = encodings.first, let decoded = String(data: theBase64DataBPlist, encoding: first) {
    print(decoded)
}

Now, if you want more infos on it:

There are two ways:

do {
    //Write the data to `tempPath` first
    let dict = try NSDictionary(contentsOf: URL(fileURLWithPath: tempPath), error: ())
    print(dict)
} catch {
    print(error)
}

or

do {
    let data = try Data(contentsOf: URL(fileURLWithPath: path))
    var format = PropertyListSerialization.PropertyListFormat.binary
    let decoded = try PropertyListSerialization.propertyList(from: data, options: [], format: &format)
    print(decoded)
} catch {
    print(error)
}

Tested on a sample bplist you can have on your Mac at /Users/[YOURUSERNAME]/Library/Keyboard/textReplacements.cache

It should you give something like that:

{
    "$archiver" = NSKeyedArchiver;
    "$objects" =     (
        "$null",
                {
            "$class" = "<CFKeyedArchiverUID 0x60000023c040 [0x7ff84c3aed70]>{value = 9}";
            "NS.objects" =             (
                "<CFKeyedArchiverUID 0x60000020c080 [0x7ff84c3aed70]>{value = 2}"
            );
        },
                {
            "$class" = "<CFKeyedArchiverUID 0x60000023c160 [0x7ff84c3aed70]>{value = 8}";
            cloudID = "<CFKeyedArchiverUID 0x60000023c120 [0x7ff84c3aed70]>{value = 7}";
            phrase = "<CFKeyedArchiverUID 0x60000023c0c0 [0x7ff84c3aed70]>{value = 3}";
            priorValue = "<CFKeyedArchiverUID 0x60000023c140 [0x7ff84c3aed70]>{value = 0}";
            shortcut = "<CFKeyedArchiverUID 0x60000023c0e0 [0x7ff84c3aed70]>{value = 4}";
            timestamp = "<CFKeyedArchiverUID 0x60000023c100 [0x7ff84c3aed70]>{value = 5}";
        },
        "J\U2019arrive\U00a0!",
        jrv,
                {
            "$class" = "<CFKeyedArchiverUID 0x60000023c180 [0x7ff84c3aed70]>{value = 6}";
            "NS.time" = "-337082460";
        },
                {
            "$classes" =             (
                NSDate,
                NSObject
            );
            "$classname" = NSDate;
        },
        "1870AE0B-3F5F-486E-8E6D-E2569299821B",
                {
            "$classes" =             (
                "_KSTextReplacementEntry",
                NSObject
            );
            "$classname" = "_KSTextReplacementEntry";
        },
                {
            "$classes" =             (
                NSMutableArray,
                NSArray,
                NSObject
            );
            "$classname" = NSMutableArray;
        }
    );
    "$top" =     {
        root = "<CFKeyedArchiverUID 0x6000002386a0 [0x7ff84c3aed70]>{value = 1}";
    };
    "$version" = 100000;
}

It's much more readable than the String conversions.

Important parts being "$archiver" = NSKeyedArchiver, there was a NSKeyedArchiver, so unless you have the initial classes (main class, subclasses, etc.), it's some extra work to reverse engineer it the initial data.
In my sample case, it's quite "simple", to read what are the data, but extract them back by code might need some more work.