将功能注入二进制文件的最佳方法
Posted
技术标签:
【中文标题】将功能注入二进制文件的最佳方法【英文标题】:Best way to inject functionality into a binary 【发布时间】:2010-09-20 17:18:42 【问题描述】:将功能插入二进制应用程序(3d 方,封闭源代码)的最佳方式是什么。
目标应用程序在 OSX 上,似乎是使用 gcc 3+ 编译的。我可以看到在二进制文件中实现的函数列表,并且已经调试和隔离了一个我想远程调用的特定函数。
具体来说,当我从复杂的 HIDevice 接收到某些数据时,我想调用这个函数 - 我们称之为 void zoomByFactor(x,y)
。
我可以轻松地修改或将指令注入二进制文件本身(即修补不需要只在 RAM 中进行)。
你会推荐什么作为“很好”这样做的方式?
编辑:
我确实需要整个应用程序。所以我不能放弃它并使用图书馆。 (对于那些需要道德解释的人:这是一款专有的 CAD 软件,其公司网站自 2006 年以来就没有更新过。我已经为这个产品付了钱(真的花了很多钱)并且有项目我无法轻易从中迁移的数据。该产品非常适合我,但我想使用我最近获得的新 HID。我已经检查了应用程序的内部结构,我相当有信心我可以使用相关数据调用正确的函数并使其正常工作)。
这是我到目前为止所做的,而且它非常贫民窟。
我已经通过这个过程修改了部分应用程序:
xxd -g 0 二进制 > binary.hex cat 二进制.hex | awk '替代工作' > modified.hex xxd -r modified.hex > newbinary chmod 777 新二进制我正在做这种跳跃,因为二进制文件几乎有 100 兆大。
我的想法是,我会在主应用程序循环中的某个地方跳转,启动一个线程,然后返回到主函数。
现在,问题是:我可以在哪里插入新代码?我需要修改符号表吗?或者,我怎样才能自动加载 dylib,以便我需要做的唯一“黑客”操作就是将对正常加载的 dylib 的调用插入到主函数中?
【问题讨论】:
【参考方案1】:在 10.5 之前的 MacOS X 版本中,您可以使用输入管理器扩展来执行此操作。输入管理器旨在处理诸如非罗马语言的输入之类的事情,其中扩展程序可以弹出一个窗口来输入适当的字形,然后将完成的文本传递给应用程序。应用程序只需要确保它是 Unicode 干净的,而不必担心每种语言和地区的确切细节。
输入管理器被广泛滥用以将各种不相关的功能修补到应用程序中,并且经常破坏应用程序的稳定性。它也正在成为特洛伊木马的攻击媒介,例如“Oompa-Loompa”。 MacOS 10.5 加强了对输入管理器的限制:它不会在 root 或 wheel 拥有的进程中运行它们,也不会在修改了其 uid 的进程中运行它们。最重要的是,10.5 不会将输入管理器加载到 64 位进程中,并且表明即使 32 位使用也不支持,并且将在未来的版本中删除。
因此,如果您可以忍受这些限制,那么输入管理器可以做您想做的事。未来的 MacOS 版本几乎肯定会引入另一种(更安全、更有限)的方式来执行此操作,因为语言输入支持确实需要该功能。
【讨论】:
你的意思是我应该假装是一个输入管理器,以便我可以免费加载 TextInput 系统的注入方法?该程序不是 Cocoa 应用程序,所以我想知道我是否能够加载 InputManager。 这就是我的意思。但是,如果该程序不是 Cocoa 应用程序,那么您不能以这种方式插入钩子是对的。哦,好吧。【参考方案2】:我最近尝试使用mach_star
源进行注入/覆盖。我最终为它写了一个教程,因为这些东西的文档总是很粗略而且经常过时。
http://soundly.me/osx-injection-override-tutorial-hello-world/
【讨论】:
【参考方案3】:我相信你也可以使用DYLD_INSERT_LIBRARIES method。
This post 也与您尝试做的事情有关;
【讨论】:
【参考方案4】:在 Windows 上,这很简单,实际上已被广泛使用,称为 DLL/代码注入。
有一个用于 OSX 的商业 SDK 允许这样做:Application Enhancer(非商业用途免费)。
【讨论】:
【参考方案5】:对于那些对我最终所做的事情感兴趣的人,这里有一个摘要:
我研究了几种可能性。它们分为运行时补丁和静态二进制文件补丁。
就文件修补而言,我基本上尝试了两种方法:
在代码中修改程序集 二进制文件的段 (__TEXT)。
修改加载命令 马赫头。
第一种方法要求有可用空间,或者您可以覆盖的方法。它还具有极差的可维护性。任何新的二进制文件都需要再次手动修补它们,尤其是在它们的源代码发生轻微变化的情况下。
第二种方法是尝试将 LC_LOAD_DYLIB 条目添加到 mach 标头中。那里没有很多 mach-o 编辑器,所以很麻烦,但我实际上修改了结构,以便otool -l
可以看到我的条目。然而,这实际上并没有工作,因为在运行时有一个dyld: bad external relocation length
。我假设我需要处理导入表等。如果没有编辑器,这太费力了。
第二条路径是在运行时注入代码。没有太多可以做到这一点。即使对于您可以控制的应用程序(即您启动的子应用程序)。也许有一种方法可以fork()
并启动初始化过程,但我从不这样做。
有 SIMBL,但这要求您的应用程序是 Cocoa,因为 SIMBL 将作为系统范围的 InputManager 并选择性地加载包。我忽略了这个,因为我的应用不是 Cocoa,此外,我不喜欢系统范围的东西。
接下来是 mach_inject 和 mach_star 项目。还有一个更新的项目叫做 谷歌托管的 PlugSuit 似乎只不过是 mach_inject 的一个薄包装。
Mach_inject 提供了一个 API 来执行顾名思义。我确实在代码中发现了一个问题。在 10.5.4 上,mach_inject.c 文件中的 mmap 方法要求存在 MAP_SHARED 或与 MAP_READ 一起使用,否则 mmap 将失败。
除此之外,整个事情实际上像宣传的那样工作。我最终使用了 mach_inject_bundle 来完成我打算将 DYLIB 静态添加到 mach 标头中所做的事情:即在模块 init 上启动一个新线程来执行其肮脏的业务。
不管怎样,我已经把它做成了一个维基。随意添加、更正或更新信息。在 OSX 上几乎没有关于此类工作的信息。信息越多越好。
【讨论】:
【参考方案6】:有趣的问题。如果我对您的理解正确,您希望添加在正在运行的可执行文件中远程调用函数的功能。
如果您真的不需要整个应用程序,您也许可以去掉主函数并将其转换为您可以链接的库文件。由您决定如何确保所有必需的初始化都发生。
另一种方法可能是像病毒一样行事。注入一个处理远程调用的函数,可能在另一个线程中。您需要通过将一些代码注入主函数或其他任何合适的地方来启动此线程。您很可能会在初始化、线程安全和/或维护正确的程序状态方面遇到重大问题。
如果可用,最好的选择是让您的应用程序供应商公开一个插件 API,让您以受支持的方式干净可靠地执行此操作。
如果您采用任何一种破解二进制方法,这将是耗时且脆弱的,但您会在此过程中学到很多东西。
【讨论】:
以上是关于将功能注入二进制文件的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章
Spark/pySpark:读取小型二进制数据文件的最佳方式