如何将二进制数据添加(和使用)到已编译的可执行文件?

时间:2015-09-07 11:34:53

标签: c exe elf portable-executable

有几个问题涉及这个问题的某些方面,但似乎都没有完全回答它。整个问题可归纳如下:

  • 你有一个已编译的可执行文件(显然希望使用这种技术)。
  • 你想为它添加一个任意大小的二进制数据(不一定是它自己,这将是另一个令人讨厌的问题)。
  • 您希望已编译的可执行文件能够访问此添加的二进制数据。

我的特定用例是一个解释器,我想让用户能够用解释器二进制文件和他提供的代码生成单个文件可执行文件(解释器二进制文件是必须的可执行文件)用用户提供的代码修补为二进制数据。)

类似的情况是自解压档案,其中一个程序(归档实用程序,如zip)能够构造这样一个包含预构建解压缩程序的可执行文件(已编译的可执行文件)和用户提供的数据(存档的内容)。显然,此过程中不涉及编译器或链接器(感谢,Mathias为注释并指出7-zip)。

使用现有问题,解决方案的特定路径如下所示:

appending data to an exe - 这涉及向任意exes添加任意数据的方面,而没有涵盖如何实际访问它(基本上简单的追加通常有效,对于Unix的ELF格式也是如此)。

Finding current executable's path without /proc/self/exe - 与上述相关,这将允许获取用于打开exe的文件名,以访问添加的数据。还有更多这类问题,但是它们都没有特别关注获得适合于实际将二进制文件作为文件打开的目的的路径问题(单独的目标可能(?)更容易实现 - 真的你不要甚至不需要路径,只是打开阅读的二进制文件。

除了填充二进制文件并打开文件以进行读取之外,还可能存在其他可能更优雅的方法。例如,可执行文件是否可以使得稍后以任意大小修补它变得相当简单数据,因此它出现在“内部”它在一些适当的数据段? (我真的找不到任何东西,对于固定大小的数据,它应该是微不足道的,尽管除非可执行文件有一些哈希值)

如果与标准C的偏差尽可能小,可以做得相当好吗?甚至或多或少的跨平台? (至少从维护的角度来看)请注意,如果执行二进制数据添加的程序不依赖于编译器工具(用户可能没有),那么首选,但是需要这些工具的解决方案也可能有用。

请注意已编译的可执行文件条件(上面列表中的第一点),这需要与C/C++ with GCC: Statically add resource files to executable/librarySDL embed image inside program executable等问题中描述的解决方案完全不同的方法,要求嵌入数据编译时。

附加说明:

上面列出并在一些评论中提出的明显方法的问题,即只是附加到二进制文件并使用它,如下:

  • 打开当前正在运行的程序的二进制文件似乎并不简单(打开可执行文件进行读取,但没有找到提供文件打开调用的路径,至少不是以合理的跨平台方式)。
  • 获取路径的方法可以提供可能不存在的攻击面。这意味着潜在的攻击者可以欺骗程序以查看可执行文件实际具有的不同二进制数据(由他提供),从而暴露可能存在于数据解析器中的任何漏洞。

1 个答案:

答案 0 :(得分:1)

这取决于您希望其他系统如何查看二进制文件。

在Windows中签名数字

exe格式允许验证自发布以来文件未被修改。这将允许您: -

  1. 编译文件
  2. 添加数据包
  3. 签署您的文件并发布。
  4. 遵循这个系统的优点是"每个人"同意自签署以来您的文件未被修改。

    实现此方案的最简单方法是使用资源。可以在链接后添加Windows资源。它们受authenticode数字签名保护,您的程序可以从自身中提取资源数据。

    过去可以增加签名以包含二进制数据。不幸的是,这已被禁止。有二进制文件在签名部分使用数据。不幸的是,这是恶意使用的。这里有一些细节msdn blog

    打破签名

    如果不能重新签名,则结果将被视为不安全。值得注意的是,附加数据是不安全的,可以在没有人能够分辨的情况下进行修改,但二进制文件中的代码也是如此。

    将数据附加到二进制文件会破坏数字签名,也意味着最终用户无法判断代码是否已被修改。

    这意味着您添加到代码中以确保数据blob仍然安全的任何自我保护都不会阻止您的代码被修改以删除检查。

    运行模块

    Windows GetModuleFileName允许找到运行路径。

    Linux提供/proc/self/proc/pid

    Unix似乎没有一种可靠的方法。

    数据读取

    zip格式的方法是将目录写入文件的末尾。这意味着可以在该位置的末尾找到数据,然后向后查看数据的开始。这里的优点是数据blob是从数据末尾标出的,而不是自然的开始。