Sandboxie/Sandboxie/core/svc/comserver9.c

639 lines
18 KiB
C

/*
* Copyright 2004-2020 Sandboxie Holdings, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//---------------------------------------------------------------------------
// COM Server Simulation
//---------------------------------------------------------------------------
//
// simulate an Internet Explorer or Windows Media Player
// COM server process in order to pick up the url to open.
// then run the requested server program (IE/WMP/etc) with
// the specified url.
// see also Custom_ComServer in core/dll/custom.c
//
#define COBJMACROS
#include <objbase.h>
#include <docobj.h>
#include <htiface.h>
#include "htiface7.h"
#include <htiframe.h>
#include <hlink.h>
#include <exdisp.h>
#include <mshtmlc.h>
#include <stdio.h>
#include "common/defines.h"
#include "common/my_version.h"
#include "core/dll/sbiedll.h"
#include "core/drv/api_flags.h"
//---------------------------------------------------------------------------
// Defines
//---------------------------------------------------------------------------
#undef COMSERVER_DEBUG
//---------------------------------------------------------------------------
// IMyUnknown / IMyClassFactory
//---------------------------------------------------------------------------
typedef struct _IMyUnknown IMyUnknown;
typedef IMyUnknown *(*P_MyCreateInstance)(REFIID riid);
typedef void *(*P_MyQueryInterface)(IMyUnknown *This, REFIID riid);
typedef struct _IMyUnknown {
ULONG_PTR Vtbls[16];
P_MyQueryInterface pMyQueryInterface;
struct {
ULONG_PTR pBackPointerToObject;
ULONG_PTR pIUnknownQueryInterface;
ULONG_PTR pIUnknownAddRef;
ULONG_PTR pIUnknownRelease;
} IUnknownVtbl;
ULONG_PTR VtblSpace;
// followed by more vtbls
} IMyUnknown;
typedef struct _IMyClassFactory {
struct IClassFactoryVtbl *lpVtbl; // struct IClassFactory
P_MyCreateInstance pMyCreateInstance;
IClassFactoryVtbl VtblSpace;
} IMyClassFactory;
static IUnknown *ComServer_MyClassFactory_New(
P_MyCreateInstance pMyCreateInstance);
static HRESULT ComServer_IClassFactory_QueryInterface(
IClassFactory *This, REFIID riid, void **ppvObject);
static ULONG ComServer_IClassFactory_AddRef(IClassFactory *This);
static ULONG ComServer_IClassFactory_Release(IClassFactory *This);
static HRESULT ComServer_IClassFactory_CreateInstance(
IClassFactory *This, IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
static HRESULT ComServer_IClassFactory_LockServer(
IClassFactory *This, BOOL fLock);
static IMyUnknown *ComServer_MyUnknown_New(
P_MyQueryInterface pMyQueryInterface, ULONG SizeofVtbls);
static HRESULT ComServer_IUnknown_QueryInterface(
IUnknown *This, REFIID riid, void **ppvObject);
static ULONG ComServer_IUnknown_AddRef(IUnknown *This);
static ULONG ComServer_IUnknown_Release(IUnknown *This);
//---------------------------------------------------------------------------
// Functions
//---------------------------------------------------------------------------
static void ComServer_RestartProgram(const WCHAR *arg);
static void *Dll_Alloc(ULONG size);
//---------------------------------------------------------------------------
// GUIDs
//---------------------------------------------------------------------------
// {00000000-0000-0000-C000-000000000046}
static const GUID IID_IUnknown = {
0x00000000, 0x0000, 0x0000,
{ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }
};
// {00000001-0000-0000-C000-000000000046}
static const GUID IID_IClassFactory = {
0x00000001, 0x0000, 0x0000,
{ 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 }
};
//---------------------------------------------------------------------------
// Variables
//---------------------------------------------------------------------------
static volatile LONG ComServer_Factory_RefCount = 0;
static volatile BOOL ComServer_Factory_Locked = FALSE;
static volatile LONG ComServer_Object_RefCount = 0;
static WCHAR *ComServer_ExeName = NULL;
static WCHAR *ComServer_BoxName = NULL;
enum {
ComServer_ImageType_Unknown,
ComServer_ImageType_IE,
ComServer_ImageType_WMP,
ComServer_ImageType_WINAMP,
ComServer_ImageType_KMPLAYER,
ComServer_ImageType_Last
};
static ULONG ComServer_ImageType = ComServer_ImageType_Unknown;
//---------------------------------------------------------------------------
// Sub modules
//---------------------------------------------------------------------------
#include "comserver9_ie.c"
#include "comserver9_wmp.c"
//---------------------------------------------------------------------------
// ComServer_Main
//---------------------------------------------------------------------------
_FX void ComServer_Main9(const WCHAR *OriginalCmdLine)
{
MSG msg;
DWORD cookie;
HRESULT hr = -1;
int hr_lvl = 9;
WCHAR *exepath, *cmdline, *pgm;
//
// extract box name from command line
//
ComServer_BoxName = wcsrchr(OriginalCmdLine, L':');
if (! ComServer_BoxName)
goto abort;
++ComServer_BoxName;
hr = SbieApi_IsBoxEnabled(ComServer_BoxName);
if (hr != 0)
goto abort;
//
// extract path to requested server program
//
exepath = Dll_Alloc((MAX_PATH + 4) * sizeof(WCHAR));
cmdline = Dll_Alloc(8192 * sizeof(WCHAR));
GetEnvironmentVariable(ENV_VAR_PFX L"COMSRV_EXE", exepath, MAX_PATH);
exepath[MAX_PATH] = L'\0';
GetEnvironmentVariable(ENV_VAR_PFX L"COMSRV_CMD", cmdline, 8190);
cmdline[8190] = L'\0';
//
// determine which server program was requested
//
pgm = wcsrchr(exepath, L'\\');
if (pgm) {
++pgm;
if (_wcsicmp(pgm, L"iexplore.exe") == 0)
ComServer_ImageType = ComServer_ImageType_IE;
else if (_wcsicmp(pgm, L"wmplayer.exe") == 0)
ComServer_ImageType = ComServer_ImageType_WMP;
else if (_wcsicmp(pgm, L"winamp.exe") == 0)
ComServer_ImageType = ComServer_ImageType_WMP;
else if (_wcsicmp(pgm, L"kmplayer.exe") == 0)
ComServer_ImageType = ComServer_ImageType_WMP;
}
if (ComServer_ImageType == ComServer_ImageType_Unknown)
goto abort;
ComServer_ExeName = exepath;
//
// register with the real system COM as the provider
// for objects with CLSID_InternetExplorer
//
hr_lvl = 1;
hr = CoInitialize(0);
if (SUCCEEDED(hr)) {
const GUID *pClsid;
P_MyCreateInstance pMyCreateInstance;
if (ComServer_ImageType == ComServer_ImageType_IE) {
pClsid = &CLSID_InternetExplorer;
pMyCreateInstance = IEServer_MyCreateInstance;
} else if (ComServer_ImageType == ComServer_ImageType_WMP) {
pClsid = &CLSID_WindowsMediaPlayer_Play;
if (cmdline && wcsstr(cmdline, L"/Enqueue"))
pClsid = &CLSID_WindowsMediaPlayer_Enqueue;
pMyCreateInstance = WMPServer_MyCreateInstance;
} else if (ComServer_ImageType == ComServer_ImageType_WINAMP) {
pClsid = &CLSID_WinAmp;
pMyCreateInstance = WMPServer_MyCreateInstance;
} else if (ComServer_ImageType == ComServer_ImageType_KMPLAYER) {
pClsid = &CLSID_KmPlayer;
pMyCreateInstance = WMPServer_MyCreateInstance;
}
hr_lvl = 2;
hr = CoRegisterClassObject(
pClsid, ComServer_MyClassFactory_New(pMyCreateInstance),
CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &cookie);
}
abort:
if (FAILED(hr)) {
SbieApi_Log(2314, L"%S [HR=%08X/%d]", pgm, hr, hr_lvl);
ExitProcess(0);
}
//
// message loop to handle COM messages
//
#if 1
SetTimer(NULL, 0, 2000, NULL);
#else
while (! IsDebuggerPresent()) Sleep(500);
__debugbreak();
#endif
while (1) {
BOOL b = GetMessage(&msg, 0, 0, 0);
if (b == 0)
break;
if (b != -1) {
if (msg.message == WM_TIMER) {
if (ComServer_Object_RefCount == 0 &&
ComServer_Factory_RefCount <= 1 &&
ComServer_Factory_Locked == FALSE) {
#ifdef COMSERVER_DEBUG
OutputDebugString(L"End by WM_TIMER\n");
#endif
break;
}
#ifdef COMSERVER_DEBUG
OutputDebugString(L"Ignoring WM_TIMER\n");
#endif
}
DispatchMessage(&msg);
}
}
#ifdef COMSERVER_DEBUG
OutputDebugString(L"COM Server Shutting Down\n");
#endif
//
// done
//
CoRevokeClassObject(cookie);
Sleep(5000);
ExitProcess(0);
}
//---------------------------------------------------------------------------
// ComServer_RestartProgram
//---------------------------------------------------------------------------
_FX void ComServer_RestartProgram(const WCHAR *arg)
{
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
WCHAR *ptr;
ULONG len = (wcslen(ComServer_ExeName) + wcslen(arg) + 32)
* sizeof(WCHAR);
WCHAR *cmd = Dll_Alloc(len);
WCHAR *dir = Dll_Alloc(len);
wsprintf(cmd, L"\"%s\" \"%s\"", ComServer_ExeName, arg);
wcscpy(dir, ComServer_ExeName);
ptr = wcsrchr(dir, L'\\');
if (ptr)
*ptr = L'\0';
#ifdef COMSERVER_DEBUG
OutputDebugString(L"ComServer Restart Command Line:\n");
OutputDebugString(cmd);
#endif
//
// run the requested server program in the sandbox
// with the requested url as an argument
//
memzero(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
StartupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
SbieDll_RunSandboxed(ComServer_BoxName, cmd, dir, 0,
&StartupInfo, &ProcessInformation);
}
//---------------------------------------------------------------------------
// ComServer_MyClassFactory_New
//---------------------------------------------------------------------------
_FX IUnknown *ComServer_MyClassFactory_New(
P_MyCreateInstance pMyCreateInstance)
{
IMyClassFactory *This;
ULONG_PTR *ptr;
This = Dll_Alloc(sizeof(IMyClassFactory));
This->pMyCreateInstance = pMyCreateInstance;
This->lpVtbl = &This->VtblSpace;
ptr = (ULONG_PTR *)This->lpVtbl;
ptr[0] = (ULONG_PTR)ComServer_IClassFactory_QueryInterface;
ptr[1] = (ULONG_PTR)ComServer_IClassFactory_AddRef;
ptr[2] = (ULONG_PTR)ComServer_IClassFactory_Release;
ptr[3] = (ULONG_PTR)ComServer_IClassFactory_CreateInstance;
ptr[4] = (ULONG_PTR)ComServer_IClassFactory_LockServer;
return (IUnknown *)This;
}
//---------------------------------------------------------------------------
// ComServer_IClassFactory_QueryInterface
//---------------------------------------------------------------------------
_FX HRESULT ComServer_IClassFactory_QueryInterface(
IClassFactory *This, REFIID riid, void **ppvObject)
{
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IClassFactory_QueryInterface - %08X-%08X-%08X-%08X\n",
((ULONG *)riid)[0], ((ULONG *)riid)[1], ((ULONG *)riid)[2], ((ULONG *)riid)[3]);
OutputDebugString(txt);
#endif
if (! ppvObject)
return E_POINTER;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IClassFactory)) {
InterlockedIncrement(&ComServer_Factory_RefCount);
*ppvObject = This;
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
//---------------------------------------------------------------------------
// ComServer_IClassFactory_AddRef
//---------------------------------------------------------------------------
_FX ULONG ComServer_IClassFactory_AddRef(IClassFactory *This)
{
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IClassFactory_AddRef - %d\n", ComServer_Factory_RefCount + 1);
OutputDebugString(txt);
#endif
InterlockedIncrement(&ComServer_Factory_RefCount);
return ComServer_Factory_RefCount;
}
//---------------------------------------------------------------------------
// ComServer_IClassFactory_Release
//---------------------------------------------------------------------------
_FX ULONG ComServer_IClassFactory_Release(IClassFactory *This)
{
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IClassFactory_Release - %d\n", ComServer_Factory_RefCount - 1);
OutputDebugString(txt);
#endif
InterlockedDecrement(&ComServer_Factory_RefCount);
return ComServer_Factory_RefCount;
}
//---------------------------------------------------------------------------
// ComServer_IClassFactory_CreateInstance
//---------------------------------------------------------------------------
_FX HRESULT ComServer_IClassFactory_CreateInstance(
IClassFactory *This, IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
HRESULT hr;
IUnknown *obj;
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IClassFactory_CreateInstance - %08X-%08X-%08X-%08X (pUnkOuter=%08X)\n",
((ULONG *)riid)[0], ((ULONG *)riid)[1], ((ULONG *)riid)[2], ((ULONG *)riid)[3], pUnkOuter);
OutputDebugString(txt);
#endif
if (! ppvObject)
return E_POINTER;
if (pUnkOuter) {
*ppvObject = NULL;
return CLASS_E_NOAGGREGATION;
}
obj = (IUnknown *)(((IMyClassFactory *)This)->pMyCreateInstance(riid));
if (obj) {
hr = IUnknown_QueryInterface(obj, riid, ppvObject);
if (FAILED(hr)) {
//Dll_Free(obj);
*ppvObject = NULL;
}
} else
hr = E_NOINTERFACE;
return S_OK;
}
//---------------------------------------------------------------------------
// ComServer_IClassFactory_LockServer
//---------------------------------------------------------------------------
_FX HRESULT ComServer_IClassFactory_LockServer(
IClassFactory *This, BOOL fLock)
{
ComServer_Factory_Locked = fLock;
return S_OK;
}
//---------------------------------------------------------------------------
// ComServer_MyUnknown_New
//---------------------------------------------------------------------------
_FX IMyUnknown *ComServer_MyUnknown_New(
P_MyQueryInterface pMyQueryInterface, ULONG SizeofVtbls)
{
ULONG len;
IMyUnknown *This;
ULONG_PTR *ptr;
len = sizeof(IMyUnknown) + SizeofVtbls;
This = Dll_Alloc(len);
memzero(This, len);
This->pMyQueryInterface = pMyQueryInterface;
ptr = (ULONG_PTR *)&This->IUnknownVtbl;
*ptr = (ULONG_PTR)This; // pBackPointerToObject
++ptr;
This->Vtbls[0] = (ULONG_PTR)ptr;
ptr[0] = (ULONG_PTR)ComServer_IUnknown_QueryInterface;
ptr[1] = (ULONG_PTR)ComServer_IUnknown_AddRef;
ptr[2] = (ULONG_PTR)ComServer_IUnknown_Release;
ptr += sizeof(IUnknownVtbl) / sizeof(ULONG_PTR);
return This;
}
//---------------------------------------------------------------------------
// ComServer_IUnknown_QueryInterface
//---------------------------------------------------------------------------
_FX HRESULT ComServer_IUnknown_QueryInterface(
IUnknown *This, REFIID riid, void **ppvObject)
{
void *xThis = *(ULONG_PTR **)((*(ULONG_PTR **)This) - 1);
IMyUnknown *MyThis = (IMyUnknown *)xThis;
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IUnknown_QueryInterface - %08X-%08X-%08X-%08X\n",
((ULONG *)riid)[0], ((ULONG *)riid)[1], ((ULONG *)riid)[2], ((ULONG *)riid)[3]);
OutputDebugString(txt);
#endif
if (! ppvObject)
return E_POINTER;
if (IsEqualIID(riid, &IID_IUnknown)) {
*ppvObject = (void *)&MyThis->Vtbls[0];
} else {
*ppvObject = MyThis->pMyQueryInterface(MyThis, riid);
}
if (*ppvObject) {
InterlockedIncrement(&ComServer_Object_RefCount);
return S_OK;
} else {
#ifdef COMSERVER_DEBUG
OutputDebugString(L"ComServer_IUnknown_QueryInterface - E_NOINTERFACE\n");
#endif
return E_NOINTERFACE;
}
}
//---------------------------------------------------------------------------
// ComServer_IUnknown_AddRef
//---------------------------------------------------------------------------
_FX ULONG ComServer_IUnknown_AddRef(IUnknown *This)
{
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IUnknown_AddRef - %d\n", ComServer_Object_RefCount + 1);
OutputDebugString(txt);
#endif
InterlockedIncrement(&ComServer_Object_RefCount);
return ComServer_Object_RefCount;
}
//---------------------------------------------------------------------------
// ComServer_IUnknown_Release
//---------------------------------------------------------------------------
_FX ULONG ComServer_IUnknown_Release(IUnknown *This)
{
#ifdef COMSERVER_DEBUG
WCHAR txt[128];
swprintf(txt, L"ComServer_IUnknown_Release - %d\n", ComServer_Object_RefCount - 1);
OutputDebugString(txt);
#endif
InterlockedDecrement(&ComServer_Object_RefCount);
return ComServer_Object_RefCount;
}
//---------------------------------------------------------------------------
// Dll_Alloc
//---------------------------------------------------------------------------
_FX void *Dll_Alloc(ULONG size)
{
return HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, size);
}