检查“NeXT / Apple typedstream”版本4(NSArchiver)类型的文件

对于数据恢复程序,我需要能够从NSArchiver编写的文件中提取值+类型,而无需访问Apple的CF / NS框架。

OS X file命令报告以下文件:

 NeXT/Apple typedstream data, little endian, version 4, system 1000 

是否有关于如何编码这些文件的文档,或者是否有人提出可以解析它们的代码?

以下是此类数据的示例(也可:可下载 ):

 04 0B 73 74 72 65 61 6D 74 79 70 65 64 81 E8 03 ..streamtyped... 84 01 40 84 84 84 12 4E 53 41 74 74 72 69 62 75 ..@....NSAttribu 74 65 64 53 74 72 69 6E 67 00 84 84 08 4E 53 4F tedString....NSO 62 6A 65 63 74 00 85 92 84 84 84 08 4E 53 53 74 bject.......NSSt 72 69 6E 67 01 94 84 01 2B 06 46 65 73 6B 65 72 ring....+.Fesker 86 84 02 69 49 01 06 92 84 84 84 0C 4E 53 44 69 ...iI.......NSDi 63 74 69 6F 6E 61 72 79 00 94 84 01 69 01 92 84 ctionary....i... 96 96 1D 5F 5F 6B 49 4D 4D 65 73 73 61 67 65 50 ...__kIMMessageP 61 72 74 41 74 74 72 69 62 75 74 65 4E 61 6D 65 artAttributeName 86 92 84 84 84 08 4E 53 4E 75 6D 62 65 72 00 84 ......NSNumber.. 84 07 4E 53 56 61 6C 75 65 00 94 84 01 2A 84 99 ..NSValue....*.. 99 00 86 86 86 ..... 

这包含NSAttributedString。 我有类似的例子,包含NSMutableAttributedStrings等,但最终都解析为NSAttributedStrings,我喜欢获取文本。 我不关心其余的,但我需要知道它是否有效。

我目前的解决方案是使用NSUnarchiver,假设我总是应该在那里找到一个NSAttributedString,获取它的第一个元素并读取它的文本,然后从中重新创建一个存档,看它是否与原始数据相同。 如果我收到exception或其他存档,我认为存档已损坏或无效:

 NSData *data = [[NSData alloc] initWithBytesNoCopy:dataPtr length:dataLen freeWhenDone:false]; NSUnarchiver *a = NULL; // The algorithm simply assumes that the data contains a NSAttributedString, retrieves it, // and then recreates the NSArchived version from it in order to tell its size. @try { a = [[NSUnarchiver alloc] initForReadingWithData:data]; NSAttributedString *s = [a decodeObject]; // re-encode the string item so we can tell its length NSData *d = [NSArchiver archivedDataWithRootObject:s]; if ([d isEqualTo:[data subdataWithRange:NSMakeRange(0,d.length)]]) { lenOut = (int) d.length; okay = true; // -> lenOut is valid, though textOut might still fail, see @catch below textOut = [s.string cStringUsingEncoding:NSUTF8StringEncoding]; } else { // oops, we don't get back what we had as input, so let's better not consider this valid } } @catch (NSException *e) { // data is invalid } 

但是,上面的代码有几个问题:

  1. 它不是x平台。 我也需要这个在Windows上工作。
  2. 一些损坏的数据示例会导致写入stderr或syslog的不必要的错误消息(不确定是哪个),例如: *** mmap(size=18446744071608111104) failed (error code=12) *** error: can't allocate region *** set a breakpoint in malloc_error_break to debug (我提交了一个关于这个的错误报告,因为“不会修复”而遗憾)。
  3. 没有什么能保证NSUnarchiver代码100%防崩溃。 malloc错误就是一个例子。 在某些情况下我可能会遇到总线错误,这将是致命的。 如果我有自定义代码进行解析,我可以自己处理(并修复我遇到的任何崩溃)。 (更新:我刚发现一些无效数据确实使NSUnarchiver崩溃了。)

因此,我需要自定义代码来解码这些类型的存档。 我看了几个,但无法理解它使用的代码。 显然,有长度字段和类型字段,显然类型在0x81到0x86范围内。 此外,前16个字节是标题,包括偏移14-15的系统代码(0x03E8 = 1000)。

我也想知道源代码是否在某些旧的NeXT源或曾经存在的Windows版本中可用,但我会在哪里找到它? ( 注意:我被引导到GNUstep源代码(“core.20131003.tar.bz2”),我在其中找到了它的NSUnarchiver源代码,但该代码显然是从1998年开始使用自己的编码,但这并不理解这个“ streamtyped“编码。”

虽然我不知道格式的任何文档,但您可以通过查看较旧的Darwin(或OpenStep)版本的公共源代码来查找您要查找的信息。

例如,看一下在旧darwin发行版的镜像中可用的objc-1.tar.gz文件中的typedstream.m中的objc-1.tar.gz

这个源代码应该能够读/写typedstream 。 请务必在使用时确认Apple的许可证。

这里的部分问题是Cocoa / NeXTSTEP / OPENSTEP中的每个类都知道如何归档自己。 在每个类中都有一个initWithCoder:/ encodeWithCoder:方法,里面有一个用于typedstream的部分和另一个用于键控存档的部分。 键控档案更现代,通常表示为XML plists。 这些可以以二进制forms编码,但是,毫无疑问,这种二进制forms与typedstream存档不同。 此外,它们是键控的,因此可以轻松地提取单个数据,而无需读取之前的所有数据。 Typedstream档案不能以这种方式工作。 它们是基于顺序的,这意味着每个对象中的每个元素都是一个接一个地写入的。 首先是类名,然后是版本,然后是每个数据。 GNUstep从未实现这一点的原因是因为几乎不可能发现编码顺序。

归档对象图的根对象时,它会调用该对象上的encodeWithCoder:方法,该方法依次调用其包含的每个对象上的encodeWithCoder:方法,然后递归,直到归档整个对象图。 使用键控存档(NSKeyedArchiver)完成此操作后,将构建存档并对其进行适当的键控。 当使用类型化流存档(NSArchiver)完成时,会发生相同的递归,但每次对象编码时,它只会按照开发人员当时认为合适的顺序将每个元素转储到存档中。

我希望这个解释可以解决一些问题。 你前面有一条艰难的道路。 有理由在GNUstep中避免这样做。 如果我们有,我们仍然会试图弄明白。

它似乎是GNU Objective-C运行时的一部分,即使它不完全是运行时的东西(参见http://gcc.gnu.org/ml/gcc-patches/2010-09/msg00495.html上的讨论)

这个文件可以实现那些东西: https : //github.com/gnustep/gnustep-libobjc/blob/master/archive.c

首先,请参阅有没有办法以TypedStream格式读取文件以获取一些有趣的信息。

很可能,使用plutil工具可以将格式转换为更易读的plutil 。 此工具也可用于Windows(它随iTunes for Windows一起提供)。 虽然不确定它的许可证。

有问题的部分是文件包含转换为二进制的对象实例。 理解文件格式是不够的,有必要了解每种类型的存储方式。

Frank Illenberger根据1999年的typedstream.m源代码编写了一个名为MEUnarchiver的NSUnarchiver替代品: https : //github.com/depth42/MEUnarchiver

它已被扩展为支持原始源代码不知道的新类型。 它仍然依赖于ObjC运行时为所有标准类型(如NSString等)提供NSCoding解码器实现,但是它非常独立,并且允许我在传递损坏的数据时防止Apple的NSUnarchiver代码发生崩溃。