layout: post
title: 多种DLL注入技术原理
description: 多种DLL注入技术原理
categories:

  • 逆向编程

    多种DLL注入技术原理

  DLL注入技术可以被正常软件用来添加/扩展其他程序,调试或逆向工程的功能性;该技术也常被恶意软件以多种方式利用。这意味着从安全角度来说,了解DLL注入的工作原理是十分必要的。

  利用DLL注入实施的攻击行为的若干示例: https://attack.mitre.org/wiki/Technique/T1055。

  文章以一种便于阅读和理解的方式,将多种能在32位和64位环境下生效的DLL注入技术(事实上一共7种不同的技术),整合到了一个单独的Visual Studio工程之中。有些朋友对这些代码感兴趣,所以它也可能会吸引你。为了区分,每种技术有其独有的源文件。

  可以利用签名认证的微软二进制文件来加载DLL,但你无法附加到一个特定的进程来干预其内存内容。为什么大部分渗透测试师实际上不知道DLL注入是什么,或者它是如何工作的?因为Metasploit平台替他们包办的太多了;他们一直盲目地使用它。学习这种“奇特的”内存操作技术的最好地点,实际上是游戏黑客论坛。如果你正在进行攻击方测试,那么你就必须干这些“脏”活儿,同时研究这些技术;除非你乐意仅仅使用别人随意编写的工具。

  大部分时间,我们使用很复杂的技术开始一次攻击方测试;如果我们未被发现,则开始降低复杂度。基本上这就是我们开始向磁盘投放二进制文件和应用DLL注入技术的时间点。

  本文试图以一种简单而高阶的方式纵览DLL注入技术,同时为GitHub中的项目(网址为:https://github.com/fdiskyou/injectAllTheThings)提供“文档”支持。

简介

  DLL注入技术,一般来讲是向一个正在运行的进程插入/注入代码的过程。我们注入的代码以动态链接库(DLL)的形式存在。DLL文件在运行时将按需加载(类似于UNIX系统中的共享库(share object,扩展名为.so))。本工程中,我将仅使用DLL文件,然而实际上,我们可以以其他的多种形式“注入“代码(正如恶意软件中所常见的,任意PE文件,shellcode代码/程序集等)。

  同时要记住,你需要合适的权限级别来操控其他进程的内存空间。但我不会在此讨论保护进程(相关网址:https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=2)和权限级别(通过Vista系统介绍,相关网址:https://msdn.microsoft.com/en-gb/library/windows/desktop/bb648648(v=vs.85).aspx);这属于完全不同的另一个主题

  再次强调一下,正如我之前所说,DLL注入技术可以被用于合法正当的用途。比如,反病毒软件和端点安全解决方案使用这些技术来将其软件的代码嵌入/拦截系统中“所有”正在运行的进程,这使得它们可以在其运行过程中监控每一个进程,从而更好地保护我们。同样存在恶意的用途。一种经常被用到的通用技术是注入“lsass”进程来获取口令哈希值。我们之前都这么干过。很明显,恶意代码同样广泛应用了代码注入技术:不管是运行shellcode代码,运行PE文件,还是在另一个进程的内存空间中加载DLL文件以隐藏自身,等等。

基础知识

  对于每一种技术,我们都将用到微软Windows API,因为它为我们提供了大量的函数来附加和操纵其他进程。从微软Windows操作系统的第一个版本开始,DLL文件就是其基石。事实上,微软Windows API中的所有函数都包含于DLL文件之中。其中,最重要的是“Kernel32.dll”(包含管理内存,进程和线程相关的函数),“User32.dll”(大部分是用户接口函数),和“GDI32.dll”(绘制图形和显示文本相关的函数)。

  你可能会有疑问,为什么会有这些API接口,为什么微软为我们提供如此丰富的函数集来操纵和修改其他进程的内存空间?主要原因是为了扩展应用程序的功能。比如,一个公司创建了一款应用程序,并且允许其他公司来扩展或增强这个应用程序;如此,这就有了一个合法正当的用途。除此之外,DLL文件还用于项目管理,内存保护,资源共享,等等

DLL注入的四个步骤

  1. 附加到目标/远程进程
  2. 在目标/远程进程内分配内存
  3. 将DLL文件路径,或者DLL文件,复制到目标/远程进程的内存空间
  4. 控制进程运行DLL文件

所有这些步骤是通过调用一系列指定的API函数来完成的。每种技术需要进行特定的设置和选项配置。我认为,每种技术都有其优点和缺点

1.技术介绍

  我们有多种方式可以控制进程运行我们的DLL文件。最普通的应该是“CreateRemoteThread()”和“NtCreateThreadEx()”函数;然而,不可能仅仅向这些函数传递一个DLL文件作为参数,我们必须提供一个包含执行起点的内存地址。为此,我们需要分配内存,使用“LoadLibrary()”加载我们的DLL文件,复制内存,等等

  我称之为“injectAllTheThings”的工程(因为我只是单纯讨厌“注入器”这个名字,加上GitHub上已经有太多的垃圾“注入器”,而我不想再多一个了)包含7种不同的技术;我并不是其中任何一种技术的原创作者,而是提炼总结了这七种技术(是的,还有更多)。其中某些已经有很多文档资料描述(像“CreateRemoteThread()”),而另一些则属于未公开API函数(像“NtCreateThreadEx()”)。以下是所实现的技术的完整列表,其中每种都可以在32位和64位环境下生效。

  • CreateRemoteThread()
  • NtCreateThreadEx()
  • QueueUserAPC
  • SetWindowsHookEx()
  • RtlCreateUserThread()
  • 利用SetThreadContext()找到的代码区域
  • 反射DLL

  你可能通过其他的名字了解其中某些技术。以上并不是包含每一种DLL注入技术的完整列表;正如我所说的,还有更多技术,如果之后我在某个工程中需要对其接触学习的话我会将它们添加进来。到目前为止,这就是我在某些工程中所用到的技术列表;其中某些可以稳定利用,某些不可以。需要注意的是,不能够稳定利用的那些技术可能是由于我所编写代码的自身问题

2.LoadLibrary()

正如MSDN中所述,“LoadLibrary()”函数“被用于向调用进程的地址空间加载指定模块,而该指定模块可能导致其他模块被加载”。函数原型与参数说明如下所示

HMODULE WINAPI LoadLibrary(
_IN_ LPCRSTR lpFileName
);
//lpFileName    [输入参数]
//模块名称。该模块可能是一个库模块(.dll文件),或者一个可执行模块(.exe文件)
//(…)
//若字符串指定了一个完全路径,则函数只在该路径下搜索模块;
//若字符串指定了一个相对路径或者无路径的模块名称,则函数使用标准搜索策略来查找模块。
//(…)
//若函数无法找到模块,则函数执行失败。当指定路径时,必须使用反斜线(\)而不是斜线(/)。
//(…)
//如果字符串指定了一个无路径的模块名称并且无文件名后缀,则函数默认在模块名称后面添加库文件后缀.dll。

  换言之,该函数只需要一个文件名作为其唯一的参数。即,我们只需要为我们的DLL文件路径分配内存,将执行起点设置为“LoadLibrary()”函数的地址,之后将路径的内存地址传递给函数作为参数。

正如你所知道(或不知道)的,最大的问题是“LoadLibrary()”会向程序注册已加载的DLL模块;这意味着这种方法很容易被检测到,但令人惊奇的是很多端点安全解决方案仍检测不出。不管怎样,正如我之前所说,DLL注入也有一些合法正当的用途,因此我们还要注意的是,如果一个DLL文件已经用“LoadLibrary()”加载过了,则它不会再次执行。你可以试验一下,但我没有对任何一种技术试过。当然,使用反射DLL注入技术不会有这方面的问题,因为DLL模块并未被注册。不同于使用“LoadLibrary()”,反射DLL注入技术将整个DLL文件载入内存,然后通过确定DLL模块的入口点偏移来将其加载;这样可以按照需求更隐蔽的对其进行调用。取证人员仍然能够在内存中找到你的DLL,但会很艰难。Metasploit平台大量使用了这项技术,而且大部分端点解决方案也还乐意始终使用它。如果你想查找这方面的技术资料,或者你在攻防游戏中处于防守方,可以参阅以下网址:

  https://www.defcon.org/html/defcon-20/dc-20-speakers.html#King

  https://github.com/aking1012/dc20

  附注一下,如果你正在折腾你的端点安全软件,而它很好地利用了以上所有这些技术,你可能需要使用以下攻防反欺骗引擎来试试(注意,我只是尝试轻松的说法,以便你能理解)。某些反欺骗工具的反Rookit性能要比某些反病毒软件要先进得多。reddit网站上有一本书你肯定读过,叫“黑客游戏”,它的作者Nick Cano对其有非常深入的研究;只需了解一下他的工作,你就会理解我所谈论的内容