c++winapiprint-spooler-apiwtsapi32

C++ Port Monitor (mfilemon.dll) RDP Virtual Channel code not executing


I recently started working with open source mfilemon.dll and trying to add additional features to it.

The feature I am adding is to transmit data over a virtual channel. I wrote a small console exe to test the code and what I have below successfully sends data (I get a message popup on the other side).

When this code placed into mfilemon.dll, nothing happens. All the other functions of the DLL work like normal but is as this code is just not executed. I think The spooler service along with something in windows is blocking certain API calls but would be nice to know for sure.

Here is the code for the exe I wrote that successfully sends data over my virtual channel (My project is target Win8.1 so I had to add legacy_stdio_definitions.lib to the Linker input files).

#include "stdafx.h"
#include "conio.h"
#include "iostream"
struct IUnknown; // Workaround for "combaseapi.h(229): error C2187: syntax error: 'identifier' was unexpected here" when using /permissive-
#include "windows.h"
#include "WtsApi32.h"
#pragma comment(lib, "wtsapi32.lib")

int main()
{
    std::cout << "Testing";

    const char* data = "You just printed data!";

    HANDLE mHandle;
    mHandle = WTSVirtualChannelOpen(NULL, (DWORD)-1, (LPSTR)"PRINTWP");
    PULONG written = 0;
    bool ret = WTSVirtualChannelWrite(mHandle, (PCHAR)data, 22, written);

    if (!ret || written == (PULONG)13)
    {

    }
    else
    {

    }

    ret = WTSVirtualChannelClose(mHandle);

    _getch();
    return 0;
}

Here is my virtual channel DLL code.

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>
#include <Shellapi.h>
#include <Cchannel.h> // Contains the definition for PCHANNEL_ENTRY_POINTS
#include <string.h>
#include <winspool.h>
#include <fstream>
#include <iostream>

PCHANNEL_ENTRY_POINTS gpEntryPoints;
LPHANDLE gphChannel;
void VCAPITYPE VirtualChannelInitEventProc(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength);

void VCAPITYPE VirtualChannelOpenEvent_PRINTWP(DWORD openHandle, UINT event, LPVOID pdata, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags);
DWORD gdwOpenChannel_PRINTWP;
const char* channel_PRINTWP = "PRINTWP";

BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
{
    MessageBox(NULL, TEXT("RDP Virtual channel DLL Loaded."), TEXT("Hosted Channel"), MB_OK);

    gpEntryPoints = (PCHANNEL_ENTRY_POINTS)LocalAlloc(LPTR, pEntryPoints->cbSize);
    CopyMemory(gpEntryPoints, pEntryPoints, pEntryPoints->cbSize);

    UINT rc_channel;
    CHANNEL_DEF channel_def[1]; // This is where you can increase the number of registered virtual channels assuming you define them later.

    ZeroMemory(&channel_def[0], sizeof(CHANNEL_DEF));
    CopyMemory(channel_def[0].name, channel_PRINTWP, strlen(channel_PRINTWP));

    rc_channel = gpEntryPoints->pVirtualChannelInit((LPVOID *)&gphChannel, channel_def, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, (PCHANNEL_INIT_EVENT_FN)VirtualChannelInitEventProc);

    if (rc_channel != CHANNEL_RC_OK)
    {
        MessageBox(NULL, TEXT("RDP Virtual Channel registration has failed!"),TEXT("Channel Message"), MB_OK);
        return FALSE;
    }

    if (channel_def[0].options != CHANNEL_OPTION_INITIALIZED)
    {
        MessageBox(NULL, TEXT("RDP Virtual Channel options initialization failure!"), TEXT("Channel Message"), MB_OK);
        return FALSE;
    }

    MessageBox(NULL, TEXT("RDP Virtual Channel initialized."), TEXT("Channel Message"), MB_OK);
    return TRUE;
}

void VCAPITYPE VirtualChannelInitEventProc(LPVOID pInitHandle, UINT event, LPVOID pData, UINT dataLength)
{
    UINT rc_channel_HLINK;
    UINT rc_channel_PREVIEW;
    UINT rc_channel_PRINTWP;

    switch (event)
    {
    case CHANNEL_EVENT_INITIALIZED:
        break;
    case CHANNEL_EVENT_CONNECTED:

        rc_channel_PRINTWP = gpEntryPoints->pVirtualChannelOpen(gphChannel, &gdwOpenChannel_PRINTWP, (PCHAR)channel_PRINTWP, (PCHANNEL_OPEN_EVENT_FN)VirtualChannelOpenEvent_PRINTWP);

        if (rc_channel_PRINTWP != CHANNEL_RC_OK)
        {
            MessageBox(NULL, TEXT("Open of RDP virtual channel failed"), TEXT("Channel Message"), MB_OK);
        }
        else
        {
            MessageBox(NULL, TEXT("Open of RDP virtual channel success"), TEXT("Channel Message"), MB_OK);
        }

        break;

    case CHANNEL_EVENT_V1_CONNECTED:
        MessageBox(NULL, TEXT("Connecting to a non Windows 2000 Terminal Server"), TEXT("Channel Message"), MB_OK);
        break;

    case CHANNEL_EVENT_DISCONNECTED:
        break;

    case CHANNEL_EVENT_TERMINATED:
        LocalFree((HLOCAL)gpEntryPoints);
        break;

    default:
        break;
    }
}

void VCAPITYPE VirtualChannelOpenEvent_PRINTWP(DWORD openHandle, UINT event, LPVOID pdata, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
{
    switch (event)
    {
    case CHANNEL_EVENT_DATA_RECEIVED:

        MessageBox(NULL, TEXT("Data received."), TEXT("Channel Message"), MB_OK);

        char* data;
        data = (char*)malloc(dataLength + 1);
        CopyMemory(data, pdata, dataLength);
        data[dataLength] = '\0';

        MessageBoxA(NULL, data, "PRINTWP", MB_OK);

        free(data);

        break;

    default:
        break;
    }
}

def file:

LIBRARY FrzHostedChannel
EXPORTS
VirtualChannelEntry

dllmain:

#include "stdafx.h"

Using this above code requires a registry change so the mstsc.exe knows to load this pluggin.

If anyone is familiar with Port Monitors and executing code that does something other than create a file, that would be swell.


Solution

  • Spoolsv.exe is a service and hence operates in session that is NOT the same session as what is connected through RDP. Any DLLs used and child processes spawned are also part of that session.

    mHandle = WTSVirtualChannelOpen(NULL, (DWORD)-1, (LPSTR)"PRINTWP");
    

    The (DWORD)-1 needs to be changed to the actual session that is connected via RDP.