Недавно я начал работать с открытым исходным кодом mfilemon.dll и пытался добавить к нему дополнительные функции.
Функция, которую я добавляю, заключается в передаче данных по виртуальному каналу. Я написал небольшой консольный exe-файл для проверки кода и то, что у меня ниже, успешно отправляет данные (я получаю всплывающее сообщение на другой стороне).
Когда этот код помещается в mfilemon.dll, ничего не происходит. Все остальные функции DLL работают как обычно, но этот код просто не выполняется. Я думаю, что служба Spooler наряду с чем-то в Windows блокирует определенные вызовы API, но было бы неплохо знать наверняка.
Вот код для exe-файла, который я написал, который успешно отправляет данные по моему виртуальному каналу (Мой проект — целевой Win8.1, поэтому мне пришлось добавить legacy_stdio_definitions.lib
к входным файлам компоновщика).
#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;
}
Вот мой код DLL виртуального канала.
#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:
LIBRARY FrzHostedChannel
EXPORTS
VirtualChannelEntry
DllMain:
#include "stdafx.h"
Использование приведенного выше кода требует изменения реестра, поэтому mstsc.exe знает, как загрузить этот плагин.
Если кто-то знаком с мониторами портов и выполнением кода, который делает что-то кроме создания файла, это было бы здорово.
Spoolsv.exe является службой и, следовательно, работает в сеансе, который НЕ совпадает с сеансом, подключенным через RDP. Любые используемые библиотеки DLL и порожденные дочерние процессы также являются частью этого сеанса.
mHandle = WTSVirtualChannelOpen(NULL, (DWORD)-1, (LPSTR)"PRINTWP");
(DWORD)-1
должен быть изменен на фактический сеанс, который подключен через RDP.
Других решений пока нет …