利用生成式AI逆向工程XLoader
关键要点
XLoader仍然是最具挑战性的恶意软件家族之一。其代码仅在运行时解密,并受到多层加密保护,每层使用隐藏在二进制文件不同位置的密钥。即使是沙箱也无能为力:规避技术会阻止恶意分支执行,真实的C2(命令与控制)域名隐藏在数十个伪造域名中。新版本的发布速度超过了研究人员调查速度,分析几乎总是与时间赛跑(且往往失败)。
生成式AI改变了这种平衡。研究人员不再需要花费数天时间进行细致的手动分析、编写解密例程和逆向工程脚本,现在可以使用AI在几小时内检查复杂函数、识别算法并生成可用的工具,加速获取解密后的代码、字符串和IoC。
Check Point Research(CPR)展示了一种直接从Web界面使用ChatGPT进行恶意软件分析的新方法。通过导出IDA数据并在ChatGPT云中分析,我们证明了无需依赖模型上下文协议(MCP)或实时反汇编会话即可实现基于AI的深度静态逆向工程。这种方法不仅消除了对本地重型工具的依赖,还使结果可重现、更易于共享,并促进研究团队间的协作。
在本研究中,我们使用新的XLoader 8.0样本来演示如何将基于ChatGPT的云静态分析与MCP结合,用于运行时密钥提取和实时调试验证。我们记录了关键任务花费的时间,并包含真实的提示示例,展示了从解包完全加密的二进制文件到恢复隐藏C2域名的完整工作流程。
引言
XLoader是一种广泛观察到的具有信息窃取能力的恶意加载器。它于2020年首次出现,是知名信息窃取器FormBook代码库的重命名版本,此后经历了显著的加固和功能增长。除了Windows变体外,其开发者还推出了macOS版本,尽管在野外出现的频率要低得多。
XLoader是极难分析的恶意软件的典型例子。它结合了多层保护:自定义加密加上额外的混合步骤、伪装为有效但无意义的汇编代码的加密块、混淆的API调用、注入系统进程以及广泛的沙箱规避技术。此外,XLoader加密其网络流量,并将真实C2地址隐藏在数十个诱饵和虚假域名中。
XLoader的一个重要特点是其持续开发。作者定期发布新版本,改变内部机制使其面目全非,并添加新的反分析方法。因此,先前的研究很快过时。在早期版本中,提取配置需要使用复杂算法提取几个密钥,同时获取解密数据只需剥离两层混淆和加密。版本5引入了内置打包器,在版本6和7中,分析人员必须处理数十个相互解密的链式函数,在每个阶段提取中间密钥。对于XLoader的新手来说,入门门槛变得非常高:除了分析本身,还需要额外的时间来熟悉。当一个研究周期完成时,恶意软件的下一个迭代可能已经发布——如果发生重大变化,则需要另一个耗时的调查。
当我们开始这项研究时,XLoader 8.0版本刚刚被发现。似乎XLoader开发者正在赢得这场竞赛。但随着生成式模型的兴起,我们问自己:AI能否改变游戏规则,帮助我们更快地分析如此复杂的恶意软件?为了探索这一点,我们以两种方式应用生成式AI辅助:通过实时MCP连接直接与我们的分析工具集成,以及利用ChatGPT的项目和文件上传功能处理导出的数据。每种方法都有其独特的优势,它们共同使我们能够更有效地解决逆向工程任务。
在本研究中,我们专注于第二种方法,并以最新的XLoader样本为例,展示如何在没有MCP的情况下有效使用ChatGPT进行逆向工程任务。
动机
为了防御XLoader,从每个新版本中提取最新的入侵指标(IoC)至关重要——包括真实的C2域名和URL、加密密钥和版本标识符。这些IoC用于检测签名并帮助跟踪活跃活动。获取IoC的主要方法是从样本中提取并解密恶意软件的配置数据。
挑战在于XLoader不断变化的策略几乎在自动提取工具和脚本开发出来后立即使其失效。恶意软件作者经常调整加密方案和打包方法,专门阻挠这些努力。昨天还能工作的自动配置提取器今天可能就失效了,这意味着每个主要版本都需要新的逆向工程周期。
沙箱提供的帮助有限:
- 积极规避:XLoader检查虚拟机和分析工具的迹象。如果检测到它们,恶意分支可能根本不会运行。
- 即时解密:关键函数和数据在使用前一直加密在内存中,并在使用后不久恢复加密状态。沙箱可能永远无法捕获它们的解密状态。即使您设法在沙箱中转储进程内存,通常也会得到一个不连贯的快照:加密和解密的数据片段混杂在一起,缺少当时不在内存中的任何内容。
- 无用的内存转储:当尝试在调用某些API函数(如NtResumeThread或NtAllocateVirtualMemory)的确切时刻获取内存转储时,您只会得到几乎完全加密的代码。
- C2伪装:恶意软件的真实命令与控制域名隐藏在众多诱饵中。捕获的网络流量通常不完整且包含大量噪音。
简而言之,沙箱无法解决问题。它不提供可重现的转储或完整的IoC集合。
最可靠的方法仍然是静态分析:逐个函数解包所有内容,解密配置,并提取IoC。缺点是手动为每个新版本执行此操作既缓慢又费力。这正是我们希望生成式AI能够作为力量倍增器的地方。
两种AI辅助分析方法
近几个月来,许多逆向工程师开始通过模型上下文协议(MCP)将LLM与IDA Pro集成,以创建AI辅助工作流程。这种代理方法允许模型直接与反汇编器和调试器交互,但它也有其实际挑战。例如,某些MCP客户端设置缺少某些ChatGPT界面功能(如项目或文件上传),并且它们仍然依赖于维护实时IDA会话和稳定连接。
我们探索了两种互补的工作流程来应用GPT-5解析XLoader:
- 实时MCP集成:使用MCP,我们赋予LLM直接访问我们的分析工具(IDA Pro、x64dbg和VMware实例)。这允许AI查询反汇编器、检查内存,甚至实时控制调试器。
- 使用ChatGPT的"离线"数据管道:我们将IDA Pro数据库(反汇编、反编译、字符串等)和恶意软件二进制文件导出到ChatGPT环境。然后,我们要求ChatGPT对这些数据执行静态分析,并在其云沙箱中生成、优化和执行自己的Python脚本。不需要与我们的工具建立实时连接。
每种方法都有其优势。MCP提供代理式交互工作流程,而离线管道提供易于共享和重现的自包含分析。这些方法并不相互排斥——您可以同时使用两者,为每个任务选择合适的工具。
MCP:使用实时工具的代理分析
将LLM连接到IDA的想法并不新鲜。例如,Cisco Talos的研究人员演示了与充当"逆向工程助手"的LLM的IDA集成。在我们的设置中,我们使用MCP将ChatGPT与IDA Pro桥接,并与x64dbg调试器和VMware虚拟机交互。这为LLM提供了对恶意软件执行的实时视图。
这种实时集成除了静态分析和注释IDA数据库外,还使AI能够执行以下操作:
- 按需提取实时数据:在关键函数设置断点,以便在运行时从内存中获取解密缓冲区或加密密钥。
- 执行"实验和观察"循环:假设函数的功能,然后用调试器中的实时运行时数据替换并比较输出,根据结果调整分析。
- 实时协助解包:如果样本自解密或解包代码,则通过这些例程,在中间值或解密代码出现时立即转储它们。
然而,MCP方法并非没有缺点:
- 设置和资源要求:这需要运行IDA Pro和其他工具的实例。分析人员的机器实际上成为循环的一部分,该环境必须保持运行和稳定。
- 单任务焦点:标准IDA不支持在一个界面中进行多个独立分析。如果我们希望并行处理两个样本并获得AI辅助,则需要两个独立的IDA会话和MCP连接。
- 网络依赖性:工作流程依赖于可靠的互联网连接。连接中断或MCP故障可能中断分析过程。
- 有限的ChatGPT UI功能:当使用基于API的自定义MCP客户端时,我们无法利用ChatGPT自身界面的某些便利功能,如长期项目历史记录或轻松的文件管理。
对于许多场景,这些问题是可以管理的,实时交互的好处超过了麻烦。一些解决方案,如MCP SuperAssistant浏览器扩展,通过将ChatGPT界面和MCP连接性结合在一起减少了摩擦。最近,ChatGPT引入了开发者模式,可以直接使用MCP,无需第三方插件。无论您使用插件还是内置模式,工作流程仍然依赖于绑定到运行工具链和稳定连接的实时MCP会话。
如果上述任何要求难以满足,例如您无法保持IDA持续运行,或者需要轻松与没有相同设置的同事共享分析进度,那么可能更倾向于使用不同的方法。这就是我们开发"离线"数据管道作为替代方案的原因。
离线IDA导出管道:在云中使用AI进行逆向工程
我们的第二种方法完全放弃了实时连接。在这里,AI充当自给自足的分析师,从样本的完整静态快照中工作。
工作流程很简单:我们将IDA Pro数据库中的所有内容导出为结构化格式(JSON和文本)。这包括每个函数的反汇编和反编译器输出、交叉引用列表、可读字符串,甚至原始二进制文件本身。我们将.zip文件上传到ChatGPT。
例如,我们的导出包包含以下文件:
|
|
在实践中,最好将存档上传到ChatGPT项目。仅附加在聊天中的文件可能在会话重启后消失,而项目中的文件在整个参与期间保持可用,并可在不同聊天中重复使用。
我们还编写了初始提示,解释数据的组织方式以及AI应如何格式化其输出(例如,以机器可读的JSON格式提议新的函数名称和注释,我们可以将其导回IDA)。本质上,我们教会了AI如何阅读我们给它的电话簿,以及我们希望如何记录其笔记。
设置完成后,该管道允许ChatGPT完全在其自身环境中执行深度静态分析。我们可以要求它查找加密算法、跟踪复杂控制流,甚至编写和执行Python脚本从sample.bin中解密一些数据。许多此类任务无需我们提供任何新信息即可完成——AI使用我们提供的数据工作,根据需要运行Python脚本来验证其逻辑。如果存在错误,它会修复脚本并重新运行测试,重复此过程直到结果收敛。与我们之前的方法相比,所有这些步骤(分析、代码、测试、校正)在单个循环中运行,无需数十次本地MCP调用。自然,这在GPT-5的"思考"模式下效果良好。
这种方法有几个明显的好处:
- 无需持久的本地会话:如果我们的IDA崩溃或我们关闭笔记本电脑,这无关紧要,因为ChatGPT可以从云中访问所需的一切。我们不需要照管实时连接。
- 易于重复和共享:由于分析的整个状态都捕获在我们的导出中,任何拥有存档和提示的人都可以重现分析。我们甚至可以在同一数据上并行运行多个ChatGPT会话(以探索不同问题或使用不同的提示策略)而不会相互干扰。
- 更好地利用ChatGPT的功能:在ChatGPT界面内,我们可以利用文件上传、持久聊天历史和可编辑画布。
- 协作友好:多个研究人员可以独立处理同一样本,之后通过共享suggestions.json文件合并结果,无需比较IDB。
- 安全的脚本执行:ChatGPT可以在安全的云环境中直接对"实时"样本进行原型设计和测试分析脚本,并向分析人员交付可用的输出。
- 极快的入门:无需设置MCP服务器或配置复杂集成。导出的数据和结果甚至可以与未安装IDA且无法打开您IDB的同事共享。
- 广泛适用性:该概念不限于x86二进制文件或IDA。它可以适应几乎任何平台或技术栈。例如,GoLang、.NET或JavaScript样本都可以以相同方式分析。主要挑战是正确准备数据并提供解释如何使用它的定制提示。分析过程保持不变。
也就是说,离线方法并非通用的魔杖。有些情况下我们仍然需要求助于实际调试(因此需要MCP),例如确认猜测的密钥或转储我们的静态分析遗漏的内容。此外,在分析其他恶意软件家族时,我们遇到了需要持续在IDA中工作、涉及不断修改实时数据库的情况。以前,我们需要在每次更改迭代后导出数据库。在这种情况下,基于MCP的方法被证明是更好(即更方便)的替代方案。
出现问题及我们如何修复
毫不奇怪,使用带有离线IDA导出的AI并非一帆风顺。我们在AI性能方面遇到了一些问题,并通过在提示中添加严格规则解决了这些问题。
猜测缺失值
有时模型试图发明缺失的数据,例如在运行时动态计算的加密密钥。为了防止这种"幻觉",我们强制执行证据优先规则:每个数值和每个算法必须由导出(functions.jsonl、decomp/*.c或data.jsonl)中的引用支持,并带有确切的EA地址。如果数据不存在,模型必须生成未找到报告,解释它在哪里查找以及为什么没有找到任何内容。
塑造输出以匹配期望
例如,字符串解密例程预期返回可打印文本,但由于提取密钥时出错,输出损坏。为了使输出"看起来正确",模型应用了Base64。我们禁止任何仅用于使结果看起来有效的装饰性转换(如Base64)。相反,模型必须找到密钥或算法中的实际错误,并重新运行测试直到输出正确。
请求存档中已有的数据
早期,模型有时会请求我们已经提供的数据。我们通过本地优先规则修复了这个问题:首先在存档文件中搜索。仅当数据真正缺失时才应生成未找到报告。
通过这些预防措施,我们的AI"助手"成为静态部分工作的可靠分析员。在接下来的部分中,我们将展示它在XLoader 8.0中的真实挑战上的表现,例如解密有效载荷和API解析,以及偶尔使用MCP驱动的动态检查。
GPT-5实践:分析XLoader的内置加密器
在使用较旧的ChatGTP模型(如o3)时,获得正确结果需要将任务拆分为许多小步骤,并明确告诉模型该做什么,甚至指出确切的代码地址和要应用的算法。没有这种详细程度,输出是不可预测的。这种方法更接近"基于文本的编程",需要我们深度参与。
然而,使用GPT-5,我们可以提出更广泛和更抽象的任务。下面我们展示了一个XLoader内置加密器分析的示例,采用混合方法:使用IDA导出作为主要数据源,并使用MCP+x64dbg进行结果验证。
对于此任务,我们取了一个最近发现的XLoader样本,SHA256为:77db3fdccda60b00dd6610656f7fc001948cdcf410efe8d571df91dd84ae53e1。整个过程我们使用GPT-5的"思考"模式。
在我们给AI助手处理数据的指令后,我们收到了一个简短报告:
接下来,我们故意将AI助手的任务表述为好像我们对分析的样本一无所知,假设这将反映不熟悉XLoader的人的行为。
第一个提示以最抽象的方式编写:
|
|
处理这个简单提示花费了8分46秒。结果,我们的助手正确识别了RC4实现,并得出结论样本已打包。值得注意的是,仅基于模型可用的数据,它建议样本看起来类似于XLoader。同时,存档或初始提示中没有任何内容明确指向这一点。
此外,它检测到API调用混淆。虽然助手在快速分类期间无法完全反混淆所有API调用,但在某些情况下,它从上下文及其签名推断出被调用的函数。
它还成功识别了执行移交到解密代码的点。
在此阶段,我们的首要任务是尽快到达有效载荷。因此,我们首先要求助手查找所有加密函数调用,然后分析有效载荷是如何被解密的。
我们发现主有效载荷块经过两轮RC4:首先是对整个缓冲区的RC4解密,然后是使用不同密钥的256字节块的第二轮处理。
此外,助手成功收集了:
- 解密代码中原始入口点的虚拟地址(0x00430CB3)
- 加密块在二进制文件中的偏移量(0x3143)
- 加密blob大小(0x44A00)
下一步是获取实时密钥并验证结果。此时,我们转向MCP。在其中一个步骤中,我们还要求助手读取一段解密数据以验证静态解密的正确性。
结果,它获得了以下密钥:
- 阶段1密钥:20EBC3439E2A201E6FC943EE95DACC6250A8A647
- 阶段2密钥:86908CFE6813CB2E532949B6F4D7C6E6B00362EE
它还获得了一段解密代码:
在最终密钥从内存中读取后,我们要求助手识别密钥在何处以及通过哪些算法生成,并使用上一步中捕获的实时数据验证分析是否正确。
最终,AI生成了一个解包分析样本的工作脚本。不幸的是,该脚本不具有普遍适用性,因为它用于定位密钥的模式紧密绑定到此特定样本。因此,当我们尝试将其应用于其他版本的样本时失败,需要进一步手动微调。
除了创建通用解包器的最后一步外,整个分析耗时约40分钟,需要39次MCP调用。
解包样本的分析
在地址0x00430CB3(原始入口点:OEP)手动创建名为oep_start的函数后,我们在IDA中打开解包样本并再次应用导出脚本。我们还创建了一个新项目来分析解包样本。
即使在开始深入分析之前,很明显IDA未能识别大部分代码,许多已识别的函数看起来无效。
这可能表明代码以某种方式混淆,或者函数已加密。事实上,我们知道XLoader使用即时函数解密,正如我们在引言中提到的。对于某些函数,应用了多层加密。
为了实验起见,我们想看看AI助手是否能在没有我们指导的情况下自行确定所有这些。我们使用与分析打包样本时相同类型的提示开始分析。
初始分析后,我们识别了:
- 使用混淆的API调用
- 使用RC4加密,并在使用RC4前后进行额外修改
- 某些阶段1构建器(加密函数的解密器)
函数解密方案I
接下来,我们要求助手专注于所谓的阶段1构建器(sub_429143)周围的逻辑,并定位所涉及函数的交叉引用。AI助手识别了90个类似函数。这些函数派生6字节头和尾标记,使用这些标记定位内存中的目标区域,用六个NOP指令(90 90 90 90 90 90)覆盖标记,转换区域,然后将执行转移到每个包装器独有的硬编码地址。
助手还实现了内联Python片段并解密了其中一个加密函数,向我们提供了所有数据和密钥,以及部分解密代码:
有趣的是,在这种情况下,甚至不需要使用MCP,因为提取密钥的有效性可以轻松由AI验证:如果可以在解密后定位代码的开始和结束标记,则意味着密钥和算法已正确恢复。此外,我们可以看到解密的数据看起来不是随机的(它包含像00 00和ff ff这样的序列),这表明函数确实已正确解密。
AI在重新实现算法方面表现非常好,包括带有额外调整的修改版RC4,以及在提供的样本中定位密钥。它还成功实现了检测6字节标记的函数。
然而,它无法完全实现一个无需人工协助即可解密所有函数的通用脚本。问题出现在定位构建20字节有效RC4密钥所需的所有XOR修饰符时。
挑战在于有效RC4密钥是通过将其4字节组件与4字节修饰符进行XOR运算得出的,该修饰符对于每个加密函数是唯一的,并按以下方式计算:
|
|
虽然seed_internal始终位于包装器函数内靠近标记的位置,但助手无法实现查找seed_external的通用方法,因为它可能位于调用函数内的各种位置,并可能故意与其他常量混合。
我们必须手动修改脚本以确保它能正确找到所有外部种子。此外,我们修改了定位剩余常量的规则,使脚本真正健壮,并能与其他样本一起工作。因此,AI显著减少了分析和脚本开发所需的时间,但在此阶段,它不能完全替代人类。
很明显,XLoader的创建者故意通过将关键常量分散在多个函数中来复杂化密钥构建过程,以至于即使是AI也无法开发定位它的算法。我们不披露我们如何派生密钥,以免给XLoader开发者任何优势。
最后,应用脚本后,我们在第一次传递中获得了51个解密函数。许多解密函数还包含对加密函数的类似调用。连续应用脚本三次,我们从最初找到的90个函数中总共获得了77个解密函数。
将结果样本加载到IDA后,我们可以看到大量代码块仍然未被识别:
在快速审查期间,我们还识别了几个仍然保持加密的函数:
还值得注意的是,在上述方案中,加密函数使用6字节序列定位,这些序列在解密后被替换为六个NOP指令。这意味着函数必须具有有效的未加密序言。同时,在图16中,右侧我们可以看到缺少有效序言的加密函数。这可能表明使用了不同的解密方法。
函数解密方案II
我们重新导出数据库并将其加载到ChatGPT中。我们使用以下提示启动分析,并上传了77个函数的解密日志:
值得注意的是,我们在提示中通过指出缺少有效序言使用了一点技巧。没有这个观察,AI助手无法识别额外的解密器。
新发现的方案计算4字节头和尾标记使用XOR。例如:
头标记锚定在真实入口前一个字节。解密后,代码在缓冲区指针加一个字节处写入规范序言(55 8B EC),并用90 90 90 90修补尾部。
函数体使用ai_xform_sub_rc4_sub通过两层解密。
- 第1层密钥:由ai_rc4key20_build(0x00404543)构建,然后与1字节修饰符(如下例中的0x36)进行XOR运算
- 第2层密钥:通过将头标记与16个零字节连接来构建
函数解密方案III
在最新方案中,也使用4字节标记来查找函数的开始和结束。与先前方法一样,应用两层加密,并使用相同的密钥解密第一层。然而,第二层的20字节密钥嵌入在包装器内(每个加密函数唯一),并使用4字节值进行修改。
此外,可能使用单独的函数来处理第二层的解密和修补过程。
我们在XLoader中识别了三种不同的函数解密方案:
- 方案1 - 最常见。在此方案中,加密函数已经以有效序言开始。全局20字节基础密钥在所有函数中重复使用,但每个函数也有唯一的4字节XOR修饰符。基础密钥和修饰符组合以派生每个函数的RC4密钥。然后使用修改后的RC4例程和此密钥解密定义加密块边界的两个6字节标记,随后将相同密钥应用于函数体本身。一旦解密,两个标记被NOP覆盖。
- 方案2 - 使用4字节标记和两层加密。第一层使用由专用密钥构建函数产生的20字节密钥,然后与单个字节进行XOR调整。第二层密钥由4字节头标记与十六个零字节连接构建。解密后,包装器通过在序言处修补55 8B EC并用NOP填充尾部来修复函数。
- 方案3 - 类似于方案2,但第二层密钥不是从头标记派生的。相反,每个包装器嵌入自己的20字节常量(五个与盐XOR的DWORD),用于解密前缀直到标记值,然后应用相同的序言/尾部修补。
值得注意的是,为了实现通用静态解密器,我们仍然必须自己将任务分解为更小的步骤:定位包装器函数、提取20字节密钥、恢复4字节修饰符,以及识别和计算标记位置。只有在确认每个步骤可靠工作并为每个函数生成正确数据后,我们才将它们组合成单个解密器。同时,AI显著减少了实现正则表达式(尽管必须手动调整)以及分析和实现加密算法所需的时间。
随着每次解密迭代,我们获得了一批新的解密函数,其中一些包含解密附加函数所需的密钥。通过连续四次迭代应用所有三个解密器,我们最终成功解密了101个函数。
不幸的是,无法准确测量此任务花费的时间,因为它需要大量额外工作和手动校正。然而,此阶段对我们和LLM来说都是最复杂和耗时的。
我们还获得了suggestions.json,其中包含建议的分析函数名称。这非常有用,因为我们可以将此文件保留用于其他分析会话,并轻松将其导入当前IDB,甚至新IDB(在解密函数后),而无需与旧数据库进行比较。
现在我们有了完全解密的样本,这使我们能够使用相同的方法继续分析。
API调用反混淆
现在我们有了完全解密的样本,我们可以对其应用相同的方法。我们首先加载suggestions.json,然后执行导出。如前所述,即使在样本的第一次分析期间(在我们获得解密函数之前),助手就指出了存在混淆的API调用。此样本中的导入表为空,并且没有可能包含库或函数名称的纯文本字符串。
当我们通过上传解密样本创建新的干净会话时,我们决定测试分析结果的可重现性。
因此,在第一个提示中,不提供任何提示,我们要求助手识别API调用混淆机制。与先前情况一样,我们指定分析应从OEP开始而不是start函数,以便AI助手不会陷入分析打包器。
四分钟后,我们收到了算法的描述:
有趣的是,在第一次分析(当部分函数仍然加密时)和第二次分析期间,我们的AI助手识别了负责API哈希解密的不同函数。在第一种情况下,它指向sub_404603(后来重命名为ai_apiid_decrypt_salt),而在第二种情况下,它仅识别了sub_4045B3(ai_apiid_decrypt)。
接下来,我们使用以下简单提示生成API调用反混淆脚本:
我们获得了一个具有以下功能的脚本:
由于助手无法访问IDA,我们必须手动测试脚本。如果存在错误,我们将结果发送回聊天并要求校正。获得完全工作版本需要五次迭代和约20分钟。
因此,我们还要求助手分析替代路径:
在获得完全功能脚本之前,又进行了五次迭代(发送回错误和校正)和14分钟。
XLoader使用相同的哈希解密机制来查找沙箱工件、虚拟机和研究人员环境的典型进程。在修复问题时,我们还添加了基于字典的哈希暴力破解(从单独文件加载单词列表),这使我们能够自动注释不仅函数,还有对应于特定规避技术的某些字符串:
作为奖励,我们收到了关于API解析如何工作的摘要,描述了两种不同的方法:
总的来说,从第一个提示到完全功能的API反混淆脚本,大约需要AI助手一小时的工作。这个数字不包括本地测试或编写提示所花费的时间。对于此任务,人力投入是最小的。
关键API调用的额外保护
在审查API调用反混淆结果时,我们注意到一些函数通过一个有趣的包装器调用,该包装器最初隐藏在地址0x0040AC93(ai_dec_func_16)的加密函数中。
此函数充当安全调用蹦床:它在调用函数指针之前临时加密几乎整个映像,然后在调用返回后解密相同区域。只有一个微小的"岛"(调用站点返回地址和标记之间的空间)保持未加密,以便执行可以继续。
这种机制值得注意的原因是什么?因为函数几乎始终处于加密状态,甚至很难检测到它的存在而不进行静态解密。在内存转储中,它仅以加密形式出现。
同时,如果某些安全软件或沙箱钩住受此包装器保护的API调用,并尝试在调用时分析或转储进程内存,该机制有效地屏蔽了恶意代码。
总共有20个函数以这种方式受到保护,包括与进程、线程、内存和文件操作相关的NTAPI例程,以及几个WinSock函数。
字符串解密
样本中不包含可读字符串。同时,代码具有一系列短例程,它们共享相同的骨架:序言分配一个小堆栈帧并初始化一个局部数组(其大小因函数而异)。随后是具有相同顺序和相同参数签名的重复三次调用:
让我们分析其中一个函数。由于我们为此任务启动了一个新会话,我们指示AI助手信任简要描述每个函数行为的现有注释,以便它不重新分析已经覆盖的例程。
结果,我们确定此函数使用先前解密的例程之一(ai_dec_func14)实现的算法解密字符串。注意AI如何从单个短提示构建调用图并描述每个例程的行为,仅依赖于先前准备的存档中的数据:
通过简单提示,我们轻松获得加密密钥和解密字符串。
现在我们确信AI助手理解加密字符串的存储方式,知道密钥如何派生,并且已经分析并正确重新实现了解密算法(在真实数据上验证),我们可以继续实现脚本来解密剩余字符串。
这次我们使用了稍微更详细的提示,因为我们希望特定信息同时出现在注释和控制台输出中:
这次我们很幸运,立即获得了一个工作脚本,我们用它解密了175个字符串:
分析所需的总时间约为20分钟。
解密域名
提取入侵指标(IoC)列表始终是恶意软件分析的关键任务。网络指标,如域名和URL,尤其重要,因为它们有助于通过流量分析检测和分类恶意软件。这就是为什么提取域名至关重要——即使有些可能是诱饵或诱饵,或者当前不活动但计划以后使用。
在恢复的字符串中,我们看到64个Base64编码的条目。查看XLoader的版本历史,我们发现从版本2.8开始,它开始以Base64形式存储加密域名。毫无疑问,这64个Base64字符串代表我们必须解密的域名。早在版本4,XLoader就添加了两个额外的修改版RC4加密层,使用不同的密钥,使解密更加复杂。在后期版本中,此过程变得更加复杂。总共,要到达解密的域名,我们需要剥离至少五层,首先识别密钥在何处以及如何初始化:解密初始化加密字符串的函数,解密字符串本身(我们之前已经完成),对结果进行base64解码,并应用另外两层解密。
同时,获取每层的密钥是最困难的部分,因为生成它们所需的不同数据片段分散在多个函数中,使它们难以定位。
在继续之前,我们更新了字符串反混淆脚本,以便它也重命名负责检索解密字符串的函数,并将前缀ai_dec_domain_{NN}分配给所有处理Base64编码字符串的函数。然后我们导出数据库以准备域名解密。
所有对ai_dec_domain_{NN}函数的调用发生在单个函数ai_dec_func_0(0x00404913)内:
我们使用最简单的提示开始分析此函数:
作为分析结果,我们获得了详细描述,显示ai_dec_func_0(后来重命名为ai_dec_func_0_domain_tag_generate)是XLoader的阶段1域名构建器。对于给定的域名索引(1..64),它从ai_dec_domain_NN拉取匹配的种子字符串,base64解码它,然后运行键控RC4-with-diff转换,其密钥是存储在ctx+0x23D0的20字节秘密(其中ctx是存储密钥、函数地址和其他数据的全局结构),并与域名索引进行字节XOR。结果重新编码为base64并写入输出缓冲区。这是一个中间工件,不是最终的ASCII域名。
接下来,我们要求助手重现ai_dec_func_0_domain_tag_generate中实现的转换。因为我们的脚本已经在先前步骤中检索了解密字符串并将它们添加为注释,我们请求这些字符串与重复的字符串解密一起使用:
结果,我们的助手无法自行定位ctx+0x23D0处的密钥:
这并不令人惊讶,因为导出的数据中没有一个包含对结构字段的引用,只能通过结构中的偏移定位,或在调试器中捕获。
因此,我们必须手动定位此密钥初始化的位置,并将该信息提供给AI助手。
结果,我们获得了由ai_dec_domain_01返回的字符串在第一解密层后的中间值,该值被重新编码为Base64为(Qvm75Acm5NpYTbnYXdcvBw==):
因为字符串仍然加密,我们尚不知道接下来会发生什么,我们需要进一步调查:
结果,我们发现获得的Base64字符串再次解码,然后使用函数ai_dec_func_11(0x004095F3)内生成的20字节密钥通过第二层解密。此密钥还与SALT_DWORD进行XOR运算。然而,ai_dec_func_11中缺少SALT_DWORD的初始化,因此助手无法自行检索它。
与先前情况一样,我们手动恢复了缺失值。复杂的是,不是使用偏移0x25C8,而是使用基址0x2000,稍后添加0x5C8,这使得搜索有点困难。
我们不仅提供了假定发生SALT_DWORD初始化的函数地址,还提供了相关代码片段:
在分析期间,我们的AI助手确认密钥正确,并成功解密了对应于ai_dec_domain_01的域名:
现在AI助手可以自动解密所有域名。让我们通过要求它解密前16个域名来验证这一点:
最终,我们获得了包含完全解密域名的表格:
让我们回想一下,在分析的最开始,我们发现了ai_dec_func_0的一个单独分支,当最后一个值等于222时激活。在这种情况下,生成一个4字符标签,后来成为URL的一部分。以前,此标签对所有域名都是相同的,并且对恶意软件活动是唯一的。然而,现在每个域名都有自己的标签。
我们尝试使用以下提示解密前16个域名的标签:
响应提示,助手提供了恶意软件如何生成那些4字符标签的描述,以及包含前16个标签的表格。
结论
从最初出现开始,XLoader一直是一个移动目标,每个新版本都为安全分析人员和防御者提高了门槛。XLoader开始是一个两层谜题,但演变成了嵌套解密器、分散密钥材料和仅运行时代码的迷宫。多年来,这意味着当研究人员完全解析样本时,攻击者已经领先一步发布了下一个版本。
生成式AI改变了这种平衡。结合基于云的分析和偶尔的MCP辅助运行时检查,我们将大部分机械逆向工程委托给LLM。我们不再花费数小时手动重写解密例程,而是要求我们的AI模型完成,并在几分钟内收到工作原型。
AI的使用并不消除对专业知识的需要。XLoader最复杂的保护,如分散的密钥派生逻辑和多层函数加密,仍然需要手动分析和有针对性的调整。但分类、反混淆和脚本编写的繁重工作现在可以显著加速。曾经需要数天的工作现在可以压缩到数小时。
对于防御者来说,这不仅仅是生产力提升。更快的周转意味着更新的IoC、更快的检测更新,以及攻击者更短的机会窗口。对于研究人员来说,它降低了分析野外一些最复杂恶意软件家族的入门门槛。
我们的研究表明,通过正确的工作流程,生成模型已经可以作为力量倍增器——帮助安全防御者跟上曾经被认为耗时过多而无法分析的威胁。
然而,宣布胜利还为时过早,因为我们预计恶意软件作者会适应AI辅助分析的技术。而反过来,我们将需要提出下一个改变游戏规则的技术。
保护
Check Point Threat Emulation和Harmony Endpoint提供对攻击策略、文件类型和操作系统的全面覆盖,并保护免受本报告中描述的 attacks 和威胁。