#1 楼
DCoder的答案很好。为了进行某种扩展,我经常在强制现有进程通过CreateRemoteThread加载DLL的情况下使用DLL注入。从那里,DLL的入口点将在加载后由操作系统执行。然后,在入口点中,我将调用一个例程,该例程对我感兴趣的原始二进制文件中的所有位置执行内存内修补,然后通过各种修改将其执行重定向到我的DLL中。如果我有兴趣修改或观察进程与某些导入函数的交互,则将覆盖该函数的IAT(导入地址表)条目,并用指向我控制的对象的指针替换它。如果要对二进制文件中存在的某些功能执行相同的操作,则将在该功能的开头进行一些绕行样式的补丁。我什至可以在任意位置进行非常外科手术和针对性的挂钩,类似于老式的字节修补。我的DLL在单独的钩子中开展业务,然后进行编程以将控制重定向到原始进程。DLL注入提供了一个用于操纵正在运行的进程的执行的平台。在逆向工程时,它非常常用于记录信息。例如,您可以挂钩给定导入的操作系统库函数的IAT条目,然后将函数参数记录到磁盘上。这为您提供了一个数据源,可以帮助您快速对目标进行反向工程。
DLL注入不仅限于日志记录。鉴于您可以自由地执行该进程地址空间中所需的任何代码,因此您可以以任何选择的方式修改程序。该技术在游戏黑客世界中经常用于编码机器人。
字节修补可以做的任何事情,DLL注入都可以做。除了DLL注入之外,它可能更容易,更快捷,因为您可以使用C而不是汇编语言来编写补丁程序,并且不必费心对二进制文件及其PE结构进行手动修改,查找代码陷阱等。DLL注入几乎完全不需要在修改二进制文件时使用汇编语言;唯一需要的汇编语言是在特定钩子的入口和出口附近的一小段代码,以保存和恢复寄存器/标志的值。它还使二进制修改既快速又简单,并且不会更改您要修补的可执行文件的加密签名。 (有关加密签名的注释仅适用于磁盘上的可执行文件,而不适用于内存中的可执行文件;当然,更改内存中的内容会影响根据更改后的内存内容计算出的加密签名。)
DLL注入可以是用于解决非同凡响的逆向工程问题。由于未公开协议,以下示例在某些方面一定是含糊的。
我对经常更新的程序(有时每天更新多次)感兴趣。该程序中包含许多部分,这些部分在编译后会在磁盘上加密,并且必须在运行时解密。该软件包括一个内核模块,该模块执行运行时加密/解密。为了请求给定节的加密或解密,该程序附带了一个DLL,该DLL导出了一个函数,该函数将节的编号和一个布尔值指示为参数,该布尔值指示应对该节进行加密还是解密。所有组件都经过数字签名。
我采用了基于DLL注入的解决方案,其工作方式如下:
创建挂起的进程。
注入DLL。
DLL在程序的IAT中将GetProcAddress挂钩。来找出调用它的函数的起始地址(称为Func)。
然后,钩子函数为每个加密部分调用Func,指示其解密每个部分。为了使这项工作有效,钩子函数必须将对这些调用的调用传递给DLL中的适当函数。
这样做之后,对于钩子函数的每个后续调用,它仅返回1,就好像调用成功。
解密了所有部分后,DLL现在将进程的映像转储到磁盘上并重建导入信息。
之后,它还会进行其他处理,以中和其他保护。
br />
最初,我是为每个新构建手动进行所有这些操作。那太乏味了。我编写了DLL注入版本的代码,我再也不必进行大量的手动工作。
DLL注入在游戏黑客之外的逆向工程中并不广为人知或使用。这是非常不幸的,因为它是一种非常强大,灵活且简单的技术,应该成为每个人的曲目中的一部分。我已经使用了数十次,它似乎在我所有的动态项目中都扮演了角色。当我的任务变得繁琐而无法使用调试器脚本执行时,我切换到DLL注入。
在逆向工程技术领域,DLL注入的每种功能都由动态二进制工具(DBI)提供。工具,以及DBI仍然更加强大。但是,DBI并不是隐身的,并且会在内存消耗和性能方面引起严重的开销。在切换到DBI之前,我总是尝试使用DLL注入。
#2 楼
DLL注入的工作原理是欺骗/强制目标进程加载您选择的DLL。之后,该DLL中的代码将作为目标进程的一部分执行,并且能够执行进程本身可以执行的任何操作。有趣的部分是弄清楚如何使目标进程调用您的代码。可以通过以下方式注入DLL:
简单地将您的DLL替换为该过程通常使用的DLL-例如,如果您将DLL命名为
ddraw.dll
,那么很多游戏会很乐意加载它而不是真正的Direct Draw DLL。我已经看到这样做可以迫使游戏仅在软件仿真模式下使用Direct Draw,以便在特定的GPU上加速游戏。让加载程序从另一个文件夹加载已知的DLL-请参见The Old New Thing。
将一些过程代码替换为加载DLL的指令。
使用许多其他方式。
下一步是将DLL代码实际执行。但是,如果您想做有意义的事情,这将很困难-您需要知道流程的工作,流程使用的数据结构等,因此很可能需要将其分解。
您可以在目标进程中创建一个新线程,以从DLL调用函数。首先挂起现有线程,以保持理智,避免出现时髦的多线程错误。
如果用自己的DLL替换了已知的DLL,则该过程将期望您的DLL响应特定的函数调用-您最好知道这些函数是什么,并在DLL中提供它们的替换。可执行文件以调用DLL,除了已知的DLL,还必须将可执行文件拆开。现在去寻找一些有趣的地方,并在其中插入对DLL函数的调用。请参见代码洞穴。
我通过将目标进程作为被调试进程启动来执行DLL注入,并使用调用
LoadLibrary("mydll.dll"); GetProcAddress(myLib, "myFunc");
的自定义代码序列覆盖其启动代码中的某些字节,并重写可执行文件中的某些代码以跳转至DLL中的函数。 使用此方法,我的一些朋友和我为Command&Conquer:Red Alert 2写了一个相当大的非官方错误修复/增强DLL:如今,DLL的大小约为原始游戏可执行文件的15%。结果,后来对该游戏的正式更新仅限于他们的工作人员无需重新编译二进制文件就可以做的事情,这在EA上通常是不错的。
评论
除了注入代码,您还可以将DLL添加到导入表中(静态或动态)。
–伊戈尔·斯科钦斯基♦
13年6月14日在17:22
执行LoadLibrary()的行为会使加载的DLL中的代码开始运行。您不需要任何其他操作,因为DLLMain可以自己执行所有必要的修改。
–彼得·弗里
2013年6月14日18:02
您可能应该提到Detours :)
–丹尼尔·斯洛夫(Daniel Sloof)
13年6月15日上午10:30
评论
DLL注入在游戏黑客之外的逆向工程中尚未广为人知或使用。 +1它通常被视为一种攻击技术,即使我们如您所展示的那样可以充分利用它!
– dna
13年6月14日在21:27
真是一个了不起的答案。您可能已经改变了我永远使用REing复杂应用程序的方式。
–乔纳森·莱因哈特(Jonathon Reinhart)
2013年6月15日8:46
+1-您提到了我跳过的许多细节,这个答案可能值得接受,而不是我的。
– DCoder
13年6月15日在8:54
+1这是我在任何stackexchange网站上看到的最好的答案之一。
–亚当·卡夫尼斯(Adam Caviness)
2014年6月6日14:20
扑克机器人程序链接似乎已断开。
–Rowan Freeman
2014年3月25日0:31在