diff --git a/SocksOverRDP-Plugin.reg b/SocksOverRDP-Plugin.reg new file mode 100644 index 0000000..5212a8c Binary files /dev/null and b/SocksOverRDP-Plugin.reg differ diff --git a/SocksOverRDP-Plugin.sln b/SocksOverRDP-Plugin.sln new file mode 100644 index 0000000..9fe6460 --- /dev/null +++ b/SocksOverRDP-Plugin.sln @@ -0,0 +1,47 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocksOverRDP-Plugin", "SocksOverRDP-Plugin\SocksOverRDP-Plugin.vcxproj", "{9402DF45-A060-4881-9F64-D16E094B97A7}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocksOverRDP-PluginPS", "SocksOverRDP-Plugin\SocksOverRDP-PluginPS.vcxproj", "{15DEE2D8-6972-49C0-9369-35730A29A2CF}" + ProjectSection(ProjectDependencies) = postProject + {9402DF45-A060-4881-9F64-D16E094B97A7} = {9402DF45-A060-4881-9F64-D16E094B97A7} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SocksOverRDP-Server", "SocksOverRDP-Server\SocksOverRDP-Server.vcxproj", "{C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9402DF45-A060-4881-9F64-D16E094B97A7}.Debug|x64.ActiveCfg = Debug|x64 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Debug|x64.Build.0 = Debug|x64 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Debug|x86.ActiveCfg = Debug|Win32 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Debug|x86.Build.0 = Debug|Win32 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Release|x64.ActiveCfg = Release|x64 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Release|x64.Build.0 = Release|x64 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Release|x86.ActiveCfg = Release|Win32 + {9402DF45-A060-4881-9F64-D16E094B97A7}.Release|x86.Build.0 = Release|Win32 + {15DEE2D8-6972-49C0-9369-35730A29A2CF}.Debug|x64.ActiveCfg = Debug|x64 + {15DEE2D8-6972-49C0-9369-35730A29A2CF}.Debug|x86.ActiveCfg = Debug|Win32 + {15DEE2D8-6972-49C0-9369-35730A29A2CF}.Release|x64.ActiveCfg = Release|x64 + {15DEE2D8-6972-49C0-9369-35730A29A2CF}.Release|x86.ActiveCfg = Release|Win32 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Debug|x64.ActiveCfg = Debug|x64 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Debug|x64.Build.0 = Debug|x64 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Debug|x86.ActiveCfg = Debug|Win32 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Debug|x86.Build.0 = Debug|Win32 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Release|x64.ActiveCfg = Release|x64 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Release|x64.Build.0 = Release|x64 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Release|x86.ActiveCfg = Release|Win32 + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/SocksOverRDP-Plugin/Resource.h b/SocksOverRDP-Plugin/Resource.h new file mode 100644 index 0000000..0a7dca0 --- /dev/null +++ b/SocksOverRDP-Plugin/Resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SocksOverRDP-Plugin.rc +// + +#define IDS_PROJNAME 100 +#define IDR_SocksOverRDPPLUGIN 101 +#define IDR_SocksOverRDPCLIENTPLUGIN 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 106 +#endif +#endif diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.cpp b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.cpp new file mode 100644 index 0000000..7eaa911 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.cpp @@ -0,0 +1,897 @@ +/* +* MIT License +* +* Copyright(c) 2020 Balazs Bucsay +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + + +#include "stdafx.h" +#include "resource.h" +#include "SocksOverRDP-Plugin.h" + +#pragma comment(lib, "Ws2_32.lib") + +#define SocksOverRDP_CHANNEL_NAME "SocksChannel" +#define BUF_SIZE 4096 + +struct arguments { + DWORD enabled; + WCHAR *ip; + WCHAR *port; +}; + +// main thread struct for global variables +struct threadargs { + struct arguments *running_args; + SOCKET sockserver; + BOOL run; + IWTSVirtualChannel *m_ptrChannel = NULL; + HANDLE hThread = NULL; +} ta; + +struct threads +{ + DWORD dwThreadId; + HANDLE hThread; + BOOL run; + SOCKET s; + struct threads *next; +}; + +// debug verbose flag +static BOOL bVerbose = FALSE; + +static struct threads *ThreadHead = NULL; +char szOverflow[BUF_SIZE * 4]; +DWORD dwOverflow = 0; + +static HANDLE ghMutex, ghLLMutex; + +using namespace ATL; + +#define CHECK_QUIT_HR( _x_ ) if(FAILED(hr)) { return hr; } + +class ATL_NO_VTABLE SocksOverRDPPlugin : + public CComObjectRootEx, + public CComCoClass, + public IWTSPlugin, + public IWTSVirtualChannelCallback, + public IWTSListenerCallback +{ +public: + CComPtr m_ptrChannel; + + DECLARE_REGISTRY_RESOURCEID(IDR_SocksOverRDPPLUGIN) + + BEGIN_COM_MAP(SocksOverRDPPlugin) + COM_INTERFACE_ENTRY(IWTSPlugin) + COM_INTERFACE_ENTRY(IWTSVirtualChannelCallback) + COM_INTERFACE_ENTRY(IWTSListenerCallback) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + // IWTSPlugin. + // + HRESULT STDMETHODCALLTYPE + Initialize(IWTSVirtualChannelManager *pChannelMgr); + + HRESULT STDMETHODCALLTYPE Connected(); + + HRESULT STDMETHODCALLTYPE Disconnected(DWORD dwDisconnectCode) + { + // Prevent C4100 "unreferenced parameter" warnings. + dwDisconnectCode; + return S_OK; + } + + HRESULT STDMETHODCALLTYPE Terminated() + { + return S_OK; + } + + VOID SetChannel(IWTSVirtualChannel *pChannel, struct threadargs *pTa); + + // IWTSVirtualChannelCallbackdwDummyThreadId + // + HRESULT STDMETHODCALLTYPE OnDataReceived(ULONG cbSize, __in_bcount(cbSize) BYTE *pBuffer); + + HRESULT STDMETHODCALLTYPE OnClose() + { + ta.run = FALSE; + //DebugPrint(0, L"[*] Terminating thread, closing socket and channel0"); + TerminateThreads(); + + closesocket(gpta->sockserver); + + ta.m_ptrChannel = NULL; + WSACleanup(); + return S_OK; + } + + HRESULT STDMETHODCALLTYPE + OnNewChannelConnection( + __in IWTSVirtualChannel *pChannel, + __in_opt BSTR data, + __out BOOL *pbAccept, + __out IWTSVirtualChannelCallback **ppCallback); + + // non-inherited ones + + struct threadargs *gpta; + struct arguments running_args; + + static struct threads *SocksOverRDPPlugin::AddThread(DWORD dwThreadId, SOCKET s); + static VOID SocksOverRDPPlugin::DeleteThread(DWORD dwThreadId); + static VOID SocksOverRDPPlugin::TerminateThreads(); + static struct threads *SocksOverRDPPlugin::LookupThread(DWORD dwThreadId); + static VOID SocksOverRDPPlugin::StopThread(DWORD dwThreadId); + + static VOID SocksOverRDPPlugin::DebugPrint(HRESULT hrDbg, __in_z LPWSTR fmt, ...); + LONG SocksOverRDPPlugin::GetDWORDRegKey(HKEY hKey, WCHAR *strValueName, DWORD *nValue); + LONG SocksOverRDPPlugin::GetStringRegKey(HKEY hKey, WCHAR *strValueName, WCHAR **strValue); + BOOL SocksOverRDPPlugin::GetRegistrySettings(); + static DWORD WINAPI SocksOverRDPPlugin::RelayToRDPChannel(PVOID param); + static DWORD WINAPI SocksOverRDPPlugin::ListenerThread(PVOID param); +}; + +OBJECT_ENTRY_AUTO(__uuidof(CompReg), SocksOverRDPPlugin) + +// Add thread to linked list +struct threads *SocksOverRDPPlugin::AddThread(DWORD dwThreadId, SOCKET s) +{ + struct threads *rolling; + struct threads *ThreadStruct; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + ThreadStruct = (struct threads *)malloc(sizeof(struct threads)); + ThreadStruct->dwThreadId = dwThreadId; + ThreadStruct->hThread = GetCurrentThread(); + ThreadStruct->run = TRUE; + ThreadStruct->s = s; + ThreadStruct->next = NULL; + + if (ThreadHead == NULL) + { + ThreadHead = ThreadStruct; + } + else + { + rolling = ThreadHead; + while (rolling->next) + { + rolling = rolling->next; + } + rolling->next = ThreadStruct; + } + + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] AddThread Release failed: %08X", dwThreadId); + } + + return ThreadStruct; + break; + + case WAIT_ABANDONED: + if (bVerbose) DebugPrint(0, L"[*] AddThread abandoned: %08X", GetCurrentThreadId()); + return NULL; + } + + return NULL; +} + +// Remove thread from linked list +VOID SocksOverRDPPlugin::DeleteThread(DWORD dwThreadId) +{ + struct threads *rolling; + struct threads *prev = NULL; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + if (!ThreadHead) + { + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] DeleteThread Release failed: %08X", GetCurrentThreadId()); + } + return; + } + + rolling = ThreadHead; + + while (rolling->dwThreadId != dwThreadId) + { + prev = rolling; + rolling = rolling->next; + } + + if (prev) + { + if (rolling->next) + { + prev->next = rolling->next; + } + else + { + prev->next = NULL; + } + } + else + { + if (ThreadHead->next) + { + ThreadHead = ThreadHead->next; + } + else + { + ThreadHead = NULL; + } + } + + rolling->run = FALSE; + closesocket(rolling->s); + rolling->dwThreadId = 0xffffffff; + rolling->next = FALSE; + rolling->hThread = NULL; + rolling->s = NULL; + free(rolling); + rolling = NULL; + + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] DeleteThread Release failed: %08X", GetCurrentThreadId()); + } + return; + break; + + case WAIT_ABANDONED: + if (bVerbose) DebugPrint(0, L"[*] DeleteThread abandoned: %08X", GetCurrentThreadId()); + return; + } +} + +/* +Terminate all running threads that handle communication +by setting the loop condition to FALSE and closing socket. +*/ + +VOID SocksOverRDPPlugin::TerminateThreads() +{ + struct threads *rolling; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + rolling = ThreadHead; + + if (rolling == NULL) + { + if (bVerbose) DebugPrint(0, L"[*] Nothing to terminate. Threadhead empty. %08X", GetCurrentThreadId()); + } + + while (rolling) + { + rolling->run = FALSE; + closesocket(rolling->s); + + rolling = rolling->next; + } + + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] TerminateThreads Release failed: %08X", GetCurrentThreadId()); + } + return; + break; + + case WAIT_ABANDONED: + if (bVerbose) DebugPrint(0, L"[*] TerminateThreads abandoned: %08X", GetCurrentThreadId()); + return; + } +} + +// Stopping a specific thread gently by setting the loop condition to FALSE +VOID SocksOverRDPPlugin::StopThread(DWORD dwThreadId) +{ + struct threads *rolling; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + rolling = ThreadHead; + + while (rolling) + { + if (rolling->dwThreadId == dwThreadId) + { + rolling->run = FALSE; + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] StopThread Release failed0: %08X", GetCurrentThreadId()); + } + return; + } + rolling = rolling->next; + } + + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] StopThread Release failed1: %08X", GetCurrentThreadId()); + } + return; + break; + + case WAIT_ABANDONED: + if (bVerbose) DebugPrint(0, L"[*] lookupThread abandoned: %08X", GetCurrentThreadId()); + return; + } + + return; +} + +// Look up thread and return a pointer to it by ThreadId +struct threads *SocksOverRDPPlugin::LookupThread(DWORD dwThreadId) +{ + struct threads *rolling; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + rolling = ThreadHead; + + while (rolling) + { + if (rolling->dwThreadId == dwThreadId) + { + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] lookupThread Release failed0: %08X", GetCurrentThreadId()); + } + return rolling; + } + rolling = rolling->next; + } + + if (!ReleaseMutex(ghLLMutex)) + { + if (bVerbose) DebugPrint(0, L"[*] lookupThread Release failed1: %08X", GetCurrentThreadId()); + } + return NULL; + break; + + case WAIT_ABANDONED: + if (bVerbose) DebugPrint(0, L"[*] lookupThread abandoned: %08X", GetCurrentThreadId()); + return NULL; + } + + return NULL; +} + +// Debug/Verbose Print function, message shows up in the debugger. +VOID SocksOverRDPPlugin::DebugPrint(HRESULT hrDbg, __in_z LPWSTR fmt, ...) +{ + HRESULT hr; + TCHAR Buffer[DEBUG_PRINT_BUFFER_SIZE]; + size_t Len; + + hr = StringCchPrintf(Buffer, DEBUG_PRINT_BUFFER_SIZE, TEXT("[hr=0x%8x]"), hrDbg); + assert(SUCCEEDED(hr)); // buffer is sure to be big enough + + hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len); + assert(SUCCEEDED(hr)); // StringCchPrintf is supposed to always NULL term + + va_list argptr; + va_start(argptr, fmt); + + hr = StringCchVPrintf(Buffer + Len, DEBUG_PRINT_BUFFER_SIZE - Len, + fmt, argptr); + + // the above could fail but we don't care since we + // should get a NULL terminated partial string + + // insert terminating eol (despite failure) + hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len); + assert(SUCCEEDED(hr)); // again there should be a NULL term + + if (Len < DEBUG_PRINT_BUFFER_SIZE - 1) + { + Len++; + Buffer[Len] = TEXT('\0'); + } + + Buffer[Len - 1] = TEXT('\n'); + + OutputDebugString(Buffer); +} + +LONG SocksOverRDPPlugin::GetDWORDRegKey(HKEY hKey, WCHAR *strValueName, DWORD *nValue) +{ + DWORD dwBufferSize(sizeof(DWORD)); + DWORD nResult; + LONG nError; + + if ((nError = RegQueryValueEx(hKey, strValueName, 0, NULL, (LPBYTE)&nResult, &dwBufferSize)) == ERROR_SUCCESS) + { + *nValue = nResult; + } + return nError; +} + +LONG SocksOverRDPPlugin::GetStringRegKey(HKEY hKey, WCHAR *strValueName, WCHAR **strValue) +{ + LPVOID szTemp = NULL; + DWORD buflen = 255; + LONG nError; + + if ((szTemp = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (size_t)buflen)) == NULL) + { + if (bVerbose) DebugPrint(GetLastError(), L"[-] Error allocating heap for read buffer %ld", GetLastError()); + return -1; + } + + if ((nError = RegQueryValueExW(hKey, strValueName, 0, NULL, (LPBYTE)szTemp, &buflen)) != ERROR_SUCCESS) + { + HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, szTemp); + szTemp = NULL; + } + *strValue = (WCHAR *)szTemp; + + return nError; +} + +BOOL SocksOverRDPPlugin::GetRegistrySettings() +{ + HKEY hKey; + LONG lRes; + WCHAR *szTemp; + + if ((lRes = RegOpenKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", 0, KEY_READ, &hKey)) != ERROR_SUCCESS) + { + DebugPrint(lRes, L"[-] Error opening registry hive/key"); + return FALSE; + } + + GetDWORDRegKey(hKey, L"enabled", &running_args.enabled); + GetStringRegKey(hKey, L"ip", &szTemp); + if (szTemp != NULL) + { + if (wcslen(szTemp) < 16) + { + running_args.ip = szTemp; + } + else + { + MessageBox(NULL, L"IP too long. Please fix it under the following key:\r\nHKCU\\SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", L"Registry value error", MB_OK); + } + } + GetStringRegKey(hKey, L"port", &szTemp); + if (szTemp != NULL) + { + if (wcslen(szTemp) < 6) + { + running_args.port = szTemp; + } + else + { + MessageBox(NULL, L"Port too long. Please fix it under the following key:\r\nHKCU\\SOFTWARE\\Microsoft\\Terminal Server Client\\Default\\AddIns\\SocksOverRDP-Plugin\\", L"Registry value error", MB_OK); + } + } + + return TRUE; +} + + +// IWTSPlugin::Initialize implementation. +HRESULT SocksOverRDPPlugin::Initialize(__in IWTSVirtualChannelManager *pChannelMgr) +{ + HRESULT hr; + CComObject *pListenerCallback; + CComPtr ptrListenerCallback; + CComPtr ptrListener; + WCHAR enabledmsg[256]; + + running_args.enabled = 0; + running_args.port = L"1080"; + running_args.ip = L"127.0.0.1"; + + if (!GetRegistrySettings()) + { + DebugPrint(-1, L"[-] Could not access the registry settings"); + } + + if (!running_args.enabled) + { + DebugPrint(0, L"[*] Plugin disabled"); + return -1; + } + + wnsprintf(enabledmsg, 255, L"The SocksOverRDP plugin is enabled. When the server binary gets executed, it will listen on: %s:%s", running_args.ip, running_args.port); + MessageBox(NULL, enabledmsg, L"SocksOverRDP plugin is enabled", MB_OK | MB_ICONWARNING); + + + // Create an instance of the CSampleListenerCallback object. + hr = CComObject::CreateInstance(&pListenerCallback); + CHECK_QUIT_HR("SocksOverRDPPlugin::CreateInstance"); + ptrListenerCallback = pListenerCallback; + + // Attach the callback to the endpoint. + hr = pChannelMgr->CreateListener( + SocksOverRDP_CHANNEL_NAME, + 0, + (SocksOverRDPPlugin*)ptrListenerCallback, + &ptrListener); + CHECK_QUIT_HR("CreateListener"); + + return hr; +} + +// MSTSC connected to server over RDP +HRESULT STDMETHODCALLTYPE SocksOverRDPPlugin::Connected() +{ + return S_OK; +} + +/* +This thread is started for each and every connection that is made to the socks +proxy. This part only handles the Client->RDP communication. +Whatever comes from the client will be piped into the RDP channel with prefixed +with: DWORD(threadid)+DWORD(sizeof(following_data))+BYTE(closurebit) +*/ +DWORD SocksOverRDPPlugin::RelayToRDPChannel(PVOID param) +{ + SOCKET c = (SOCKET)param; + DWORD dwWaitResult, dwThreadId = GetCurrentThreadId(); + char buf[BUF_SIZE + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)]; + int ret; + struct threads *pta; + + pta = AddThread(dwThreadId, c); + if (!pta) + { + if (bVerbose) DebugPrint(0, L"%08X: Adding thread failure.", dwThreadId); + return -1; + } + + // running till error or thread stopped + while (pta->run) + { + // reading from client socket + if ((ret = recv(c, buf + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), BUF_SIZE, 0)) > 0) + { + // might have been blocked for a long time and the thread should be + // stopped already + if (!pta->run) break; + // prefixing with the necessities + memcpy(buf, &dwThreadId, sizeof(DWORD)); + memcpy(buf + sizeof(DWORD), &ret, sizeof(DWORD)); + buf[sizeof(DWORD) + sizeof(DWORD)] = 0; + } + else + { + // SOCKET_ERROR or RST was received + if (!pta->run) + break; + if (ret && bVerbose) + DebugPrint(0, L"%08X: SOCKS thread(%d) Rsend() failed with errorelayToRDPChannel recv error: %ld %ld", dwThreadId, dwThreadId, ret, WSAGetLastError()); + ret = 0; + memcpy(buf, &dwThreadId, sizeof(DWORD)); + memcpy(buf + sizeof(DWORD), &ret, sizeof(DWORD)); + buf[sizeof(DWORD) + sizeof(DWORD)] = 1; + + pta->run = FALSE; + } + + // wait on mutex, when it is acquired, block other threads to write + dwWaitResult = WaitForSingleObject(ghMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + // write prefixed received data on channel + if (ta.m_ptrChannel != NULL) + if (ta.m_ptrChannel->Write(ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), (BYTE *)buf, NULL)) + { + if (bVerbose) DebugPrint(0, L"Write to channel failed for some reason."); + } + if (!ReleaseMutex(ghMutex)) + { + if (bVerbose) DebugPrint(0, L"Release failed"); + } + break; + + case WAIT_ABANDONED: + return FALSE; + + } + } + + DeleteThread(dwThreadId); + return 0; +} + +DWORD SocksOverRDPPlugin::ListenerThread(PVOID param) +{ + WSADATA wsaData; + ADDRINFOW *result = NULL; + ADDRINFOW hints; + SOCKET s, c; + HANDLE hDummyThread; + DWORD dwDummyThreadId; + int ret; + + DebugPrint(0, L"[*] Setting up server socket"); + if ((ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) + { + DebugPrint(ret, L"WSAStartup() failed with error: %ld", ret); + return -1; + } + + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + if ((ret = GetAddrInfoW(ta.running_args->ip, ta.running_args->port, &hints, &result)) != 0) { + DebugPrint(ret, L"[-] GetAddrInfoW() failed with error: %ld", ret); + return -1; + } + + if ((s = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) == INVALID_SOCKET) { + DebugPrint(WSAGetLastError(), L"[-] socket() failed with error: %ld", WSAGetLastError()); + FreeAddrInfoW(result); + return -1; + } + + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, "1", 1) == SOCKET_ERROR) + { + DebugPrint(WSAGetLastError(), L"[-] setsockopt() failed with error: %ld", WSAGetLastError()); + } + + ta.sockserver = s; + + if ((ret = bind(s, result->ai_addr, (int)result->ai_addrlen)) == SOCKET_ERROR) { + DebugPrint(WSAGetLastError(), L"[-] bind() failed with error: %ld", WSAGetLastError()); + FreeAddrInfoW(result); + closesocket(s); + return -1; + } + + FreeAddrInfoW(result); + DebugPrint(0, L"[*] Listening on: %s:%s", ta.running_args->ip, ta.running_args->port); + + if ((ret = listen(s, SOMAXCONN)) == SOCKET_ERROR) { + DebugPrint(0, L"[-] listen() failed with error: %ld", WSAGetLastError()); + closesocket(s); + return -1; + } + + // while till the RDP connection wasn't closed and + // create a new thread for all new connections + while (ta.run) + { + if ((c = accept(s, NULL, NULL)) == INVALID_SOCKET) { + DebugPrint(0, L"[-] accept() failed with error: %ld", WSAGetLastError()); + return -1; + } + + hDummyThread = CreateThread( + NULL, + 0, + &SocksOverRDPPlugin::RelayToRDPChannel, + (void *)c, + 0, + &dwDummyThreadId); + if (bVerbose) DebugPrint(0, L"[+] New thread created: %08X", dwDummyThreadId); + } + + return 0; +} + +// IWTSListenerCallback::OnNewChannelConnection implementation. +HRESULT SocksOverRDPPlugin::OnNewChannelConnection(__in IWTSVirtualChannel *pChannel, + __in_opt BSTR data, __out BOOL *pbAccept, __out IWTSVirtualChannelCallback **ppCallback) +{ + HRESULT hr; + CComObject *pCallback; + CComPtr ptrCallback; + + // Prevent C4100 "unreferenced parameter" warnings. + data; + + *pbAccept = FALSE; + + hr = CComObject::CreateInstance(&pCallback); + CHECK_QUIT_HR("SocksOverRDPPlugin::CreateInstance"); + ptrCallback = pCallback; + + ptrCallback->SetChannel(pChannel, &ta); + + ta.running_args = &running_args; + ta.m_ptrChannel = pChannel; + ta.run = TRUE; + + ghMutex = CreateMutex(NULL, FALSE, NULL); + ghLLMutex = CreateMutex(NULL, FALSE, NULL); + + running_args.enabled = 0; + running_args.port = L"1080"; + running_args.ip = L"127.0.0.1"; + + if (!GetRegistrySettings()) + { + DebugPrint(-1, L"[-] Could not access the registry settings"); + } + + DebugPrint(0, L"[+] Starting Listener thread"); + + // Create main thread that will spawn other threads upon new connections + HANDLE hListenerThread = CreateThread( + NULL, + 0, + &SocksOverRDPPlugin::ListenerThread, + NULL, + 0, + NULL); + + ta.hThread = hListenerThread; + + *ppCallback = ptrCallback; + (*ppCallback)->AddRef(); + + *pbAccept = TRUE; + + return hr; +} + +VOID SocksOverRDPPlugin::SetChannel(IWTSVirtualChannel *pChannel, struct threadargs *pTa) +{ + m_ptrChannel = pChannel; + gpta = pTa; +} + +// When data is received from the RDP server on the channel, this function is +// getting called with the *data on stack +HRESULT STDMETHODCALLTYPE SocksOverRDPPlugin::OnDataReceived(ULONG cbSize, __in_bcount(cbSize) BYTE *pBuffer) +{ + DWORD dwThreadId, ret, dwRecvdLen, cbFullSize; + BOOL bClose = FALSE, ofused = FALSE; + char *buf; + struct threads *pta; + + buf = (char*)pBuffer; + cbFullSize = cbSize; + + // if overflow was present from previous call, it was saved and should be + // prepended with the new data + // ofused if set, signals that szOverflow has the data instead of pBuffer + if (dwOverflow) + { + if (bVerbose) DebugPrint(0, L"Overflow was present from previous call. Stored bytes: %ld. Received bytes: %ld", dwOverflow, cbSize); + memcpy_s(szOverflow + dwOverflow, BUF_SIZE*4, pBuffer, cbSize); + buf = szOverflow; + cbFullSize = cbSize + dwOverflow; + dwOverflow = 0; + ofused = TRUE; + } + while (cbFullSize) + { + // parsing the header, getting threadId from server side, that matches + // the local threadID; recvlen which has the appended data length ; + // bClose bit which shows weather the connection will be/was closed + // on the server side. + memcpy(&dwThreadId, buf, sizeof(DWORD)); + memcpy(&dwRecvdLen, buf + sizeof(DWORD), sizeof(DWORD)); + if (buf[sizeof(DWORD) + sizeof(DWORD)] == 0x01) bClose = TRUE; + + // finding thread based on the header. If no thread found, that is a + // problem, since there is no thread that has the associated info for + // this received data + pta = LookupThread(dwThreadId); + if (pta == NULL) + return -1; + + if (bVerbose) DebugPrint(0, L"%08X: Read: %ld content length: %ld", dwThreadId, cbFullSize, dwRecvdLen); + + if (dwRecvdLen <= cbFullSize - (sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE))) + { + if (bVerbose) DebugPrint(0, L"%08X: Last read, smaller or equal.", dwThreadId); + if (dwRecvdLen) + { + if (pta->s) + { + // send recvl bytes from the received data to the + // corresponding socket + if ((ret = send(pta->s, buf + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE), dwRecvdLen, 0)) == SOCKET_ERROR) + { + // error happened, stopping thread gently and + // recalculating pointers to the next packet + StopThread(dwThreadId); + if (bVerbose) DebugPrint(0, L"%08X: send() failed with error %ld %ld", dwThreadId, ret, WSAGetLastError()); + + cbFullSize -= (dwRecvdLen + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)); + buf += (dwRecvdLen + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)); + } + else + { + // recalculating pointers to the next packet + cbFullSize -= (ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)); + buf += (ret + sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE)); + if (bVerbose) DebugPrint(0, L"%08X: Sent on socket: %ld. cbFullSize: %ld", dwThreadId, ret, cbFullSize); + } + } + } + // bClose bit was set on the server side. Closing down connection + if (bClose) + { + StopThread(dwThreadId); + if (pta->s) + shutdown(pta->s, SD_SEND); + + // only RST was sent from server side + if (!dwRecvdLen) + { + cbFullSize -= sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE); + buf += sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE); + } + } + } + else + { + // data remained on the buffer, less than specified in the header + if (bVerbose) DebugPrint(0, L"!!! OVERFLOW HAPPENED %ld > %ld", dwRecvdLen, cbFullSize); + if (ofused) + { + // ofused set, memmove needed + memmove_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize); + dwOverflow = cbFullSize; + } + else + { + memcpy_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize); + dwOverflow = cbFullSize; + } + + // not enough data to send, need to get more, stopping loop + cbFullSize = 0; + } + } + + return S_OK; +} \ No newline at end of file diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.def b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.def new file mode 100644 index 0000000..dea3183 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.def @@ -0,0 +1,10 @@ +; SocksOverRDP-Plugin.def : Declares the module parameters. + +LIBRARY + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DllInstall PRIVATE diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.h b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.h new file mode 100644 index 0000000..2953442 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.h @@ -0,0 +1,144 @@ +// TsClientPlgn.h : Declaration of the CTsClientPlgn + +#pragma once +#include "stdafx.h" +#include "resource.h" // main symbols + +#include "SocksOverRDPPlugin_i.h" + +// debug +#include +#include + + +#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) +#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." +#endif + + +// CTsClientPlgn +class ATL_NO_VTABLE CTsClientPlgn : + public CComObjectRootEx, + public CComCoClass, + public IDispatchImpl, + public IWTSPlugin, + public IWTSListenerCallback, + public IWTSVirtualChannelCallback +{ +public: + CTsClientPlgn(): _hCurrentFile(INVALID_HANDLE_VALUE) + { + } + +DECLARE_REGISTRY_RESOURCEID(IDR_SocksOverRDPCLIENTPLUGIN) + + +BEGIN_COM_MAP(CTsClientPlgn) + COM_INTERFACE_ENTRY(IComponentRegistrar) + COM_INTERFACE_ENTRY(IDispatch) + COM_INTERFACE_ENTRY(IWTSPlugin) + COM_INTERFACE_ENTRY(IWTSListenerCallback) + COM_INTERFACE_ENTRY(IWTSVirtualChannelCallback) +END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + HRESULT FinalConstruct() + { + return S_OK; + } + + void FinalRelease() + { + } + + // IWTSPlugin +public: + /* + * Called immediately after instantiating the COM class + */ + STDMETHOD(Initialize)( + IWTSVirtualChannelManager *pChannelMgr + ); + + /* + * Called when the TS client is connected to the TS server + */ + STDMETHOD(Connected)(); + + /* + * Called when the TS client is disconnected to the TS server + * Might be followed by another Connected() call + */ + STDMETHOD(Disconnected)( + DWORD dwDisconnectCode + ); + + /* + * The last method called by the TS client before + * terminating the object + */ + STDMETHOD(Terminated)(); + + // IWTSListenerCallback +public: + VOID CTsClientPlgn::SetChannel(IWTSVirtualChannel *pChannel); +public: + /* + * Called whenever a request for new channel connection + * from the server is received. + */ + STDMETHOD(OnNewChannelConnection)( + IWTSVirtualChannel *pChannel, + BSTR data, // optional data passed as part of the connect method + BOOL *pbAccept, // the callee should return TRUE if connection is accepted + IWTSVirtualChannelCallback **ppCallback // connection related events + ); + + // IWTSVirtualChannelCallback +public: + + /* + * Called whenever a full message from the server is received + * The message is fully reassembled and has the exact size + * as the Write() call on the server + */ + STDMETHOD(OnDataReceived)( + ULONG cbSize, // size of data in bytes + BYTE *pBuffer // data buffer + ); + + /* + * The channel is disconnected, all Write() calls will fail + * no more incomming data is expected. + */ + STDMETHOD(OnClose)(); + +private: + + HRESULT CleanState(); + + + HRESULT StartFile( + __in_z LPWSTR szFileName, + BOOL bDir); + + HRESULT WriteData( + __in_bcount(usLen) PBYTE pData, + USHORT usLen); + + HRESULT EndFile(BOOL bDir); + + CComPtr _spListener; + CComPtr _spChannel; + + HANDLE _hCurrentFile; + + WStringVector _vDirList; +}; + +//OBJECT_ENTRY_AUTO(__uuidof(CompReg), CTsClientPlgn) + + +// DEBUG: +#define DEBUG_PRINT_BUFFER_SIZE 1024 diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rc b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rc new file mode 100644 index 0000000..4d94256 Binary files /dev/null and b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rc differ diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rgs b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rgs new file mode 100644 index 0000000..8238b17 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.rgs @@ -0,0 +1,52 @@ +HKCR +{ + CLSID + { + SocksOverRDP-Plugin.SocksOverRDP-Plugin.1 = s 'SocksOverRDP-Plugin Class' + { + CLSID = s '{61715a27-63a8-4dc1-9857-83eb00fb60a3}' + } + SocksOverRDP-Plugin.SocksOverRDP-Plugin = s 'SocksOverRDP-Plugin Class' + { + CLSID = s '{61715a27-63a8-4dc1-9857-83eb00fb60a3}' + CurVer = s 'SocksOverRDP-Plugin.SocksOverRDP-Plugin.1' + } + ForceRemove {61715a27-63a8-4dc1-9857-83eb00fb60a3} = s 'SocksOverRDP-Plugin Class' + { + ProgID = s 'SocksOverRDP-Plugin.SocksOverRDP-Plugin.1' + VersionIndependentProgID = s 'SocksOverRDP-Plugin.SocksOverRDP-Plugin' + ForceRemove 'Programmable' + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Free' + } + 'TypeLib' = s '{392B9DEE-7E00-4C58-831B-50C58EAA887F}' + } + } +} + +HKCU +{ + Software + { + Microsoft + { + 'Terminal Server Client' + { + Default + { + AddIns + { + SocksOverRDP-Plugin + { + val Name = s '{61715a27-63a8-4dc1-9857-83eb00fb60a3}' + val enabled = d '1' + val ip = s '127.0.0.1' + val port = s '1080' + } + } + } + } + } + } +} diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj new file mode 100644 index 0000000..2584461 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj @@ -0,0 +1,285 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {9402DF45-A060-4881-9F64-D16E094B97A7} + 8.1 + AtlProj + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + Unicode + + + + + + + + + + + + + + + + + + + + + true + true + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + + + true + true + $(SolutionDir)$(Configuration)\$(Platform)\ + + + true + false + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + + + true + false + $(SolutionDir)$(Configuration)\$(Platform)\ + + + + Use + Level3 + Disabled + WIN32;_WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions) + true + + + false + Win32 + _DEBUG;%(PreprocessorDefinitions) + SocksOverRDPPlugin_i.h + SocksOverRDPPlugin_i.c + SocksOverRDPPlugin_p.c + true + $(IntDir)SocksOverRDPPlugin.tlb + + true + + + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + + + Windows + .\SocksOverRDP-Plugin.def + comsvcs.lib;%(AdditionalDependencies) + true + + + + + Use + Level3 + Disabled + _WINDOWS;_DEBUG;_USRDLL;%(PreprocessorDefinitions) + true + + + false + _DEBUG;%(PreprocessorDefinitions) + SocksOverRDPPlugin_i.h + SocksOverRDPPlugin_i.c + SocksOverRDPPlugin_p.c + true + $(IntDir)SocksOverRDPPlugin.tlb + + true + + + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + _DEBUG;%(PreprocessorDefinitions) + + + Windows + .\SocksOverRDP-Plugin.def + comsvcs.lib;%(AdditionalDependencies) + true + + + + + Use + Level3 + MaxSpeed + WIN32;_WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + true + + + false + Win32 + NDEBUG;%(PreprocessorDefinitions) + SocksOverRDPPlugin_i.h + SocksOverRDPPlugin_i.c + SocksOverRDPPlugin_p.c + true + $(IntDir)SocksOverRDPPlugin.tlb + + true + + + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + + + Windows + .\SocksOverRDP-Plugin.def + comsvcs.lib;%(AdditionalDependencies) + true + true + true + + + + + Use + Level3 + MaxSpeed + _WINDOWS;NDEBUG;_USRDLL;%(PreprocessorDefinitions) + true + + + false + NDEBUG;%(PreprocessorDefinitions) + SocksOverRDPPlugin_i.h + SocksOverRDPPlugin_i.c + SocksOverRDPPlugin_p.c + true + $(IntDir)SocksOverRDPPlugin.tlb + + true + + + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + NDEBUG;%(PreprocessorDefinitions) + + + Windows + .\SocksOverRDP-Plugin.def + comsvcs.lib;%(AdditionalDependencies) + true + true + true + + + + + + + false + + + false + + + false + + + false + + + + + Create + Create + Create + Create + + + + false + + + false + + + false + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj.filters b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj.filters new file mode 100644 index 0000000..4881fed --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Plugin.vcxproj.filters @@ -0,0 +1,85 @@ + + + + + {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;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 + + + {2467f173-a17f-4363-a3f3-5f66776727f8} + False + + + + + Source Files + + + Source Files + + + Source Files + + + Generated Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Generated Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + + + + Resource Files + + + Source Files + + + + + Source Files + + + \ No newline at end of file diff --git a/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj b/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj new file mode 100644 index 0000000..edca157 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj @@ -0,0 +1,174 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {15DEE2D8-6972-49C0-9369-35730A29A2CF} + AtlPSProj + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + Unicode + + + + + + + + + + + + + + + + + + + + + $(Platform)\$(Configuration)PS\ + $(SolutionDir)$(Configuration)\$(Platform)\ + + + $(Platform)\$(Configuration)PS\ + $(SolutionDir)$(Configuration)\$(Platform)\ + + + $(Platform)\$(Configuration)PS\ + $(SolutionDir)$(Configuration)\$(Platform)\ + + + $(Platform)\$(Configuration)PS\ + $(SolutionDir)$(Configuration)\$(Platform)\ + + + + WIN32;REGISTER_PROXY_DLL;_DEBUG;%(PreprocessorDefinitions) + + + kernel32.lib;rpcns4.lib;rpcrt4.lib;oleaut32.lib;uuid.lib;ole32.lib;advapi32.lib;comsvcs.lib;%(AdditionalDependencies) + SocksOverRDP-PluginPS.def + true + + + if exist dlldata.c goto :END +echo Error: MIDL will not generate DLLDATA.C unless you have at least 1 interface in the main project. +Exit 1 +:END + + Checking for required files + + + + + REGISTER_PROXY_DLL;_DEBUG;%(PreprocessorDefinitions) + + + kernel32.lib;rpcns4.lib;rpcrt4.lib;oleaut32.lib;uuid.lib;ole32.lib;advapi32.lib;comsvcs.lib;%(AdditionalDependencies) + SocksOverRDP-PluginPS.def + true + + + if exist dlldata.c goto :END +echo Error: MIDL will not generate DLLDATA.C unless you have at least 1 interface in the main project. +Exit 1 +:END + + Checking for required files + + + + + MaxSpeed + WIN32;REGISTER_PROXY_DLL;NDEBUG;%(PreprocessorDefinitions) + + + kernel32.lib;rpcns4.lib;rpcrt4.lib;oleaut32.lib;uuid.lib;ole32.lib;advapi32.lib;comsvcs.lib;%(AdditionalDependencies) + SocksOverRDP-PluginPS.def + true + true + true + + + if exist dlldata.c goto :END +echo Error: MIDL will not generate DLLDATA.C unless you have at least 1 interface in the main project. +Exit 1 +:END + + Checking for required files + + + + + MaxSpeed + REGISTER_PROXY_DLL;NDEBUG;%(PreprocessorDefinitions) + + + kernel32.lib;rpcns4.lib;rpcrt4.lib;oleaut32.lib;uuid.lib;ole32.lib;advapi32.lib;comsvcs.lib;%(AdditionalDependencies) + SocksOverRDP-PluginPS.def + true + true + true + + + if exist dlldata.c goto :END +echo Error: MIDL will not generate DLLDATA.C unless you have at least 1 interface in the main project. +Exit 1 +:END + + Checking for required files + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj.filters b/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj.filters new file mode 100644 index 0000000..fc58bed --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-PluginPS.vcxproj.filters @@ -0,0 +1,26 @@ + + + + + {3ae88138-7c28-4919-a9b2-4b5e1473c72b} + + + {d3ce00e0-04ef-4a7b-ad63-435441b697a5} + False + + + + + + + + Generated Files + + + Generated Files + + + Generated Files + + + \ No newline at end of file diff --git a/SocksOverRDP-Plugin/SocksOverRDP-Pluginps.def b/SocksOverRDP-Plugin/SocksOverRDP-Pluginps.def new file mode 100644 index 0000000..2a4265a --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDP-Pluginps.def @@ -0,0 +1,8 @@ + +LIBRARY + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/SocksOverRDP-Plugin/SocksOverRDPPlugin.idl b/SocksOverRDP-Plugin/SocksOverRDPPlugin.idl new file mode 100644 index 0000000..edd3f74 --- /dev/null +++ b/SocksOverRDP-Plugin/SocksOverRDPPlugin.idl @@ -0,0 +1,42 @@ +// SocksOverRDPPlugin.idl : IDL source for SocksOverRDP-Plugin +// + +// This file will be processed by the MIDL tool to +// produce the type library (SocksOverRDPPlugin.tlb) and marshalling code. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(a817e7a2-43fa-11d0-9e44-00aa00b6770a), + dual, + pointer_default(unique) +] +interface IComponentRegistrar : IDispatch +{ + [id(1)] HRESULT Attach([in] BSTR bstrPath); + [id(2)] HRESULT RegisterAll(); + [id(3)] HRESULT UnregisterAll(); + [id(4)] HRESULT GetComponents([out] SAFEARRAY(BSTR)* pbstrCLSIDs, [out] SAFEARRAY(BSTR)* pbstrDescriptions); + [id(5)] HRESULT RegisterComponent([in] BSTR bstrCLSID); + [id(6)] HRESULT UnregisterComponent([in] BSTR bstrCLSID); +}; + +[ + uuid(392B9DEE-7E00-4C58-831B-50C58EAA887F), + version(1.0), + custom(a817e7a1-43fa-11d0-9e44-00aa00b6770a,"{61715a27-63a8-4dc1-9857-83eb00fb60a3}") +] +library SocksOverRDPPluginLib +{ + importlib("stdole2.tlb"); + [ + uuid(61715a27-63a8-4dc1-9857-83eb00fb60a3) + ] + coclass CompReg + { + [default] interface IComponentRegistrar; + }; +}; + diff --git a/SocksOverRDP-Plugin/compreg.cpp b/SocksOverRDP-Plugin/compreg.cpp new file mode 100644 index 0000000..752168e --- /dev/null +++ b/SocksOverRDP-Plugin/compreg.cpp @@ -0,0 +1,7 @@ +// compreg.cpp : Implementation of CCompReg + +#include "stdafx.h" +#include "compreg.h" + + +// CCompReg diff --git a/SocksOverRDP-Plugin/compreg.h b/SocksOverRDP-Plugin/compreg.h new file mode 100644 index 0000000..84971fc --- /dev/null +++ b/SocksOverRDP-Plugin/compreg.h @@ -0,0 +1,7 @@ +// compreg.h : Declaration of the CCompReg + +#pragma once + +#include "resource.h" // main symbols +#include "SocksOverRDPPlugin_i.h" + diff --git a/SocksOverRDP-Plugin/dllfunc.cpp b/SocksOverRDP-Plugin/dllfunc.cpp new file mode 100644 index 0000000..ed74058 --- /dev/null +++ b/SocksOverRDP-Plugin/dllfunc.cpp @@ -0,0 +1,92 @@ +/* +* MIT License +* +* Copyright(c) 2020 Balazs SocksOverRDP +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "stdafx.h" +#include "resource.h" +#include "SocksOverRDPPlugin_i.h" +#include "dllmain.h" +#include "compreg.h" + + +using namespace ATL; + +// Used to determine whether the DLL can be unloaded by OLE. +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); + } + +// Returns a class factory to create an object of the requested type. +_Check_return_ +STDAPI DllGetClassObject(_In_ REFCLSID rclsid, _In_ REFIID riid, _Outptr_ LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + +// DllRegisterServer - Adds entries to the system registry. +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + +// DllUnregisterServer - Removes entries from the system registry. +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} + +// DllInstall - Adds/Removes entries to the system registry per user per machine. +STDAPI DllInstall(BOOL bInstall, _In_opt_ LPCWSTR pszCmdLine) +{ + HRESULT hr = E_FAIL; + static const wchar_t szUserSwitch[] = L"user"; + + if (pszCmdLine != NULL) + { + if (_wcsnicmp(pszCmdLine, szUserSwitch, _countof(szUserSwitch)) == 0) + { + ATL::AtlSetPerUserRegistration(true); + } + } + + if (bInstall) + { + hr = DllRegisterServer(); + if (FAILED(hr)) + { + DllUnregisterServer(); + } + } + else + { + hr = DllUnregisterServer(); + } + + return hr; +} + + diff --git a/SocksOverRDP-Plugin/dllmain.cpp b/SocksOverRDP-Plugin/dllmain.cpp new file mode 100644 index 0000000..6ae02c3 --- /dev/null +++ b/SocksOverRDP-Plugin/dllmain.cpp @@ -0,0 +1,38 @@ +/* +* MIT License +* +* Copyright(c) 2020 Balazs Bucsay +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "stdafx.h" +#include "resource.h" +#include "SocksOverRDPPlugin_i.h" +#include "dllmain.h" +#include "compreg.h" + +CSocksOverRDPPluginModule _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + hInstance; + return _AtlModule.DllMain(dwReason, lpReserved); +} diff --git a/SocksOverRDP-Plugin/dllmain.h b/SocksOverRDP-Plugin/dllmain.h new file mode 100644 index 0000000..8b575eb --- /dev/null +++ b/SocksOverRDP-Plugin/dllmain.h @@ -0,0 +1,10 @@ +// dllmain.h : Declaration of module class. + +class CSocksOverRDPPluginModule : public ATL::CAtlDllModuleT< CSocksOverRDPPluginModule > +{ +public : + DECLARE_LIBID(LIBID_SocksOverRDPPluginLib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_SocksOverRDPPLUGIN, "{B8DC075B-7F8D-4B06-8733-7EB586CA06F0}") +}; + +extern class CSocksOverRDPPluginModule _AtlModule; diff --git a/SocksOverRDP-Plugin/stdafx.cpp b/SocksOverRDP-Plugin/stdafx.cpp new file mode 100644 index 0000000..0171ef9 --- /dev/null +++ b/SocksOverRDP-Plugin/stdafx.cpp @@ -0,0 +1,25 @@ +/* +* MIT License +* +* Copyright(c) 2020 Balazs Bucsay +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files(the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions : +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE. +*/ + +#include "stdafx.h" diff --git a/SocksOverRDP-Plugin/stdafx.h b/SocksOverRDP-Plugin/stdafx.h new file mode 100644 index 0000000..2389c77 --- /dev/null +++ b/SocksOverRDP-Plugin/stdafx.h @@ -0,0 +1,42 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, +// but are changed infrequently + +#pragma once + +#ifndef STRICT +#define STRICT +#endif + +#include "targetver.h" + +#define _ATL_APARTMENT_THREADED +#define _ATL_NO_AUTOMATIC_NAMESPACE + +#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit +#define _ATL_REGISTER_PER_USER // This setting must be consistent with the project's RGS file + + +#include "resource.h" +//#include +#include +#include +#include + +using namespace ATL; + +// adtn'l includes +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef std::basic_ofstream ByteFile; +typedef std::basic_string WString; +typedef std::vector WStringVector; diff --git a/SocksOverRDP-Plugin/targetver.h b/SocksOverRDP-Plugin/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/SocksOverRDP-Plugin/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/SocksOverRDP-Server/SocksOverRDP-Server.cpp b/SocksOverRDP-Server/SocksOverRDP-Server.cpp new file mode 100644 index 0000000..d930dc5 --- /dev/null +++ b/SocksOverRDP-Server/SocksOverRDP-Server.cpp @@ -0,0 +1,643 @@ +/* + * MIT License + * + * Copyright(c) 2018 Balazs Bucsay + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files(the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions : + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + /* + TODO List: + - verbose/debug mode +others? + - anti-timeout + - check socket activity and close after timeout + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SocksOverRDP-Server.h" +#include "SocksServer.h" + + +#pragma comment(lib, "wtsapi32.lib") +#pragma comment(lib, "Ws2_32.lib") + +#define SocksOverRDP_CHANNEL_NAME "SocksChannel" +#define DEBUG_PRINT_BUFFER_SIZE 1024 +#define BUF_SIZE 4096 + +extern DWORD WINAPI HandleClient(void *param); + +DWORD OpenDynamicChannel(LPCSTR szChannelName, HANDLE *phFile); + +struct arguments { + WCHAR *ip; + WCHAR *port; + BYTE priority; +} running_args; + +struct threadhandles { + HANDLE hRDP = NULL; + SOCKET sock = NULL; + HANDLE pipe = NULL; +}; + +struct threads *ThreadHead = NULL; + +int CTRLC = 0; +HANDLE ghMutex, ghLLMutex; +HANDLE hChannel; +HANDLE hWTSHandle = NULL; +BOOL bVerbose = FALSE, bDebug = FALSE; + +VOID DebugPrint(HRESULT hrDbg, __in_z LPWSTR fmt, ...) +{ + HRESULT hr; + TCHAR Buffer[DEBUG_PRINT_BUFFER_SIZE]; + size_t Len; + + hr = StringCchPrintf(Buffer, DEBUG_PRINT_BUFFER_SIZE, TEXT("[hr=0x%8x]"), hrDbg); + assert(SUCCEEDED(hr)); // buffer is sure to be big enough + + hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len); + assert(SUCCEEDED(hr)); // StringCchPrintf is supposed to always NULL term + + va_list argptr; + va_start(argptr, fmt); + + hr = StringCchVPrintf(Buffer + Len, DEBUG_PRINT_BUFFER_SIZE - Len, + fmt, argptr); + + // the above could fail but we don't care since we + // should get a NULL terminated partial string + + // insert terminating eol (despite failure) + hr = StringCchLength(Buffer, DEBUG_PRINT_BUFFER_SIZE, &Len); + assert(SUCCEEDED(hr)); // again there should be a NULL term + + if (Len < DEBUG_PRINT_BUFFER_SIZE - 1) + { + Len++; + Buffer[Len] = TEXT('\0'); + } + + Buffer[Len - 1] = TEXT('\n'); + //OutputDebugString(Buffer); + wprintf(Buffer); +} + +// Add thread to linked list +struct threads *AddThread(DWORD dwThreadId, DWORD dwRemoteThreadId, HANDLE hSlot_r, HANDLE hSlot_w) +{ + struct threads *rolling; + struct threads *ThreadStruct; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + ThreadStruct = (threads *)malloc(sizeof(struct threads)); + ThreadStruct->dwThreadId = dwThreadId; + ThreadStruct->dwRemoteThreadId = dwRemoteThreadId; + ThreadStruct->hThread = NULL; + ThreadStruct->run = TRUE; + ThreadStruct->hSlot_r = hSlot_r; + ThreadStruct->hSlot_w = hSlot_w; + ThreadStruct->hSlot_event = NULL; + ThreadStruct->next = NULL; + + if (ThreadHead == NULL) + { + ThreadHead = ThreadStruct; + } + else + { + rolling = ThreadHead; + while (rolling->next) + { + rolling = rolling->next; + } + rolling->next = ThreadStruct; + } + + if (!ReleaseMutex(ghLLMutex)) + { + printf("Release failed\n"); + } + return ThreadStruct; + break; + + case WAIT_ABANDONED: + printf("%08X: AddThread lock abandoned\n", dwThreadId); + return NULL; + } + + return NULL; +} + +// Look up thread and return a pointer to it by local ThreadId +struct threads *LookupThread(DWORD dwThreadId) +{ + struct threads *rolling; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + rolling = ThreadHead; + if (rolling == NULL) + { + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThread Release Failed0: %08X", GetCurrentThreadId()); + } + return NULL; + } + + while (rolling) + { + if (rolling->dwThreadId == dwThreadId) + { + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThread Release Failed1: %08X", GetCurrentThreadId()); + } + return rolling; + } + rolling = rolling->next; + } + + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThread Release Failed2: %08X", GetCurrentThreadId()); + } + return NULL; + break; + case WAIT_ABANDONED: + printf("%08X: LookupThread lock abandoned\n", dwThreadId); + return NULL; + } + + return NULL; +} + +// Look up thread and return a pointer to it by remote threadid +struct threads *LookupThreadRemote(DWORD dwRemoteThreadId) +{ + struct threads *rolling; + + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + rolling = ThreadHead; + + if (rolling == NULL) + { + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThreadRemote Release Failed0: %08X", GetCurrentThreadId()); + } + return NULL; + } + + while (rolling) + { + if (rolling->dwRemoteThreadId == dwRemoteThreadId) + { + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThreadRemote Release Failed1: %08X", GetCurrentThreadId()); + } + return rolling; + } + rolling = rolling->next; + } + + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] LookupThreadRemote Release Failed2: %08X", GetCurrentThreadId()); + } + return NULL; + break; + case WAIT_ABANDONED: + printf("%08X: LookupThreadRemote lock abandoned\n", GetCurrentThreadId()); + return NULL; + } + return NULL; +} + +// Remove thread from linked list +VOID DeleteThread(DWORD dwThreadId) +{ + struct threads *rolling; + struct threads *prev = NULL; + + //printf("%08X: DeleteThread lock wait\n", dwThreadId); + DWORD dwWaitResult = WaitForSingleObject(ghLLMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + if (!ThreadHead) + { + if (!ReleaseMutex(ghLLMutex)) + { + DebugPrint(0, L"[-] Trying to delete an empty list. %ld", GetCurrentThreadId()); + } + return; + } + + rolling = ThreadHead; + + while (rolling->dwThreadId != dwThreadId) + { + prev = rolling; + rolling = rolling->next; + } + + if (prev) + { + if (rolling->next) + { + prev->next = rolling->next; + } + else + { + prev->next = NULL; + } + } + else + { + if (ThreadHead->next) + { + ThreadHead = ThreadHead->next; + } + else + { + ThreadHead = NULL; + } + } + + rolling->dwThreadId = 0xffffffff; + rolling->next = NULL; + rolling->run = FALSE; + rolling->hSlot_w = NULL; + rolling->hSlot_r = NULL; + rolling->hSlot_event = NULL; + rolling->hSlot_r = NULL; + free(rolling); + rolling = NULL; + + if (!ReleaseMutex(ghLLMutex)) + { + printf("Release failed\n"); + } + return; + break; + + case WAIT_ABANDONED: + printf("%08X: DeleteThread lock abandoned\n", dwThreadId); + return; + } +} + +// If CTRL+C pressed, all threads should be stopped and safely exit +// Double press: closing channel and leaving the rest to the OS. +BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) +{ + struct threads *rolling; + + switch (fdwCtrlType) + { + case CTRL_C_EVENT: + if (CTRLC) + { + wprintf(L"[*] Forced terminating\n"); + WTSVirtualChannelClose(hWTSHandle); + exit(0); + } + + wprintf(L"[*] CTRL+C pressed. Closing down.\n"); + + CTRLC = 1; + if (!ThreadHead) + { + WTSVirtualChannelClose(hWTSHandle); + exit(0); + } + + rolling = ThreadHead; + + do + { + rolling->run = FALSE; + SetEvent(rolling->hSlot_event); + } while (rolling = rolling->next); + + wprintf(L"[*] All threads are signaled to stop\n"); + rolling = ThreadHead; + + wprintf(L"[*] Closing channel\n"); + + WTSVirtualChannelClose(hWTSHandle); + exit(0); + return TRUE; + default: + return FALSE; + } +} + +INT _cdecl wmain(INT argc, __in_ecount(argc) WCHAR **argv) +{ + WSADATA wsaData; + BOOL bSucc, ofused, bClose = FALSE; + HANDLE hEvent, hSlot_r, hSlot_w = NULL; + OVERLAPPED Overlapped, Overlapped_MailSlot; + DWORD dwRecvdLen, dwRead, dwWritten, bufWritelen, dwOverflow = 0; + DWORD dwRemoteThreadId, dwThreadId; + ULONG cbFullSize; + BYTE ReadBuffer[CHANNEL_PDU_LENGTH]; + CHANNEL_PDU_HEADER *pHdr = (CHANNEL_PDU_HEADER *)ReadBuffer; + char *buf, *bufWrite, szOverflow[BUF_SIZE * 4], szMailSlotName[32]; + int ret; + struct threadhandles threadhandle; + struct threads *pta; + + running_args.port = L"1080"; + running_args.priority = 4; + running_args.ip = L"127.0.0.1"; + + wprintf(L"Socks Over RDP by Balazs Bucsay [[@xoreipeip]]\n\n"); + + if ((ret = OpenDynamicChannel(SocksOverRDP_CHANNEL_NAME, &hChannel)) != ERROR_SUCCESS) + { + if (ret == 31) + wprintf(L"[-] Could not open Dynamic Virtual Channel, plugin was not loaded on the client side: %ld\n", ret); + else + wprintf(L"[-] Could not open Dynamic Virtual Channel: %ld %08X\n", ret, ret); + return -1; + } + + wprintf(L"[*] Channel opened over RDP\n"); + + ghMutex = CreateMutex(NULL, FALSE, NULL); + ghLLMutex = CreateMutex(NULL, FALSE, NULL); + + Overlapped_MailSlot = { 0 }; + hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + Overlapped = { 0 }; + Overlapped.hEvent = hEvent; + + if ((ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) + { + wprintf(L"[-] WSAStartup() failed with error: %ld\n", ret); + return -1; + } + + // set handler for Ctrl+C + SetConsoleCtrlHandler(CtrlHandler, TRUE); + + // whatever arrives to the channel, it needs to be parsed and written to + // the corresponding mailslot + while (TRUE) + { + bSucc = ReadFile(hChannel, ReadBuffer, sizeof(ReadBuffer), &dwRead, &Overlapped); + if (!bSucc) + { + if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(Overlapped.hEvent, INFINITE); + bSucc = GetOverlappedResult(hChannel, &Overlapped, &dwRead, FALSE); + } + } + if (!bSucc) + { + wprintf(L"[-] ReadFile()/WaitForSingleObject() error: %ld\n", GetLastError()); + return -1; + } + + // no need to pass the header. + bufWrite = (char *)(pHdr + 1); + bufWritelen = dwRead - sizeof(CHANNEL_PDU_HEADER); + + buf = (char*)bufWrite; + cbFullSize = bufWritelen; + ofused = FALSE; + + // if overflow was present from previous call, we append it to the + // saved bytes + if (dwOverflow) + { + if (bDebug) printf("[*] Overflow was present from previous call. Stored bytes: %ld. Received bytes: %ld == %ld \n", dwOverflow, bufWritelen, dwOverflow+bufWritelen); + memcpy_s(szOverflow + dwOverflow, BUF_SIZE * 4, bufWrite, bufWritelen); + buf = szOverflow; + cbFullSize = bufWritelen + dwOverflow; + dwOverflow = 0; + ofused = TRUE; + } + + // run while anything left in buffer + while (cbFullSize) + { + // edge case, less than necesary data in buffer + if (cbFullSize < 9) + { + if (ofused) + { + // overflow was used and still not enough data + if (bDebug) printf("[*] Not enough data, ofused: %ld\n", cbFullSize); + dwOverflow = cbFullSize; + } + else + { + // no overflow was used, saving the data to overflow + if (bDebug) printf("[*] Not enough data, no ofused: %ld\n", cbFullSize); + memcpy_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize); + dwOverflow = cbFullSize; + } + + // exit loop since not enough data + cbFullSize = 0; + break; + } + + // parsing header + memcpy(&dwRemoteThreadId, buf, sizeof(DWORD)); + memcpy(&dwRecvdLen, buf + sizeof(DWORD), sizeof(DWORD)); + + // corresponding thread lookup + if ((pta = LookupThreadRemote(dwRemoteThreadId)) == NULL) + { + // failed to find existing thread, creating one + memset(szMailSlotName, 0, 32); + snprintf(szMailSlotName, 32, "\\\\.\\mailslot\\RDPSocks_%08X", dwRemoteThreadId); + + hSlot_r = CreateMailslotA(szMailSlotName, 0, MAILSLOT_WAIT_FOREVER, (LPSECURITY_ATTRIBUTES)NULL); + if (hSlot_r == INVALID_HANDLE_VALUE) + { + printf("CreateMailslot_r failed with %d\n", GetLastError()); + break; + } + + hSlot_w = CreateFileA(szMailSlotName, GENERIC_WRITE, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); + + if (hSlot_w == INVALID_HANDLE_VALUE) + { + printf("CreateMailslot_w failed with %d.\n", GetLastError()); + break; + } + + pta = AddThread(0, dwRemoteThreadId, hSlot_r, hSlot_w); + + HANDLE hDummyThread = CreateThread( + NULL, + 0, + &HandleClient, + (void *)pta, + 0, + &dwThreadId); + + pta->dwThreadId = dwThreadId; + pta->hThread = hDummyThread; + if (bDebug) printf("[*] %08X: Thread not found, hSlot_w; + } + + // Is data in buffer bigger than expected data + header? + if (dwRecvdLen <= cbFullSize - (sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE))) + { + bSucc = WriteFile(hSlot_w, buf + sizeof(DWORD) + sizeof(DWORD), dwRecvdLen + 1, &dwWritten, &Overlapped_MailSlot); + if (!bSucc) + { + if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(Overlapped_MailSlot.hEvent, INFINITE); + bSucc = GetOverlappedResult(hChannel, &Overlapped_MailSlot, &dwWritten, TRUE); + } + } + if (!bSucc) + { + if (bDebug) printf("[-] %08X: WriteChannel error: %ld\n", dwRemoteThreadId, GetLastError()); + cbFullSize = 0; + } + else + { + if (bDebug) printf("[+] %08X: no error, written: %ld, should have written: %ld | cbFullSize: %ld\n", dwRemoteThreadId, dwWritten, dwRecvdLen + 1, cbFullSize); + cbFullSize -= (dwWritten + sizeof(DWORD) + sizeof(DWORD)); + buf += (dwWritten + sizeof(DWORD) + sizeof(DWORD)); + } + } + else + { + // less data then expected, saving to szOverflow + if (bDebug) printf("[*] !!! OVERFLOW HAPPENED %ld < %ld -9\n", dwRecvdLen, cbFullSize); + if (ofused) + { + if (bDebug) printf("[*] Data left in buffer, ofused: %ld\n", cbFullSize); + memmove_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize); + dwOverflow = cbFullSize; + } + else + { + if (bDebug) printf("[*] Data left in buffer, not ofused: %ld\n", cbFullSize); + memcpy_s(szOverflow, BUF_SIZE * 4, buf, cbFullSize); + dwOverflow = cbFullSize; + } + cbFullSize = 0; + } + } + } + + CloseHandle(ghMutex); + CloseHandle(hChannel); + + return 0; +} + +/* +* Open a dynamic channel with the name given in szChannelName. +* The output file handle can be used in ReadFile/WriteFile calls. +*/ +DWORD OpenDynamicChannel(LPCSTR szChannelName, HANDLE *phFile) +{ + HANDLE hWTSFileHandle; + PVOID vcFileHandlePtr = NULL; + DWORD len; + DWORD rc = ERROR_SUCCESS; + BOOL fSucc; + + + hWTSHandle = WTSVirtualChannelOpenEx(WTS_CURRENT_SESSION, (LPSTR)szChannelName, + WTS_CHANNEL_OPTION_DYNAMIC | running_args.priority); + if (NULL == hWTSHandle) + { + rc = GetLastError(); + goto exitpt; + } + + fSucc = WTSVirtualChannelQuery(hWTSHandle, WTSVirtualFileHandle, + &vcFileHandlePtr, &len); + if (!fSucc) + { + rc = GetLastError(); + goto exitpt; + } + if (len != sizeof(HANDLE)) + { + rc = ERROR_INVALID_PARAMETER; + goto exitpt; + } + + hWTSFileHandle = *(HANDLE *)vcFileHandlePtr; + fSucc = DuplicateHandle(GetCurrentProcess(), hWTSFileHandle, + GetCurrentProcess(), phFile, 0, FALSE, DUPLICATE_SAME_ACCESS); + + if (!fSucc) + { + rc = GetLastError(); + goto exitpt; + } + + rc = ERROR_SUCCESS; + +exitpt: + if (vcFileHandlePtr) + { + WTSFreeMemory(vcFileHandlePtr); + } + if (hWTSHandle) + { + WTSVirtualChannelClose(hWTSHandle); + } + + return rc; +} \ No newline at end of file diff --git a/SocksOverRDP-Server/SocksOverRDP-Server.h b/SocksOverRDP-Server/SocksOverRDP-Server.h new file mode 100644 index 0000000..8758527 --- /dev/null +++ b/SocksOverRDP-Server/SocksOverRDP-Server.h @@ -0,0 +1,18 @@ +#pragma once + +struct threads +{ + DWORD dwThreadId; + DWORD dwRemoteThreadId; + HANDLE hThread; + BOOL run; + HANDLE hSlot_r; + HANDLE hSlot_w; + HANDLE hSlot_event; + struct threads *next; +}; + +struct threads *AddThread(DWORD dwThreadId, DWORD dwRemoteThreadId, HANDLE hSlot_r, HANDLE hSlot_w, HANDLE hChannel); +threads *LookupThread(DWORD dwThreadId); +threads *LookupThreadRemote(DWORD dwRemoteThreadId); +VOID DeleteThread(DWORD dwThreadId); \ No newline at end of file diff --git a/SocksOverRDP-Server/SocksOverRDP-Server.vcxproj b/SocksOverRDP-Server/SocksOverRDP-Server.vcxproj new file mode 100644 index 0000000..94e57fa --- /dev/null +++ b/SocksOverRDP-Server/SocksOverRDP-Server.vcxproj @@ -0,0 +1,173 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {C7E1D0A7-0ADA-4DF4-99DA-8527E856670F} + Win32Proj + SocksOverRDPServer + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + + + true + $(SolutionDir)$(Configuration)\$(Platform)\ + + + false + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Platform)\$(Configuration)\ + + + false + $(SolutionDir)$(Configuration)\$(Platform)\ + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + + Console + true + + + + + NotUsing + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + + Console + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + + Console + true + true + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + + + Console + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SocksOverRDP-Server/SocksOverRDP-Server.vcxproj.filters b/SocksOverRDP-Server/SocksOverRDP-Server.vcxproj.filters new file mode 100644 index 0000000..545a030 --- /dev/null +++ b/SocksOverRDP-Server/SocksOverRDP-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;hh;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 + + + + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/SocksOverRDP-Server/SocksServer.cpp b/SocksOverRDP-Server/SocksServer.cpp new file mode 100644 index 0000000..c3c8334 --- /dev/null +++ b/SocksOverRDP-Server/SocksServer.cpp @@ -0,0 +1,639 @@ +/* +TODO: check closing flag at negotiation phases - two functions. +TODO: slotevent hslot passed from socksoverrdp-server.cpp +*/ + + +#include +#include +#include +#include +#include +#include "SocksServer.h" +#include "SocksOverRDP-Server.h" + +#pragma comment(lib, "Ws2_32.lib") + +#define METHOD_NUMBER 1 +#define BUF_SIZE 4096 + +// hardcoded inactive creds, use proper creds check if you want this +#define PREDEF_USERNAME "hello" +#define PREDEF_PASSWORD "bello" + +extern threads *LookupThread(DWORD dwThreadId); +extern VOID DeleteThread(DWORD dwThreadId); + +extern HANDLE ghMutex; +extern HANDLE hChannel; +extern BOOL bVerbose, bDebug; + +int method_no_auth_required(HANDLE c, int count, char *rv); + +typedef int(*fn)(HANDLE, int, char*); +static fn method_functions[METHOD_NUMBER] = +{ + method_no_auth_required, + // method_username_password, // disabled for security purposes +}; +static char method_numbers[METHOD_NUMBER] = +{ + 0, + // 2, +}; + +int method_no_auth_required(HANDLE c, int count, char *rv) +{ + return TRUE; +} + +// dedicated function to handle writes on the virtual channel +BOOL WriteChannel(char *Buffer, DWORD nBytesToWrite, DWORD *nBytesWrittes, DWORD dwRemoteThreadId, BOOL bClose) +{ + HANDLE hEvent; + OVERLAPPED Overlapped; + DWORD i, dwLimit, dwToWrite, dwLocalSent, dwWaitResult; + DWORD dwHeaderSize = sizeof(DWORD) + sizeof(DWORD) + sizeof(BYTE); + BOOL bSucc = FALSE; + + dwToWrite = BUF_SIZE; + dwLocalSent = 0; + + hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + Overlapped = { 0 }; + Overlapped.hEvent = hEvent; + + // number of writes + dwLimit = (unsigned int)ceil(nBytesToWrite / (double)BUF_SIZE); + // no content but close connection request. + if (!dwLimit && bClose) + dwLimit = 1; + + // mandatory header with + memcpy_s(Buffer - dwHeaderSize, 4, &dwRemoteThreadId, sizeof(DWORD)); + memcpy_s(Buffer + sizeof(DWORD) - dwHeaderSize, 4, &dwToWrite, sizeof(DWORD)); + Buffer[sizeof(DWORD) + sizeof(DWORD) - dwHeaderSize] = 0; + + dwWaitResult = WaitForSingleObject(ghMutex, INFINITE); + switch (dwWaitResult) + { + case WAIT_OBJECT_0: + for (i = 0; i < dwLimit; i++) + { + if (i == dwLimit - 1) + { + dwToWrite = nBytesToWrite % (BUF_SIZE + 1); + memcpy_s(Buffer + sizeof(DWORD) - 9, 4, &dwToWrite, sizeof(DWORD)); + if (bClose) Buffer[sizeof(DWORD) + sizeof(DWORD) - 9] = 0x01; + } + //printf("%08X: CHANNEL Sent: %ld content length: %ld\n", dwRemoteThreadId, dwToWrite + 9, dwToWrite); + + bSucc = WriteFile(hChannel, Buffer - 9, dwToWrite + 9, &dwLocalSent, &Overlapped); + if (!bSucc) + { + if (GetLastError() == ERROR_IO_PENDING) + { + WaitForSingleObject(Overlapped.hEvent, INFINITE); + bSucc = GetOverlappedResult(hChannel, &Overlapped, &dwLocalSent, TRUE); + } + } + if (!bSucc) + { + if (bVerbose) printf("[-] hChannel thread(%08X) WriteChannel: %ld\n", GetCurrentThreadId(), GetLastError()); + if (!ReleaseMutex(ghMutex)) + { + printf("Release failed1\n"); + } + return FALSE; + } + *nBytesWrittes += dwLocalSent; + } + + if (!ReleaseMutex(ghMutex)) + { + printf("Release failed2\n"); + } + return bSucc; + break; + + case WAIT_ABANDONED: + return FALSE; + } + + return bSucc; +} + +int CheckAuthentication(char *buf_full, int ret) +{ + DWORD i, j, dwWritten = 2; + char *answer, *buf, answer_tosend[2 + sizeof(DWORD) + sizeof(DWORD) + 1]; + struct threads *pta; + + buf = buf_full + 1; + answer = answer_tosend + sizeof(DWORD) + sizeof(DWORD) + 1; + + pta = LookupThread(GetCurrentThreadId()); + + answer[0] = 5; + + for (i = 0; i < METHOD_NUMBER; i++) + for (j = 0; j < (unsigned char)buf[1]; j++) + if (buf[j + 2] == method_numbers[i]) + { + answer[1] = method_numbers[i]; + WriteChannel(answer, 2, &dwWritten, pta->dwRemoteThreadId, FALSE); + return i; + } + + answer[1] = (unsigned)0xFF; + + WriteChannel(answer, 2, &dwWritten, pta->dwRemoteThreadId, FALSE); + + return -1; +} + +void sendReply(char replyField, char addressType, char *addr, char *port) +{ + char null[20], *answer, answer2[300 + sizeof(DWORD) + sizeof(DWORD) + 1]; + DWORD dwWritten, ret; + + struct threads *pta; + + answer = answer2 + sizeof(DWORD) + sizeof(DWORD) + 1; + memset(answer, 0, 300 + sizeof(DWORD) + sizeof(DWORD) + 1); + memset(null, 0, 20); + + pta = LookupThread(GetCurrentThreadId()); + + // if addr or port set to NULL, we will send nulls instead of the address + // it isn't RFC compliant but I do not support info leak either. + if (addr == NULL) addr = null; + if (port == NULL) port = null; + + answer[0] = 5; + + answer[1] = replyField; + answer[3] = addressType; + + switch (addressType) + { + case 3: + memcpy_s(answer + 4, 296, (void *)(addr + 1), (unsigned char)(addr[0])); + memcpy_s(answer + 4 + (unsigned char)(addr[0]), 396 - (unsigned char)(addr[0]), port, 2); + ret = (unsigned char)(addr[0]) + 2; + break; + case 4: + memcpy_s(answer + 4, 296, addr, 16); + memcpy_s(answer + 20, 280, port, 2); + ret = 22; + break; + default: + memcpy_s(answer + 4, 296, addr, 4); + memcpy_s(answer + 8, 292, port, 2); + ret = 10; + break; + } + + if (replyField == 0x00) + WriteChannel(answer, ret, &dwWritten, pta->dwRemoteThreadId, FALSE); + else + WriteChannel(answer, ret, &dwWritten, pta->dwRemoteThreadId, TRUE); +} + +int getAddressInfo(sockaddr_in *sockaddrin, sockaddr_in6 *sockaddrin6, char *buf, int ret) +{ + ADDRINFOA hints; + ADDRINFOA *result = NULL; + + char domain[256]; + + //IPv4 + if (buf[3] == 1) + { + if (ret != 10) + { + if (bVerbose) printf("[-] SOCKS thread(%08Xd) getAddressInfo IPv4 selected, length mismatch: %ld\n", GetCurrentThreadId(), ret); + return -1; + } + sockaddrin->sin_family = AF_INET; + memcpy_s(&(sockaddrin->sin_port), 2, buf + 8, 2); + memcpy_s(&(sockaddrin->sin_addr), 4, buf + 4, 4); + + char *s = (char *)malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(sockaddrin->sin_addr), s, INET_ADDRSTRLEN); + if (bVerbose) printf("[+] SOCKS thread(%08X) getAddressInfo CONNECT IPv4: %s:%hd\n", GetCurrentThreadId(), s, htons(sockaddrin->sin_port)); + free(s); + } + //DNS + if (buf[3] == 3) + { + if ((7 + (unsigned char)buf[4]) != ret) + { + if (bVerbose) printf("[-] SOCKS thread(%08X) getAddressInfo DNS selected, length mismatch: %ld\n", GetCurrentThreadId(), ret); + return -1; + } + ZeroMemory(&hints, sizeof(hints)); + ZeroMemory(domain, 256); + + // change for IPv6? + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = AI_PASSIVE; + + memcpy_s(domain, 256, (void *)(buf + 5), (unsigned char)(buf[4])); + + if ((ret = GetAddrInfoA(domain, "1", &hints, &result)) != 0) { + if (bVerbose) printf("[-] SOCKS thread(%08X) getAddressInfo GetAddrInfoA failed with error: %ld %ld\n", GetCurrentThreadId(), ret, WSAGetLastError()); + return -1; + } + memcpy_s(sockaddrin, sizeof(sockaddr_in), result->ai_addr, sizeof(sockaddr_in)); + memcpy_s(&(sockaddrin->sin_port), 2, buf + ((unsigned char)buf[4]) + 5, 2); + + char *s = (char *)malloc(INET_ADDRSTRLEN); + inet_ntop(AF_INET, &(sockaddrin->sin_addr), s, INET_ADDRSTRLEN); + if (bVerbose) printf("[+] SOCKS thread(%08X) getAddressInfo CONNECT DNS: %s(%s):%hd\n", GetCurrentThreadId(), domain, s, htons(sockaddrin->sin_port)); + free(s); + } + //IPv6 + if (buf[3] == 4) + { + if (ret != 22) + { + if (bVerbose) printf("[-] SOCKS thread(%08X) getAddressInfo IPv6 selected, length mismatch: %ld\n", GetCurrentThreadId(), ret); + return -1; + } + sockaddrin6->sin6_family = AF_INET6; + memcpy_s(&(sockaddrin6->sin6_port), 2, buf + 20, 2); + memcpy_s(&(sockaddrin6->sin6_addr), 30, buf + 4, 16); + + char *s = (char *)malloc(INET6_ADDRSTRLEN); + inet_ntop(AF_INET6, &(sockaddrin6->sin6_addr), s, INET6_ADDRSTRLEN); + if (bVerbose) printf("[+] SOCKS thread(%08X) getAddressInfo CONNECT IPv6: %s:%hd\n", GetCurrentThreadId(), s, htons(sockaddrin6->sin6_port)); + free(s); + } + + return 0; +} + +SOCKET DoConnection(char *buf, int ret) +{ + SOCKET sock; + sockaddr_in sockaddrin; + sockaddr_in6 sockaddrin6; + + if (buf[0] == 5) + { + if (getAddressInfo(&sockaddrin, &sockaddrin6, buf, ret) < 0) { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection could not create socket structs\n", GetCurrentThreadId()); + // this isnt "general SOCKS server failure", but there no better error code + sendReply(0x01, 0x01, NULL, NULL); + return NULL; + } + + // CONNECT + if (buf[1] == 1) + { + if (buf[3] == 4) + { + if ((sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection socket6() failed with: %ld\n", GetCurrentThreadId(), WSAGetLastError()); + sendReply(0x01, 0x04, NULL, NULL); + return NULL; + } + + if ((ret = connect(sock, (SOCKADDR *)&sockaddrin6, sizeof(sockaddrin6))) == SOCKET_ERROR) { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection connect6() failed with: %ld\n", GetCurrentThreadId(), WSAGetLastError()); + sendReply(0x05, 0x04, NULL, NULL); + return NULL; + } + } + else + { + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection socket() failed with: %ld\n", GetCurrentThreadId(), WSAGetLastError()); + sendReply(0x01, 0x01, NULL, NULL); + return NULL; + } + + if ((ret = connect(sock, (SOCKADDR *)&sockaddrin, sizeof(sockaddrin))) == SOCKET_ERROR) { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection connect() failed with: %ld\n", GetCurrentThreadId(), WSAGetLastError()); + sendReply(0x05, 0x01, NULL, NULL); + return NULL; + } + } + + sendReply(0x00, 0x01, NULL, NULL); + + return sock; + } + // BIND + if (buf[1] == 2) + { + printf("[+] SOCKS DoConnection BIND\n"); + } + // UDP ASSOCIATE + if (buf[1] == 3) + { + //SOCK_DGRAM + printf("[+] SOCKS DoConnection UDP ASSOCIATE\n"); + } + } + else + { + if (bVerbose) printf("[-] SOCKS thread(%08X) DoConnection unknown SOCKS version\n", GetCurrentThreadId()); + return NULL; + } + + return NULL; +} + +// Thread's main function that handles all communication of the corresponding +// connection. It's remote ThreadID attribute matches the server's threadID +// which will be used to track the connections. +DWORD WINAPI HandleClient(void *param) +{ + struct threads *pta = (struct threads *)param; + WSANETWORKEVENTS NetworkEvents; + WSAOVERLAPPED SendOverlapped; + OVERLAPPED Overlapped_MailSlot; + HANDLE hEvents[2], hMailSlot = pta->hSlot_r; + DWORD dwRet, dwRecvN, dwSentN, dwSentPointer, Flags; + SOCKET sRelayConnection; + BOOL bClose = FALSE; + WSABUF DataBuf; + char buf[BUF_SIZE + 1]; + int rc, iSrecvN, iAuthNum = -1; + + Overlapped_MailSlot = { 0 }; + + // socks basics + if (ReadFile(hMailSlot, buf, BUF_SIZE + 1, &dwRet, NULL)) + { + if (buf[0] == 0x01) + goto exitthread; + + if (buf[1] == 4) + { + //socks4 + if (bDebug) printf("%08X: SOCKS HandleClient Socks4 request\n", pta->dwRemoteThreadId); + goto exitthread; + } + else + if (buf[1] == 5) + { + // socks5 + if (dwRet - 3 != buf[2]) + { + if (bDebug) printf("%08X: SOCKS HandleClient wrong list length: %ld\n", pta->dwRemoteThreadId, dwRet); + goto exitthread; + } + + if ((iAuthNum = CheckAuthentication(buf, dwRet)) < 0) + { + if (bDebug) printf("%08X: SOCKS HandleClient auth failed: %ld\n", pta->dwRemoteThreadId, iAuthNum); + goto exitthread; + } + } + else + { + if (bDebug) + { + printf("%08X: SOCKS HandleClient unknown Socks version number\n", pta->dwRemoteThreadId); + printf("%08X: Readfile ret: %ld\n", pta->dwRemoteThreadId, dwRet); + for (DWORD i = 0; i < dwRet; i++) + printf("%02X ", (unsigned char)buf[i]); + printf("\n"); + } + goto exitthread; + } + } + else + { + if (bDebug) printf("%08X: SOCKS HandleClient while ReadFile error: %ld\n", pta->dwRemoteThreadId, GetLastError()); + goto exitthread; + } + + if (iAuthNum == -1) + { + if (bDebug) printf("%08X: SOCKS HandleClient wrong authnum: %ld\n", pta->dwRemoteThreadId, iAuthNum); + goto exitthread; + } + + // socks authentication + if (iAuthNum > 0) + { + if (bDebug) printf("%08X: SOCKS HandleClient authentication invoked: %ld\n", pta->dwRemoteThreadId, iAuthNum); + + if (ReadFile(hMailSlot, buf, BUF_SIZE, &dwRet, NULL)) + { + if (dwRet > 2) + { + if (!method_functions[iAuthNum](hMailSlot, dwRet, buf)) + { + if (bDebug) printf("%08X: SOCKS HandleClient authentication failed: %ld\n", pta->dwRemoteThreadId, iAuthNum); + goto exitthread; + } + } + else + { + if (bDebug) printf("%08X: SOCKS HandleClient less than 2 recv error: %ld %ld\n", pta->dwRemoteThreadId, dwRet, WSAGetLastError()); + goto exitthread; + } + } + else + { + if (bDebug) printf("%08X: SOCKS HandleClient authentication recv error: %ld %ld\n", pta->dwRemoteThreadId, dwRet, WSAGetLastError()); + goto exitthread; + } + } + + // socks connection phase + if (ReadFile(hMailSlot, buf, BUF_SIZE, &dwRet, NULL)) + { + if (dwRet > 0) + if (buf[0] == 0x01) + goto exitthread; + if (dwRet > 6) + { + if ((sRelayConnection = DoConnection(buf + 1, dwRet - 1)) == NULL) + { + if (bDebug) printf("%08X: SOCKS HandleClient no socket created\n", pta->dwRemoteThreadId); + goto exitthread; + } + } + else + { + if (bDebug) printf("%08X: SOCKS HandleClient connection request less than 6 error: %ld %ld\n", pta->dwRemoteThreadId, dwRet, WSAGetLastError()); + goto exitthread; + } + } + else + { + if (bDebug) printf("%08X: SOCKS HandleClient connection request recv error: %ld %ld\n", pta->dwRemoteThreadId, dwRet, WSAGetLastError()); + goto exitthread; + } + + + hEvents[0] = CreateEvent(NULL, FALSE, FALSE, NULL); + hEvents[1] = CreateEvent(NULL, FALSE, FALSE, NULL); + pta->hSlot_event = hEvents[0]; + + Overlapped_MailSlot.hEvent = hEvents[0]; + + SecureZeroMemory((PVOID)& SendOverlapped, sizeof(WSAOVERLAPPED)); + SendOverlapped.hEvent = WSACreateEvent(); + if (SendOverlapped.hEvent == NULL) { + printf("Mailslot thread(%08X) WSACreateEvent failed with error: %d\n", pta->dwRemoteThreadId, WSAGetLastError()); + goto exitthread; + } + + if ((hEvents[0] == NULL) || ((hEvents[1] == NULL))) + { + printf("[-] Mailslot thread(%08X) CreateEvent failed with %ld\n", pta->dwRemoteThreadId, GetLastError()); + goto exitthread; + } + + WSAEventSelect(sRelayConnection, hEvents[1], FD_READ | FD_CLOSE); + + BOOL read = true; + + while (pta->run) + { + if (read) + { + if (ReadFile(hMailSlot, buf, BUF_SIZE + 1, &dwRecvN, &Overlapped_MailSlot) == FALSE) + { + if (GetLastError() != ERROR_IO_PENDING) + { + printf("[-] Mailslot thread(%08X) ReadFile error %ld\n", pta->dwRemoteThreadId, GetLastError()); + pta->run = FALSE; + break; + } + } + read = FALSE; + } + + dwRet = WaitForMultipleObjects(2, hEvents, FALSE, INFINITE); + if ((dwRet - WAIT_OBJECT_0) < 0 || (dwRet - WAIT_OBJECT_0) > (2 - 1)) + { + printf("[-] Mailslot thread(%08X) WaitForMultipleObjects index out of range %ld\n", pta->dwRemoteThreadId, dwRet); + pta->run = FALSE; + break; + } + if (pta->run == FALSE) + { + break; + } + + switch (dwRet - WAIT_OBJECT_0) + { + case 0: + if (bDebug) printf("%08X: READ recvn: %ld - internalhigh: %ld - internal: %ld\n", pta->dwRemoteThreadId, dwRecvN, Overlapped_MailSlot.InternalHigh, Overlapped_MailSlot.Internal); + dwRecvN = Overlapped_MailSlot.InternalHigh; + if (buf[0] == 0x01) + { + pta->run = FALSE; + } + + dwSentPointer = 1; + while (dwSentPointer < dwRecvN) + { + if (!pta->run) + { + //printf("%08X: was set to exit1\n", pta->dwRemoteThreadId); + break; + } + + DataBuf.len = dwRecvN - dwSentPointer; + DataBuf.buf = buf + dwSentPointer; + + rc = WSASend(sRelayConnection, &DataBuf, 1, &dwSentN, NULL, &SendOverlapped, NULL); + if ((rc == SOCKET_ERROR) && (WSA_IO_PENDING != WSAGetLastError())) + { + printf("[-] Mailslot thread(%08X) WSASend failed with error: %d\n", pta->dwRemoteThreadId, WSAGetLastError()); + pta->run = FALSE; + break; + } + rc = WSAWaitForMultipleEvents(1, &SendOverlapped.hEvent, TRUE, INFINITE, TRUE); + if (rc == WSA_WAIT_FAILED) + { + printf("[-] Mailslot thread(%08X) WSAWaitForMultipleEvents failed with error: %d\n", pta->dwRemoteThreadId, WSAGetLastError()); + pta->run = FALSE; + break; + } + rc = WSAGetOverlappedResult(sRelayConnection, &SendOverlapped, &dwSentN, FALSE, &Flags); + if (rc == FALSE) + { + printf("[-] Mailslot thread(%08X) WSASend failed with error: %d\n", pta->dwRemoteThreadId, WSAGetLastError()); + pta->run = FALSE; + break; + } + WSAResetEvent(SendOverlapped.hEvent); + + dwSentPointer += dwSentN; + } + read = TRUE; + + break; + case 1: + if (WSAEnumNetworkEvents(sRelayConnection, hEvents[1], &NetworkEvents) == SOCKET_ERROR) { + printf("[-] SOCKS thread(%08X) HandleClient WSAEnumNetworkEvents error: %ld\n", pta->dwRemoteThreadId, GetLastError()); + } + else + { + if (NetworkEvents.lNetworkEvents & FD_CLOSE) + { + if (bDebug) printf("%08X: bClose FD_CLOSE was set\n", pta->dwRemoteThreadId); + bClose = TRUE; + } + } + do + { + if ((iSrecvN = recv(sRelayConnection, buf + sizeof(DWORD) + sizeof(DWORD) + 1, BUF_SIZE, 0)) > 0) + { + if (bDebug) printf("%08X: RECV recvn: %ld\n", pta->dwRemoteThreadId, iSrecvN); + dwSentPointer = 0; + if (!WriteChannel(buf + sizeof(DWORD) + sizeof(DWORD) + 1, iSrecvN, &dwSentPointer, pta->dwRemoteThreadId, FALSE)) + { + pta->run = FALSE; + } + } + else + { + bClose = TRUE; + } + + // run again the loop only if there is more data and the last data packet was set to FIN + } while (bClose && (iSrecvN > 0)); + if (bClose) + { + if (bDebug) printf("%08X: CLOSE\n", pta->dwRemoteThreadId); + + if (!WriteChannel(buf + sizeof(DWORD) + sizeof(DWORD) + 1, 0, &dwSentPointer, pta->dwRemoteThreadId, TRUE)) + printf("[-] Mailslot thread(%08X) HandleClient select1 WriteChannel error: %ld %ld\n", pta->dwRemoteThreadId, dwSentN, GetLastError()); + + // FIN recv'd + if (iSrecvN == SOCKET_ERROR) + { + if (bVerbose) printf("[-] SOCKS thread(%08X) HandleClient select1 recv error: %ld %ld\n", pta->dwRemoteThreadId, iSrecvN, WSAGetLastError()); + } + pta->run = FALSE; + } + break; + default: + break; + } + } + WSACloseEvent(SendOverlapped.hEvent); +exitthread: + // closing down the thread + shutdown(sRelayConnection, SD_SEND); + CloseHandle(pta->hSlot_r); + CloseHandle(pta->hSlot_w); + CloseHandle(hEvents[0]); + CloseHandle(hEvents[1]); + DeleteThread(GetCurrentThreadId()); + + return 0; +} diff --git a/SocksOverRDP-Server/SocksServer.h b/SocksOverRDP-Server/SocksServer.h new file mode 100644 index 0000000..e502d61 --- /dev/null +++ b/SocksOverRDP-Server/SocksServer.h @@ -0,0 +1,6 @@ +#pragma once +#include +#include +#include + +int StartServer(WCHAR *ip, WCHAR *port); \ No newline at end of file diff --git a/SocksOverRDP-Server/targetver.h b/SocksOverRDP-Server/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/SocksOverRDP-Server/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include