编写任意 VB6 应用程式

案例中心

在进行恶意软体分析时,通常需要与正在运行的程式码互动,以了解它的运作方式。这可以通过API钩取、调试、系统监控等技术来实现。有些任务甚至需要提取、重组或重用恶意软体的程式码,以执行特定的功能。这在像是域名生成和解密例程的程式码中是很常见的。

每当有简单的方法可以重用现有功能时,我的兴趣就会被激发。这篇文章中展示的研究为我们提供了一种强大的程式码重用机制。我们今天要详细说明的技术,让我们可以获得任何现有的Visual Basic 6VB6可执行档的脚本访问权限,这种技术不需对源代码进行任何修改。

一旦实施,我们将可以访问已加载的表单、嵌入的控件和公共成员,这包括对以表单层公共实例持有的任何类层次结构的访问。靠著一些额外的工作,程式中的任何活动类实例也可以以类似的方式被访问。手动创建内部类的新实例也被证明可行。

过去已经探讨过类似的技术。接下来是我对这个问题集的探索。

背景

所有VB6对象都是支持IDispatch接口的COM对象。这是该语言的一个核心特征,允许它们被脚本引擎轻松使用。由于这样很方便,开发人员通常会在他们的应用中内部添加脚本支持以便于自动化。

为了开发一个允许外部自动化的组件,Visual Basic 6支持ActiveX DLL和ActiveX Exe项目类型。如果您曾经使用过调用CreateObject()的脚本,那么您已经与ActiveX COM对象打过交道。COM的支持在Windows操作系统中根深蒂固,并包含许多强大的功能。

既然我们知道所有的VB6 COM对象本质上是可脚本化的,这种功能是否可以在不访问源代码的情况下为随机可执行文件启用?

探索运行时

在探索这个问题时,我开始检查运行时,以查找如何轻松提取活动对象引用并手动实现可脚本访问。

首先要查看的,影响最大的是Forms对象。

这个对象持有每个VB组件加载表单的引用。一旦我们获得了对单独表单的访问,则也可以访问它的所有嵌入控件和公共成员。这将是我们旅程的一个重要第一步。

如果我们查看一个使用全局Forms对象的原生可执行档,我们将看到以下生成的代码:

这个代码足够简单,可以重复使用,应该能够按需获得全局Forms对象的引用。请注意,每个VB组件都有其自己的Global对象。ActiveX DLL无法访问主可执行文件的表单,除非明确传递引用

在我们测试这个理论之前,首先需要一种方法在主VB6过程中执行我们自己的代码。DLL注入是显而易见的答案,但这并不像您想的那么简单。

在尝试在VB6过程中运行注入代码时,有两个因素必须考虑。

首先是运行时初始化。在调用任何运行时函数之前,必须执行一定数量的内部步骤。在我们之前的论文Binary Reuse of VB6 PCode Functions中,这是CreateIExprSrvObj的调用完成的。我们可以注入到完全加载的进程中,但这会产生一些侧面效果,稍后会讨论。为了我们的目的,我们将需要在进程启动时进行注入。

第二点是所有VB6可执行档都使用单线程公寓STA模型。要使用运行时函数,我们需要在主VB6线程中进行工作。在其他线程中工作会导致进程崩溃,因为运行时试图访问它预期存在的线程本地存储TLS成员。虽然人们已经设法在VB6中使用多个线程,但我们在这里将避免这样做。

我们的初始标准是:

必须在进程启动时进行注入在初始化之前不能调用运行时函数仅能从主VB6线程调用运行时函数

第一次实验是在启动时进行注入,然后对user32BeginPaint API进行钩取。这个钩取是从主VB线程在创建表单时调用的。此时,运行时已完全初始化并准备使用。我创建了一个快速的模拟并进行了尝试。

在这里我们遇到了第一个障碍。显然vbaNew2实际上无法创建此对象的实例,并且抛出Class not registered错误。这令人困惑,因为我们实际上在使用的代码是从编译器本身中提取的。

为了验证这个结果,我首先确认了我对vbaNew2函数的使用是基于其他已知的有效数据,然后在钩取中运行时进行了测试。这两者的测试都表现良好。

然后我仔细查看了范例VB应用,发现了一个小惊喜。在上面的反组装代码中,我们看到它检查是否已经存在有效的对象引用。如果是,那么创建将被跳过。

惊讶的是,即使在FormLoad的第一次使用中,对象引用已经被设置。vbaNew2调用从未使用,仅仅是编译器的产物。事实上,强制调用vbaNew2也导致同样的Class not registered错误。

Forms对象的引用会在任何用户代码执行之前设置。这表明它必须是运行时在某处设置的特殊情况。在objRefdataSect地址上设置硬件断点很快证实了这一点。

VB运行时有一个名为TipRegAppObject的内部函数。这个函数会扫描VBHeaderProjectInfoExternalTable,查找类型为6且CLSID匹配的条目。如果找到,它会手动设置对象实例地址。

如果应用程序开发者从未使用全局Forms类,则TipRegAppObject没有任何操作可执行,且将不会设置引用。

如果开发者未使用此类方式,我们无法正式创建所需的类,因为它不会在任何容易抓取的缓存中。我们可以寻找其他方法来尝试查找它,但我们已经有一个普遍的位置,保证它会出现。

在这个时候,我决定钩取一个内部运行时函数。钩取硬编码的偏移有一些缺点,比如将我们锁定到特定的DLL版本。对于一个研究工具,这是一个可以接受的权衡。

经过进一步分析后,我决定在TipRegAppObject上方钩取一层,在CVBApplicationInit(CVBApplication this)中。

火烧云npv

这使我们能够访问完整的CVBApplication对象,其中包括其他内部类的引用以及可执行档的VBHeader结构的引用。

实施

最初的注入例程现在设置了两个钩子。一个在CVBApplicationInit上,以获取对内部运行时类的引用。第二个在BeginPaint上,这样我们可以在初始化完成后在主VB线程上触发。

接下来要考虑的是如何在主VB6线程中执行我们的最终有效负载。一种技术是简单地将我们自己的ActiveX DLL加载到进程中,同时传递对主组件Forms对象的引用。这基本上是在应用程序中强制实现插件模型。这应该运行良好,但不是我的首选。

我的主要目标是获得某种远程脚本访问权。这就是运行对象表ROT的魔力所在。ROT是ActiveX可执行文件如何注册自己为GetObject()可用的方式。

COM对象存在于一个执行档中,作为伺服器运行。外部客户端然后连接并从另一个进程使用它。

在手动注册ROT时,我注意到它适用于表单对象,但不允许访问内部类。

Error 0x62 A property or method call cannot include a reference to a private object either as an argument or as a return value

我们知道这个类的核心实现完全支持脚本。这在使用内部脚本引擎或插件模型时不会成为问题。ActiveX Exe类和标准可执行档类之间有什么区别?

在结构检视器中比较两者时,我们发现ActiveX Exe类的ObjectObjectType设置了0x800位。如果我们在磁碟上修补它,我们现在就可以完全访问所有标准类。ObjectType也可以在内存中进行修补,但必须在创建类之前进行修补以生效。这就是为什么我们必须在进程创建时进行注入。

我们的CVBApplicationInit钩取是进行这项操作的完美时机,因为我们已经拥有了对VBHeader结构的引用,且尚未创建任何类实例。我们完整的CVBApplicationInit钩取如下所示:

完整的BeginPaint钩取如下:

在这段代码中,我们首先禁用钩取,以便它不会再次触发。我们只需要在主VB线程中获得一个初步的 foothold。

为了保持持续的访问,我们注册了一个新的系统菜单项,并对主窗口进行了子类化。

这样,我们可以手动触发以后可能需要的新功能。在设置了窗口子类化的情况下,我还实现了一个基本的进程间通信IPC服务器,以便程序化访问。这些附加功能并非必需,但对于未来的探索是很有帮助的。

最终,我们将主可执行档的全局Forms对象注册为remoteforms在ROT中。

完成这一切后,一切都已经运行起来了!

下面展示了一个与我们的测试应用互动的Windows脚本主机WSHJavascript:

编写任意 VB6 应用程式

甚至可以使用Python:

结论

在这篇文章中,我们介绍了如何使用语言和操作系统的内置功能,使任何VB6应用程序都可以进行远程脚本化。

虽然我们在过程中遇到了一些障碍,但一切最终都是可控的,并成功完成了试验。

对事件的简要回顾:

目标VB6进程启动时注入了一个DLL新线程钩取CVBApplicationInit和BeginPaintCVBApplicationInit钩取:在运行时初始化期间存储内部VB对象的引用遍历VBHeaderProjectInfoObjectTable,设置所有类为公共BeginPaint钩取:从主VB6线程运行添加系统菜单项并子类化主窗口以实现IPC可选在ROT中注册内部Forms对象IPC允许注入者与目标进程之间进行双向通信。

我们的第一步是基于表单集合,以简单性和影响深度为考量。这项技术可以应用于任何COM对象实例。

除了表单集合外,还可以通过对多个运行时API如vbaNew、vbaNew2、vbaFreeObj等钩取来拦截对其他内部对象的访问。

对这项技术未来创新的一些想法:

跟踪新的类实例,并将其添加到ROT中的一个公开VB集合中注入者保持活动对象的可视管理器使用IPC回调注入者实现自己的脚本主机以进行集成能够在访问过渡对象时暂停主进程人为增加引用计数以保持实例存活使用IPC和vbaNew触发任意类/表单创建已进行测试

这项研究的完整源代码包含在以下git库中。这里面包括了一个注入器、DLL、测试应用和示范脚本。

该代码作为概念验证工作发布。对于特定的目标,可能需要进行调整。这项研究仅仅是这项技术可能性的开始。

标签为研究、脚本、VB

分享XFacebook

数位生活什么是物联网 (IoT)?如今,不仅仅是电脑、手机和平板电脑能够连接网络。越来越多的事物也在上线:门锁、冰箱、电视等等。继续阅读以了解物联网的起源及其工作方式。然后,安装像 Avast One 这样的专用安全工具,以增强您的网络安全性。免费安装 Avast One适用于 MacPCAndro...