如何向现有的二进制可执行文件添加功能?

逆向工程 linux C 可执行 全映射
2021-07-02 00:34:54

我想向现有的二进制文件添加一些功能。二进制文件是使用gcc.

  • 即使我充分了解程序的功能,我是否需要先反编译二进制文件?
  • 我应该如何添加必要的代码?
  • 我需要任何工具才能做到这一点吗?
4个回答

有几种广泛的方法可以做到这一点。

  1. 动态仪表

    PINValgrindDynamoRIO等工具允许您动态更改程序的行为。例如,您可以在特定地址添加对新函数的调用,拦截库调用并更改它们,等等。

    缺点是动态检测通常具有很高的开销。

  2. 静态仪表

    您还可以尝试静态修改程序以添加所需的行为。一个挑战是您经常需要处理可执行文件格式。为此存在一些工具,例如elfsh来自ERESI项目的工具,但我发现它们有缺陷且难以使用。

    静态检测的另一个策略是“重新编译”。您可以通过反编译程序、修改源代码并重新编译来完成此操作。理论上,你也可以使用BAP之类的工具将程序提升到IL,修改它,然后使用LLVM重新编译它。但是,当前版本可能还不够成熟。

  3. 动态加载

    您可以使用LD_PRELOAD覆盖将要动态链接的函数。当您想要更改库函数的行为时,这是一个不错的选择。当然,它不适用于静态链接的二进制文件或静态函数。

  4. 二进制补丁

    您通常可以使用十六进制编辑器对二进制文件进行简单的更改。例如,如果您想跳过某个函数调用或分支,通常可以将其替换为nop指令。如果您需要添加大量新代码,您可能需要使用elfsh来自ERESI项目的类似工具来帮助您调整二进制文件的大小。

很多时候,你可以通过小心地钩住一个程序来改变它的行为。您是否可以通过这种方式添加您想要的功能取决于程序的构建方式。如果程序以一个主要可执行文件和多个库的形式出现,这会有所帮助。

您可以通过首先将您自己的库与LD_PRELOAD. 编写一个定义函数的库foo,并在启动程序时将环境变量设置为LD_PRELOAD编译 ( .so) 库的路径:然后程序将调用您的foo而不是它想要的您可以foo通过使用dlsym().

以下是一些示例和教程:

使用 的程序的一些示例LD_PRELOAD

的限制LD_PRELOAD是您只能拦截在运行时解析的函数调用(动态链接)。如果你想拦截一​​个内部调用,你将不得不求助于更重的技术(修改磁盘上的可执行文件,或者使用 修改内存中的可执行文件ptrace)。

我想向现有的二进制文件添加一些功能。

所以总的来说,这四个更大的问题适用于修改一个可执行文件:

提出的第一个基本问题: 程序是否警惕代码修改(自检、反调试技巧、复制保护等)?

如果是这样:

  1. 甚至可以轻松去除/规避这些保护措施(例如拆包,如果已打包)
  2. 值得花时间这样做吗?

第二个问题是:
您能找出使用哪种编译器/语言来生成可执行文件吗?

更多细节更好,但大多数基本结构(if和其他控制结构)在各种编译器上的映射应该非常相似。

这与之前关于 RE-Stackexchange 的问题有关

第三个问题是:
用户界面如何实现的(CLI、Win32-Window Controls、Custom...)?

如果这是已知的:
您能找出常见的 HLL 结构(菜单、下拉菜单、复选框等)与您要修改的使用的编译器/语言的映射吗?

第四个也是最大的问题是:
如何在程序中创建所需的功能?

从本质上讲,这可能需要相当多的逆向工程,以找出如何最好地挂钩程序而不打扰它。

中心点:您如何利用现有的内部 API 来实现您的目标,而不会破坏 Stuff(如 CRTL+Z、版本控制、恢复功能)?

  • 现有的数据结构(以及它们之间的关系?)
  • 现有函数(参数、参数格式等)
    • 它有什么作用?
    • 它还能做什么?
    • 它真的做什么?
    • ...
  • 现有流程(= 程序如何在内部进行,逐步实现类似的功能)
    • 调用什么函数,按什么顺序?
    • 使用了哪些数据结构?
  • 功能/程序的主要内容在哪里(数据,例如主要绘画区域,以及它在内部是如何关联的?)
  • 需要注意的东西(如果它涉及所需的功能):
    • 日记
    • 恢复功能
    • 版本控制
  • 如何处理与所需特征相关的元数据(例如,快门速度、光圈值等)。

示例项目:

  • 在图形程序中构建一种新的绘画工具(没有插件 API)。
  • 扩展程序的插件 API。
  • 将插件 API 构建到没有插件的程序中。
  • 为文件添加新的保存/导出格式(如果无法将输出格式转换为所需格式,或者导出文件中缺少关键信息)。
  • 添加新的导入格式(如果无法将输入格式转换为可导入格式或某些信息未正确导入)。
  • 使用颜色搜索和替换工具扩展 Mspaint(在选择范围内,或在整个图片中)
  • 向程序添加代理支持/基本代理验证。
  • 将(新)命令行开关添加到暴露新/现有功能的程序。
  • 添加用于远程过程调用的 API,以从外部管理程序的操作。
  • 添加 Scripting-Support 以自动化经常重复的操作(如果没有 plugin-/scripting-API 开始)或支持批处理。

关于包装的代码和反编译器:
我不会谈论用 VM / 解释器(Py2Exe、Java 2 Exe 等)打包的其他语言中的包装代码,或者使用已安装的语言(JVM、C#)。对于其中一些情况,有非常好的反编译器。成功反编译后,它几乎可以归结为击败代码混淆(如果有的话)。

关于 C/C++-Decompilers:
我不能谈论 C/C++-Decompilers,尽管它可以归结为尽力而为的 HLL-Remapping(对于 Decompiler 没有得到的东西)和 Code-Deobfuscation(如果它是在没有符号的情况下编译的)前提是可执行文件中没有进一步的保护。

关于 HLL 映射的建议:
本质上,这个问题的很大一部分涉及“HLL 映射”(高级语言映射(机器代码))以及相应机器代码中这些结构的修改。

我在此处(binary-auditing.com)上找到了关于此主题的优秀可下载入门课程,该课程使用“IDA Free”

(有点过时了,但因为之前在这个线程中没有提到)

很久以前,我花了几个月的时间来扩展一个只有二进制文件的软件。

  • 我使用 IDA 进行分析,使用 SoftICE 进行实时调试。如果您可以在操作码/字节码级别理解目标,则不需要反编译。
  • 然后,因为它是一个 x86 PE 二进制文件,所以我使用了 Tasm 和 Iczelion 的Code Snippet Creator:它不再是一个著名的工具,但它允许透明地使用 Tasm 并重新注入代码,以及 PE 转换等......

    它在 EntryPoint 增加了代码,所以我手动做了我自己的补丁,然后跳转到原始的 EntryPoint。

现在有点老派了——这些天我可能会注入一个 DLL——但它确实有效。

至少,它让您可以通过 ASM 进行完全控制,同时通过自动修补保持可维护性。