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


即使我足够了解程序的功能,我还是需要先反编译二进制文件吗?
我应该如何添加必要的代码?
我需要任何工具来做到这一点吗?


评论

适用于Windows,Linux等平台?

您要添加什么功能?因为这取决于不同的方法。例如,要使GUI自动化,您可以使用其他技术来改变数据库引擎。

stackoverflow.com/questions/4309771/…

#1 楼

您可以通过多种方法进行此操作。



动态仪表

诸如PIN,Valgrind或DynamoRIO之类的工具可让您动态更改程序的行为。例如,您可以在特定地址添加对新函数的调用,拦截库调用并对其进行更改,等等。

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


静态检测

您还可以尝试静态修改程序以添加所需的行为。挑战之一是您经常需要弄混可执行文件格式。存在一些工具,例如ERESI项目中的elfsh,但是我发现它们有故障并且难以使用。您可以通过反编译程序,修改源代码并重新编译来实现。从理论上讲,您还可以使用BAP之类的工具将程序提升为IL,进行修改,然后使用LLVM重新编译。但是,当前版本可能还不够成熟。


动态加载

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


二进制修补程序

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



#2 楼

通常,您可以通过仔细钩住程序来更改其行为。是否可以通过这种方式添加所需的功能取决于程序的构造方式。如果程序以一个主要可执行文件加几个库的形式出现,将很有帮助。

您可以通过首先链接自己的库,并使用LD_PRELOAD来挂接到程序对共享库进行的任何调用。编写一个定义函数foo的库,并在启动程序时将环境变量LD_PRELOAD设置为已编译(.so)库的路径:然后,该程序将调用foo而不是它的预期名称。您可以通过使用foo获取指向原始dlsym()函数的指针来调用它。 >使用LD_PRELOAD覆盖函数-一个最小的源代码示例
在不更改源代码的情况下修改动态库
LD_PRELOAD对于Userland Rootkits的魔力

通过LD_PRELOAD(a详尽的演示文稿)

使用LD_PRELOAD的一些程序示例:



fakechroot,PlasticFS-重写程序使用的文件名

Valgrind电子围栏-通过覆盖malloc检测不良堆使用


Libshape-限制网络带宽

KGtk-使用KDE对话框Gtk程序中的对话框

LD_PRELOAD的局限性在于,您只能拦截在运行时解析的函数调用(动态链接)。如果要拦截内部呼叫,则必须采用较重的技术(在磁盘上或使用ptrace在内存中修改可执行文件)。

#3 楼


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


因此,通常,这四个更大的问题适用于修改Executeable:

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

如果这样:


是否甚至可以轻松地移除/绕开这些保护措施(例如,拆开包装,如果已包装的话)
是否值得这样做?

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

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

这与先前有关RE-Stackexchange的问题有关。

第三个问题是:
用户界面是否已实现(CLI,Win32-Window控件,自定义...)?

如果已知:
可以吗u找出常见的HLL构造(菜单,下拉菜单,复选框等)与要修改的二手编译器/语言的映射?

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

本质上,这可能需要大量的逆向工程,以找出如何最好地挂接到程序而又不会对其造成干扰。

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

现有数据结构(
它们之间的关系?)
现有功能(参数,参数格式等)

它有什么作用?
它还能做什么?
>它实际上是做什么的?叫,按什么顺序?
使用哪种数据结构?


特征/程序的实质在哪里(数据,例如主绘画区域,以及它在内部如何关联?)
需要寻找的东西(如果它涉及所需的功能):
日志记录
恢复功能
版本控制


元数据的处理方式(例如快门速度) ,f-Stops,...),与所需的功能有关。

示例项目:


将新的绘画工具构建到图形程序(没有plugin-API)。
扩展程序的plugin-API。
在没有程序的情况下将plugin-API构建到程序中。
添加新的save / export-format用于文件(如果无法将输出格式转换为所需格式,或者导出的文件中缺少关键信息)。
添加新的导入格式(如果无法转换输入-format转换为可导入格式,或者如果某些信息未正确导入)。
用颜色搜索和替换工具(在选择中或在整个图片中)
向程序添加代理支持/基本代理身份验证。
添加(新)命令行切换到公开显示的程序新功能/现有功能。
添加用于远程过程调用的API,以在外部管理程序的操作。
添加脚本支持,以自动执行经常重复的操作(如果没有plugin- / scripting-API首先)或支持批处理。

关于包装的代码和反编译器:
我不会谈论VM /解释器(Py2Exe,Java)包装的其他语言的包装代码2 Exe,...),或使用已安装的版本(JVM,C#)。在某些情况下,有相当不错的反编译器。成功进行反编译后,它几乎可以归结为克服代码混淆(如果有的话)。

关于C / C ++-反编译器:
我不能谈论C / C ++-Decompilers,尽管它可以归结为尽力而为的HLL-Remapping(对于Decompiler没得到的东西)和Code-Defufuscation(如果它是在没有符号的情况下编译的),前提是没有进一步的保护。关于HLL映射的建议:
本质上,该问题的很大一部分涉及的“ HLL映射”(高级语言映射(在机器代码中))和的修改。这些结构在相应的机器代码中。

在此主题(binary-auditing.com)上,我发现了一个出色的可下载的入门课程,它使用了“ IDA Free”。

#4 楼

(稍有过时,但正如本线程中先前未提到的那样)

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


我使用IDA进行分析,并使用SoftICE进行实时调试。如果您可以在操作码/字节码级别理解目标,则不需要进行反编译。

然后,因为它是x86 PE二进制文件,所以我使用了Tasm和Iczelion的代码段创建器:它不再是著名的工具,但是它允许透明地使用Tasm并重新注入代码,以及进行PE转换等...

它在EntryPoint上添加了代码,所以我手动制作了自己的补丁,然后跳到原始的EntryPoint。 br />

现在有点老套了-这些天我可能会注入DLL-但它确实起作用。

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

#5 楼

您不需要反编译二进制文件。如果您了解要进行哪些更改,并且仅通过修改二进制文件或其依赖项就可以进行更改,则只需在磁盘或内存中进行这些更改即可。

您可以选择几种方法来实现修改本身。

您可以使用LD_PRELOAD使链接器在二进制文件运行之前加载共享对象。然后,您根本不需要修改磁盘上的二进制文件。这是valgrind的工作方式,它作为共享对象加载,然后开始动态二进制检测。

您可以使用valgrind。 Valgrind将允许您动态地重新编写程序并任意修改其行为。 Valgrind是一个动态的二进制检测程序,允许其工具在执行时编辑该程序。如果您只想更改程序行为,则可能会起作用,但是valgrind也会导致全局变慢,并且如果您想修补和重新分发程序,则可能不理想。

您还可以使用elfsh / eresi之类的工具将新代码插入程序。这些工具应注意将代码插入与ELF程序标头之类的东西有关的行为。您可以在Google上搜索“ ELF感染器”的概念,在该概念中,注入的代码将成为新程序的入口点,执行某些操作,然后跳转到旧程序的入口点。

#6 楼

在Windows上执行此操作

尽管此问题集中在Linux上,我个人将使用其他答案中概述的简单LD_PRELOAD方法,但Windows知道类似的机制实际上在最近的版本中已被滥用过去(另请参见下面的替代方法)。我使用该方法“破解”了一个加密狗系统。

Enter ...

DLL放置(即预加载,又称为劫持)攻击

该方法的名称是在最近才发现的,因为它发现将DLL放置在远程共享上,然后导航到共享(例如,媒体播放器)会导致媒体播放器加载远程DLL而不是本地版本。这是设计使然。现在更改它会破坏成百上千的应用程序。

Microsoft已经以某些方式解决了这一问题,尽管唯一的解决方案是在应用程序端正确实现。但是自从Windows 2000成为第一个基于NT平台的消费者操作系统以来,即使我们不得不处理NT安全,许多开发人员甚至都没有掌握NT安全。

它与它有什么关系您描述的目标?

添加功能不一定意味着您在磁盘上修补了可执行文件。您还可以在内存中执行此操作。

如何利用它?

只要应用程序使用DL​​L,并且可以通过Dependency Walker或在调试器中,您可以选择它导入的DLL之一并替换(在其当前位置),或在加载顺序中将另一个DLL放置在现有DLL之前的路径中。

另一种方法是更改导入的DLL的名称。在极少数情况下(例如,众所周知的DLL),这是加载备用DLL的唯一可行方法,并且在某些特殊情况下仍然可能失败。

限制

如果所用的DLL位于DLL搜索顺序的第一个位置,则必须从字面上替换磁盘上的文件,除非您如上所述重命名了导入。

实现

只有很少的导出符号可以用于DLL的手动方法。最简单的方法是从DLL创建模块定义文件,然后从该函数创建仅具有函数转发器的DLL。这样,您放置的DLL将已经加载,并且只需通过调用即可。

但是,此方法将因导出的变量(而不是函数)而失败。

这里一个基于pefile的简单Python脚本,我在StackOverflow上写了另一个答案:名称。

因此,您可以将代码穿梭到目标应用程序中并从那里运行。

替代方法

已经提到过。绕道行驶是经常被提及的示例,它出于大多数实际目的而与不方便的EULA挂钩。请参阅有关这种方法的现有答案。

还可以使用AppInit_DLL注册表值尽早插入DLL。或者,您可以编写一个带有调试器循环的启动器,并使用Image File Execution Options使目标首先启动调试器。调试器还可以影响DLL的加载,也可以方便地在可执行文件和DLL之间的边界处拦截(方便地)调用。 Process Explorer。


您将注意到如何将这些方法分类为Ed McMan已经在其答案中提到的类别。但是,我将其留给读者练习:)

#7 楼

我是在Windows上使用Notepad.exe完成的。我想添加一个顶级菜单项来打开calc.exe只是为了好玩(我知道您的问题被标记为Linux和gcc编译器,但想法可能是相同的。)

所以我用了Resource黑客工具,用于添加Calc菜单并在Immunity Debugger上打开notepad.exe,在代码中寻找一些空间来放置WinExec shellcode。最初我没有更改可执行文件,我不得不查看内存中的程序以找到一些空间,可以在其中粘贴我的汇编指令而不会导致记事本崩溃。

一旦我找到足够的空间(更改原始代码)通过消除一些不需要的汇编指令甚至优化它们),我在XVI Hex Editor上打开了notepad.exe,并搜索了在Immunity上运行的操作码。我的意思是,调试器正在运行一些操作码,对吗?我只是搜索了一系列操作码,以确保我位于要更改的软件的正确位置,并用我的shellcode(现在不是汇编代码,而是“已编译”的汇编-机器代码)对其进行了封装。

#8 楼

Giles偶然提到了“ ptrace()”。但是我认为它本身应该值得一整节。 “ ptrace()”是OS提供的系统调用API(Linux和所有UNIX都有,Windows也是如此),以对另一个进程施加调试控制。当您使用PTRACE_ATTACH(作为ptrace()的一部分)附加到另一个进程时,内核将完全暂停运行该进程的CPU,从而允许您对进程的任何部分进行更改:CPU,任何寄存器,该部分的任何部分进程内存等。这就是动态内联挂钩的工作方式。 (ptrace()附加,修改内存中的二进制文件,然后不附加ptrace())。据我所知,对另一个进程的所有动态修改都必须使用ptrace()-这是内核提供的唯一机制,可以确保通过系统调用在此时进行完整性。
但是最近类似的API,例如utrace()正在弹出,因此理论上也可以进行内联挂钩:
http://landley.net/kdocs/ols/2007/ols2007v1-pages-215-224.pdf
对于内核挂钩有很多方法:系统调用,中断和内联挂钩。这是用于中断挂钩的:
http://mammon.github.io/Text/linux_hooker.txt
当CPU处于STOP模式时,基本上您可以对CPU /内存空间做任何您想做的事情/ register-只需确保恢复到其原始状态,然后再返回到其停止的原始地址即可。
如果使用库注入技术,则可以实现任何功能-调用远程库,远程shell等:
https://attack.mitre.org/techniques/T1055/001/
https://stackoverflow.com/questions/24355344/inject-shared-library-into-a-process
https://backtrace.io/blog/backtrace/elf-shared-library-injection-forensics/