【问题标题】:Is it possible to intercept a matlab save() bytestream是否可以拦截 matlab save() 字节流
【发布时间】:2011-06-15 23:24:46
【问题描述】:

在 matlab 中,可以使用 matlab save() 调用将 matlab 对象甚至整个工作区写入文件。我想拦截字节流并在它进入文件之前对其进行后处理,这可能吗?或者,是否可以指定写入字节流的文件描述符,而不是通常作为参数进入 save() 调用的文件名。

请注意,我不是在寻找在 matlab 中编写文件的替代方法,我知道我可以 fopen() 一个文件并编写我想要的任何内容,但重点是我想(重新)使用该对象保存调用内部的序列化,不要再发明我自己的了。

load() 调用当然会出现一个模拟问题,但在这种情况下,在字节流进入反序列化过程之前拦截它,但我想 save() 是否有可能解决 load()问题自然会出现。

一些澄清:

  1. 我不是在寻找序列化 matlab 数据的新方法,它已经存在,练习的重点是在 save() 调用中使用现有的序列化,以便 1) 我不需要开始更新新版本的matlab中新类型对象的序列化代码,或者天堂禁止人们开始使用自定义OOP对象,2)我仍然可以轻松使用现有代码读取mat文件,例如scipy的支持用于 mat 文件。

  2. 在后处理之前,流不能输出到文件或任何东西,这个想法是为了安全而加密,将流直接写入文件完全破坏了这个目的。

并发症:

  • matlab 中save 函数中使用的功能似乎不仅仅是常规的顺序写入。检查库的目标代码,似乎保存函数是使用matPutVariable(以前称为matPutArray)实现的,它将mxArray* 类型的给定变量写入MATFile* 类型的文件,用matOpen 打开.这里的问题是matPutVariable的描述中的如下文字:

    如果mxArray 不存在于 MAT 文件中,该函数会将其附加到末尾。如果文件中存在同名的mxArray,该函数通过重写文件将现有的mxArray替换为新的mxArray

    这意味着matPutVariable函数必须在文件中进行查找,显然使用管道时无法进行查找,因此在使用现有的序列化功能时,无法使用管道来实现我们对字节流的处理。

【问题讨论】:

  • 您到底想完成什么?您是要对数据进行后处理,然后让它继续写入文件,还是要对其进行后处理并将其定向到其他地方?
  • @gnovice 如果后处理的字节流进入文件会很好,但如果我可以重定向字节流(未后处理),我可以将它定向到后处理设施。所以简而言之,我要么想对字节流内联进行后处理,要么我想重定向未后处理的字节流。
  • 您始终可以只使用默认的 SAVE 行为,然后使用 FREAD 将创建的文件作为字节流读取..
  • @Amro 不行,关键是在它到达任何地方之前对其进行后处理。

标签: serialization matlab save


【解决方案1】:

使用getByteStreamFromArraygetArrayFromByteStream 进行序列化/反序列化。您可以在将结果字节写入文件之前对其进行修改

% A cell array of several data types
>> byteStream = getByteStreamFromArray({pi, 'abc', struct('a',5)});  % 1x312 uint8 array
>> getArrayFromByteStream(byteStream)
ans = 
    [3.14159265358979]    'abc'    [1x1 struct]

http://undocumentedmatlab.com/blog/serializing-deserializing-matlab-data 中所述

【讨论】:

    【解决方案2】:

    使用虚拟文件系统怎么样?在 Windows 上有一个名为 BoxedAPP SDK 的商业库,它允许您创建一个仅对创建过程可见的虚拟文件(也可能是子进程)。您可能必须制作一个 MEX 来连接库。首先,您将创建虚拟文件,然后您可以在 matlab 中使用具有相同文件名的 save 命令。然后,您可以使用 matlab 中的普通 fopen/fread 函数读取序列化的 .mat 字节流,并随心所欲地使用它。这至少可以防止在硬盘上创建文件。我不确定在某些情况下文件或文件的一部分是否可以到达交换文件,因为文件实际上是在内存中创建的。

    libmx 中似乎还有未记录的函数 mxSerialize 和 mxDeserialize 可以使用,例如。通过直接来自 matlab 的 loadlibrary/calllib 函数或包装器 mex。谷歌搜索了一下,发现这些函数的签名应该是

    mxArray* mxSerialize(const mxArray*);
    mxArray* mxDeserialize(const void*, size_t);
    

    并且一些测试表明 mxSerialize() 将 matlab 变量作为参数并返回一个序列化的字节作为 uint8 数组。 mxDeserialize() 将此 uint8 数组(第一个参数)转换回 matlab 对象作为返回值。 mxDeserialize 的第二个参数似乎是第一个参数中的元素数。尽管 TMW 可能会更改 API,但不保证将来可以使用这些未记录的函数。

    【讨论】:

    【解决方案3】:

    在考虑了几个月之后,我要说,不,这是不可能的。至少,不是没有核心的非便携式二进制/ELF hacking。

    【讨论】:

      【解决方案4】:

      第 1 步:mkfifo /tmp/fifo -- 这将创建一个 FIFO,即代表管道的文件名。写入管道的任何内容都会保留在那里,直到某个进程将其从管道中读回。数据永远不会到达磁盘。

      第 2 步:在一个终端中,运行:openssl enc -aes-256-cbc -a -e -in fifo -out safe -- 这将运行 OpenSSL 程序以使用 AES、256 位密钥、CBC 模式进行加密(openssl 支持更多的密码类型和参数,选择一个适用于你,这是一个安全的默认值); -a Base64 对输出进行编码(这对测试很有用,但是当你真正使用它时,你可以不使用它,Base64 会导致大小增加 4/3); -e以加密模式运行,-in fifo指定输入文件名为fifo(可能使用全路径); -out safe 指定输出文件名为safe(同样,可能使用完整路径)。 OpenSSL 将休眠,直到数据到达管道。

      当某些数据到达管道时,OpenSSL 会提示您输入密码。

      测试一下:在另一个终端运行“echo foo > /tmp/fifo”。在第一个终端看到密码提示,给它一个密码并确认密码,然后查看文件'safe'的内容:

      $ openssl enc -aes-256-cbc -a -e -in fifo -out safe
      # (in another terminal, "echo foo > fifo")
      enter aes-256-cbc encryption password:
      Verifying - enter aes-256-cbc encryption password:
      $ cat safe
      U2FsdGVkX18aWBw0Uz8N3SfrRg4PigL609F+HQPuc6o=
      

      测试另一个方向:

      $ openssl enc -aes-256-cbc -a -d -in safe
      enter aes-256-cbc decryption password:
      foo
      

      现在,从第 2 步重新运行 OpenSSL 命令:openssl enc -aes-256-cbc -a -e -in fifo -out safe,运行您的 Matlab,并将 /tmp/fifo 提供给 SAVE() 命令。

      Matlab 有可能会做一些愚蠢的事情,比如删除具有给定文件名的 any 现有文件,在这种情况下,您会在 中找到未加密的数据>常规文件名为/tmp/fifo。所以请先用一些不重要的数据进行测试。但我希望 Matlab 在编写时考虑到 Unix 工具,并且会简单地写入您给它的命名管道。

      【讨论】:

      • 这是不可能的。正如我在问题中指出的那样,Matlab 在写入过程中会在文件中查找,因此管道是不可能的。此外,它会在文件系统中打开一个我不想要的入口点,太容易被中间人拦截。
      • @wich,你试过管道吗?大多数程序优雅地处理管道,降级为非常好的面向管道的方法。此外,如果您在管道上正确设置了权限 (chmod 600),它只能由您和管理员(有困难)使用,他们可以总是附加调试器,例如 gdb、ltrace ,或 strace 到您的程序并直接从内存中读取数据。老实说,调试程序可能比拦截管道更容易。
      • 是的,我试过了,还是不行。是的,我知道数据永远不会 100% 安全,操作员可以进行物理访问,所以即使他们无法直接访问正在运行的用户,他们甚至可以使用易失性内存取证。但这是我必须工作的界限。当使用文件系统中的一个点时,只需要一个通用工具来拦截命名管道,当它全部留在进程中时,至少需要正确的知识或为特定实现量身定制的自定义工具。在你说什么之前,是的,我知道通过默默无闻的安全是没有安全的。
      【解决方案5】:

      我也对这个问题感兴趣。我发现了一些东西,但没有任何效果:

      • ma​​tlab save stdio你发现了这个隐藏功能,但它不起作用
      • engGetArray/engPutArray “此例程允许您将变量复制到工作区之外。”

      查看 MAT 文件规范,也许我们可以使用 Mex 文件重现 matlab 序列化:

      更新:

      我发现了一些非常有趣的东西:在 Matlab 控制台中运行这个命令

      edit([matlabroot '/extern/examples/eng_mat/matcreat.c']);
      

      或者这个

      edit([matlabroot '/extern/examples/eng_mat/matcreat.cpp']);
      

      这是文档,如何编译:http://www.mathworks.com/help/techdoc/matlab_external/f14500.html

      在我看来,在pmat = matOpen(file, "w"); 命令中使用STDOUT 应该是可行的。

      【讨论】:

      • 我认为 matOpen 不能用于将输出重定向到另一个文件描述符。 file 在这种情况下只是一个包含文件路径的字符串。
      • 哦,save stdio 在编译后的 matlab 中似乎不起作用。
      • 也许在 Linux 中可以使用文件字符串 '/dev/stdout'
      【解决方案6】:

      你不能加密变量的内容吗?

      使用whos,您可以按字母顺序获得所有变量的列表。对于每一个,您使用您的加密算法生成一个相同大小的掩码,然后将“真”值替换为自身对掩码进行异或运算。最后,您使用save 保存加密的变量。变量的名称和大小是可见的,但这可能并不重要(如有必要,您也可以加密名称)。

      以同样的方式加载。

      【讨论】:

      • 这将非常复杂和繁琐,您需要为不同的数据结构制作各种不同的案例,更不用说人们开始使用自定义 oop 的东西了。我会尽快实现自己的序列化/反序列化,这确实需要在序列化后完成。
      【解决方案7】:

      也许您可以执行以下操作:

      %# serialize objects into a byte array using Java
      bout = java.io.ByteArrayOutputStream();
      out = java.io.ObjectOutputStream(bout);
      out.writeObject( rand(3) )                %# MATLAB matrix
      out.writeObject( num2cell(rand(3)) )      %# MATLAB cell array
      out.flush()
      out.close()
      bout.close()
      b = bout.toByteArray();                   %# vector of type int8
      
      %# perform processing on `b` ...
      
      %# write byte[] stream to file
      save file.mat b
      

      然后在相反的方向上,您只需加载保存的 MAT 文件,反转您执行的任何处理,然后反序列化字节流以恢复原始对象。

      %# load MAT-file
      load file.mat b
      b = typecast(b,'int8');                   %# cast as int8 just to be sure
      
      %# undo any processing on `b`...
      
      %# deserialize
      in = java.io.ObjectInputStream( java.io.ByteArrayInputStream(b) );
      X1 = double( in.readObject() )            %# recover matrix
      X2 = cell( in.readObject() )              %# recover cell array
      in.close()
      

      请注意,您必须自己维护变量元信息,例如它们的数量和类型(也许您可以以某种方式将其保存在同一个 MAT 文件中),并使用自定义包装函数来处理所有编组,但你明白了......


      我还在 FEX 上看到了一些有助于序列化/反序列化 MATLAB 类型的提交:

      【讨论】:

      • 据我所知,这不会像 save() 生成的“Mat 文件”那样序列化。如问题中所述,这是一项要求。还是我错过了什么?
      • @wich: 不,这是使用 Java 序列化,与 MATLAB 的 MAT 文件无关......我能想到的唯一另一件事是使用 HDF5 格式(因为你可以使用它在 MATLAB 内部或外部使用任何可用的库/接口)
      【解决方案8】:

      您最好的选择可能是将 mat 文件写入 tmpfs/ramdisk,然后在将其保存到磁盘之前对其进行加密。您牺牲了可移植性并依靠操作系统来提供安全的虚拟内存,但如果您甚至不能信任本地磁盘,您可能无法获得令人满意的安全性。

      顺便说一句,为什么您根本无法信任本地磁盘,即使您无法将临时文件放在权限设置为仅允许拥有 matlab 进程的用户访问的目录中(和根)?您是否正在尝试实施 DRM 系统?

      【讨论】:

      • 系统处理操作员不应该知道的机密数据。是的,我知道只要该过程由操作员控制的用户运行,它就永远不会真正安全,但是由于这不是我现在可以改变的情况,我所能做的就是让它变得更难尽可能。
      • 您也可以使用命名管道进行通信,但在 Windows 上,命名管道很难使用。在 unix 系统上,您可以使用 mkfifo 创建管道,然后让您的加密器在 MATLAB 保存之前以只读方式打开它。任何试图在该管道上侦听的进程都会导致加密器无法接收其他侦听器获取的字节,您至少可以在之后检测到。
      • 是的,命名管道是我之前提到的“疯狂的想法”之一,但我放弃了它,因为它太容易被窃听并且非常依赖于平台。实际上在 Windows 中它会更好地工作,因为您实际上可以防止管道被打开以供另一个进程读取,因为命名管道连接到特定进程,而不仅仅是文件系统中的设备节点。
      【解决方案9】:

      编辑:(基于 cmets)嗯,我想我的旧答案并没有太大帮助。我不知道你将如何截取字节流,但我想你有一个选择(诚然这有点麻烦)就是 SAVE 函数创建然后立即从文件中逐字节读取数据,对其进行处理,然后将其写回文件。比如:

      save('workspace.mat');
      fid = fopen('workspace.mat','r');
      byteData = fread(fid,inf,'*uint8');
      fclose(fid);
      %# ... Process byteData here ...
      fid = fopen('workspace.mat','w');
      fwrite(fid,byteData,'uint8');
      fclose(fid);
      

      旧答案:

      对于用户定义的类对象,我相信您正在寻找的内容体现在重载的 SAVEOBJLOADOBJ 方法中,这些方法在将对象保存到文件或从文件加载之前在对象上调用。当saving or loading objects 与.MAT 文件相互转换时,您可以使用这些方法来modify the save/load process,以便可以以不同方式格式化对象。但是,我认为您不能对内置数据类型执行此操作,只能对用户定义的对象执行此操作。

      【讨论】:

      • 谢谢,但我不是在谈论 Matlab OOP,只是普通的旧矩阵和几个结构
      • 我不想改变对象的序列化过程,事实上我希望它保持原样。但我想对生成的字节流进行后处理。
      • 对不起,还是没有骰子,流在后处理之前不能去任何地方。
      • @wich:嗯,这个很难。哦,好吧,回到绘图板......;)
      • 就是这样,我一直在幻想各种疯狂的想法,例如通过链接到提供相同签名的 open() 调用的库中来插入 open() 系统调用和将管道返回到后处理设施。但是 mcc 不允许您链接其他库,并且 mex 也无济于事,因为在 libc 已经动态链接之后,它是 dlopen()'d。使用 elfedit 破坏生成的二进制文件可能会提供一个解决方案,但这会使它更加混乱。最重要的是,整个交易将非常依赖于平台...... :(
      【解决方案10】:

      对于 HG 对象,您可以通过此处解释的内部(可修改)*.m 文件拦截保存处理: http://undocumentedmatlab.com/blog/handle2struct-struct2handle-and-matlab-8/

      【讨论】:

      • 谢谢,但这对我没有帮助,在这里谈论常规的旧矩阵和几个结构。我不想改变序列化,实际上我明确地想使用 save() 的序列化,只是我想在生成的字节流进入文件之前对其进行后处理。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-04
      • 2011-08-26
      • 1970-01-01
      • 2020-02-05
      • 1970-01-01
      • 2013-09-10
      • 1970-01-01
      相关资源
      最近更新 更多