COM简介
COM是 Microsoft 标准,用于内存中对象的二进制布局。
该标准独立于所使用的编程语言,尽管类似于通过 vtables 进行的著名 C++ 虚拟调度。
基本上,COM 由对象和接口组成。
对象是抽象实体,它们是接口的集合。
接口由 UUID(称为 CLSID)和可选的对用户更友好的程序 ID (ProgID) 标识。
一个接口由一组方法和一个最终的父接口组成。
程序员从不使用对象,只使用接口。他们必须以某种方式获取指向接口的指针并调用其方法。
在底层,接口指针只是一个指向 vtable 的指针。
如果程序员已经有一个指向对象的任何接口的指针,它可以获取同一对象的任何其他接口,因为所有接口都继承自IUnknown
它公开了一个QueryInterface
方法。
然而,要获得第一个接口,需要边带机制。
特别是,对象(接口)实现所在的模块必须加载到调用进程地址空间(如果它是进程内服务器)或必须启动并建立 IPC/网络通道(如果它是进程外服务器)。
所有的工作都由CoGetClassObject
给定 CLSID 的 API 处理,并将完成所有艰苦的工作并返回一个指向给定接口的指针(最终通过进程内代理)。
COM服务器注册简介
为了CoGetClassObject
工作,它需要从 CLSID 或 ProgID 中找到服务器的类型和相关的模块(DLL 或 EXE)。
此信息存储在注册表中(下HKCR
和HKCR\CLSID
),当缺少 COM 客户端时,将无法调用服务器上的方法。
服务器模块负责创建正确的密钥。微软开发了一个约定(或者它是一个标准?),这样程序员就不需要承担注册负担:一个模块将导出DllRegisterServer
,DllUnregisterServer
安装向导将为每个 COM 组件调用它们(EXE 模块可能使用不同的名字,我不记得了)。
执行注册的工具是regsvr32.exe
,当给定 COM 模块的路径时,它将加载模块并调用其中一个函数。
可选的模块可以导出 ( DllInstall
) 用于执行除了纯 COM 注册之外的其他安装步骤。
这个函数接受一个布尔参数来告诉安装和卸载(注意:没有DllUninstall
,文档是错误的)和一个表示为“命令行”的字符串。
该实现可以随意使用它。
使用时/i
regsvr32
将调用DllRegisterServer
,然后DllInstall
使用给定的命令行。
如果/n
也通过了,DllRegisterServer
则不会调用(这意味着/n
单独是无效的)。如果/i
未给出DllInstall
将不会被调用。
如果/u
使用,则采用卸载路径(DllUnregisterServer
和DllInstall
with FALSE
)。
/s
用于不显示消息框。
COM Scriptlet 简介
如果您使用 VB6 或 VC++ 或 Delphi,编写 COM 组件很容易,但否则会很痛苦。
为了使脚本语言能够编写 COM 组件,Micosoft 为 COM 服务器引入了一种新格式:COM Scriptlets。
它们是带有源代码和一些元数据的 XML 文件。
<?XML version="1.0" ?>
<scriptlet validate="true" error="true" debug="true">
<registration
description="XXX"
progid="YYY"
version="1.00"
classid="ZZZZ"
>
</registration>
元数据只是 ProgID、CLSID 和描述加上版本。
一个完整的例子是here。
标签内的registration
代码作为服务器注册、注销过程的一部分执行。
<registration
...
>
<script language="JScript">
<![CDATA[
...
]]>
</script>
</registration>
scriptlet 还可以(必须?)声明其 COM 接口的方法。
srcobj.dll
我不知道srcobj.dll
在 Windows 中用于什么。
我刚刚启动了 IDA 并检索了 DLL 的调试符号以查看发生了什么。
首先,DLL 导出所有 COM 注册方法:

DllInstall
无论参数如何,该方法都bInstall
将加载命令行参数调用中给出的脚本RegisterScriptlet

RegisterScriptlet
将反过来创建一个ComScriptletSite
对象ComScriptletConstructor
来调用LoadFromPath
:

调用链从这里有点深,它是这样的:LoadFromInput
> ... > CompileScriptlet
> ... > CompileRegistration
。
稍后,scriptlet 注册代码作为其初始化的一部分执行。
旁路
加载 scriptlet 的最终效果是一些脚本代码在regsvr32.exe
(作为注册阶段的一部分)的上下文中执行。
作为regsvr32.exe
受信任的(签名?)可执行文件,这绕过了 Windows 对允许的可执行文件 (AppLocker) 的限制。