【问题标题】:Inspecting files of type "NeXT/Apple typedstream" version 4 (NSArchiver)检查类型为“NeXT/Apple typedstream”版本 4 (NSArchiver) 的文件
【发布时间】:2013-09-20 23:26:43
【问题描述】:

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

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

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

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

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

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,我喜欢为此获取文本。其余的我不关心,但我需要知道它是否有效。

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

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 错误就是一个例子。在某些情况下,我可能会遇到总线错误,那将是致命的。如果我有用于解析的自定义代码,我可以自己处理(并修复我遇到的任何崩溃)。 (更新:我刚刚发现一些无效数据确实会使用 SIGSEGV 使 NSUNarchiver 崩溃。)

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

我还想知道源代码是否在一些旧的 NeXT 源或曾经存在的 Windows 版本中可用,但我在哪里可以找到呢? (注意:我被定向到 GNUstep 源代码(“core.20131003.tar.bz2”),我在其中找到了它的 NSUNarchiver 源代码,但是该代码显然是 1998 年的,它使用自己的编码,而不是理解这种“流式”编码。

【问题讨论】:

    标签: ios objective-c macos nscoding nsarchiving


    【解决方案1】:

    【讨论】:

    • 不幸的是,它使用了不同的格式,就像 GNUstep 一样:(
    • 由于这两个类已被弃用,您是否正在查看NSKeyed*rchiver
    • 不,这是旧的无键版本。事实上,当我将数据提供给 NSKeyedUnarchiver 时,它会因错误而中止。
    【解决方案2】:

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

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

    【讨论】:

    • 谢谢,明天去看看。我只是想知道为什么有人对您的答案投了反对票 - 如果有人这样做,他们应该对此发表评论。
    【解决方案3】:

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

    例如,查看objc-1.tar.gz 中的typedstream.m 文件中的typedstream 的实现this mirror of an old darwin distribution

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

    【讨论】:

    【解决方案4】:

    首先,请参阅Is there a way to read in files in TypedStream format 了解一些有趣的信息。

    很可能,可以使用plutil 工具将格式转换为更具可读性的格式。此工具也可用于 Windows(它附带 iTunes for Windows)。不过不确定它的许可证。

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

    【讨论】:

    • 我已经阅读了其他 SO 问题并关注了它的链接。似乎没有什么对我有特别帮助的。此外,plutil 无法读取这些 typedstream 文件。我已经制作了该文件的可下载版本。
    【解决方案5】:

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

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

    我希望这个解释能澄清一点。你前面有一条艰难的道路。在 GNUstep 中避免这样做是有原因的。如果我们有,我们仍然会试图弄清楚。

    【讨论】:

    • 感谢 Greg,这是一个很好的总结(比 Apple 自己的要好)。但是尽管类的存储值的顺序和含义是未知的,但每个项目仍然单独打包在里面,每个项目都有一个明确的类型。这就是我想要摆脱的一切:类型+值的树。我知道这对于复杂 NS 类型的通用检索是不够的,但对于我只想从此类数据中恢复文本的需求来说已经足够了。
    • NB - 您可以通过简单地创建自己的 NSArchiver 和 NSKeyedArchiver 子类并使用它们来归档一些常见对象(NSString 可能是一个好的开始)来找出一些类型,以查看写入的数据所有,用哪个名字。我猜想所有归档最终都归结为原始调用,例如 -encodeInt: 和 -encodeBytes:。
    【解决方案6】:

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

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

    【讨论】:

    • 我觉得很有趣的是,我的第一句话在这次谈话中根本没有被理解。您可以获得正确的格式,但您需要弄清楚存储内容的顺序。 NSArchiver 没有键控。很简单... className data data data data 以作者决定存储“数据”的任何顺序。有时那里有一个版本,但它是字节的。所以 MEUnarchiver 可能得到了正确的存档格式,但是由于每个类都有自己的存储方式,所以它没有说明 ORDER。
    猜你喜欢
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 2013-08-26
    • 1970-01-01
    • 2011-04-03
    • 2016-11-17
    相关资源
    最近更新 更多