将功能注入二进制文件的最佳方法
将功能插入二进制应用程序(3d 方、闭源)的最佳方式是什么。
目标应用程序位于 OSX 上,似乎是使用 gcc 3+ 编译的。 我可以看到二进制文件中实现的函数列表,并调试和隔离了我想要远程调用的一个特定函数。
具体来说,当我从复杂的 HIDevice 接收到某些数据时,我想调用此函数 - 让我们称之为 void zoomByFactor(x,y)
。
我可以轻松修改或将指令注入二进制文件本身(即修补不需要仅发生在 RAM 中)。
作为“很好地”做到这一点的一种方式,您会推荐什么?
编辑:
我确实需要整个应用程序。 所以我不能放弃它并使用图书馆。 (对于那些需要道德解释的人:这是一款专有的 CAD 软件,其公司网站自 2006 年以来就没有更新过。我已经为这个产品支付了费用(确实花了很多钱)并且有项目该产品本身就很适合我,但我想使用我最近检查过该应用程序的内部结构的新 HID,并且我相当有信心。我可以使用相关数据调用正确的函数并使其正常工作)。
这是我到目前为止所做的,而且相当贫民区。
我已经通过这个过程修改了应用程序的部分内容:
xxd -g 0 binary > binary.hex cat binary.hex | awk 'substitute work' > modified.hex xxd -r modified.hex > newbinary chmod 777 newbinary
我正在做这种跳跃式的操作,因为二进制文件几乎有 100 兆大。
我的想法是,我会跳转到主应用程序循环中的某个位置,启动一个线程,然后返回到主函数。
现在的问题是:我可以在哪里插入新代码? 我需要修改符号表吗? 或者,我如何自动加载 dylib,以便我需要做的唯一“黑客”操作是将对正常加载的 dylib 的调用插入到主函数中?
What would be the best way of inserting functionality into a binary application (3d party, closed source).
The target application is on OSX and seems to have been compiled using gcc 3+. I can see the listing of functions implemented in the binary and have debugged and isolated one particular function which I would like to remotely call.
Specifically, I would like to call this function - let's call it void zoomByFactor(x,y)
- when I receive certain data from a complex HIDevice.
I can easily modify or inject instructions into the binary file itself (ie. the patching does not need to occur only in RAM).
What would you recommend as a way of "nicely" doing this?
Edit:
I do indeed need to entire application. So I can't ditch it and use a library. (For those who need an ethical explanation: this is a proprietary piece of CAD software whose company website hasn't been updated since 2006. I have paid for this product (quite a lot of money for what it is, really) and have project data which I can not easily migrate away from it. The product suits me just fine as it is, but I want to use a new HID which I recently got. I've examined the internals of the application, and I'm fairly confident that I can call the correct function with the relevant data and get it to work properly).
Here's what I've done so far, and it is quite gheto.
I've already modified parts of the application through this process:
xxd -g 0 binary > binary.hex cat binary.hex | awk 'substitute work' > modified.hex xxd -r modified.hex > newbinary chmod 777 newbinary
I'm doing this kind of jumping through hoops because the binary is almost 100 megs large.
The jist of what I'm thinking is that I'd jmp somewhere in the main application loop, launch a thread, and return to the main function.
Now, the questions are: where can I insert the new code? do I need to modify symbol tables? alternatively, how could I make a dylib load automatically so that the only "hacking" I need to do is inserting a call to a normally loaded dylib into the main function?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
对于那些对我最终所做的事情感兴趣的人,这里有一个总结:
我已经研究了几种可能性。 它们分为运行时修补和静态二进制文件修补。
就文件修补而言,我基本上尝试了两种方法:
修改代码中的程序集
二进制文件的段 (__TEXT)。
修改加载命令
mach 头。
第一种方法需要有可用空间,或者可以覆盖的方法。 它还具有极差的可维护性。 任何新的二进制文件都需要再次手动修补它们,特别是如果它们的源代码发生了轻微的变化。
第二种方法是尝试将 LC_LOAD_DYLIB 条目添加到 mach 标头中。 那里没有很多 mach-o 编辑器,所以它很毛茸茸,但我实际上修改了结构,以便我的条目可以通过
otool -l
看到。 然而,这实际上并没有起作用,因为运行时存在dyld: bad external relocation length
。 我假设我需要处理导入表等问题。如果没有编辑器,要做到这一点需要付出太多的努力。第二条路径是在运行时注入代码。 没有太多的东西可以做到这一点。 即使对于您可以控制的应用程序(即您启动的子应用程序)也是如此。 也许有一种方法可以
fork()
并启动初始化过程,但我从来没有这样做过。有 SIMBL,但这要求您的应用程序是 Cocoa,因为 SIMBL 将充当系统范围的输入管理器并有选择地加载包。 我驳回了这一点,因为我的应用程序不是 Cocoa,而且我不喜欢系统范围的东西。
接下来是 mach_inject 和 mach_star 项目。 还有一个较新的项目称为
PlugSuit 托管在 google 上,它似乎只不过是 mach_inject 的一个薄包装。
Mach_inject 提供了一个 API 来执行其名称所暗示的操作。 不过我确实在代码中发现了一个问题。 在 10.5.4 上,mach_inject.c 文件中的 mmap 方法要求存在 MAP_SHARED 或与 MAP_READ 的关系,否则 mmap 将失败。
除此之外,整个事情实际上正如广告所宣传的那样。 我最终使用 mach_inject_bundle 来完成我原本打算将 DYLIB 静态添加到 mach 头中的操作:即在模块 init 上启动一个新线程来执行其脏操作。
不管怎样,我已经把它做成了维基。 请随意添加、更正或更新信息。 实际上没有关于 OSX 上此类工作的信息。 信息越多越好。
For those interested in what I've ended up doing, here's a summary:
I've looked at several possibilities. They fall into runtime patching, and static binary file patching.
As far as file patching is concerned, I essentially tried two approaches:
modifying the assembly in the code
segments (__TEXT) of the binary.
modifying the load commands in the
mach header.
The first method requires there to be free space, or methods you can overwrite. It also suffers from extremely poor maintainability. Any new binaries will require hand patching them once again, especially if their source code has even slightly changed.
The second method was to try and add a LC_ LOAD_ DYLIB entry into the mach header. There aren't many mach-o editors out there, so it's hairy, but I actually modified the structures so that my entry was visible by
otool -l
. However, this didn't actually work as there was adyld: bad external relocation length
at runtime. I'm assuming I need to muck around with import tables etc. And this is way too much effort to get right without an editor.Second path was to inject code at runtime. There isn't much out there to do this. Even for apps you have control over (ie. a child application you launch). Maybe there's a way to
fork()
and get the initialization process launched, but I never go that.There is SIMBL, but this requires your app to be Cocoa because SIMBL will pose as a system wide InputManager and selectively load bundles. I dismissed this because my app was not Cocoa, and besides, I dislike system wide stuff.
Next up was mach_ inject and the mach_star project. There is also a newer project called
PlugSuit hosted at google which seems to be nothing more than a thin wrapper around mach_inject.
Mach_inject provides an API to do what the name implies. I did find a problem in the code though. On 10.5.4, the mmap method in the mach_inject.c file requires there to be a MAP_ SHARED or'd with the MAP_READ or else the mmap will fail.
Aside from that, the whole thing actually works as advertised. I ended up using mach_ inject_ bundle to do what I had intended to do with the static addition of a DYLIB to the mach header: namely launching a new thread on module init that does its dirty business.
Anyways, I've made this a wiki. Feel free to add, correct or update information. There's practically no information available on this kind of work on OSX. The more info, the better.
在 10.5 之前的 MacOS X 版本中,您可以使用输入管理器扩展来执行此操作。 输入管理器旨在处理非罗马语言输入之类的事情,其中扩展程序可以弹出一个窗口来输入适当的字形,然后将完整的文本传递给应用程序。 应用程序只需要确保它是 Unicode 干净的,而不必担心每种语言和地区的确切细节。
输入管理器被广泛滥用,将各种不相关的功能修补到应用程序中,并且经常破坏应用程序的稳定性。 它还成为“Oompa-Loompa”等木马的攻击媒介。 MacOS 10.5 加强了对输入管理器的限制:它不会在 root 或 wheel 拥有的进程中运行它们,也不会在修改了其 uid 的进程中运行它们。 最重要的是,10.5 不会将输入管理器加载到 64 位进程中,并且表明甚至不支持 32 位使用,并将在未来版本中删除。
因此,如果您可以忍受这些限制,输入管理器就可以满足您的需求。 未来的 MacOS 版本几乎肯定会引入另一种(更安全、更有限)的方式来实现这一点,因为语言输入支持确实需要该功能。
In MacOS X releases prior to 10.5 you'd do this using an Input Manager extension. Input Manager was intended to handle things like input for non-roman languages, where the extension could popup a window to input the appropriate glyphs and then pass the completed text to the app. The application only needed to make sure it was Unicode-clean, and didn't have to worry about the exact details of every language and region.
Input Manager was wildly abused to patch all sorts of unrelated functionality into applications, and often destabilized the app. It was also becoming an attack vector for trojans, such as "Oompa-Loompa". MacOS 10.5 tightens restrictions on Input Managers: it won't run them in a process owned by root or wheel, nor in a process which has modified its uid. Most significantly, 10.5 won't load an Input Manager into a 64 bit process and has indicated that even 32 bit use is unsupported and will be removed in a future release.
So if you can live with the restrictions, an Input Manager can do what you want. Future MacOS releases will almost certainly introduce another (safer, more limited) way to do this, as the functionality really is needed for language input support.
我相信您还可以使用 DYLD_INSERT_LIBRARIES 方法。
这篇文章也与您的内容相关正在尝试做;
I believe you could also use the DYLD_INSERT_LIBRARIES method.
This post is also related to what you were trying to do;
我最近尝试使用
mach_star
源进行注入/覆盖。 我最终为它写了一个教程,因为这些东西的文档总是那么粗略而且经常过时。http://soundly.me/osx-injection-override-tutorial-hello-世界/
I recently took a stab at injection/overriding using the
mach_star
sources. I ended up writing a tutorial for it since documentation for this stuff is always so sketchy and often out of date.http://soundly.me/osx-injection-override-tutorial-hello-world/
有趣的问题。 如果我理解正确的话,您希望在正在运行的可执行文件中添加远程调用函数的功能。
如果您并不真正需要整个应用程序,您也许可以删除主函数并将其转换为可以链接的库文件。 您需要弄清楚如何确保所有必需的初始化都发生。
另一种方法可能是像病毒一样行动。 注入一个处理远程调用的函数,可能在另一个线程中。 您需要通过将一些代码注入主函数或其他合适的地方来启动该线程。 您很可能会遇到初始化、线程安全和/或维护正确的程序状态方面的重大问题。
最好的选择(如果可用)是让应用程序的供应商公开一个插件 API,让您以受支持的方式干净、可靠地完成此操作。
如果您采用任何一种破解二进制路线,这将是耗时且脆弱的,但您会在这个过程中学到很多东西。
Interesting problem. If I understand you correctly, you'd like to add the ability to remotely call functions in a running executable.
If you don't really need the whole application, you might be able to strip out the main function and turn it into a library file that you can link against. It'll be up to you to figure out how to make sure all the required initialization occurs.
Another approach could be to act like a virus. Inject a function that handles the remote calls, probably in another thread. You'll need to launch this thread by injecting some code into the main function, or wherever else is appropriate. Most likely you'll run into major issues with initialization, thread safety, and/or maintaining proper program state.
The best option, if its available, is to get the vendor of your application to expose a plugin API that lets you do this cleanly and reliably in a supported manner.
If you go with either hack-the-binary route, it'll be time consuming and brittle, but you'll learn a lot in the process.
在 Windows 上,这很简单,实际上也很广泛,被称为 DLL/代码注入。
有一个适用于 OSX 的商业 SDK 允许执行此操作:应用程序增强器(免费用于非商业用途) 。
On Windows, this is simple to do, is actually very widely done and is known as DLL/code injection.
There is a commercial SDK for OSX which allows doing this: Application Enhancer (free for non-commercial use).