Brief introduction to COM
COM is a Microsoft standard for the binary layout of objects in memory.
This standard is independent of the programming language used though is similar to the well-known C++ virtual dispatching through the vtables.
Basically, COM consists of objects and interfaces.
Object are abstract entities, they are a collection of interfaces.
Interfaces are identified by an UUID (known as CLSID) and optionally a more user friendly Program ID (ProgID).
An interface consists of a set of methods and an eventual parent interface.
A programmer never uses objects, only interfaces. They have to somehow get a pointer to an interface and invoke its methods.
At the low level, an interface pointer is just a pointer to a vtable.
If the programmer already have a pointer to any interface of an object, it can get any other interface of the same object because all interfaces inherit from IUnknown
that exposes a QueryInterface
method.
However to get the first interface a side band mechanism is needed.
Particularly, the module where the object (interfaces) implementations reside must be loaded in the calling process address space (if it's an in-process server) or must be started and an IPC/Network channel established (if it's an out-of-process server).
All the work is handled by the CoGetClassObject
API that given a CLSID will do all the hard work and return a pointer to the given interface (eventually through an in-process proxy).
Brief introduction to COM server registration
For CoGetClassObject
to work it need to find the type of server and the associated module (DLL or EXE) from a CLSID or a ProgID.
This information is stored in the registry (Under HKCR
and HKCR\CLSID
), when missing a COM client will fail to invoke methods on the server.
It's the responsibility of the server module to create the right keys. Microsoft developed a convention (or it's a standard?) so that a programmer didn't need to conceive their self with the registration burden: A module will export DllRegisterServer
and DllUnregisterServer
and the installation wizard will call them for each COM component (EXE module may use different names, I don't remember).
The tools performing the registration is regsvr32.exe
, when given the path of a COM module it will load the module and call one of those functions.
Optionally a module can export (DllInstall
) which is used to perform additional installation steps besides the pure COM registration.
This function accepts a boolean parameter to tell installation from uninstallation (note: there is no DllUninstall
, the doc is wrong) and a string denoted as the "command line".
The implementation is free to use it however it wants.
When used with /i
regsvr32
will call DllRegisterServer
and then DllInstall
with the given command line.
If /n
is also passed, DllRegisterServer
is not called (which implies that /n
alone is not valid).
If /i
is not given DllInstall
won't be called.
If /u
is used, the uninstall path is taken (DllUnregisterServer
and DllInstall
with FALSE
).
/s
is for not showing message boxes.
Brief introduction to COM Scriptlet
Writing COM components is easy if you use VB6 or VC++ or Delphi but otherwise is painful.
To enable scripting languages to write COM component Micosoft introduced a new format for the COM server: the COM Scriptlets.
They are XML files with the source code and a bit of metadata.
<?XML version="1.0" ?>
<scriptlet validate="true" error="true" debug="true">
<registration
description="XXX"
progid="YYY"
version="1.00"
classid="ZZZZ"
>
</registration>
The metadata are just the ProgID, the CLSID and a description plus a version.
A full example is here.
The code inside the registration
tag is executed as part of the server registration, unregistration process.
<registration
...
>
<script language="JScript">
<![CDATA[
...
]]>
</script>
</registration>
The scriptlet can (must?) also declare the methods of its COM interface.
The srcobj.dll
I don't know what srcobj.dll
is used for in Windows.
I just fired up IDA and retrieved the DLL's debug symbols to see what's going on.
First, the DLL exports all the COM registration methods:
![Exports](../../images/3815443565.webp)
The DllInstall
method, regardless of the bInstall
parameter, will load the scriptlet given in the command line argument calling RegisterScriptlet
![DllInstall](../../images/3831303312.webp)
RegisterScriptlet
will in turn create a ComScriptletSite
object to get a ComScriptletConstructor
to invoke LoadFromPath
:
![Scriptlet loading](../../images/3824356490.webp)
The chain of call is a bit deep from here, it goes like this: LoadFromInput
> ... > CompileScriptlet
> ... > CompileRegistration
.
Later the scriptlet registration code is executed as part of its initialisation.
The bypass
The net effect of loading a scriptlet is that some script code is executed in the context of regsvr32.exe
(as part of the registration phase).
As regsvr32.exe
is a trusted (signed?) executable, this bypass the Windows restriction on allowed executables (AppLocker).