吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 13152|回复: 65
上一主题 下一主题
收起左侧

[分享] 恶意软件分析系列 第一篇

  [复制链接]
跳转到指定楼层
楼主
BeneficialWeb 发表于 2022-2-9 17:02 回帖奖励
使用论坛附件上传样本压缩包时必须使用压缩密码保护,压缩密码:52pojie,否则会导致论坛被杀毒软件等误报,论坛有权随时删除相关附件和帖子!
病毒分析分区附件样本、网址谨慎下载点击,可能对计算机产生破坏,仅供安全人员在法律允许范围内研究,禁止非法用途!
禁止求非法渗透测试、非法网络攻击、获取隐私等违法内容,即使对方是非法内容,也应向警方求助!
本帖最后由 BeneficialWeb 于 2022-2-9 17:10 编辑

恶意软件分析系列(MAS) :文章一

1. 简介

​        欢迎阅读MAS系列文章。在过去的四年里,为了写一篇文章而停止我的研究工作是相当困难的事情,更别说写一系列的文章,但是现在不一样了,我想试试看。还记得上一次我写了一篇浅显的文章都是在2017年去了,当然,我甚至都不记得了,直到最近有同时提及它。

​        我们的宗旨是写一系列的恶意软件分析文章,并逆向分析由易到难的二进制文件,其中涵盖了大量的主题,比如说脱壳,API解析,C2 提取,C2 仿真,逆向工程,此外还涉及动态分析,可能还会使用少量的去混淆技术。必要时,还会涉及其他的话题,比如组件对象模型(COM),密码学,IDC/IDA Python脚本,以帮助读者更好的理解和分析。

​        此外,我还会写一些短文章。涉及的主题包括恶意文件,编程技术,去混淆,操作系统机制等等。不过,主要的焦点还是MAS。

​        由于恶意软件分析会产生大量的分析笔记,必要时,我会把他切分成第一部分,第二部分等等。

​        每一篇文章都会使用不同的字体发布在我的新博客上,在每篇文章的开头,都会有该文章的pdf版本。

​        在这一系列文章中,我将会使用几个工具,并尝试指出你可以在哪里获得他,以简化分析。

​        我不打算只提复杂的样本,因为在我看来,这种样本对于大部分人来说没有太多意义,这类样本主要是针对那些学习特定东西的专业人士,讲解这种样本就是在浪费时间,是一种无用的技术炫耀。因此,我将分析不同的样本,每个样本都有着不同的难度,讨论一些代码片段。前面提到过,我有可能会把一篇文章分成几个部分,减轻读者的阅读负担。

2. 实验环境搭建

​        我将使用以下一个或者多个系统分析所有的样本。

  1. Windows 7,Windows 8.1 或者 Windows 10: 如果你需要一个Windows 10虚拟机,微软在这个网站上会持续提供一个有到期时间的虚拟机:https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/

  2. REMnux(专为逆向工程而生的发行版Linux虚拟机):https://docs.remnux.org/install-distro/get-virtual-appliance

  3. Ubuntu 20.04.x: https://ubuntu.com/download/desktop

    我会避免使用无意义的技术,并使用一些知名工具。不幸的事,其中有些工具是收费的。比如说IDA Pro,他是我最喜欢的工具,目前看来,他是世界上最棒的逆向工具。Hex-Ray 也提供了IDA Free和 IDA Home版本,价格还算合理。

    IDA Free: https://hex-rays.com/blog/announcing-version-7-6-for-ida-freeware/

    IDA Home: https://hex-rays.com/ida-pro/#main-differences-between-ida-editions]

​        毫无疑问,如果你更喜欢Ghidra的话,你也可以使用它进行反汇编,反编译和调试任何代码。https://github.com/NationalSecurityAgency/ghidra/releases

​        你还需要一个好的调试器,当然最好的是x64dbg/x32dbg,你可以从这里下载到:

​                x64dbg: https://x64dbg.com/#start

​        此外,还有大量的x64dbg插件可以安装,以扩展x64dbg/x32dbg的功能,这在动态分析过程中非常值得推荐,主要是为了避免恶意软件使用的反调试技术。你可以安装以下的几个插件:

​        SycllaHide: 这是一个高级的反反调试库,它hook了一系列的函数来隐藏调试活动。https://github.com/x64dbg/ScyllaHide/releases

​        Labeless: 这是一个相当值得推荐的插件,他为逆向人员提供了两个主要功能:

  1. 标签,函数名,全局变量和注释在x64dbg和IDA Pro之间的同步
  2. 动态dump被调试进程的内存区域,这是非常有用的,比方说在一个二进制完成字符串解码或者API解析后。
  3. Labeless 插件可以在这里下载到:https://github.com/a1ext/labeless/releases

​        DbgChild: 他提供了一个自动检测被调试进程创建的子进程的功能,如果主进程创建了子进程,那么它会自动附加到一个新的x64dbg实例上。很多时候,这可以为你节省时间。

​        当然还有其它有用的插件存在,等我们在适当的时机在讨论它们。时间上:有些有用的插件并没有被作者和维护者进行定期维护,所以他们实现的功能不能总是随时有效。请一定要注意!

​        剩下的其他工具将在我们的分析和未来的许多文章中展示。

​        最后,文章不可避免会存在错误,这些错误修复后,我会发布修正后的pdf版本。

3. 恶意软件分析的目的

​        毫无疑问,这是个有趣的问题:我们在二进制分析中要寻找什么?

​        具体问题具体分析,因为在恶意软件分析调研中,有许多可能的目标和方面需要考虑。然而在现实世界的分析中,总的来说,有以下几点需要时刻考虑到:

​        a. 内存分析:这是一种非常强大的技术,在过去10年中已经证明了其无限的价值,并在分析过程中作为首要方法,以了解恶意软件感染事件、其后果、副作用,采用这种方法还有可能获得大量可能难以从磁盘或任何其他来源收集的证据。

​        b. 网络分析:pcap等文件是相当有用的资源,通过对二进制文件,恶意文件,Cobalt Strike beacons等的流量分析和收集工作来理解和检测非授权通信(C2-命令和控制通道)。

​        c. 文件系统/磁盘分析:最后的调查项,我们可以分析和检测入侵的副作用,漏洞,欺诈,泄露,当然还有恶意感染。

所有的这些在现实世界的分析中都非常重要。然后,让我们回到关键点:为什么你应该学习逆向工程,特别是学习恶意软件分析?很简单,通过恶意软件分析,你有机会从邪恶的源头了解入侵者的意图和目标,而不仅仅是效果。换句话说,你可以学习技术,技巧,规避策略,如果幸运的话,你可以收集重要的样本来进行正确的溯源。大多数时候,这是一项艰巨的任务。谁知道呢,也许能帮着逮捕坏人。

因此,在开始任何逆向任务之前,我们应该记住,有许多问题应该考虑:

  1. 二进制加壳了吗?如果加壳了,是使用的商业壳还是自写壳。
  2. 恶意软件使用的什么网络通信技术/API集合?从现有技术,如winsock2,wininet,com或者更低层次的东西,如WSK(WinSock 内核),甚至是自定义实现的技术中,找出哪一个正在被使用?
  3. 是否存在代码注入或者是否使用hook技术?还是什么?
  4. 是否使用了反取证技术?是否存在反调试技术?反反汇编?反虚拟机?反沙箱?
  5. 是否存在API/DLL编码?
  6. 字符串是否加密?
  7. 恶意软件使用了什么同步机制?这里有时可能潜藏了重要的反调试技术。
  8. 恶意软件使用了什么密码学算法?
  9. 该威胁软件使用了什么持久化方法?注册表,服务,任务,还是内核驱动?
  10. 是否存在注入操作系统进程的shellcode?
  11. 恶意软件是否安装了文件系统微过滤驱动?
  12. 是否有内核驱动被安装?是否存在回调(一种现代化的hook手法)?或者安装了定时器?
    第一篇文章中,我们只关注两点:
    ​        a. 脱壳
    ​        b. 提取和解密他的C2配置数据

我们将回顾一些著名的脱壳技术,以及提取C2配置数据的不同方法。此外,我将提供一些相关话题的最低背景,以帮助读者能够继续进一步的研究所提及的东西。

4.收集初步信息

第一个样本的hash是:

(SHA256) 8ff43b6ddf6243bd5ee073f9987920fa223809f589d151d7e438fd8cc08ce292

我们能够从许多Malware Bazaar等许多终端收集一些样本信息,如下图所示。

通过上图,我们知道了一些重要信息:

  1. 目标恶意软件看上去似乎来自 Hancitor 家族
  2. 他使用了 EnumerateProcess() 函数,因此,我们需要了解这是否有任何特殊的原因,例如为了完成代码注入。
  3. WriteProcessMemory() 被触发,就像我们通常在脱壳代码和代码注入中看到的那样,这里没有消息就是最好的消息。

进一步展开数据收集,我们可以在Triage上检查样本。

还有其他与这个样本有关的任务ID,但我们先只关注第一个。关于第一个任务ID的细节可以通过执行以下命令来显示(由于输出比较多,有些输出被截断了)

当然,我们可以获得关于样本的额外信息,但现在已经足够了,因为我们已经有了可能的C2 URL。

下一步,我们要找出这个样本是否像大多数恶意威胁软件一样加壳了。

进一步的,如果加壳了,我们要学习如何使用调试器,比如使用x64dbg进行脱壳处理。

让我们回顾一下关于在Windows系统上恶意软件脱壳相关的几个概念

5. 脱壳概念

每一次我听到有人谈论脱壳,似乎印象中都会转换为同样的结论:这可没那么容易。当然,正如我之前提到的,在可能的字符串解码和API/DLL 解析等之前,样本脱壳很可能是第一个,但我们需要从某个地方开始,有一个目标。

关于恶意代码加壳的动机,有一长串的相关的原因。

  1. 规避杀软检测。虽然不是非常隐蔽,但是会是分析人员的逆向工作更加困难,而且也会给防御系统带来一些问题。
  2. 加壳的样本并没有显示出实际的恶意行为。
  3. 由于许多反分析技术,如反调试和反虚拟机,动态脱壳可能会变得非常困难。
  4. 恶意软件通常会使用定制的例程将有意义的代码分层加壳。
  5. 最终,整个恶意软件或者只有壳代码可能是多态的。

有很多旧的出名加壳器拥有对应的脱壳机。但大多数恶意软件作者基本已经使用了定制的加壳器使其代码在安全防御监控下无法被检测。此外还有一些特殊的壳,被称为保护壳。如Themida,Arxan,VMProtect,Agile.NET和其他的壳。他们通常用来将指令虚拟化,并采用各种反取证和混淆技术。下面介绍其特点:

  1. 通常被用于64bit的程序
  2. IAT被删除,或者最多只有一个导入函数。
  3. 通常字符串会被加密
  4. 内存的完整性被检查和保护,所以不可能从内存中转储一个干净的可执行文件,因为原始指令还没有被完全解码。
  5. 指令被虚拟化,转化为等价的精简指令。
  6. 这些虚拟指令在内存中被加密
  7. 混淆是基于栈的,所以很难使用静态方法来处理虚拟化代码。
  8. 虚拟化代码大多数是多态的,很多虚拟指令代表的都是同一个原始指令。
  9. 有几千行假的push指令,当然,其中有许多都是垃圾代码或者无用代码,不会被执行的代码。
  10. 保护器使用无条件跳转实现了代码乱序。
  11. 所有的这些现代化强壳使用了代码平坦化技术,以及许多的反调试和反虚拟机技术。
  12. 不是所有的x64指令都被虚拟化了,所以你会发现二进制代码混合了虚拟化指令和非虚拟化的指令。
  13. 大多数时候,函数序言和结尾都没有被虚拟化。
  14. 原始代码部分可以被分割或者分散在程序中,所以指令和数据是混合的。
  15. 引用导入函数的指令被清零甚至被替换为nop指令,因此这些引用将会被动态还原。有时这些引用不会被清零,但是会被替换会跳转指令。使用RVA来跳转到同样的导入地址,这种技术被称为IAT混淆。
  16. 由于恶意软件通常使用shellcodes,API的名字会被hash化。
  17. 从原生寄存器到虚拟寄存器的转换通常是一对一的,但也不一定。此外,上下文切换组件负责将寄存器和标志信息转移到虚拟机上下文中。
  18. 虚拟机handlers来自数据块。
  19. 许多原生API被重定向到桩代码(转发调用)
  20. 混淆技术,例如常量展开,基于模式的混淆,间接控制流,内联函数,代码复用和不透明谓词等。
    在开始脱壳之前或者期间,我们要审视以下几点:
  21. 真的加壳了吗?
  22. 什么证明了确实存在壳代码?
  23. 恶意软件是否执行了自我注入或者远程注入?
  24. 恶意软件是否执行了自我改写?
  25. payload被写在哪里了?
  26. payload是怎么执行起来的?
  27. 在一个脱壳函数后,有什么证据证明有一个脱壳相关的代码?
  28. 还有额外的加壳层吗?
    上述列表中的第一点引出了一个关键问题:我们如何知道一个恶意软件是否真的加壳了?

这个问题没有一个简单而明确的答案,但是可以通过一个或者多个证据表明样本确实有壳。

  1. 二进制样本有少量的导入函数和DLL依赖
  2. 存在许多的混淆字符串
  3. 存在特别的系统调用
  4. 非标准的节区名字
  5. 不常见的可执行节区,.text .code节区应该是可执行的
  6. 非期望的可写节区
  7. 熵值高的节区,通常在7.0以上,但不一定,这是个弱指标
  8. 一个节区的raw size和virtual size之间有很大差异
  9. 大小为0的节区
  10. 缺少网络通信相关的API
  11. 缺少恶意软件功能的基本API,例如,勒索软件中的Crypt*函数。
  12. 不常见的文件格式和头。
  13. 入口点指向其他节区,而不是.text/.code 节区
  14. LoadResource和资源节区.rsrc的大小
  15. 存在覆盖
  16. IDA Pro中观察到大的数据或者未探索的代码颜色条
    只是出现上面的一点是不足以判定是否加壳的。还需要更多的观察和判定:
  17. 大多数的样本使用LoadLibrary()GetProcAddress()动态获取API地址(除了反射式代码注入)
  18. 网络相关的APIs也可以被动态解析
  19. 畸形的头文件可能在首次分析时难以被检测
  20. 大的资源节区可能不意味着是恶意软件,它可能是GUI相关的控件和数字证书。
  21. 可能存在加密混淆的字符串和明文字符串并存的情况,这也不太好作为样本被加壳的特征
    使用调试器脱壳可能会带来一系列需要理解和绕过的挑战:
  22. 反调试技术(时间检测,CPUID,堆检查,调试标志位检查,NtSetInformationThread()等),因此推荐使用反反调试插件,x64dbg/x32dbg有ScyllaHide,OllyDbg有StrongOD。这些都可以用Google找到。
  23. 反虚拟机技巧,例如检测VMware,VirtualBox,Hyper-V和Qemu。
  24. 文件名,主机名和账户检测(避免使用hash作为文件名)
  25. 虚拟机上可获得的磁盘大小(推荐至少是100GB)
  26. 测试虚拟机上的cpu核心数(2个以上比较合适)
  27. 正常运行时间(试着保持虚拟机快照的正常运行时间在20分钟以上)
  28. 许多无意义的调用(返回值不被使用),或者不存在的APIs(虚假API)
  29. 异常处理被作为反调试手段
  30. 软件断点被清除,DR寄存器被占用操控
  31. hash函数使用经典的算法,比如crc32,conti,add_ror13,...
  32. 恶意代码检测其他安全工具,例如Process Hacker,Process Explorer,Process Monitor等。
    不幸的是,反虚拟机和反调试不能总是被插件处理掉,我们不得不设法使用调试器手动绕过。这种情况下,存在一种绕过的可能性,即使用其他的调试器如Windbg进行ring3调试。最近的例子是一个叫GuLoader的恶意软件。

在脱壳过程中,或者脱壳后,我们可能需要修复二进制文件。原因如下:

  1. DOS/PE头在内存中被破坏或者被压缩库给修改了
  2. 许多情况下,当你从内存中提取出二进制后,你需要清理清理,因为在DOS头(MZ signature)和PE头之前存在一些垃圾。
  3. 入口点被抹除或者是错误的
  4. 脱壳的程序由于转储破坏了导入表,因为这些东西是和虚拟地址相关的,以及节区的对齐问题。
  5. 基地址是错误的
  6. PE格式的一些字段和之前呈现出不一致
  7. 很难确定OEP,壳代码运行之后,他通常出现在使用间接调用的地方,例如call [eax]jmp [eax]。此外,为解析API可能是恶意代码还没达到OEP的线索之一。OEP就是程序在加壳之前原来的入口点。在加壳后,一个新的入口点被用于先执行壳代码,后跳到OEP。
  8. 互斥体作为两层壳之间的一种解锁钥匙。这种情况下,如果第一阶段没有发生,那么第二阶段就不会发生。这种壳使用了互斥体进行同步的技术。
  9. 代码可能会执行自修改
  10. 第一阶段的壳代码只会从特定目录运行。
  11. 你可能提取到的是一个诱饵文件。在许多真实案例中,恶意软件作者会将一个或者多个无用的可执行文件作为诱饵,以消耗分析人员的时间。因此,明智的做法是不要第一次尝试时就相信自己已经提取到了想要的二进制文件。

    这个问题清单不足以涵盖所有情况,在脱壳后的二进制文件上还有无尽的其他问题。当然,对于这些问题,都有着不同的解决方案,他们将在下一篇文章中进行解释并给出例子。可以先了解一些问题的解决方案:

  12. 从其他可执行文件或者恶意软件样本自身拷贝一个完好的PE头,对齐节区。观察脱壳后的节区是否完成了映射,未映射时.text节区通常0x400开头,映射时.text以0x1000开头
  13. 修正节区的Raw Address和 Raw 大小。这个完成以后通常能修复导入表,并使导入函数变得可视化。请注意可能存在的陷阱:一些脱壳后的程序即使在修正节区后,也看不到导入表。这并不意味着你修正错了,他有可能是动态解析获取的API。
  14. 重建导入表和恢复OEP
  15. 如果你在寻找OEP方面遇到问题,请记住OEP很可能是在IAT被解析后才出现的。在这种情况下,可能的方法之一是检查IAT是否已经被解析完成(检查x64dbg或OllyDbg上的模块内的调用)或在恶意软件的关键API上设置断点(例如勒索软件威胁中的CryptoAcquireContext( ) ),因为当执行到这些关键API时,IAT肯定已经被解析完成了。之后,建议寻找无条件跳转到特定的内存地址的指令,甚至间接调用指令(例如,call [eax])。另外一个有趣的方法是使用调试器的图形可视化(x64dbg上的 "g"),并在最后的 "代码块 "检查这些过渡点(间接调用或内存地址的无条件跳转)。最后,一个专门的工具可能会帮助你找出OEP。正如你所注意到的,并没有一个单一的方法来做这件事。
  16. 修正基地址,使其和内存中dump出来的段基地址匹配
  17. 为了检测是否存在代码子修改技术,我们可以在.text、.code节区下断点。这样在代码执行或者写入时就会触发断点。
  18. 在存在两阶段脱壳的情况下,第一个脱壳的二进制文件可能是一个DLL。因此,根据上下文,将DLL文件转换为EXE文件可能是有用的,有很多方法可以完成这项任务,但我最喜欢的方法是编辑PE头,改变Characteristics字段,使其导出函数成为入口点。
    在脱壳二进制文件后,为了可视化、处理和修复大多数问题,你可以使用以下众所周知的工具:
  19. PEBear: 一款优秀的PE修复工具。作者是Aleksandra Doniec。下载链接地址:https://github.com/hasherezade/pe-bear-releases
  20. Pestudio:主要用于分流和收集恶意软件的潜在信息。https://www.winitor.com/features
  21. CFF Explorer,Explorer工具包的一部分,非常有名的PE编辑器,用于可视化和修复PE头。下载地址:https://ntcore.com/?page_id=388
  22. pe_unmapper 是另外一款由Aleksandra Doniec编写的工具。用于将PE二进制从映射版本转换为非映射版本,修正一切PE对齐问题。下载链接:https://github.com/hasherezade/libpeconv/tree/master/pe_unmapper
  23. Scylla 是一款x86/x64导入表重建工具,他已经被嵌入到x64dbg中。如果你需要独立版本,可以从这里下载:https://github.com/NtQuery/Scylla
  24. Hxd 是一款优秀的十六进制编辑器,用来手动检查和修复PE头。下载链接:https://mh-nexus.de/en/hxd/
  25. XVI32 Hex Editor 是另外一款十六进制编辑器,他可以很好地清理dump出来的内存区域,限制脱壳后的程序大小。下载链接:http://www.chmaas.handshake.de/delphi/freeware/xvi32/xvi32.htm
    请记住,脱壳只是恶意软件分析中的第一个障碍,还有其他困难的挑战,如字符串去混淆,API解析,C2配置提取,C2仿真以及其他障碍,这些都会在后续文章中逐渐涉及。

现在我们对脱壳过程中存在的问题,解决方案等有了最基本的了解,是时候讲解一下不同的代码注入技术了,这可以帮助你更好的理解脱壳。

6.代码注入

代码注入是Window系统支持的操作,当然,这是一个相当有用的规避方法,因为恶意软件能够将恶意代码注入(写)到进程本身(自我注入)或远程(远程注入)的内存区域(有些人使用术语 "段"),并且这个payload将在目标环境中执行,成为他的一部分,不会留下许多证据。此外,源进程(恶意软件)可以干净地终止自己,而恶意的payload继续在一个所谓的健康进程中运行(例如,explorer.exe和svchost.exe)。这是一种绕过安全防御的隐蔽方法。

有意思的是,从Windows 8.1开始,主要是Windows 10和11, 就有一系列的缓解和保护措施,如代码完整性防护、扩展点禁用策略、控制流防护、代码完整性防护、动态代码限制和任意代码防护(动态代码限制的一种升级),在这些Windows版本上进行代码注入而不被发现和阻止,并不是那么容易。关于这些缓解和保护的进一步信息,可以在以下网站上阅读:https://docs.microsoft.com/en-us/windows/security/threat-protection/overview-of-threat-mitigations-in-windows-10 , https://docs.microsoft.com/en-us/microsoft-365/security/defender-endpoint/customize-exploit-protectionhttps://techcommunity.microsoft.com/t5/core-infrastructure-and-security/windows-10-memory-protection-features/ba-p/259046

有一些很好的公开文档解释了几种代码注入技术,但总结起来,主要的代码注入方式有以下几种:

  1. DLL 注入:很老的技术了,强制使进程加载一个DLL。可能涉及的主要API:OpenProcess()VirtualAllocEx()WriteProcessMemory()CreateRemoteThread() | NtCreateThread() | RtlCreateUserThread()
  2. PE 注入:在这种技术中,恶意代码会被写入远程进程或者自身进程(自我注入)。涉及的API: OpenThread(),SuspendThread(),VirtualAllocEx(),WriteProcessMemory(),SetThreadContext(),ResumeThread() | NtResumeThread()
  3. 反射式注入: 这种技术和PE注入类似,但是这种方式避免了使用LoadLibrary()CreateRemoteThread()。这个方法有很多有趣的派生。其中一种在Cobalt Strike上也有使用,他首先由以下API完成映射操作,CreateFileMapping(),Nt/MapViewOfFile(),OpenProcess(),memcpy()和Nt/MapViewOfSection()。最后,通过OpenProcess(),CreateThread(),NtQueueApcThread(),CreateRemoteThread(),或者RtlCreateUserThread()去执行远程进程的代码。当然也会存在一些变体,使用VirtualQueryEx()和ReadProcessMemory()
  4. APC注入:这种代码注入技术允许一个程序通过附加到一个APC队列来在一个特定的线程中执行代码。当线程因为调用SleepEx(),SignalObjactAndWait(),MsgWaitForMultipleObjectsEx(),WaitForMultiObjectsEx()或者 WaitForSingleObjectEx()后退出警戒状态后就会执行被注入的代码。因此使用这种技术,常常会看见CreateToolhelp32Snapshot(),Process32First(),Process32Next(),Thread32First(),Thread32Next(),QueueUserAPC()和KeInitializeAPC()这些API。
  5. 僵尸或者进程替换:简而言之,这种技术被恶意软件用来借尸还魂。抽空一个进程的全部内容,插入自己的恶意内容。
  6. AtomBombing: 这种技术是前一种技术(APC注入)的变种,其工作原理是将恶意的payload分割成独立的字符串,为每个给定的字符串创建一个Atom,将它们复制到RW段(使用GlobalGetAtomName()NtQueueApcThread()),并通过使用NtSetContextThread()设置上下文。因此,这类技术进一步的API清单是OpenThread(),GlobalAddAtom(),GlobalGetAtomName() 和 QueueUserAPC()
  7. Process Doppelganging:这种技术可以作为Process Hollowing的一种演变来处理。这两种技术的关键区别在于,Hollowing是在进程恢复之前替换进程的内容(图像),而Process Doppelganging则是在进程创建之前就替换图像,即在加载目标镜像之前用一个恶意的镜像覆盖。这里的关键概念是,NTFS操作是在事务中进行的,因此,在一个事务中的所有这些操作要么一起提交,要么都不提交。同时,恶意镜像只存在于事务中,而且在事务中是可见的,对其他进程是不可见的。因此,恶意镜像被加载到内存中,恶意软件从文件系统中删除恶意的有效载荷(通过回滚事务),因为该文件之前从未存在过。这种技术涉及的APIs:CreateTransaction(), CreateFileTransaction(), NtCreateSection(), NtCreateProcessEx(), NtQueryInformationProcess(), NtCreateThreadEx() and RollbackTransaction().
  8. Process Herpaderping:这种技术类似于Process Doppelgänging,但在过程上有一个微妙的区别。Process Herpaderping是基于这样一个事实:安全防御系统通常通过使用PsSetCreateProcessNotifyRoutineEx( )在内核侧注册一个回调例程或在驱动程序的DispatchCleanup例程(IRP_MJ_CLEANUP)中监测进程的创建,这种例程在线程被创建之后被调用。这就是关键问题:如果对抗者创建并映射了一个进程,之后再修改文件镜像,然后创建线程,那么安全软件就能够检测到这样一个恶意的payload。尽管如此,对抗者却能够在磁盘上创建恶意二进制文件,打开一个文件句柄,使用NtCreateSection函数(需包括SEC_IMAGE标志)将其映射为一个镜像section,使用该section句柄创建一个进程(NtCreateProcesEx()),然后修改文件内容,使其看起来不像是恶意的,并使用这个 "好镜像 "创建一个线程(NtCreateThreadEx())。重点是:当线程被创建时,进程回调被触发,磁盘上的文件(好的)内容被检查,所以安全防御系统认为一切都很好,因为磁盘上的镜像是无害的,但真正的恶意内容存在于内存中。换句话说,安全防御系统不能有效地检测到磁盘上的镜像与内存上的镜像不同。这种技术使用了一些API。CreateFile(), NtCreateSection(), NtCreateProcessEx()和NtCreateThreadEx()
  9. Hook注入: 这种技术通常使用一些拥有hook活动的函数,比如说SetWindowsHookEx()和PostThreadMessage()来注入恶意DLL。
  10. Extra Windows Memory 注入: 这种技术在注册窗口类时使用额外的窗口内存将代码注入进程,其内存大小最多为40字节。秘密在于这个额外字节能存储一个指针,而这个指针可能会指向恶意代码。涉及的API有FindWindowsA(), GetWindowThreadProcessId(), OpenProcess(), VirtualAllocEx(), WriteProcessMemory(), SetWindowLongPtrA() and SendNotify()
  11. Propagate 注入: 这种技术已经被RIG Exploit Kit和Smoke Loader等恶意软件威胁用来向explorer.exe进程(中等完整性级别)和其他持久性的注入恶意代码,它基于枚举(EnumWindows( ) → EnumWindowsProc → EnumChildWindows( ) → EnumChildWindowsProc → EnumProps( ) → EnumPropsProc → GetProp)窗口实现SetWindowsSubclass( ) 的方法(这个进一步信息https://docs.microsoft.com/en-us/windows/win32/api/commctrl/nf-commctrl-setwindowsubclass)。这个函数安装了一个窗口子类的回调,回调在安全领域被解释为hook方法。它是如何工作的?一旦发现子类窗口(检查UxSubclassInfo、CC32SubclassInfo,它们提供了子类头),就可以保留旧的窗口程序,但我们也可以通过更新CallArray字段给窗口分配一个新的程序。当向目标进程发送事件时,新的程序被调用,之后,旧的程序也被调用(保持以前和预期的行为)。因此,恶意软件在内存中插入一个恶意的payload(shellcode),并使用SetPropA( ) 更新子类程序。当这个新的属性被调用时(通过一个窗口消息),执行被转发到payload。这种技术所涉及的一些Windows API是:FindWindow(), FindWindowEx(), GetProp(), GetWindowThreadProcessId(), OpenProcess(), ReadProcessMemory(), VirtualAllocEx(), WriteProcessMemory(), SetProp() 和 PostMessage()
    这个关于代码注入技术的简短讲解有助于你了解恶意软件如何试图隐藏自身,也讲间接帮你熟悉了脱壳技术。

下面是一个非常常见的来自恶意软件威胁的代码注入代码序列的例子(来自IDA Pro的反编译输出),当然,通过本节之前介绍的信息,你应该能识别他所使用的技术。

7.脱壳方法

对与脱壳技术进行分类和描述是相当复杂的,但一般来说,有几种方法用于脱壳恶意软件样本,如使用调试器,自动化工具,网络服务,甚至是自己编写的脱壳代码完成静态分析的任务。所选择的方法取决于具体环境和情况。

​        a. 调试器+特殊函数断点

​        这是最著名的方法,包括将恶意软件加载到调试器中,并在相关API上设置软件断点,其中大部分与内存管理和操作有关,并寻找可执行文件或者从内存中提取shellcode。使用x64dbg/x32dbg([ctrl]+g或其CLI上的bp <function>)在API上插入软件断点。主要是这些API:

▪ CreateProcessInternalW( )
▪ VirtualAlloc( )
▪ VirtualAllocEx( )
▪ VirtualProtect( ) | ZwProtectVirtualMemory( )
▪ WriteProcessMemory( ) | NtWriteProcessMemory( )
▪ ResumeThread( ) | NtResumeThread( )
▪ CryptDecrypt( ) | RtlDecompressBuffer( )
▪ NtCreateSection( ) + MapViewOfSection( ) | ZwMapViewOfSection( )
▪ UnmapViewOfSection( ) | ZwUnmapViewOfSection( )
▪ NtWriteVirtualMemory( )
▪ NtReadVirtualMemory( )

在脱壳过程中,我们可能会面临一些问题(例如,恶意软件正在使用的反调试技术)和其他副作用。因此,脱壳前后的一些注意事项可能是有用的:

  1. 在恶意软件达到入口点后在设置断点(系统断点之后)

  2. 如前所述,建议使用反反调试插件,在少数情况下,忽略0x00000000到0xFFFFFFFF范围内的所有异常(在x64dbg上,进入选项→首选项→异常,包括这个范围)。

  3. 有时忽略异常可能是一个坏主意,因为恶意软件可能是他们调用脱壳函数。此外(也不在本文的范围内),还有一些威胁是利用中断和异常来调用API的。

  4. 通过使用MSDN学习所有列出的API和它们各自的参数,是成功脱壳恶意软件威胁的关键知识。

  5. 如果你使用VirtualAlloc( ),建议在其退出点(ret 10)设置断点。此外,有时通过设置一个写内存的断点,更容易在转储时跟踪分配的内容。

  6. 在某些情况下,恶意软件将其payload提取到内存中,但它破坏了PE头,所以你必须重建整个头,尽管使用像HxD这样的十六进制编辑器重建PE头。

  7. 提取的payload可能是映射格式或非映射格式。如果是映射格式,那么可能是导入表被搞乱了,你需要通过PEBear(最喜欢的方法)手动重新调整section头或者使用pe_unmapper这样的工具来修复它们。你可能需要修复基地址,以及查看入口点是否被抹。

  8. 为了重建一个被破坏的IAT,建议使用Scylla(x64dbg内置)。有必要进入OEP,找到它的方法之一是通过寻找代码转移指令,如jmp eax, call eax, call [eax],等等。

  9. 很少有脱壳后的恶意软件样本在IAT中没有任何函数,所以有两种可能:要么部分错位(映射版本),要么脱壳后的恶意软件动态解析其使用的所有函数。

  10. 在x64dbg上使用 "g "热键可能对可视化代码块和寻找可能的OEP跳转很有用。

  11. 另一个寻找OEP的好办法是通过代码插桩,如PIN。https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html

  12. 像tiny_tracer(https://github.com/hasherezade/tiny_tracer)这样的工具使用PIN更容易进行插桩分析,可以用来了解被恶意软件调用的函数(对脱壳和学习反分析技术相当有用),还可以找到可能的OEP

  13. 在很多情况下,壳代码可能只是恶意软件的第一阶段,所以有必要重复步骤来进行接下来的阶段。

  14. 少数恶意软件样本会进行自修改,所以你可能不得不在.text节区设置一个断点,以检测壳代码运行后二进制文件的执行情况。

  15. 根据提取的二进制文件(例如,一个shellcode),它可能无法从一个特定的进程上下文中运行,所以有必要将其注入一个正在运行的进程(例如,explorer.exe)以进行进一步分析。

  16. 你如何检查提取的恶意软件是否可能是最终版的?没有一个明确的答案,通过寻找DLLs的网络函数,如WS2_32.dll(Winsock)和Wininet.dll,纯文本字符串,加密功能(主要是恶意软件是否是一个勒索软件),以及许多其他证据,可以发现一些迹象。在重新对齐节区和/或重建IAT后,使用IDA Pro上打开提取出来的代码是一个很好的方法。

    b. 调试器+DLL加载断点

    这是一个古老而简单的技术,通过在每个加载的DLL上停止被调试进程,并检查内存中是否存在可疑的PE格式文件的内存映射。注意:不要只关注RWX段,因为许多恶意软件在RW区域提取其payload,并在将执行环境转移到提取的可执行文件之前,通过使用VirtualProtect( )将该区域的权限改为RWX。毫无疑问,这可能会消耗一些时间,但在许多情况下它仍然是奏效的。常见的调试器(x64dbg、OllyDbg和Immunity Debugger)有一个配置选项,可以在每次加载DLL时中断。在x64dbg上,这个选项在选项→首选项→事件和标记DLL加载中。在OllyDbg上,你可以进入选项→调试选项→事件,并标记 "在新模块(DLL)上断下"。

    c. 自动化方法

    恶意软件分析可以使用工具来自动化脱壳。Aleksandra Doniec提供了优秀的工具来完成这个目标。

    ▪ hollows-hunter: https://github.com/hasherezade/hollows_hunter/releases
    ▪ pe-sieve: https://github.com/hasherezade/pe-sieve/releases
    ▪ mal_unpack: https://github.com/hasherezade/mal_unpack/releases

她的工具有着类似的方法,所以你应该在一个隔离的虚拟机中运行恶意软件,并执行适当的命令,我在下面展示了一些语法例子,可以用来快速处理。

▪ hollow_hunter.exe /pname <filename> /loop /imp
▪ mal_unpack.exe /exe <filename> /timeout <timeout: ms>
▪ pe-sieve64.exe /pid <process ID>
▪ pe-sieve64.exe /pid <process ID> /dmode 3 /imp 3

脱壳后的二进制文件和一些附加信息被保存到工具创建的目录中。

​        d. Process Hacker

另一个从内存中提取二进制文件的方法是通过Process Hacker,双击正在运行的进程,进入 "内存 "标签,寻找感兴趣的区域/基址(RWX),双击它并按 "保存 "按钮。当然,在自我注入的情况下,更容易找到恶意的二进制文件/payload。在远程注入的情况下,你需要逆向恶意软件,以了解要注入的目标进程,或做一个 "有根据的猜测",并在目标上寻找注入的代码,如explorer.exe或svchost.exe,虽然这是一个有限的和简单的方法,但有时可以节省时间。

​        e. 使用公共/付费网络服务

你可以使用一个互联网服务,就像神奇的Unpacme(https://www.unpac.me/#/),它提供了一个自动脱壳服务。有一个免费的公共计划(每月提交10次)和其他付费计划,对研究人员和公司来说相当有趣。此外,它还提供了一套API,可以将你的定制应用程序与Unpacme服务对接(https://api.unpac.me/)。

​        f. 编写脱壳代码

虽然这种方法听起来很耗时。恶意软件在使用shellcode的情况下,或者在恶意软件在线程里几种反虚拟机和反调试技术的情况下,编写Python代码来完成脱壳是很常见的。此外,在处理类似的恶意软件样本时,我们还有一个优势,那就是将脱壳过程自动化。

8.脱壳实战

现在我们对恶意软件分析的基本原理进行了快速回顾,让我们开始分析吧。我选择的这个样本很简单,不过它对我们的系列文章的开始很有帮助。请记住这个样本的 SHA256 哈希值如下8ff43b6ddf6243bd5ee073f9987920fa223809f589d151d7e438fd8cc08ce292

使用PEBear检查它可能有助于从恶意软件中收集一些有价值的信息。

正如我们所看到的,在IAT中没有任何直接与网络通信、加密或者相关的DLL(和函数)。因此,这是一个迹象,表明我们的样本可能被加壳了。

由于这个恶意软件样本是一个DLL,所以我们可以观察其导出函数,然后我们将其用x64dbg/x32dbg调试起来。

使用pestudio工具,我们能够找出.data部分的raw size和virtual size之间的差值很大,该节区还被标记为 "可写"。正如我们预期的那样,这个额外的迹象表明我们的样本可能是加过壳的。

我们的恶意软件样本有五个导出函数,如果不使用IDA Pro进行分析,很难猜测它们到底是干什么的。然而,我们可以尝试第一个名为 Callrun的函数,显然,它是一个很好的赌注。因此,使用x32dbg(这是32位的样本),我们可以通过打开rundll32.exe并将命令行(文件→改变命令行)改为 "C:\Windows\SysWOW64\rundll32.exe" C:\Users\Administrador\Desktop\sample_1.bin,#1来运行它。

更改后,重新启动/重新加载调试会话,在到达入口点后,在几个经典函数上设置以下断点(你可以用CTRL+G甚至x32dbg命令行接口来完成)。

▪ VirtualAlloc (出口点上)
▪ VirtualProtect
▪ ResumeThread

F9跑起来后,断在了VirutalAlloc函数上, 在EAX寄存器上右键,选择内存窗口跟随。继续F9, 当第二次触发到这个断点时,你可以依旧右键跟随内存窗口到dump2。如果你想的话,可以选择前四个字节下一个内存写入断点,这在碰到某些恶意软件花很长时间才写入这片内存区域时,该方法很有用。如果一切顺利,你将会看到下图类似的内存区域。

正如你所看到的,ASCII显示起首的字符是 "M8Z",这表明它正在使用aPLib压缩。然而,如果你转储这个区域,你会发现在同一区域的解压缩的二进制文件。因此,在 "转储2 "标签中鼠标右键点击字节,选择 "在内存布局中转到 "选项。

在灰色高亮的基址上,鼠标右击并选择 "将内存转存到文件 "来将这片内存区域存到桌面上。现在,保持调试器打开(你可能需要修复最终被破坏的IAT),并在XVI 编辑器上打开刚才转储的文件。按CTRL-F键,寻找 "This program"字符串(注意:在这种情况下这种搜索是有效的,因为PE Header没有被破坏)。

一旦你找到它,你应该在之前的两三行寻找 "MZ "字符。现在,把光标放在 "MZ "标记前的字节上,之后编辑→删除,如下面几个图所示:

接着保存,然后用PEBear打开,跳到导入表选择夹,可以看到如下的信息:

现在可以确认的时,IAT是完好的,有一个与网络通信有关的DLL(WININET.dll),你也可以看到一个稍微不同的入口点(EP)。因此,这似乎是我们的第一个脱壳阶段(你应该总是假设可能有进一步的脱壳阶段,并重复同样的分析,除非你有来自其他来源的外部信息或报告)。在这一点上,你可以关闭x32dbg,因为我们不再需要它了。

9.逆向和解密代码

现在我们有了脱壳后的二进制文件,我们在IDA Pro中打开它。有很多方法可以找到加密的配置,但当然其中一个更容易的方法是在IDA Pro 导航条上寻找Data或Unexplorered区域(实际上,Unexplorered区域比下面显示的大得多)。

在Unexplored 区域开始的地方点击后,我们会发现下面的代码:

非常有趣的是,我们注意到在这里看到了对特定地址的交叉引用:

▪ byte_10004000 (16 bytes)
▪ pbData (8 bytes)
▪ unk_10004018 (大概 0x2000 bytes)

根据我分析恶意软件的经验,我已经知道pbData是微软加密API中几个API的重要参数,所以这说明我们的方向是对的。在许多著名的恶意软件样本中发现的另一种模式是结构密钥+加密数据,所以即使我对这种情况没有任何进一步的指示,我也可以假设 "pbData "是一些密钥(长度为8字节),尽管有时它不是最终的密钥,因为恶意威胁使用KDF(密钥衍生函数)从提供的密码中生成一个明确的密钥。根据我们的分析,"unk10004018 "将指向的是潜在的加密数据。

根据pbData(X键)的交叉引用,我们得到了子程序sub_10001CB7。从这一点上,我们清楚地看到我们的数据(pbData)被推入堆栈,作为子程序sub_10002131的参数,如下所示。

观察其他参数并根据我以前的经验,我知道被传递给子程序sub_10002131的其他参数也涉及到了密码学。请注意,我们还应该看到,dwBytes(来自esi寄存器,其值为0x2000)被用作子程序sub_100011A4的参数,它只执行缓冲区分配以接收内容,如下所示

分析子程序sub_10002131(有点长)的几个部分,我们有以下代码。

虽然CryptAcquireContextA( )是一个被废弃的函数,但它仍然被恶意软件威胁所使用。这个API被用来获取一个CSP(加密服务提供商)的密钥容器的句柄,它使用一个默认的密钥容器名称和一个用户默认的提供商,因为两个参数都是零(来自xor edi, edi)。以dwFlags(0x0F0000000)为例,在wincrypt.h上搜索这个值,我们知道它指的是CRYPT_VERIFYCONTEXT提供者类型,这通常出现在使用短暂密钥或不需要访问持久化私钥的应用程序中。实际上,正如你将了解到的那样,这个恶意软件样本使用了一种密钥衍生的方式

接下来的代码块为我们的分析提供了重要信息

这个代码块包含三个API,我们必须逐一分析它们:

​        a. CryptCreateHash( ):

​        这个函数创建了一个CSP哈希对象,只有它的一个参数Algid相当有趣,它被设置为8004h。

​        通过查阅在线文档https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id,我们能够了解到0x8004h意味着CALG_SHA1,所以这个恶意软件正在操纵SHA1哈希值(160位/20字节)。这个函数的返回是一个CSP哈希对象的句柄(保存在phHash参数中,最初被清零),将在下一个函数中使用。

​        b. CryptHashData( ):

​        该函数将特定大小(由参数dwDataLen给出)的数据(由参数pbData给出)添加到由CryptCreateHash( )返回的哈希对象中。这两个参数是子程序sub_10002131的第三和第四个参数。在它们之后,这些参数来自子程序sub_10001CB7,显示大小为8字节,指的是可能的密钥(pbData)。此时,CryptHashData( )函数正在将可能的密钥(8个字节)传入哈希对象中,因此产生了一个SHA1哈希值。换句话说,恶意软件的代码正在生成一个hash值,从CryptCreateHash( )返回的句柄指的是CSP哈希对象,它持有这个哈希数据。

​        c. CryptDeriveKey( ):

​        这个函数生成来自给定种子数值的会话密钥,根据其定义,它保证使用相同的基本数据生成相同的会话密钥,所以它完全符合我们的情况,因为我们正在解密一个配置数据。这里有两个有趣的条件:a. Algid是0x6801,b. dwFlags是0x280011,我们需要进一步讨论它们。

​        第二个参数(Algid == 0x6801)确定使用的是对称加密算法,根据文档(https://docs.microsoft.com/en-us/windows/win32/seccrypto/alg-id),0x6801意味着CALG_RC4

​        第四个参数(dwFlags == 0x280011)值得进一步详细说明。根据文档(https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptderivekey)。"钥匙大小,代表钥匙模数的长度(比特),用这个参数的前16位来设置。因此,如果要生成一个128位的RC4会话密钥,0x00800000的值要与任何其他dwFlags预定义的值进行位数OR操作"。因此,我们知道RC4密钥有40位(0x28),所以它有5个字节。MSDN文档的另一部分告诉我们,"这个参数的低16位可以是0,或者你可以通过使用bitwise-OR操作来指定一个或多个下面的标志来组合它们"。从MSDN页面中获取可能的值,并在wincrypt.h文件中搜索它们(ReactOS为我们提供了一个例子:https://doxygen.reactos.org/d7/d4a/wincrypt_8h_source.html),我们发现0x11意味着CRYPT_NO_SALT + CRYPT_EXPORTABLE(两个标志位不言而喻)。

​        因此,非常有趣的是,该恶意软件的作者使用SHA1作为KDF(密钥衍生函数)来生成最终的解密密钥,这可以用以下的流程来解释

a) pbData = C58B00157F8E9288 (导出数据在IDA里是使用快捷键SHIFT+E)
b) pbData → SHA1 (20 bytes)
c) SHA1 → CryptHashData( ) → CryptDeriveKey( ) → RC4 key (5 bytes)

当然,还有更好的KDF,如Bcrypt、Scrypt和Argon2,可能会在实际应用中使用,但我们确信,恶意软件作者并不关心使用或不使用抗FPGA、ASIC和GPU攻击的KDF函数。

​        d. CryptDecrypt( )

​        该函数负责通过使用提供的密钥句柄(hKey)来解密加密数据。提供的其他参数有hHash(CryptCreateHash( )产生的哈希对象句柄),Final(值等于1,因为是被解密的最后和唯一的部分),pbData(包含要解密的数据的缓冲区)和pbwDataLen(指向一个DWORD的指针,表示pbData缓冲区的长度,在本例中是0x2000)。

​        记住,pbwDataLen(0x2000)是被分析的函数的第二个参数(sub_10002131(BYTE rc4_encrypted_data, DWORD pdwDataLen, BYTE pbData, DWORD dwDataLen))。因此,最终结果(RC4解密数据)被保存到pbData中,相应的大小被保存到pdwDataLen参数中。

​        另一个有趣的问题是,这里的pbData不是8字节的密钥,而是来自unk_10004018数据引用的加密数据,该数据被转移到子程序sub_10006264内存定义的数组中,我给它的参数重新命名("N"键是IDA进行函数重命名快捷键),如下图所示

其余的函数(CryptDestroyHash、CryptDestroyKey和CryptReleaseContext)有显而易见的含义,我们不需要过多解释。

最后,我们有了所有必要的信息,我们将在下一步使用这些信息来编写一个配置提取器和解密器

▪ 初始 key: C58B00157F8E9288 (after first 16 bytes of .data section)
▪ 初始数据地址: 0x10004018
▪ 数据大小: 0x2000
▪ hash 算法: SHA1 (20 bytes)
▪ 解密算法: RC4
▪ RC4 key 大小: 5 bytes

让我们进入下一节,写一个C2数据配置提取器和解密器。

10.编写数据配置提取器

编写配置提取器是一项任务,一开始可能看起来很复杂,但这只是一个学习路径的问题,之后,你可以采取自己的步骤。做这种事情不是最近几年才有的,我个人已经做了很多年了。

有几种语言可以使用,但我选择了Python 3,它有编写任何解码器的好用的函数。下面我不仅要解释每一行的作用,还要说明为什么那么写。

我的数据C2数据解密器代码(文件名为hancitor_conf_extractor_1.py)如下所示:

import binascii
import pefile
from Crypto.Cipher import ARC4

def extract_data(filename):

    pe = pefile.PE(filename)
    for section in pe.sections:
        if ".data" in section.Name.decode(encoding='utf-8').rstrip('0x00'):
            return section.get_data(section.VirtualAddress,section.SizeOfRawData)

def data_decryptor(rc4key,encrypted_config):

    rc4_cipher = ARC4.new(rc4key)
    decrypted_config = rc4_cipher.decrypt(decrypted_config)
    return decrypted_config

def main():

    filename = input("Filename: ")
    datasec = extract_data(filename)
    datasec2 = datasec[16:]
    key = (datasec2[:8])
    encrypted_data = binascii.hexlify(datasec2[8:256])
    hashed_key = SHA.new(key).hexdigest()
    true_key = hashed_key[:10]
    c2_config = data_decryptor(binascii.unhexlify(true_key),binascii.unhexlify(encrypted_data))
    print("\n\nThe decrypted configuration follows: \n")
    print(c2_config.decode('utf-8'))

if __name__ == '__main__':
    main()

让我们试着逐行解释代码,但不是按照Python 3脚本的确切顺序来解释的

​        a. 代码前4行导入了必要的包,因为代码正在处理一个PE文件,这个Python 3脚本的目的是用RC4和SHA1算法解密数据,同时还需要处理从二进制到ascii的转换,所以也需要binascii包。

​        b. main()函数中,代码要求用户输入脱壳后的Hancitor二进制文件的文件名,加密的数据将从该文件中提取。提取的数据以字节形式存储在datasec变量中。

​        c. extract_data( )函数没有任何新内容,但我们应该强调以下几点:

​                i.)我们的目标部分是".data",那里存放着密钥和加密数据;

​                ii. )我们关注Unicode格式,这就是使用decode(encoding='utf-8')的原因;

​                iii.)代码从section name中去除了结尾的'0x00'(处理Unicode名称时很常见);

​                iv.)我们使用PE属性来划分.data部分的开始和结束。

​        d. datasec2 = datasec[16:],定义了datasec2变量,其中包含密钥+所有加密数据,除了前16个字节,这可能与compaign ID或类似内容有关。

​        e. key = datasec2[:8] 定义了key变量,正如我们之前所了解的,它由datasec2的前8个字节组成。

​        f. encrypted_data = binascii.hexlify(datasec2[8:256] 将加密的数据配置存入encrypted_data。实际上,正如我们从逆向代码时知道,二进制代码预留的缓冲区大小为0x2000,但事实上,收集248字节就足够了,但你可以根据你的需要调整。hexlify( ) 将二进制数据翻译成十六进制格式。

​        g. hashed_key = SHA.new(key).hexdigest() 生成一个十六进制的 SHA1 hash

​        h.true_key = hashed_key[:10]根据CryptDeriveKey( ) 的解释,只收集前10个十六进制(5个字节)。

​        i. data_decryptor(binascii.unhexlify(true_key),binascii.unhexlify(encrypted_data)调用解密函数,其中一个重要的事实是,我们必须在使用RC4函数之前将数据从十六进制字符串转化为二进制表示。

​        j. data_decryptor( )函数非常简单,基本上是通过RC4算法用给定的密钥(true_key变量)对加密数据进行解密。

​        k. 最后,print(c2_config.decode('utf-8'),结果被打印到终端。你应该再次注意到,在打印解密数据之前,我们需要对其进行解码(假设其可能使用的是Unicode字符集)。

​        针对我们脱壳后的Hancitor二进制样本执行这个Python 3脚本,我们可以得到

这下好了! 我们成功地从脱壳的Hancitor二进制文件中提取并解密了Hancitor C2配置。写一个解密脚本的好处是,我们可以用它来对付所有遵循相同二进制模式的Hancitors样本。

为了确认我们的脚本,让我们寻找另一个Hancitor样本,脱壳后尝试提取和解密其C2数据配置。Malware Bazaar提供了无穷无尽的Hancitor样本,我们可以使用Malwoverview来列出和下载这些样本,如下图所示

收集许多可能的Hancitor哈希值是相当容易的,如下图所示(列表输出有些已被截断)。

因此,我们可以挑这些哈希值中的一个,从Malware Bazaar下载相应的样本,解压密码:infected,并将其加载到PE Bear中。

在x32dbg使用同样的方法和断点脱壳这个样本后,我们应该在PE Bear上验证它,如下图所示

使用我们的C2数据解密脚本来对付第二个脱壳的Hancitor,我们有

正如我们所期望的那样,一切又都运作良好,我们有一个很好的Hancitor脚本来提取和解密其C2配置数据。

还有两种方法可以用来从Hancitor提取C2数据配置。

     1. Cyber Chef: https://gchq.github.io/CyberChef/
     2. 通过调试器

这两种方法都很好,但它们都被理解为 "手动 "方法,我们需要每次在一个样本上工作。使用调试器是非常简单的,因为只需在CryptDecrypt( )上设置一个断点,一旦命中,就执行到其退出点,并检查(Follow in Dump)其堆栈中的输出参数(第5个参数)。当然,读者现在应该知道如何执行这些步骤了。

为了使用CyberChef,我们需要选择密钥的字节在IDA中使用SHIFT+E(编辑→导出数据)导出,然后将这些数据复制到输入区。选取From Hex Recipe(因为导出的数据是十六进制格式),然后选取SHA1 Recipe,因为我们想从输入中获得SHA1哈希值,如图所示。

对于加密的数据(从0x10004018地址开始),重复同样的程序,将其导出(SHIFT+E)并复制到输入区。将RC4配方拖入配方区,并注意几点:

​        a.) 口令是由SHA1输出的前10个十六进制数字(5个字节)组成的(正如我们从CryptDeriveKey( )中学到的);

​        b.) 口令是十六进制格式;c.) 输入格式也是十六进制格式。

最后,和我们的Python 3脚本得到了同样的结果,如下图所示。

11.总结

在这第一篇文章中,我展示了如何通过编写Python 3脚本从Hancitor恶意软件中提取和解密C2数据配置。此外,我还介绍了几个概念和基础,如代码注入和脱壳,这在本系列的下一篇文章中会很有用。无论如何,也许在这里强调几点是很有意义的

​        a. 我喜欢这个简单的恶意软件样本,因为我的最终目的是帮助其他专业人士采取自己的步骤进行恶意软件分析,而且,作为一系列文章的开始,它当然是相当有用的。

​        b.目前,我已经隐藏了很多关于这个恶意软件的逆向工程分析细节,因为最初的目标是在这篇文章中只关注C2数据配置的提取和解密。

​        c. 再次,与许多逆向工程的初学者可能想到的不同,数据提取/解密并不是什么新鲜事(甚至不是),而且我已经做了很多年,所以这是一个很好的开端。此外,获取C2配置是分析恶意软件的主要目标之一(其他几个目标包括感染方式、持久性、规避和网络通信)。

​        d. 我选择Python 3作为脚本语言,是因为我认为它更容易理解,而且大多数安全研究人员对它很了解。毫无疑问,我们可以用C或Golang编写程序,最终我们可以在下一篇文章中使用它们。

​        e.我们将在接下来的文章中研究几个样本和背景,并讲解COM、脱壳、代码注入、C2仿真、.NET逆向、反分析技术、API解析、字符串解密、IDC/IDA Python、IDA AppCall等,别着急,让我们一步一步来。

不要忘记:这是个在线文件,我发现错误和误差后会尽快更新。

我从事逆向工程已经有十多年了,我想在以前就开始这个系列,但不幸的是,这并不可能。因此,现在我的计划是写一本关于恶意软件分析的书,为安全社区作出贡献,并继续这个系列。让我们看看会发生什么。

下面是我的联系方式:

▪ Twitter: @ale_sp_brazil
▪ LinkedIn: https://www.linkedin.com/in/aleborges
▪ Blog: https://exploitreversing.com

逆向分析,下次见!

​                                                                                                                                                                                                                                                                                                                                           Alexandre Borges

点评

真心好文章和技术贴。都说谁都能搞逆向,都说需要有逆向经验。啥叫逆向经验这就是逆向经验。而且这种经验没个3-5年积累不起来的。 逆向入门的兄弟如果想学习可以跟着列出的技术去自己实现一遍或者了解一边。  发表于 2022-2-11 15:58

免费评分

参与人数 32吾爱币 +33 热心值 +30 收起 理由
SCUL + 1 + 1 谢谢@Thanks!
DQQQQQ + 1 + 1 用心讨论,共获提升!
xzhtx + 1 + 1 用心讨论,共获提升!
Crazystone0 + 1 + 1 我很赞同!
Ginobili + 1 + 1 鸿篇巨制!
tsecond + 1 + 1 我很赞同!
dukeimp + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Nattevak + 3 + 1 我很赞同!
standley + 1 + 1 热心回复!
努力加载中 + 1 + 1 热心回复!
飞行者 + 1 谢谢@Thanks!
YEFL123 + 1 + 1 我很赞同!
Tony2009 + 1 谢谢@Thanks!
太阳柠檬 + 1 + 1 鼓励转贴优秀软件安全工具和文档!
duya1961 + 1 + 1 谢谢@Thanks!
bsdn321321 + 1 谢谢@Thanks!
陈世界 + 1 + 1 热心回复!
scodec + 1 + 1 我很赞同!
sakura-galaxy + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
蓝纹鲸 + 1 + 1 &lt;font style=&quot;vertical-align: inherit;&quot;&gt;&lt;font style=
hope11111 + 1 + 1 感谢分享!
qinxiaoben + 1 + 1 热心回复!
mhaitao + 1 + 1 我很赞同!
HelloHi + 2 + 1 我很赞同!
hongshen233 + 1 + 1 热心回复!
KylinYang + 1 + 1 热心回复!
woyucheng + 1 + 1 谢谢@Thanks!
limis + 1 谢谢@Thanks!
lingyun011 + 1 + 1 用心讨论,共获提升!
小南跑 + 1 + 1 太强悍了!大佬后续也出个dll文件的分析方法吧
积积 + 1 + 1 热心回复!
清炒藕片丶 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
咔c君 发表于 2022-2-9 19:34
不错学习了
推荐
asalibra 发表于 2022-4-8 22:54
额 分享一个我安装X64 DBG的插件labeless时候遇到的一个非常低级的错误 x64无法识别出来labeless,查看日志是无法正确装载插件但是x32dbg可以显示虽然我不知道为什么我的论坛专用x32是俄文显示的, 错误原因就是是使用x32python  pip install labeless的 使用x64 python下载一遍就可以识别插件了
3#
积积 发表于 2022-2-9 17:18
4#
18703507478 发表于 2022-2-9 17:20
学习学习 感谢分享
5#
夏驰 发表于 2022-2-9 17:21
感谢分享
6#
小南跑 发表于 2022-2-9 19:13
感谢大佬的分享。请教一下近几年编译器有什么变化吗?总感觉有些反汇编代码看不太懂
7#
tinaql 发表于 2022-2-9 19:42
非常好,认真学习
8#
狄人3 发表于 2022-2-10 00:02
感觉查壳没必要这么麻烦吧....用die比较方便一点
9#
shehuizhuyihao 发表于 2022-2-10 08:58
感谢分享
~~~~
10#
mhaitao 发表于 2022-2-10 10:41
朕早就想到   破解没那么简单
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 07:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表