解码海量 (.nmsv) fm8 (.nfm8) 和 absynth (.nabs) 的合成器预设格式

逆向工程 二元分析 文件格式 解压
2021-06-21 18:47:55

我正在尝试解码 Massive、FM8 和 absynth VST 合成器的文件格式。文件格式是二进制的,有几个部分。这样做的原因是将上述格式转换为通用 vst .fxp 格式,以便通过 vst api 自动加载和渲染预设。

通过在更改合成器参数时尝试保存文件,我发现了以下有关格式的事实:

  • 文件以长度字段开始
  • 文件包含一些由“DSIN”、“hsin”标记引入的固定长度部分
  • effGetChunk vst sdk api调用返回的vst二进制块与.nmsv、.nabs、.nfm8预设文件内容99%相似
  • 更改合成器中的单个参数会更改文件中多个位置 (3-4) 的一些字节
  • 预设文件的主要部分似乎被压缩,而合成参数应该是 0 和 1 之间的浮点值,它们似乎没有作为浮点写入文件 - 除了无法识别的二进制值之外,还有明文字符串,例如用户定义的宏似乎对原始数据进行了某种压缩。压缩似乎引用了先前遇到的字符串并在流的后面部分引用这些字符串:如果“THIS_IS_A_MACRO_NAME_ABC”在流中位于“THIS_IS_A_MACRO_NAME_XYZ”之前,则第二个字符串将被压缩为“[短字节序列]_XYZ”。
  • 这些文件似乎不包含任何用于压缩的字典,这让我认为字典必须存储在其他地方,否则可能根本没有字典。

任何人都可以在这里帮忙:

  • 这里可以应用什么压缩方案?
  • 有谁知道已成功解码的类似格式?

添加了一个示例文件:

http://s000.tinyupload.com/index.php?file_id=08960658549599455274包含一个示例文件。字符串“TESTSTRING1”和“PREFIXTESTSTRING1SUFFIX”包含在未压缩的流中。

数据块的香农熵是 5.84154,它介于英文文本和加密文本之间。

在此处输入图片说明

下面是一个示例,它应该演示如何计算长度字段:字符串“TESTSTRING123”在字符串“PREFIX...SUFFIX”之前。

                             P  R  E  F  I  X  L1 L2 D     S  U  F  L1 D
----------------------------------------------------------------------------------------
PREFIXTESTSTRING123SUFFIX 05 50 52 45 46 49 58 E0 04 16 02 53 55 46 20 12 40 42 00
PREFIXTESTSTRING12SUFFIX  05 50 52 45 46 49 58 E0 03 16 02 53 55 46 20 11 40 41 00
PREFIXTESTSTRING1SUFFIX   05 50 52 45 46 49 58 E0 02 16 02 53 55 46 20 10 40 40 00 33 40
PREFIXTESTSTRINGSUFFIX    05 50 52 45 46 49 58 E0 01 16 02 53 55 46 20 0F 40 3F 00 33 40
PREFIXTESTSTRINSUFFIX     05 50 52 45 46 49 58 E0 00 16 02 53 55 46 20 0E 40 3E 00 33 40
PREFIXTESTSTRISUFFIX      05 50 52 45 46 49 58 C0    16 02 53 55 46 20 0D 40 3D 00 33 40
PREFIXTESTSTRSUFFIX       05 50 52 45 46 49 58 A0    16 02 53 55 46 20 0C 40 3C 00 33 40
PREFIXTESTSTSUFFIX        05 50 52 45 46 49 58 80    16 02 53 55 46 20 0B 40 3B 00 33 40
PREFIXTESTSSUFFIX         05 50 52 45 46 49 58 60    16 02 53 55 46 20 0A 40 3A 00 33 40
PREFIXTESTSUFFIX          05 50 52 45 46 49 58 60    16 01    55 46 20 09 40 39 00 33 40 
PREFIXTESSUFFIX           05 50 52 45 46 49 58 20    16 02 53 55 46 20 08 40 38 00 33 40
PREFIXTESUFFIX            05 50 52 45 46 49 58 20    16 01    55 46 20 07 40 37 00 33 40 
PREFIXTSUFFIX             09 50 52 45 46 49 58 54 53 55 46 20 06 40 36 00 33 40  
3个回答

我认为你精心设计的测试的结果几乎有助于解释一切。

它是 LZ77 变体。尝试使用以下方案来解码长度和偏移量。(编码字节如下所示为 8 个二进制位。)

1st byte  2nd byte  3rd byte      token type  length    offset(*)
========  ========  ========      ==========  ======  ==============
000qqqqq     --        --     =>  literal     1 + Q         --      
001qqqqq  rrrrrrrr     --     =>  dictionary  3       (Q<<8) + R + 1
010qqqqq  rrrrrrrr     --     =>  dictionary  4       (Q<<8) + R + 1
011qqqqq  rrrrrrrr     --     =>  dictionary  5       (Q<<8) + R + 1
100qqqqq  rrrrrrrr     --     =>  dictionary  6       (Q<<8) + R + 1
101qqqqq  rrrrrrrr     --     =>  dictionary  7       (Q<<8) + R + 1
110qqqqq  rrrrrrrr     --     =>  dictionary  8       (Q<<8) + R + 1
111qqqqq  rrrrrrrr  ssssssss  =>  dictionary  9 + R   (Q<<8) + S + 1  

* the offsets are backwards in the decoded data from the current output position.

编辑 从偏移量00000410开始的整个文件似乎可以使用上述编码方案进行解压缩。这样做给了我以下内容。您可以看到我的第一个摘录的末尾有许多 32 位浮点值。第二节摘录有您的测试字符串。

00000000:  F9 15 00 00 00 00 00 00 01 00 00 00 68 73 69 6E  ............hsin
00000010:  01 00 00 00 00 00 00 00 EC 2D F1 91 50 BC 4D 41  .........-..P.MA
00000020:  96 E1 F3 EA B1 70 A9 B9 18 00 00 00 00 00 00 00  .....p..........
00000030:  44 53 49 4E 01 00 00 00 01 00 00 00 01 00 00 00  DSIN............
00000040:  01 00 00 00 01 00 00 00 00 00 00 00 44 53 49 4E  ............DSIN
00000050:  6D 00 00 00 A5 15 00 00 00 00 00 00 01 00 00 00  m...............
00000060:  68 73 69 6E 01 00 00 00 00 00 00 00 85 50 7B 20  hsin.........P{ 
00000070:  83 65 FA 41 80 7B CA 65 CA E4 1D FA 75 15 00 00  .e.A.{.e....u...
00000080:  00 00 00 00 44 53 49 4E 6D 00 00 00 01 00 00 00  ....DSINm.......
00000090:  18 00 00 00 00 00 00 00 44 53 49 4E 01 00 00 00  ........DSIN....
000000A0:  01 00 00 00 01 00 00 00 01 00 00 00 01 00 00 00  ................
000000B0:  01 00 00 00 2D 15 00 00 00 00 00 00 18 00 00 00  ....-...........
000000C0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000000D0:  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000000E0:  00 00 00 3F 00 00 7D 3F 00 00 80 3F 00 00 80 3F  ...?..}?...?...?
000000F0:  00 00 00 3F BA 9E FF 3E 00 00 80 3F 00 00 80 3F  ...?...>...?...?
00000100:  00 00 80 3F 00 00 00 3F A6 30 00 3F 00 00 80 3F  ...?...?.0.?...?
00000110:  00 00 70 3F 00 00 80 3F 00 00 00 3F 00 00 80 3F  ..p?...?...?...?
00000120:  00 80 91 3E 00 00 00 3F 00 00 00 00 00 00 00 3F  ...>...?.......?
00000130:  00 00 00 3F 00 00 00 3F 00 00 00 3F 00 00 00 3F  ...?...?...?...?
00000140:  00 00 00 3F 00 40 41 3F 00 00 00 3F 00 00 00 00  ...?.@A?...?....
00000150:  00 80 99 3E 00 00 00 3F 00 00 00 3F 00 00 00 00  ...>...?...?....
00000160:  00 00 00 3F 00 00 00 3F 00 00 00 3F 00 00 00 3F  ...?...?...?...?
...
00000430:  00 00 00 00 00 01 00 00 00 07 00 00 00 00 00 00  ................
00000440:  00 00 00 00 00 01 01 00 00 00 00 C0 00 00 00 40  ...............@
00000450:  01 01 00 00 00 00 00 01 00 00 00 0B 00 00 00 54  ...............T
00000460:  45 53 54 53 54 52 49 4E 47 31 17 00 00 00 50 52  ESTSTRING1....PR
00000470:  45 46 49 58 54 45 53 54 53 54 52 49 4E 47 31 53  EFIXTESTSTRING1S
00000480:  55 46 46 49 58 01 00 00 00 33 01 00 00 00 34 01  UFFIX....3....4.
00000490:  00 00 00 35 01 00 00 00 36 01 00 00 00 37 01 00  ...5....6....7..
000004A0:  00 00 38 04 00 00 00 00 00 00 00 55 55 29 41 02  ..8........UU)A.
...

我实际上在这种类型的各种文件上取得了一些进展,我也为 LZ 块编写了一个解压缩器。

我认为hsin代表'header section in'并DSIN代表'data section in'。还有4kin,不知道是什么。

在某些文件中有一些情况会使用不正确的字典跳转 - 例如,偏移 1,长度 5。可能是我自己的错误,但上述算法似乎没有考虑到这种情况。

我正在分析部分块,它们有点古怪。我发现长度描述符似乎被完全忽略了,除此之外。如此多的文件可以毫无问题地归零,没有明显的影响(我认为它用于 NI 索引器/库的东西)。

块实际上不是固定长度,只是看起来像(也许第一个标题是固定长度)。到处都有长度字段,但不是前两个块 - 它们在块长度应该是“剩余字节”的地方。如果我将这些字段清零,文件就会读取而不会出错。奇怪的。

你们有兴趣在存储库上进行合作并分享我们的发现吗?

我现在有了 NI 文件容器、块和解压缩器的工作原理图和实现。我仍然无法解释数据块(基本上是打包程序中的变量),我希望在那里得到一些帮助或讨论。我了解有关DSIN块的基本知识,但不确定这些值与数据本身的关系。

我已经把我的发现放在https://github.com/monomadic/ni-decompressor

这适用于许多 NI 文件,不仅仅是海量文件,但我一直专注于 kontakt。我也可以阅读整体,但它们是一种完全不同的格式,但它们看起来更容易。

如果您只是对目录中的那些(尽管它们不会在 kontakt、massive 等中读取,因为它们周围没有包装器)感兴趣,我已经预先解压缩了一些压缩段并转储了解压缩的部分/examples,扩展名 .deflate