恶意代码分析(一):从入门到精通

2025-02-19 5 0

概述

由于恶意软件分析涉及的内容广泛,我将把文章分为第1部分、第2部分等
这个系列的目标是制作一系列关于恶意软件分析的文章,从简单的恶意软件二进制文件讲起,到最复杂的恶意软件,涵盖一系列话题,例如解包、API解析、C2提取、C2仿真,当然还包括逆向工程,以及一些动态分析,或许还会使用一些反混淆技术。必要时,我还会涉及其他话题,如COM(组件对象模型)、密码学、IDC/IDA Python等,以及所有必要的内容,帮助读者更好地理解分析过程。
在这个系列的文章中,我将使用多个工具,并尽量指出你可以在哪里获得这些工具,以简化你的工作。
我不会只提供困难的样本,因为在我看来,这种方法对任何人都没有帮助(特别是那些希望学习的专业人士),最后只会浪费时间(并且只是无用的炫耀)。因此,我们将分析不同的样本,每个样本的难度不同,并讨论一些代码行。策略是将一篇文章分成不同的部分,以避免让阅读变得过于枯燥。

实验室设置

关于实验室设置的说明,我通常使用以下一种或多种系统来分析所有样本:
▪ Windows 7、Windows 8.1 或 Windows 10:https://developer.microsoft.com/en-us/windows/downloads/virtual-machines/
▪ REMnux(最适合逆向工程的发行版):https://docs.remnux.org/install-distro/get-virtual-appliance
▪ Ubuntu 20.04.x:https://ubuntu.com/download/desktop

我会尽量避免使用任何不必要的技术,专注于使用知名工具。不幸的是,其中一些工具并非免费的(比如 IDA Pro,这是我最喜欢的工具,毫无疑问是世界上最好的逆向工具),Hex-Rays 提供了 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 的功能,在动态分析中非常推荐使用(主要是为了避免恶意软件使用的反调试技术),以下是一些你可能想安装的插件:
▪ ScyllaHide:它是一个高级反调试库,挂钩了多个函数,能隐藏调试活动以避免被恶意软件发现:https://github.com/x64dbg/ScyllaHide/releases
▪ Labeless:这是一个非常推荐的插件,提供两个关键功能给逆向工程师:
▪ 在 x64dbg 和 IDA Pro 之间同步标签、函数名、全局变量和注释。
▪ 动态转储内存中的区域,适用于被调试进程,这将非常有用,例如,在任务 API 解析和/或字符串解码后转储二进制文件。
▪ Labeless 插件可以从以下网址下载:https://github.com/a1ext/labeless/releases
▪ DbgChild:它自动检测由调试进程创建的子进程,并在主进程创建新进程时自动将 x64dbg 的新实例附加到子进程上,从而节省了大量时间。DbgChild 可从以下网址获取:https://github.com/David-Reguera-Garcia-Dreg/DbgChild

还有许多有用的插件,但我们将等到适当的时候再讨论它们。值得注意的是,许多可用插件并没有得到作者和维护者的定期更新,因此它们可能随时不可用。请小心!所有剩余的工具将在我们的分析和未来的多篇文章中展示。

恶意软件分析目标

毫无疑问,这是一个有趣的问题:我们在分析二进制文件时在寻找什么?
这个问题很有意义,因为在分析恶意软件时有许多可能的目标和需要考虑的方面。然而,在真实世界的调查中,除了恶意软件分析外,还有其他重要的领域,当然,我们应该在整个分析过程中考虑它们:

a. 内存分析:这是一种极其强大的技术,在过去10年中证明了其无限的价值,并作为调查中的首要方法,用来理解恶意软件感染事件、其后果、负面影响,并能够收集大量的证据,这些证据可能很难从磁盘或其他来源收集到。

b. 网络分析:这是一个非常有用的资源(例如,pcap文件),可以通过流量分析来理解和检测未授权的通信(C2 - 命令与控制通道),并帮助收集恶意文件、恶意文档和 Cobalt Strike 信标等数据。

c. 文件系统/磁盘分析:这是任何调查的最后一道防线,可以分析并检测对手入侵、漏洞、欺诈、泄漏,以及恶意软件感染的副作用。

再次强调,以上所有内容都非常重要,在真实世界的调查中必须使用它们。然而,让我们回到关键点:为什么你应该学习逆向工程,特别是恶意软件分析?
很简单:通过恶意软件分析,你有机会从“恶”的源头了解对手的意图和目标,而不仅仅是其影响。换句话说,你可以学习到技术、技巧、规避策略,并且,如果你足够幸运,还能收集到重要的证据,帮助正确归因(大多数时候这是一项艰难的任务),甚至帮助将坏人绳之以法。

因此,在开始任何逆向任务之前,我们应该记住有许多问题需要考虑并问自己:
▪ 这个二进制文件是否经过打包?如果是,恶意软件使用的是常见的打包工具还是自定义的打包工具?
▪ 恶意软件使用的是什么网络通信技术/API 集?例如,Winsock2、Wininet、COM(组件对象模型),还是更低级的技术,如 WSK(Winsock 内核)或甚至自定义实现的技术?
▪ 是否有代码注入或 hooking 技术被使用?是哪种技术?
▪ 使用了哪些反取证技术?是否有反调试技术?反反汇编?反虚拟机/沙箱?
▪ 是否有 API/DLL 编码?
▪ 字符串是否被加密?
▪ 恶意软件使用了什么同步原语?有时它们隐藏着重要的反调试技术。
▪ 恶意软件使用了什么加密算法?
▪ 恶意软件使用了哪些持久性方法:注册表、服务、任务还是内核驱动程序?
▪ 是否有 Shellcode 被注入到操作系统进程中?
▪ 是否有文件系统微过滤驱动程序被恶意软件安装?
▪ 如果安装了内核驱动程序,是否有任何回调(现代钩子)或定时器被安装?

在这篇文章中,我们将专注于两个简短的目标:
a. 解包恶意软件
b. 提取并解密其 C2 配置数据

我们将回顾一些知名的恶意软件解包技术以及提取 C2 配置数据的不同方法。此外,我还将为一些基础主题提供最少的背景知识,帮助读者能够继续深入研究上述主题。

收集初步信息

第一个样本具有以下哈希值:
(SHA256) 8ff43b6ddf6243bd5ee073f9987920fa223809f589d151d7e438fd8cc08ce292
我们可以从许多终端收集到大量的信息,例如从恶意软件市场(Malware Bazaar)收集到的,如下图所示:

恶意代码分析(一):从入门到精通插图
根据此,我们收集到了一些重要信息:
▪ 目标恶意软件似乎属于 Hancitor 家族。
▪ 它使用了 EnumerateProcesses() 函数,因此了解是否存在特别的原因(例如代码注入)可能会很有趣;
▪ WriteProcessMemory() 被触发,这通常在解包程序和代码注入过程中出现,所以在这里没有发现新的问题。

为了进一步扩展数据采集,我们可以将样本提交到 Triage 进行更多的信息收集:

恶意代码分析(一):从入门到精通插图1
这个样本还有其他相关的任务 ID,但我们先集中讨论第一个任务 ID。关于第一个任务 ID 的详细信息可以通过执行以下命令来查看(输出已被截断):
恶意代码分析(一):从入门到精通插图2
当然,我们可以获取更多关于样本的信息,但现在已经足够了,因为我们已经得到了可能的 C2(URL)。
我们的下一步是找出这个样本是否被打包(像大多数恶意软件威胁一样)。
此外,如果它确实被打包,那么我们将学习如何使用调试器(如 x64dbg)来解包它。
不过,在这之前,我们先回顾一下关于在 Windows 系统上解包恶意软件的一些概念。

解包概念

每当我听到有人谈论解包时,似乎大家的印象最终都得出同一个结论:这可能并不那么简单。当然,正如我们之前提到的,解包样本通常是进行字符串解密和API/DLL解析等工作的第一步,但我们需要从某个地方开始,并有一个目标。

恶意代码打包的动机和原因有很多种,常见的包括:

  • 隐藏恶意代码:使恶意代码对防病毒软件“隐藏”。当然,它并不是完全隐形的,但它是一种软规避技术,增加了分析师的工作难度,并可能给防御系统带来问题。

  • 隐藏真实的恶意目标:打包后的样本不直接暴露实际的恶意行为。

  • 反调试和反虚拟化:打包后的样本可能使用了许多反分析技巧(反调试、反虚拟机技巧),这使得动态解包变得更加困难。

  • 多层打包:恶意软件通常将有价值的代码分成多个层次,并使用定制的解包程序来隐藏。

  • 多态性:恶意软件的解包代码或整个程序可能是多态的。

虽然有很多老式的著名打包器我们有解包的程序,但大多数恶意软件作者都使用了定制的打包器,这些打包器让代码更难以被安全防御检测到。以下是一些常见的打包保护工具(如 Themida、Arxan、VMProtect 等)使用的技巧:

  • 支持 64 位二进制文件。

  • 删除或仅保留一个导入函数的导入地址表(IAT)。

  • 大多数字符串加密。

  • 内存完整性受到保护,无法直接从内存中提取解包后的可执行文件。

  • 使用虚拟化指令,并将其转换为 RISC 指令。

  • 代码栈加密,静态分析难度较大。

  • 代码虚拟化使用多态性,虚拟指令和原始指令不同,但功能相同。

  • 使用假指令、无效代码等反分析技巧。

  • 在反调试和反虚拟机方面使用了很多技术。

解包时需要考虑的问题:

  1. 恶意软件是否真的被打包?

  2. 如何识别一个被打包的程序?

  3. 恶意软件是否执行自注入或远程注入?

  4. 恶意软件是否执行自覆盖?

  5. 有效载荷被写入哪里?

  6. 如何执行有效载荷?

  7. 解包后代码的证据是什么?

  8. 是否有额外的打包层?

如何判断恶意软件是否被打包?

没有简单且明确的答案,但以下一些特征可能表明样本已经被打包:

  • 导入的 DLL 和函数数量较少

  • 存在大量加密或混淆的字符串

  • 使用特定的系统调用

  • 存在非标准的节名称

  • 可执行二进制的节不常见(除了 .text/.code 外的节)。

  • 存在意外的可写节

  • 节的熵值较高(通常超过 7.0,但不是绝对的指示器)。

  • 原始大小和虚拟大小之间存在显著差异

解包时的常见挑战:

  1. 反调试技术:时间检查、CPUID、堆检查等技术,建议使用像 ScyllaHide(用于 x64dbg)这样的插件来绕过这些技术。

  2. 反虚拟机技巧:检查 VMware、VirtualBox 等虚拟化环境的痕迹。

  3. 恶意软件的自覆盖或自注入:可能需要设置断点,确保能够监控关键的代码写入或执行过程。

解包后可能需要修复的问题:

  • DOS/PE 头部可能已损坏或被修改。

  • 可能需要清理垃圾数据,如在提取的二进制文件前存在无用的内容。

  • 入口点(EP)可能被零化或不正确。

  • 解包后的二进制可能无法正确显示导入表,可能需要手动对齐节。

修复工具:

  • PEBearPestudioCFF ExplorerHxD是一些常用的工具,用于修复 PE 文件头、修复导入表等。

  • Scyllape_unmapper等工具可以帮助解决导入表和节对齐的问题。

总之,解包是恶意软件分析的第一步,但这只是一个开始。接下来,仍然需要解决字符串解密、API 解析、C2 配置提取等问题。

代码注入

代码注入是Windows系统支持的一种操作,当然,这也是一种非常有效的规避方法,因为恶意软件能够将恶意代码注入(写入)到进程的内存区域(有些人称其为“段”)中,可以是自身的进程(自我注入)或远程进程(远程注入),而且这个有效载荷将在目标进程上下文中执行,就像它本身就是该进程的一部分,并且不会留下太多证据。此外,源进程(恶意软件)可以干净利落地终止自身,而恶意有效载荷继续在一个看似正常的进程(例如 explorer.exe 和 svchost.exe)中运行。最终,这是一个隐蔽的规避安全防御的方式。

值得注意的是,Windows 8.1(主要是Windows 10和11)以来,已经存在很多的缓解和保护措施,例如代码完整性保护(Code Integrity Guard)、扩展点禁用策略(Extension Point Disable Policy)、控制流保护(Control Flow Guard)、动态代码限制(Dynamic Code Restriction)和任意代码保护(Arbitrary Code Guard),这些保护措施使得在这些Windows版本上进行代码注入变得更加困难,而且很容易被检测和阻止。有关这些缓解和保护措施的更多信息可以参考以下链接:Windows 10威胁缓解概述Defender端点保护自定义Windows 10内存保护功能

有许多优秀的公开文档解释了几种代码注入技术,但简而言之,主要的代码注入技术如下:

  • DLL注入:这种老旧的技术用于强制进程加载一个DLL。主要可能涉及的API包括:OpenProcess()、VirtualAllocEx()、WriteProcessMemory() 和 CreateRemoteThreat | NtCreateThread() | RtlCreateUserThread()。

  • PE注入:在这种技术中,恶意代码被写入并强制在远程进程甚至自身进程(自我注入)中执行。主要相关API包括:OpenThread()、SuspendThread()、VirtualAllocEx()、WriteProcessMemory()、SetThreatContext() 和 ResumeThreat() | NtResumeThread()。

  • 反射注入:这种技术类似于PE注入,但恶意代码避免使用LoadLibrary()和CreateRemoteThread()等函数。例如,有一种常见的变种(也用于Cobalt Strike),使用以下API实现:CreateFileMapping()、Nt/MapViewOfFile()、OpenProcess()、memcpy()和Nt/MapViewOfSection()。最终,远程进程中的代码可以通过调用OpenProcess()、CreateThread()、NtQueueApcThread()、CreateRemoteThread()或RtlCreateUserThread()来执行。有趣的是,这种变种也可能使用VirtualQueryEx()和ReadProcessMemory()。

  • APC注入:这种代码注入技术允许程序通过附加到APC队列,执行特定线程中的代码。当线程退出警戒状态时(由SleepEx()、SignalObjectAndWait()、MsgWaitForMultipleObjectsEx()、WaitForMultipleObjectsEx() 或 WaitForSingleObjectEx()等调用触发),注入的代码就会被执行。因此,常见的API包括:CreateToolhelp32Snapshot()、Process32First()、Process32Next()、Thread32First()、Thread32Next()、QueueUserAPC() 和 KeInitializeAPC()。

  • 进程空洞注入(Hollowing)或进程替换:简单来说,恶意软件使用这种技术“抽空”一个进程的整个内容,并将恶意内容插入其中。相关API包括:CreateProcess()、NtQueryProcessInformation()、GetModuleHandle()、Zw/NtUnmapViewOfSection()、VirtualAllocEx()、WriteProcessMemory()、GetThreadContext()、SetThreadContext() 和 ResumeThread()。

  • 原子炸弹(AtomBombing):这是一种变种的APC注入技术,通过将恶意负载拆分成分离的字符串,为每个字符串创建一个原子,并将它们复制到一个读写段(使用GlobalGetAtomName()和NtQueueApcThread()),然后通过NtSetContextThread()设置上下文。因此,进一步的API包括:OpenThread()、GlobalAddAtom()、GlobalGetAtomName() 和 QueueUserAPC()。

  • 进程双胞胎(Process Doppelgänging):这种技术可以视为进程空洞技术的一种演变。两者的关键区别在于,进程空洞是在进程恢复之前替换其内容(镜像),而进程双胞胎则能够在进程被创建之前通过覆盖目标镜像,使用恶意镜像替换它。这里的关键概念是NTFS操作是在事务中执行的,因此这些操作要么全部提交,要么都不提交。同时,恶意镜像仅在事务中存在,它对任何其他进程不可见。因此,恶意镜像被加载到内存中,恶意软件从文件系统中丢弃恶意有效载荷(通过回滚事务)就像文件之前从未存在过一样。相关API包括:CreateTransaction()、CreateFileTransaction()、NtCreateSection、NtCreateProcessEx()、NtQueryInformationProcess()、NtCreateThreadEx() 和 RollbackTransaction()。

  • 进程伪装(Process Herpaderping):这种技术类似于进程双胞胎,但在过程上存在微妙的不同。进程伪装基于这样一个事实:安全防御通常会通过注册回调例程来监控进程创建,回调在内核侧使用PsSetCreateProcessNotifyRoutineEx()或在驱动程序的DispatchCleanup例程中(IRP_MJ_CLEANUP)被触发。当线程被创建后,回调被调用。关键问题是:如果攻击者能够创建并映射一个进程,并且随后能够修改文件镜像再创建线程,安全产品就会检测到这种恶意有效载荷。然而,这个检查顺序可能会受到影响,如果攻击者能够创建恶意二进制文件,打开文件句柄,使用NtCreateSection函数将其映射为图像段(并包括SEC_IMAGE标志),然后使用该段句柄创建进程(NtCreateProcessEx()),再修改文件内容使其看起来不具恶意,并最终创建线程(NtCreateThreadEx()),此时安全防御可能不会检测到。几个相关API包括:CreateFile()、NtCreateSection()、NtCreateProcessEx() 和 NtCreateThreadEx()。

  • 钩子注入(Hooking Injection):使用此技术时,我们会看到涉及钩子活动的函数,例如SetWindowsHookEx()和PostThreadMessage(),用于注入恶意DLL。

  • 额外Windows内存注入(Extra Windows Memory Injection):通过此技术,恶意软件威胁通过使用额外Windows内存(EWM)注入代码到进程中,EWM的大小最多为40字节,并在注册Windows类时附加到类实例。诀窍是,附加的空间足以存储一个指针,将执行转发到恶意代码。一些可能涉及的API有:FindWindowsA()、GetWindowThreadProcessId()、OpenProcess()、VirtualAllocEx()、WriteProcessMemory()、SetWindowLongPtrA() 和 SendNotify()。

  • 传播注入(Propagate Injection):这种技术被恶意软件威胁(如RIG Exploit Kit和Smoke Loader)用于将恶意代码注入到explorer.exe进程(中等权限级别)及其他持久性进程中,基于枚举Windows窗口的方式(EnumWindows() → EnumWindowsProc → EnumChildWindows() → EnumChildWindowsProc → EnumProps() → EnumPropsProc → GetProp),该方法实现了SetWindowsSubclass()(详细信息见此链接)。这项技术的工作原理是,一旦子类化的窗口被找到,旧的窗口过程可以被保留,但我们也可以通过更新CallArray字段来为窗口分配一个新的过程。相关的Windows API包括:FindWindow()、FindWindowEx()、GetProp()、GetWindowThreadProcessId()、OpenProcess()、ReadProcessMemory()、VirtualAllocEx()、WriteProcessMemory()、SetProp() 和 PostMessage()。

这段关于代码注入技术的讲解有助于理解恶意软件如何保持隐匿,同时也间接帮助您理解解包技术。

恶意代码分析(一):从入门到精通插图3

解包方法

解包技术的分类和描述相当复杂,但一般来说,有几种方法可以解包恶意软件样本,例如使用调试器、自动化工具、网络服务,甚至编写自己的解包代码以静态执行任务。选择的方法取决于特定的上下文和情况。

a. 调试器 + 在特定函数上设置断点
这是最常见的方法,通常是将恶意软件加载到调试器中,并在一些已知的API上设置软件断点,其中大多数与内存管理和操作有关,接着查找从内存中提取的可执行文件和/或shellcode。使用x64dbg/x32dbg(在命令行中输入[ctrl]+g或bp )很容易在以下API上插入软件断点:

  • CreateProcessInternalW( )

  • VirtualAlloc( )

  • VirtualAllocEx( )

  • VirtualProtect() | ZwProtectVirtualMemory( )

  • WriteProcessMemory() | NtWriteProcessMemory( )

  • ResumeThread() | NtResumeThread( )

  • CryptDecrypt() | RtlDecompressBuffer( )

  • NtCreateSection() + MapViewOfSection() | ZwMapViewOfSection()

  • UnmapViewOfSection() | ZwUnmapViewOfSection()

  • NtWriteVirtualMemory()

  • NtReadVirtualMemory()

在解包过程中,可能会遇到一些问题(例如,恶意软件使用反调试技术)和副作用。因此,在解包前后的注意事项可能会很有帮助:

  • 在恶意软件到达其入口点后(系统断点后)设置断点。

  • 如前所述,建议使用反调试插件,并在少数情况下忽略从0x00000000到0xFFFFFFFF范围内的所有异常(在x64dbg中,转到“选项”→“首选项”→“异常”来包括此范围)。

  • 有时忽略异常可能是个坏主意,因为恶意软件可能会利用它们来调用解包过程。此外(本文不讨论),有些威胁使用中断和异常来调用API。

  • 通过使用MSDN学习所有列出的API及其相应的参数是成功解包恶意软件的关键。

  • 如果使用VirtualAlloc(),建议在其退出点(ret 10)设置断点。此外,有时通过设置内存写入断点可以更容易地跟踪分配的内容。

  • 在某些情况下,恶意软件会将其有效负载提取到内存中,但会销毁PE头,因此你需要通过像HxD这样的十六进制编辑器重新构建整个头部。

  • 提取的有效负载可能是映射格式或未映射格式。如果是映射格式,可能会破坏导入表,你需要通过PEBear(最常用的方法)或使用pe_unmapper手动对齐节头来修复它们。如果基础地址和入口点被置零,还需要修复它们。

  • 为了重建被破坏的IAT,建议使用Scylla(集成在x64dbg中)。这需要输入OEP,可以通过寻找由jmp eax、call eax、call [eax]等指令引起的代码转换来找到OEP。

  • 很少有解包后的恶意软件样本没有IAT中的任何函数,所以有两种可能性:要么节对齐错误(映射版本),要么解包后的恶意软件会动态解析所有功能。

  • 在x64dbg中使用“g”热键可以帮助可视化代码块并查找可能的OEP过渡。

  • 另一个很好的找到OEP的方法是通过代码插装工具,如PIN(https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html)。

  • 像tiny_tracer(https://github.com/hasherezade/tiny_tracer)这样的工具使用PIN进行更轻松的插装,可以用于了解恶意软件调用的函数(对于解包和学习反分析技术非常有用),也可以找到可能的OEP。

  • 在许多情况下,解包的代码可能只是恶意软件的第一阶段,因此需要重复步骤以解包后续阶段。

  • 很少有恶意软件样本会执行自我覆盖,因此你可能需要在.text节上设置断点,以检测解包后的二进制执行。

  • 根据提取的二进制文件(例如shellcode),它可能无法在特定进程上下文之外运行,因此可能需要将其注入到正在运行的进程中(例如explorer.exe)以执行进一步分析。

  • 如何检查提取的恶意软件是否可能是最终的恶意软件?没有明确的答案,少数指示可能通过查看DLL如WS2_32.dll(Winsock)和Wininet.dll中的网络功能、明文字符串、加密功能(主要是恶意软件是否为勒索病毒)以及其他证据来发现。一个好的方法是在IDA Pro中打开提取的代码,尤其是在重新对齐节和/或重建IAT之后。

b. 调试器 + DLL加载断点
这是一种简单而古老的解包恶意软件的方法,方法是在每次加载DLL时停止调试器,并检查内存映射,查看是否有可能从内存中提取的PE格式文件(注意:不要仅关注RWX段,因为许多恶意软件会将有效负载提取到RW区域,并且在将执行上下文转移到提取的可执行文件之前,它们会使用VirtualProtect()将该区域的权限更改为RWX)。毫无疑问,这可能会消耗一些时间,但在许多情况下,它仍然是有效的。常见的调试器(如x64dbg、OllyDbg和Immunity Debugger)提供了一个配置选项,在每次加载DLL时设置断点。在x64dbg中,这个选项位于“选项”→“首选项”→“事件”,并勾选“DLL加载”。在OllyDbg中,可以转到“选项”→“调试选项”→“事件”,并勾选“新模块(DLL)时断点”。

c. 自动化方法
恶意软件分析师可以使用工具来自动化解包过程。Aleksandra Doniec(Hasherezade)提供了出色的工具来实现这个目标:

  • 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 /loop /imp

  • mal_unpack.exe /exe /timeout <timeout: ms>

  • pe-sieve64.exe /pid

  • pe-sieve64.exe /pid /dmode 3 /imp 3

解包后的二进制文件和一些附加信息将保存在工具创建的目录中。

d. Process Hacker
另一种简单(但有限)的方法是通过Process Hacker从内存中提取二进制文件,通过双击运行的进程,转到“内存”选项卡,查找有趣的区域/基地址(RWX),双击它并按“保存”按钮。当然,如果是自注入恶意软件,更容易找到恶意二进制文件/有效负载。如果是远程注入,你需要反向工程恶意软件,理解目标进程,或做出“有根据的猜测”,并寻找在知名目标(如explorer.exe或svchost.exe)中注入的代码。再次强调,这是一种有限且简单的方法,但有时可以节省时间。

e. 使用公共/付费互联网服务
你可以使用像Unpacme(https://www.unpac.me/#/)这样的互联网服务,它提供了自动解包服务。它有一个免费的公共计划(每月10次提交)和其他付费计划,对于研究人员和公司来说非常有趣。此外,它还提供了一套API接口,可以将自定义应用程序与Unpacme服务连接(https://api.unpac.me/)。

f. 编写解包器代码
尽管这种方法看起来很耗时,但编写Python代码进行解包,尤其是在处理shellcode案例或恶意软件线程使用多种反虚拟机和反调试技术的情况下,实际上是相当常见的。此外,我们在处理类似恶意软件案例时可以自动化解包过程。

解包二进制文件

现在我们已经快速讲解了恶意软件分析的基础知识,接下来让我们开始分析。正如我之前所解释的,我选择了这个样本,因为它相对简单,在我看来,它非常适合用作我们系列文章的开端。记住,这个样本的SHA256哈希值是:
8ff43b6ddf6243bd5ee073f9987920fa223809f589d151d7e438fd8cc08ce292

使用PEBear检查它可以帮助我们从恶意软件中收集一些初步有价值的信息。通过PEBear分析恶意软件的结构,可以提供以下有用的信息:

  • PE文件头:检查PE文件的结构,了解是否有异常或被破坏的部分。

  • 节表:查看不同节(如.text、.data等)中的内容,评估是否存在被篡改或隐藏的代码。

  • 导入表:评估恶意软件是否包含对已知API或外部库的引用,有助于理解其功能和行为。

  • 重定位表:如果存在,可能是恶意软件在内存中的位置被改变,可能需要进一步分析如何恢复。

PEBear的使用可以帮助识别潜在的恶意代码部分,从而为进一步的动态分析做好准备。
恶意代码分析(一):从入门到精通插图4
正如我们所看到的,IAT中并没有直接与网络通信、加密或其他真正有趣的功能相关的DLL(及其函数)。因此,这是一个初步的迹象,表明我们的样本可能是被打包的。
由于这个恶意软件样本是一个DLL,我们可以了解它的导出函数,因为我们将在x64dbg/x32dbg中使用这些函数来运行恶意软件。
恶意代码分析(一):从入门到精通插图5

使用pestudio工具,我们能够发现.raw大小和虚拟大小在.data节上的显著差异,而该节也被标记为“可写”。这进一步表明我们的样本可能是被打包的,正如我们预期的那样。
恶意代码分析(一):从入门到精通插图6

我们的恶意软件样本有五个导出函数,在没有在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运行,第一次触发断点时(在VirtualAlloc()上),右键点击EAX并选择“Follow In Dump”。第二次触发断点时会在相同区域,第三次触发时,你可以右键点击EAX并选择“Follow in Dump 2”。如果你愿意(这里不是必须的),你可以进入“Dump 2”选项卡,选择前四个十六进制字节,右键 → 断点 → 内存,写入 → 单次执行(这在恶意软件需要很长时间写入该区域时可能有用)。如果一切顺利(希望如此),你将看到类似于以下的图像:
恶意代码分析(一):从入门到精通插图7

如你所见,ASCII表示中的第一个字符是“M8Z”,这表明它使用了aPLib压缩。然而,如果你转储该区域,你会发现解压后的二进制文件就在相同的区域。因此,右键点击“Dump 2”选项卡中的字节,并选择“Follow in Memory Map”选项:
恶意代码分析(一):从入门到精通插图8
在灰色高亮的基址上,右键点击并选择“Dump Memory to File”将内存区域保存到桌面。现在,保持调试器打开(你可能需要修复可能被破坏的IAT),然后在XVI Editor中打开转储的文件。按CTRL-F并搜索字符串“This program”(注意:此搜索在此情况下有效,因为PE头部没有被破坏):

恶意代码分析(一):从入门到精通插图9

一旦找到字符串 “This program”,你应该向上查找两到三行中的 “MZ” 字符。现在,将光标放在 “MZ” 标记之前的字节上,前往 Edit → Delete to cursor,如下图所示:

恶意代码分析(一):从入门到精通插图10
恶意代码分析(一):从入门到精通插图11
保存文件后,打开 PEBear,并转到 “Imports” 选项卡,如下图所示:

恶意代码分析(一):从入门到精通插图12
正如你所确认的,IAT 完整,包含一个与网络通信相关的 DLL(WININET.dll),你还可以看到一个略微不同的入口点(EP)。因此,这似乎是我们的第一阶段已解包(你应该始终假设可能有进一步的打包阶段,并重复相同的分析,除非你有来自其他来源或报告的外部信息)。此时,你可以关闭 x32dbg,因为我们不再需要它了。

逆向解密代码

现在我们已经解包了二进制文件,让我们在 IDA Pro 中打开它。有很多方法可以找到加密的配置,但其中一种较简单(但有些不准确)的方法是通过查找处理数据或 IDA Pro 中未探索区域的函数(实际上,未探索的区域比下面显示的要大得多)。

恶意代码分析(一):从入门到精通插图13
点击上面未探索区域的起始位置,你将看到以下代码:
恶意代码分析(一):从入门到精通插图14
有趣的是,我们在这个区域看到了一些指向特定地址的交叉引用:
▪ byte_10004000(16字节)
▪ pbData(8字节)
▪ unk_10004018(可能是0x2000字节)

根据我分析恶意软件的经验,我已经知道pbData是微软Crypto API中几个函数的重要参数,因此这表明我们走在正确的轨道上。在许多著名的恶意软件样本中,还有一个常见的模式是结构化的密钥+加密数据,尽管我没有关于这个案例的进一步指示,但我可以推测“pbData”是某种密钥(长度为8字节),虽然有时它并不是最终的密钥,因为恶意软件可能使用KDF(密钥派生函数)来从提供的密码生成最终的密钥。根据我们的分析,“unk_10004018”可能是指潜在的加密数据。

尽管我们的分析进展顺利,我之前提到过,这种“逆向思维”并不精确,因为我们实际上并不知道存储在此地址位置的加密数据的性质。最好是分析恶意软件,从使用这些数据的重要引用和函数入手,这样我们可以对这里涉及的数据类型有更清晰的了解和上下文。

通过跟踪pbData的交叉引用(X快捷键),我们进入了子程序sub_10001CB7。从这一点开始,我们清楚地看到我们的数据(pbData)作为参数被推送到栈中,传递给子程序sub_10002131,如下所示:
恶意代码分析(一):从入门到精通插图15
观察到其他参数后,基于我之前的经验,我知道传递给子程序sub_10002131的其他参数也与加密有关。请注意,我们还应该看到dwBytes(来自esi寄存器,接收到值0x2000)作为参数传递给子程序sub_100011A4,该子程序仅执行缓冲区分配以接收内容,如下所示:
恶意代码分析(一):从入门到精通插图16
分析子程序sub_10002131(稍长一些)的几部分后,我们得到了以下代码:
恶意代码分析(一):从入门到精通插图17
虽然CryptAcquireContextA()是一个已弃用的函数,但它仍然被恶意软件广泛使用。这个 API 用于获取一个密钥容器的句柄,并与 CSP(加密服务提供者)配合使用,它使用默认的密钥容器名称和用户默认提供者,因为两个参数的值为零(来自xor edi, edi)。通过查看dwFlags(值为0x0F0000000),并在wincrypt.h中查找这个值,我们知道它指的是CRYPT_VERIFYCONTEXT提供者类型,这通常出现在使用临时密钥或不需要访问持久化私钥的应用程序中。事实上,正如你将了解到的,这个恶意软件样本使用了一种密钥派生方法。

接下来的代码块揭示了对我们分析非常重要的信息:
恶意代码分析(一):从入门到精通插图18
这个代码块包含了三个 API,我们需要逐个分析它们:

a. CryptCreateHash( )
这个函数创建一个 CSP 哈希对象,它的一个参数Algid很有意思,值被设置为8004h
通过查阅 Microsoft 文档,我们了解到0x8004h表示CALG_SHA1,因此这段恶意代码正在操作 SHA1 哈希(160 位/20 字节)。该函数的返回值是一个指向 CSP 哈希对象的句柄(保存在phHash参数中,最初被置为零),该句柄将会在下一个函数中使用。

b. CryptHashData( )
这个函数将数据(由pbData参数指定)和特定大小(由dwDataLen参数指定)添加到CryptCreateHash( )返回的哈希对象中。两个参数分别是子例程sub_10002131的第三和第四个参数(见图 18)。追踪这些参数,它们来自子例程sub_10001CB7(见图 16),其中显示大小为 8 字节,可能是密钥(pbData)。此时,CryptHashData( )函数将可能的密钥(8 字节)输入哈希对象,从而生成 SHA1 哈希。换句话说,恶意代码正在从一个输入生成一个哈希输出,并且从CryptCreateHash( )返回的句柄指向存储这个哈希数据的 CSP 哈希对象。

c. CryptDeriveKey( )
这个函数根据给定的种子数据值生成会话密钥,并且根据它的定义,它保证使用相同的基础数据会生成相同的会话密钥,因此它非常适合我们的情况,因为我们正在解密配置数据。这里有两个有趣的参数:a.Algid0x6801dwFlags0x280011,我们需要进一步讨论它们。
第二个参数(Algid == 0x6801)标识对称加密算法,根据文档 Cryptographic Algorithm Identifiers0x6801代表CALG_RC4
第四个参数(dwFlags == 0x280011)需要进一步解释。根据文档 CryptDeriveKey
“密钥大小(表示密钥模数的位长)由此参数的高 16 位设置。因此,如果要生成一个 128 位的 RC4 会话密钥,值0x00800000会与其他任何预定义的dwFlags值进行按位或操作”。因此,我们知道 RC4 密钥有 40 位(0x28),即 5 字节。MSDN 文档的另一部分提到,“此参数的低 16 位可以为零,或者你可以使用按位或操作符指定一个或多个以下标志”。我们从 MSDN 页面获取的可能值,并在wincrypt.h文件中查找(ReacOS 提供了一个样本:wincrypt.h)发现0x11代表CRYPT_NO_SALTCRYPT_EXPORTABLE(两个是显而易见的)。

因此,很有趣的是,恶意软件的作者使用 SHA1 作为密钥派生函数(KDF)来生成最终的解密密钥,这可以通过以下步骤解释:
a)pbData = C58B00157F8E9288(记得:要导出数据,可以使用 SHIFT-E)
b)pbData → SHA1(20 字节)
c)SHA1 → CryptHashData( ) → CryptDeriveKey( ) → RC4 密钥(5 字节)

当然,现实中的应用可能会使用更强大的 KDF,例如 Bcrypt、Scrypt 或 Argon2,但我们可以确定,恶意软件的作者并没有考虑是否使用抗 FPGA、ASIC 和 GPU 攻击的 KDF。
恶意代码分析(一):从入门到精通插图19
d. CryptDecrypt( )

这个函数负责使用提供的密钥句柄(hKey)解密加密数据。其他参数包括hHash(来自CryptCreateHash( )的哈希对象句柄)、Final(值为 1,因为这是最后一个也是唯一需要解密的部分)、pbData(包含待解密数据的缓冲区)和pbwDataLen(指向一个 DWORD 的指针,表示pbData缓冲区的长度,在本例中为0x2000)。

请记住,pbwDataLen0x2000)是我们正在分析的函数(sub_10002131(BYTE *rc4_encrypted_data, DWORD pdwDataLen, BYTE *pbData, DWORD dwDataLen))的第二个参数。因此,最终结果(RC4 解密后的数据)被保存在pbData中,大小则保存在pdwDataLen参数中。

另一个有趣的点是,这里的pbData不是 8 字节的密钥,而是来自unk_10004018数据引用的加密数据,该数据在子例程sub_10001214中被传输到dword_10006264内存定义的数组中,我已经重命名了它的参数(使用了“ N ”快捷方式),如下图所示:
恶意代码分析(一):从入门到精通插图20
剩余的函数(CryptDestroyHashCryptDestroyKeyCryptReleaseContext)含义明确,不需要在这里进一步解释。

最终,我们已经获得了所有必要的信息,将在下一步中使用这些信息编写配置提取器和解密器:

  • 初始密钥:C58B00157F8E9288(在.data部分的前 16 字节后)

  • 初始数据地址:0x10004018

  • 数据大小:0x2000

  • 哈希算法:SHA1(20 字节)

  • 解密算法:RC4

  • RC4 密钥大小:5 字节

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

编写数据配置提取器

编写配置提取器的任务一开始看起来可能很复杂,但这只是一个学习路径的问题,一旦掌握了,你就可以自己迈出步伐。此外,这并不是一件新鲜的事情(也不是近几年才出现的),实际上,我个人已经做了很多年。
可以使用多种编程语言,但我选择了 Python 3,因为它具备编写解码器的优良特性。我不仅会解释每行代码的作用,还会解释每个决策背后的理由。
以下是我的 C2 数据解密器代码(命名为 hancitor_conf_extractor_1.py):

恶意代码分析(一):从入门到精通插图21
让我们逐行解释代码,但不按 Python 3 脚本的确切顺序:

a. 第 1、2、3 和 4 行执行了必要的导入,因为代码需要处理 PE 文件,并且这个 Python 3 脚本的目的是解密涉及 RC4 和 SHA1 算法的数据,同时还需要处理二进制到 ASCII 的转换,因此也需要导入 binascii 包。

b. 第 21、22、23 和 24 行开始定义 main() 函数,代码要求用户输入解包后的 Hancitor 二进制文件名,该文件将从中提取加密数据。提取的数据以字节形式存储在 datasec 变量中。

c. extract_data()(第 6 到 13 行)没有什么新内容,但我们应该强调三点:
i.) 我们的目标部分是 ".data",其中存储了密钥和加密数据(参见图 15);
ii.) 我们关心 Unicode 格式,因此使用了 decode(encoding='utf-8');
iii.) 代码会去掉部分可能的 ‘0x00’ 字符(在处理 Unicode 名称时很常见);
iv.) 我们使用 PE 属性来确定 ".data" 部分的开始和结束位置。

d. 第 25 行 (datasec2 = datasec[16:]) 定义了 datasec2 变量,其中包含密钥和所有加密数据,但去除了前 16 个字节,这些字节可能与某个活动 ID 或类似的东西相关。

e. 第 26 行 (key = (datasec2[:8])) 定义了 key 变量,正如我们之前所学,它由 datasec2 的前 8 个字节组成。

f. 第 27 行 (encrypted_data = binascii.hexlify(datasec2[8:256])) 将加密的数据配置存储在 encrypted_data 中。实际上,正如我们从反向代码中学到的,二进制代码为缓冲区分配的大小为 0x2000,但正如你将看到的,收集 248 字节就足够了,当然,你可以根据需要进行调整。hexlify() 将二进制数据转换为十六进制格式。

g. 第 28 行 (hashed_key = SHA.new(key).hexdigest()) 生成一个十六进制的 SHA1 哈希值。

h. 第 29 行 (true_key = hashed_key[:10]) 提取哈希值的前 10 个十六进制字符(即 5 个字节),这一点是根据我们在第 27 页的 CryptDeriveKey() 解释得出的。

i. 第 30 行 (data_decryptor(binascii.unhexlify(true_key), binascii.unhexlify(encrypted_data))) 调用了 decryptor 函数,一个重要的事实是,在使用 RC4 函数之前,我们必须将数据从十六进制字符串转换为二进制表示。

j. data_decryptor()(第 15 到 19 行)非常简单,基本上是通过 RC4 算法使用给定的密钥(来自第 29 行的 true_key 变量)解密加密数据。

k. 最后,第 32 行 (print(c2_config.decode('utf-8'))) 将结果输出到终端。再次提醒,在打印解密后的数据之前,我们需要假设它可能是 Unicode 字符集,并进行解码。

执行这个 Python 3 脚本,针对我们解包的 Hancitor 二进制样本进行操作,我们会得到:
恶意代码分析(一):从入门到精通插图22
太棒了!我们成功地从解包后的 Hancitor 二进制文件中提取并解密了 Hancitor C2 配置。编写解密脚本的优势在于,我们可以将其用于所有遵循相同二进制模式的 Hancitor 样本。

为了验证我们的脚本,让我们寻找另一个 Hancitor 样本,解包它,尝试提取并解密其 C2 数据配置。Malware Bazaar 提供了大量的 Hancitor 样本,我们可以使用 Malwoverview 列出并下载它们,如下图所示:
恶意代码分析(一):从入门到精通插图23
收集许多可能的 Hancitor 哈希值非常容易,如下所示(列表已被截断):

恶意代码分析(一):从入门到精通插图24
因此,我们可以选择其中一个哈希值,从 Malware Bazaar 下载相应的样本,解压缩(密码:infected),然后将其加载到 PE Bear 中:
恶意代码分析(一):从入门到精通插图25

恶意代码分析(一):从入门到精通插图26
使用与之前相同的方法和断点(在 x32dbg 中)解包此样本后,我们应在 PE Bear 中进行验证,如下所示:

恶意代码分析(一):从入门到精通插图27
使用我们的 C2 数据解密脚本对第二个解包的 Hancitor 进行处理,我们得到了:
恶意代码分析(一):从入门到精通插图28

正如我们预期的那样,一切再次顺利完成,我们已经拥有了一个优秀的 Hancitor 脚本,用于提取和解密其 C2 配置数据。

还有两种其他方法可以用来提取 Hancitor 的 C2 数据配置:
▪ Cyber Chef: https://gchq.github.io/CyberChef/
▪ 通过调试器(x32dbg)

这两种方法都很不错,但它们被视为“手动”方法,需要逐个样本进行处理。使用调试器非常简单,因为只需要在 CryptDecrypt() 上设置一个断点,一旦命中,继续执行直到退出点,并在栈上检查其输出参数(第五个参数)。当然,读者应该知道如何执行这些步骤。

要使用 CyberChef,我们需要选择密钥的字节并使用 SHIFT+E(编辑 → 导出数据)将其导出,然后将数据复制到输入区域。选择 "From Hex" 这个食谱(因为导出的数据是十六进制格式),然后选择 SHA1 食谱,因为我们想要从输入数据中生成 SHA1 哈希。
恶意代码分析(一):从入门到精通插图29
对于加密数据(从 0x10004018 获取),重复相同的过程,导出它(SHIFT+E)并将其复制到输入区域。将 RC4 食谱拖入食谱区域,并注意以下几点:
a.) 密码短语由 SHA1 输出的前 10 个十六进制数字(5 字节)组成(正如我们从 CryptDeriveKey() 中学到的);
b.) 密码短语是十六进制格式;
c.) 输入格式也是十六进制格式。

最后,我们得到了与我们的 Python 3 脚本相同的结果。

恶意代码分析(一):从入门到精通插图30

结语

在这篇文章中,我展示了如何通过编写 Python 3 脚本来提取和解密 Hancitor 恶意软件的 C2 数据配置。此外,我还介绍了一些概念和基础知识,例如代码注入和解包,这些将在本系列的后续文章中有所帮助。不过,也许值得在这里强调几点:

a. 我选择了这个简单的恶意软件样本,是因为我的最终目的是帮助其他专业人士在恶意软件分析中迈出第一步,并且为了开始这个系列的文章,这个样本确实很有用。

b. 至今为止,我故意隐去了很多关于该恶意软件的逆向工程细节,因为文章的初衷是专注于 C2 数据配置的提取和解密。

c. 再次强调,尽管许多逆向工程的初学者可能会认为数据提取/解密是全新的,但这是一个非常适合入门的话题。此外,获取 C2 配置是分析恶意软件时的主要目标之一(其他目标还包括感染向量、持久性、规避和网络通信等)。

d. 我选择 Python 3 作为脚本语言,因为我认为它更易于理解,大多数安全研究人员对它比较熟悉。毫无疑问,我们可以用 C 或 Golang 编写程序,未来的文章中也可以使用它们。

e. 在接下来的文章中,我们将研究多个样本和背景,诸如 COM、解包、代码注入、C2 仿真、.NET 逆向工程、反分析技术、API 解析、字符串解密、IDC/IDA Python、IDA AppCall 等话题,所以让我们一步一步来。

继续逆向工程,我们下次见!


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

Web应用&企业产权&域名资产&网络空间&威胁情报
【CTF】Python Jail沙箱逃逸手法总结 PyJail All in One
风投巨头Insight Partners遭遇网络攻击,敏感数据或泄露
网络犯罪转向社交媒体,攻击量达历史新高
雅虎数据泄露事件:黑客涉嫌兜售60.2万个电子邮件账户
黑客如何利用提示词工程操纵AI代理?

发布评论