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
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
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\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
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)[https://docs.microsoft.com/en-us/windows/desktop/api/shlwapi/nf-shlwapi-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
regsvr32 will call
DllRegisterServer and then
DllInstall with the given command line.
/n is also passed,
DllRegisterServer is not called (which implies that
/n alone is not valid).
/i is not given
DllInstall won't be called.
/u is used, the uninstall path is taken (
/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">
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.
The scriptlet can (must?) also declare the methods of its COM interface.
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:
DllInstall method, regardless of the
bInstall parameter, will load the scriptlet given in the command line argument calling
RegisterScriptlet will in turn create a
ComScriptletSite object to get a
ComScriptletConstructor to invoke
The chain of call is a bit deep from here, it goes like this:
LoadFromInput > ... >
CompileScriptlet > ... >
Later the scriptlet registration code is executed as part of its initialisation.
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).
regsvr32.exe is a trusted (signed?) executable, this bypass the Windows restriction on allowed executables (AppLocker).