# NSKeyedArchive plist deserializer
Deserializes NSKeyedArchiver created plists, which are frequent in macOS/iOS. These are serialized versions of plists (or data classes) and are meant for machine reading. The deserialized version is human readable for analysts and investigators who need to review the data.
The library recursively deserializes the entire plist and returns a dictionary/list object representing the entire plist. Certain NSKeyedArchiver plists contain circular references, which results in infinite looping. The code detects and breaks these loops wherever found to return useable data.
#### _New: From version 1.4.0, you can pass any plist, even if not an NSKA. If the option `full_recurse_convert_nska=True` is used, it will recurse any plist, and find and convert all nested NSKA from any NS.data field found. Also new in 1.5.0 is the ability to get a dictionary returned as the top level plist object (instead of list)_
#### Requirements: Python 3.6+ (3.8 or higher recommended)
Due to improvements in the built-in `plistlib` library in Python 3.8, it is recommended to use 3.8 or above. For 3.7 or lower, it should work fine for most plists, some might fail to save correctly. If you don't care about saving the deserialized plist (using the built-in library functions), then this should make no difference.
#### Installation (via pip/pip3)
```
pip3 install nska_deserialize
```
### Usage
Use the functions `deserialize_plist` or `deserialize_plist_from_string` to convert NSKeyedArchives (NSKA).
By default `full_recurse_convert_nska=False` maintaining old behaviour which is to throw an exception if the archive is not NSKA, and no recursive processing for nested NSKA data blobs. If set to `True`, then it will process any plist even if not an NSKA (at root level).
By default `format=list` maintains old behaviour providing a list as the top level of the plist. If `format=dict` is used, this switches to a dictionary.
##### From a file
```python
import nska_deserialize as nd
input_path = '/Users/yogesh/Desktop/sample.sfl2'
with open(input_path, 'rb') as f:
try:
deserialized_plist = nd.deserialize_plist(f, full_recurse_convert_nska=True, format=dict)
print(deserialized_plist)
except (nd.DeserializeError,
nd.biplist.NotBinaryPlistException,
nd.biplist.InvalidPlistException,
nd.plistlib.InvalidFileException,
nd.ccl_bplist.BplistError,
ValueError,
TypeError, OSError, OverflowError) as ex:
# These are all possible errors from libraries imported
print('Had exception: ' + str(ex))
deserialized_plist = None
if deserialized_plist:
output_path_plist = input_path + '_deserialized.plist'
output_path_json = input_path + '_deserialized.json'
nd.write_plist_to_json_file(deserialized_plist, output_path_json)
nd.write_plist_to_file(deserialized_plist, output_path_plist)
```
##### From a String
```python
import nska_deserialize as nd
plist_in_string = b"{notional plist as string that might have come from a database}"
try:
deserialized_plist = nd.deserialize_plist_from_string(plist_in_string, full_recurse_convert_nska=True, format=dict)
print(deserialized_plist)
except (nd.DeserializeError,
nd.biplist.NotBinaryPlistException,
nd.biplist.InvalidPlistException,
nd.plistlib.InvalidFileException,
nd.ccl_bplist.BplistError,
ValueError,
TypeError, OSError, OverflowError) as ex:
# These are all possible errors from libraries imported
print('Had exception: ' + str(ex))
deserialized_plist = None
if deserialized_plist:
output_path_plist = input_path + '_deserialized.plist'
output_path_json = input_path + '_deserialized.json'
nd.write_plist_to_json_file(deserialized_plist, output_path_json)
nd.write_plist_to_file(deserialized_plist, output_path_plist)
```
### Change log
**v1.5.1**
Minor bug fix - Empty NSKeyedArchive will not raise an exception if it is valid.
**v1.5.0**
Minor bug fix - If root level element was NULL, this would create an invalid plist as None/NULL values are not allowed in plists. This now converts it to a blank string.
New option `format=dict` will return a dictionary at the top level of a plist instead of the default list format (Thanks @cvandeplas - Christophe Vandeplas).
**v1.4.0**
New boolean option `full_recurse_convert_nska` for full recursion of plist and conversion of all nested NSKeyedArchive data.
**v1.3.3**
Fixes an issue with CF$UID conversion, this was not being applied to all plists resulting in empty output for certain plists.
Python 3.12 compatible and tested.
**v1.3.2**
Adds NSUUID type to ccl_bplist, which should remove at least some exceptions related to `unhashable type: 'NsKeyedArchiverDictionary'`.
**v1.3.1**
Python 3.9 compatible (earlier versions of library may have problems with XML plists on python 3.9).
**v1.2**
Support for macOS Big Sur plists, some have hexadecimal integers in XML, which caused problems with underlying plist parsers.
Raw data
{
"_id": null,
"home_page": "https://github.com/ydkhatri/nska_deserialize",
"name": "nska-deserialize",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": null,
"keywords": null,
"author": "Yogesh Khatri",
"author_email": "yogesh@swiftforensics.com",
"download_url": "https://files.pythonhosted.org/packages/bc/e4/31b373daac149996eac5cca3967f9b8f0a9506fa59a0fd4f360037009b3b/nska_deserialize-1.5.1.tar.gz",
"platform": null,
"description": "# NSKeyedArchive plist deserializer\nDeserializes NSKeyedArchiver created plists, which are frequent in macOS/iOS. These are serialized versions of plists (or data classes) and are meant for machine reading. The deserialized version is human readable for analysts and investigators who need to review the data.\n\nThe library recursively deserializes the entire plist and returns a dictionary/list object representing the entire plist. Certain NSKeyedArchiver plists contain circular references, which results in infinite looping. The code detects and breaks these loops wherever found to return useable data.\n\n#### _New: From version 1.4.0, you can pass any plist, even if not an NSKA. If the option `full_recurse_convert_nska=True` is used, it will recurse any plist, and find and convert all nested NSKA from any NS.data field found. Also new in 1.5.0 is the ability to get a dictionary returned as the top level plist object (instead of list)_\n\n#### Requirements: Python 3.6+ (3.8 or higher recommended)\nDue to improvements in the built-in `plistlib` library in Python 3.8, it is recommended to use 3.8 or above. For 3.7 or lower, it should work fine for most plists, some might fail to save correctly. If you don't care about saving the deserialized plist (using the built-in library functions), then this should make no difference.\n\n#### Installation (via pip/pip3)\n```\npip3 install nska_deserialize\n```\n\n### Usage\n\nUse the functions `deserialize_plist` or `deserialize_plist_from_string` to convert NSKeyedArchives (NSKA). \n\nBy default `full_recurse_convert_nska=False` maintaining old behaviour which is to throw an exception if the archive is not NSKA, and no recursive processing for nested NSKA data blobs. If set to `True`, then it will process any plist even if not an NSKA (at root level).\n\nBy default `format=list` maintains old behaviour providing a list as the top level of the plist. If `format=dict` is used, this switches to a dictionary.\n\n##### From a file\n\n```python\nimport nska_deserialize as nd\n\ninput_path = '/Users/yogesh/Desktop/sample.sfl2'\n\nwith open(input_path, 'rb') as f:\n try:\n deserialized_plist = nd.deserialize_plist(f, full_recurse_convert_nska=True, format=dict)\n print(deserialized_plist)\n except (nd.DeserializeError, \n nd.biplist.NotBinaryPlistException, \n nd.biplist.InvalidPlistException,\n nd.plistlib.InvalidFileException,\n nd.ccl_bplist.BplistError, \n ValueError, \n TypeError, OSError, OverflowError) as ex:\n # These are all possible errors from libraries imported\n\n print('Had exception: ' + str(ex))\n deserialized_plist = None\n\n if deserialized_plist:\n output_path_plist = input_path + '_deserialized.plist'\n output_path_json = input_path + '_deserialized.json'\n\n nd.write_plist_to_json_file(deserialized_plist, output_path_json)\n nd.write_plist_to_file(deserialized_plist, output_path_plist)\n```\n\n##### From a String\n\n```python\nimport nska_deserialize as nd\n\nplist_in_string = b\"{notional plist as string that might have come from a database}\"\n\ntry:\n deserialized_plist = nd.deserialize_plist_from_string(plist_in_string, full_recurse_convert_nska=True, format=dict)\n print(deserialized_plist)\nexcept (nd.DeserializeError, \n nd.biplist.NotBinaryPlistException, \n nd.biplist.InvalidPlistException,\n nd.plistlib.InvalidFileException,\n nd.ccl_bplist.BplistError, \n ValueError, \n TypeError, OSError, OverflowError) as ex:\n # These are all possible errors from libraries imported\n\n print('Had exception: ' + str(ex))\n deserialized_plist = None\n\nif deserialized_plist:\n output_path_plist = input_path + '_deserialized.plist'\n output_path_json = input_path + '_deserialized.json'\n\n nd.write_plist_to_json_file(deserialized_plist, output_path_json)\n nd.write_plist_to_file(deserialized_plist, output_path_plist)\n```\n\n### Change log\n**v1.5.1** \nMinor bug fix - Empty NSKeyedArchive will not raise an exception if it is valid.\n\n**v1.5.0** \nMinor bug fix - If root level element was NULL, this would create an invalid plist as None/NULL values are not allowed in plists. This now converts it to a blank string. \nNew option `format=dict` will return a dictionary at the top level of a plist instead of the default list format (Thanks @cvandeplas - Christophe Vandeplas).\n\n**v1.4.0** \nNew boolean option `full_recurse_convert_nska` for full recursion of plist and conversion of all nested NSKeyedArchive data.\n\n**v1.3.3** \nFixes an issue with CF$UID conversion, this was not being applied to all plists resulting in empty output for certain plists. \nPython 3.12 compatible and tested.\n\n**v1.3.2** \nAdds NSUUID type to ccl_bplist, which should remove at least some exceptions related to `unhashable type: 'NsKeyedArchiverDictionary'`.\n\n**v1.3.1** \nPython 3.9 compatible (earlier versions of library may have problems with XML plists on python 3.9).\n\n**v1.2** \nSupport for macOS Big Sur plists, some have hexadecimal integers in XML, which caused problems with underlying plist parsers.\n",
"bugtrack_url": null,
"license": "MIT License",
"summary": "Convert NSKeyedArchiver plist into a deserialized human readable plist",
"version": "1.5.1",
"project_urls": {
"Homepage": "https://github.com/ydkhatri/nska_deserialize"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4899dda67703b590584d0a303cf64875befedc0ff51b994d11e016eed9f9af5a",
"md5": "82014b2117468baf4200dc93c510de5d",
"sha256": "ef8bf253f2f8cf5d73e56ac5315e090cbc8614ef3c769ff2a86ff341a5214977"
},
"downloads": -1,
"filename": "nska_deserialize-1.5.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "82014b2117468baf4200dc93c510de5d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 13341,
"upload_time": "2024-09-13T09:01:53",
"upload_time_iso_8601": "2024-09-13T09:01:53.396212Z",
"url": "https://files.pythonhosted.org/packages/48/99/dda67703b590584d0a303cf64875befedc0ff51b994d11e016eed9f9af5a/nska_deserialize-1.5.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bce431b373daac149996eac5cca3967f9b8f0a9506fa59a0fd4f360037009b3b",
"md5": "afc97afda1a83354a32c627d7bbb1565",
"sha256": "a8040fdce19c54673c08bfdba9a3bc9eb1ec519b06b9575f227440ba78ec0809"
},
"downloads": -1,
"filename": "nska_deserialize-1.5.1.tar.gz",
"has_sig": false,
"md5_digest": "afc97afda1a83354a32c627d7bbb1565",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 14155,
"upload_time": "2024-09-13T09:01:54",
"upload_time_iso_8601": "2024-09-13T09:01:54.839249Z",
"url": "https://files.pythonhosted.org/packages/bc/e4/31b373daac149996eac5cca3967f9b8f0a9506fa59a0fd4f360037009b3b/nska_deserialize-1.5.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-13 09:01:54",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ydkhatri",
"github_project": "nska_deserialize",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "nska-deserialize"
}