tsmfhook.cpp revision 2a7322928382e52db8e320d19df4f03dee16b3ed
/* $Id$ */
/** @file
* VBoxMMR - Multimedia Redirection
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "stdafx.h"
#include "tsmfhook.h"
#include "tsmf.h"
#include "logging.h"
#include <DbgHelp.h>
#include <stdio.h>
#include <mfidl.h>
#include <WtsApi32.h>
#include <VBox/VBoxGuestLib.h>
#include <VBox/VBoxGuest.h>
#define CODECAVE_SIZE 0x64
/* Generic byte code for API calls redirection.
* It corresponds to the following set of instructions:
* push ebp
* mov ebp, esp
* pop ebp
* mov eax, 0DEADBEEFh
* jmp eax
*/
static const char g_szCodeCave[] = "\x55\x8B\xEC\x5D\xB8\xEF\xBE\xAD\xDE\xFF\xE0";
const WCHAR *g_pwszMMRAdditions =
L"SOFTWARE\\Oracle\\VirtualBox Guest Additions";
const char *g_pszVRDETSMF = "/vrde/tsmf";
{
if (lResult == ERROR_SUCCESS)
{
if (lResult == ERROR_SUCCESS &&
{
}
}
return fEnabled;
}
/* Events, which are specific for the tsmf channel. */
#define VBOX_TSMF_HCH_CREATE_ACCEPTED (VBOX_HOST_CHANNEL_EVENT_USER + 0)
extern bool isWMP;
struct HookEntry
{
};
struct HostChannelCtx
{
bool fShutdown;
};
HostChannelCtx g_HostChannelCtx = {0};
struct ReadReq
{
};
{
if (NULL != g_hTraceSession)
{
/* Size FieldTypeFlags */
{
}
else
}
delete pReq;
}
class VBOX_RDP_CHANNEL
{
// we need to ignore the first write operation(s), as they are handled by VRDP for us
// we probably don't want this in the final solution.
protected:
{
// FIXME : we really want the VBox RDP server to preserve these for us
delete [] pBuffer;
}
public:
{
ChannelList.push_back(this);
ChannelMap[Id] = this;
};
{
ChannelList.remove(this);
{
}
}
void PushRequest(PBYTE Buffer, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionFunc)
{
// the pseudo handle returned by GetCurrentTHread() can not be used with QueueUserAPC()
THREAD_SET_CONTEXT, FALSE, 0);
if (!m_BufferList.empty())
{
m_BufferList.pop();
}
else
{
}
}
{
{
}
else
{
}
}
{
{
return true;
}
return false;
}
static uint32_t ChannelCount()
{
return ChannelList.size();
}
{
return NULL;
}
#ifdef TRACE
{
// avoid filling up the map too much
if (pSharedHdr[2] != 0x100 && pSharedHdr[2] != 0x101 && pSharedHdr[2] != 0x106 && pSharedHdr[2] != 0x107 && pSharedHdr[2] != 0x108)
return;
}
#endif
};
{
const size_t nImportDescOffset = pOptHdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
}
{
DWORD nCCAddress = 0;
PIMAGE_SECTION_HEADER pSectionsHdr = (PIMAGE_SECTION_HEADER)((DWORD)pBaseAddr + pDosHdr->e_lfanew + sizeof(IMAGE_NT_HEADERS32));
for (DWORD i = 0; i < nNumberOfSections; i++)
{
{
break;
}
}
if(0 != pTextSectionHdr)
{
}
return nCCAddress;
}
{
if ( 0 != nNew )
{
{
{
return true;
}
}
}
return false;
}
{
unsigned int nCCOffSet = sizeof(g_szCodeCave);
{
}
{
if (!nCodeCaveAddr)
{
/* If fails perform the hooking without code caving */
VBoxMMRHookLog("VBoxMMR: Retrieving code cave location had failed\n");
WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &pfnHook, 4, (SIZE_T *) &cbWritten);
return;
}
{
}else{
}
0xDEADBEEF,
if (!bResult)
{
/* If fails perform the hooking without code caving */
VBoxMMRHookLog("VBoxMMR: Replacing code cave stub address failed\n");
WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &pfnHook, 4, (SIZE_T *) &cbWritten);
return ;
}
sizeof(g_szCodeCave),
0 );
if (!bResult)
{
/* If fails perform the hooking without code caving*/
VBoxMMRHookLog("VBoxMMR: Writing byte code to the text section failed\n");
WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &pfnHook, 4, (SIZE_T *) &cbWritten);
return;
}
bResult = WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &nCodeCaveAddr, 4, &cbWritten);
if (!bResult)
{
VBoxMMRHookLog("VBoXMMR: Patching the IAT failed\n");
WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &pfnHook, 4, (SIZE_T *) &cbWritten);
return;
}
}else
{
WriteProcessMemory(GetCurrentProcess(), &pIATEntry->u1.Function, &pfnHook, 4, (SIZE_T *) &cbWritten);
}
}
void InstallHooks(const IMAGE_IMPORT_DESCRIPTOR *pDescriptor, const PBYTE pBaseAddr, HookEntry *pEntries)
{
DWORD nCodeCaveOffSet= 0;
while(pDescriptor->FirstThunk)
{
{
{
{
{
{
VBoxMMRHookLog("VBoxMMR: Install Hook for dll %s function %s \n",
}
}
}
}
}
++pDescriptor;
}
}
{
int rc;
if (RT_FAILURE(rc))
{
}
return rc;
}
{
int rc;
if (*hMonitor != NIL_RTTHREAD)
{
if (RT_SUCCESS(rc))
{
// rc = RTThreadWait(*hMonitor, RT_INDEFINITE_WAIT, NULL);
if (RT_FAILURE(rc))
{
}
}
else
{
}
*hMonitor = NIL_RTTHREAD;
}
return rc;
}
DECLCALLBACK(int)
{
maskInfo.u32NotMask = 0;
VBoxMMRHookLog("VBoxMMR: MonitorDetach starting\n");
if (DeviceIoControl (ghVBoxDriver, VBOXGUEST_IOCTL_CTL_FILTER_MASK, &maskInfo, sizeof (maskInfo), NULL, 0, &cbReturned, NULL))
{
VBoxMMRHookLog("VBoxTray: VBoxVRDPThread: DeviceIOControl(CtlMask - or) succeeded\n");
}
else
{
VBoxMMRHookLog("VBoxTray: VBoxVRDPThread: DeviceIOControl(CtlMask) failed\n");
return 0;
}
for(;;)
{
/* Call the host to get VRDP status and the experience level. */
if (DeviceIoControl (ghVBoxDriver,
sizeof(VMMDevVRDPChangeRequest),
sizeof(VMMDevVRDPChangeRequest),
&cbReturned, NULL))
{
{
"VBoxMMR: VRDP active status changed: %d\n",
VBOX_RDP_CHANNEL::ChannelCount() > 0)
{
VBoxMMRHookLog("VBoxMMR: exiting ...\n");
ExitProcess(0);
break;
}
}
}
else
{
VBoxMMRHookLog("VBoxMMR: VBoxVRDPThread: Error from DeviceIoControl VBOXGUEST_IOCTL_VMMREQUEST\n");
}
{
VBoxMMRHookLog("VBoxMMR: detach monitor received stop signal\n");
break;
}
}
VBoxMMRHookLog("VBoxMMR: MonitorDetach stopping\n");
return VINF_SUCCESS;
}
/*
* WTSQuerySessionInformationW
*/
BOOL (WINAPI * g_pfnWTSQuerySessionInformation)(HANDLE, DWORD, WTS_INFO_CLASS, LPWSTR *, DWORD *) = NULL;
BOOL WINAPI MMRWTSQuerySessionInformation(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPWSTR *ppBuffer, DWORD *pBytesReturned)
{
BOOL b = g_pfnWTSQuerySessionInformation(hServer, SessionId, WTSInfoClass, ppBuffer, pBytesReturned);
if (WTSIsRemoteSession == WTSInfoClass)
{
*pb = 1;
return b;
}
else if (WTSClientProtocolType == WTSInfoClass)
{
*pus = 2;
return b;
}
return b;
}
BOOL WINAPI LocalWTSQuerySessionInformation(HANDLE hServer, DWORD SessionId, WTS_INFO_CLASS WTSInfoClass, LPWSTR *ppBuffer, DWORD *pBytesReturned)
{
}
/*
* WriteFile
*/
BOOL WINAPI MMRWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
if (NULL != g_hTraceSession)
{
/* Size FieldTypeFlags */
}
{
*lpNumberOfBytesWritten = 0;
int rc = VbglR3HostChannelSend(
(void *) lpBuffer, nNumberOfBytesToWrite);
if (RT_SUCCESS(rc))
{
}
"VBoxMMR: TSMF send, channel: %d, sent: %d, result: %d\n",
}
else
{
}
return b;
}
BOOL WINAPI LocalWriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
}
/*
/*
* ReadFileEx
*/
BOOL (WINAPI *g_pfnReadFileEx)(HANDLE, LPVOID, DWORD, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE) = 0;
BOOL WINAPI MMRReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
if (NULL != g_hTraceSession)
{
/* Size FieldTypeFlags */
}
{
}
else
{
}
return br;
}
BOOL WINAPI LocalReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
{
}
/*
* CloseHandle
*/
{
if (NULL != g_hTraceSession)
{
/* Size FieldTypeFlags */
}
{
delete pChannel;
return TRUE;
}
return g_pfnCloseHandle(hObject);
}
{
return MMRCloseHandle(hObject);
}
/*
* CancelIo
*/
{
{
// FIXME
return TRUE;
}
return g_pfnCancelIo(hFile);
}
{
return MMRCancelIo(hFile);
}
/*
* WinStationIsSessionRemoteable
*/
{
*pb = 1;
return b;
}
{
}
/*
* WinStationVirtualOpenEx
*/
HANDLE WINAPI MMRWinStationVirtualOpenEx(HANDLE hServer, DWORD hSession, LPSTR pVirtualName, DWORD flags)
{
// assert(WTS_CURRENT_SERVER == hServer)
// assert(WTS_CURRENT_SESSION == hSession)
{
uint32_t u32ChannelHandle = 0;
int rc = VbglR3HostChannelAttach(
if (RT_SUCCESS(rc))
{
{
h = new VBOX_RDP_CHANNEL(u32ChannelHandle);
if (hDetachMonitor == NIL_RTTHREAD)
{
RTTHREADFLAGS_WAITABLE, "mmrpoll");
}
}
else
{
}
}
else
{
}
}
if (NULL != g_hTraceSession)
{
/* Size FieldTypeFlags */
}
return h;
}
HANDLE WINAPI LocalWinStationVirtualOpenEx(HANDLE hServer, DWORD hSession, LPSTR pVirtualName, DWORD flags)
{
}
/*
* GetSystemMetrics
*/
{
const char *format = (nIndex < 100) ? "VBoxMMR: GetSystemMetrics(%d)\n" : "VBoxMMR: GetSystemMetrics(0x%x)\n";
if (0x1000 == nIndex)
return 1;
return g_pfnGetSystemMetrics(nIndex);
}
{
return MMRGetSystemMetrics(nIndex);
}
HookEntry g_TSMFHooks[] =
{
{ "winsta.dll", "WinStationIsSessionRemoteable", LocalWinStationIsSessionRemoteable, (PVOID *) &g_pfnWinStationIsSessionRemoteable, (PVOID *) &MMRWinStationIsSessionRemoteable },
{ "winsta.dll", "WinStationVirtualOpenEx", LocalWinStationVirtualOpenEx, (PVOID *) &g_pfnWinStationVirtualOpenEx, (PVOID *) &MMRWinStationVirtualOpenEx },
{ "user32.dll", "GetSystemMetrics", LocalGetSystemMetrics, (PVOID *) &g_pfnGetSystemMetrics, (PVOID *) &MMRGetSystemMetrics},
{ "kernel32.dll", "ReadFileEx", LocalReadFileEx, (PVOID *) &g_pfnReadFileEx, (PVOID *) &MMRReadFileEx },
{ "kernel32.dll", "CloseHandle", LocalCloseHandle, (PVOID *) &g_pfnCloseHandle,(PVOID *) &MMRCloseHandle },
{ "wtsapi32.dll", "WTSQuerySessionInformationW", LocalWTSQuerySessionInformation, (PVOID *) &g_pfnWTSQuerySessionInformation, (PVOID *) &MMRWTSQuerySessionInformation },
};
/*
* MFCreateRemoteDesktopPlugin
*/
HRESULT (STDAPICALLTYPE *g_pfnMFCreateRemoteDesktopPlugin)(IMFRemoteDesktopPlugin **ppPlugin) = NULL;
{
VBoxMMRHookLog("VBoxMMR: LocalMFCreateRemoteDesktopPlugin: *ppPlugin = %p, result: %x\n", *ppPlugin, hr);
static bool IsTSMFHooked = false;
if (!IsTSMFHooked)
{
if (hModule)
{
VBoxMMRHookLog("VBoxMMR: Installing hooks for tsmf.dll\n");
IsTSMFHooked = true;
}
}
return hr;
}
{
return MMRMFCreateRemoteDesktopPlugin(ppPlugin);
}
/*
* GetProcAddress
*/
{
{ "kernel32.dll", "GetProcAddress", LocalGetProcAddress, (PVOID *) &g_pfnGetProcAddress,(PVOID *) &MMRGetProcAddress },
};
{
{ "user32.dll", "GetSystemMetrics", LocalGetSystemMetrics, (PVOID *) &g_pfnGetSystemMetrics,(PVOID *) &MMRGetSystemMetrics},
{ "kernel32.dll", "GetProcAddress", LocalGetProcAddress, (PVOID *) &g_pfnGetProcAddress,(PVOID *) &MMRGetProcAddress },
};
{
{ "user32.dll", "GetSystemMetrics", LocalGetSystemMetrics, (PVOID *) &g_pfnGetSystemMetrics,(PVOID *) &MMRGetSystemMetrics },
{ "kernel32.dll", "GetProcAddress", LocalGetProcAddress, (PVOID *) &g_pfnGetProcAddress,(PVOID *) &MMRGetProcAddress },
};
{
static bool IsMFHooked = false;
if (!IsMFHooked)
{
{
VBoxMMRHookLog("VBoxMMR: Installing hooks for mf.dll\n");
IsMFHooked = true;
}
}
static bool IsWinMMHooked = false;
if (!IsWinMMHooked)
{
{
VBoxMMRHookLog("VBoxMMR: Installing hooks for winmm.dll\n");
IsWinMMHooked = true;
}
}
// if an ordinal, all but the lower word must be 0
// FIXME: should be pointer size
{
szDllName[0] = '\0';
}
else
{
if (0 == strncmp(lpProcName, "WTS", 3) || 0 == strncmp(lpProcName, "MF", 2) || 0 == strncmp(lpProcName, "GetTS", 5))
{
g_pfnMFCreateRemoteDesktopPlugin = (HRESULT (STDAPICALLTYPE *)(IMFRemoteDesktopPlugin **ppPlugin)) ret;
{
szDllName[0] = '\0';
}
}
{
g_pfnWTSQuerySessionInformation = (BOOL (__stdcall *)(HANDLE,DWORD,WTS_INFO_CLASS,LPWSTR *,DWORD *)) ret;
{
szDllName[0] = '\0';
}
}
}
return ret;
}
{
}
HookEntry g_WMPHooks[] =
{
{ "user32.dll", "GetSystemMetrics", LocalGetSystemMetrics, (PVOID *) &g_pfnGetSystemMetrics, (PVOID *)&MMRGetSystemMetrics },
{ "kernel32.dll", "GetProcAddress", LocalGetProcAddress, (PVOID *) &g_pfnGetProcAddress, (PVOID *)&MMRGetProcAddress },
{ "mf.dll", "MFCreateRemoteDesktopPlugin", LocalMFCreateRemoteDesktopPlugin, (PVOID *) &g_pfnMFCreateRemoteDesktopPlugin, (PVOID *) &MMRMFCreateRemoteDesktopPlugin },
{ "wtsapi32.dll", "WTSQuerySessionInformationW", LocalGetProcAddress, (PVOID *) &g_pfnGetProcAddress,(PVOID *) &MMRGetProcAddress },
};
/*
* Note that McAffee hooks into GetProcAddress as well (HIPIS0e011b5.dll), so there is no point
* in patching "GetProcAddress" of ole32.dll
*/
static ULONG WINAPI ControlCallback(WMIDPREQUESTCODE RequestCode, PVOID Context, ULONG *Reserved, PVOID pBuffer)
{
switch(RequestCode)
{
case WMI_ENABLE_EVENTS: // Enable the provider
if (NULL == g_hTraceSession)
break;
case WMI_DISABLE_EVENTS: // Disable the provider
// igonore disable requests from other sessions
if (hTraceSession == g_hTraceSession)
break;
default:
break;
}
return status;
}
static int VBoxMMROpenBaseDriver(void)
{
/* Open VBox guest driver. */
NULL,
NULL);
if (INVALID_HANDLE_VALUE == ghVBoxDriver )
{
dwErr = GetLastError();
VBoxMMRHookLog("VBoxMMR: Could not open VirtualBox Guest Additions driver! Please install / start it first! Error = %08X\n", dwErr);
}
return RTErrConvertFromWin32(dwErr);
}
static void VBoxMMRCloseBaseDriver(void)
{
if (ghVBoxDriver)
{
ghVBoxDriver = NULL;
}
}
void
{
uint32_t u32SizeReceived = 0;
uint32_t u32SizeRemaining = 0;
pBuffer + sizeof(u32SizeAvailable),
"VBoxMMR: TSMF recv, channel: %d, available: %d, received %d, remaining %d, result: %d\n",
{
{
"VBoxMMR: Unknown TSMF Data: %X %X %X\n",
}
}
else
{
delete [] pBuffer;
}
}
DECLCALLBACK(int)
{
VBoxMMRHookLog("VBoxMMR: MonitorTSMFChannel starting\n");
{
uint32_t u32ChannelHandle = 0;
uint32_t u32EventId = 0;
uint32_t u32SizeReturned = 0;
int rc = VbglR3HostChannelEventWait(
if (RT_SUCCESS(rc))
{
switch(u32EventId)
{
{
} break;
{
} break;
{
} break;
{
VBoxMMRHookLog("VBoxMMR: VBOX_HOST_CHANNEL_EVENT_CANCELLED\n");
} break;
{
VBoxMMRHookLog("VBoxMMR: VBOX_HOST_CHANNEL_EVENT_UNREGISTERED\n");
} break;
{
if (p->u32SizeAvailable > 0)
{
}
else
{
}
} break;
default:
{
} break;
}
}
}
VBoxMMRHookLog("VBoxMMR: MonitorTSMFChannel stopping\n");
return VINF_SUCCESS;
}
{
{
}
else
{
}
}
{
static bool bInit = false;
{
bInit = true;
VBoxMMRHookLog("VBoxMMR: WMP APIs Hooking started ...\n");
{
}
else
{
VBoxMMRHookLog("VBoxMMR: Error hooking wmp -> not found\n");
}
if (ERROR_SUCCESS != ret)
{
}
if (!bInRDPSession)
{
uint32_t u32HGCMClientId = 0;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
"tsmfio");
}
else
{
"VBoxMMR: TSMF HGCM unavailable: hgcmid: %d, rc: %d\n",
}
}
else
{
}
}
}
}
void Shutdown()
{
if (isWMP)
{
VBoxMMRHookLog("VBoxMMR: Shutdown\n");
if (g_HostChannelCtx.u32HGCMClientId != 0)
{
g_HostChannelCtx.fShutdown = true;
}
if (hCreateEvent)
{
}
if (g_hTraceRegistration)
}
}