// rdpthread.exe - RDP Credential Validator for FastRDP-NG // Uses Windows WNetAddConnection2 API to validate credentials // against the remote machine's IPC$ share. // // Compilation: csc.exe /target:exe /out:rdpthread.exe rdpthread.cs // or via build.bat // // Usage: rdpthread.exe // Returns: exit code 0 + prints "success" on valid credentials // exit code 1 on failure (invalid, timeout, unreachable) using System; using System.Runtime.InteropServices; using System.Text; namespace RDPThread { class Program { // ── Win32 P/Invoke ────────────────────────────────────── [DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int WNetAddConnection2( ref NETRESOURCE netResource, string password, string username, int flags ); [DllImport("mpr.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern int WNetCancelConnection2( string name, int flags, bool force ); [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)] private static extern IntPtr socket(int af, int type, int protocol); [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)] private static extern int connect(IntPtr s, byte[] addr, int addrlen); [DllImport("ws2_32.dll", CharSet = CharSet.Ansi)] private static extern int closesocket(IntPtr s); [DllImport("ws2_32.dll")] private static extern int WSAStartup(ushort version, ref WSADATA data); [DllImport("ws2_32.dll")] private static extern int WSACleanup(); [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct NETRESOURCE { public int dwScope; public int dwType; public int dwDisplayType; public int dwUsage; [MarshalAs(UnmanagedType.LPWStr)] public string lpLocalName; [MarshalAs(UnmanagedType.LPWStr)] public string lpRemoteName; [MarshalAs(UnmanagedType.LPWStr)] public string lpComment; [MarshalAs(UnmanagedType.LPWStr)] public string lpProvider; } [StructLayout(LayoutKind.Sequential)] private struct WSADATA { public ushort wVersion; public ushort wHighVersion; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szDescription; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szSystemStatus; public ushort iMaxSockets; public ushort iMaxUdpDg; public IntPtr lpVendorInfo; } [StructLayout(LayoutKind.Sequential)] private struct sockaddr_in { public short sin_family; public ushort sin_port; public uint sin_addr; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] public byte[] sin_zero; } private const int RESOURCETYPE_ANY = 0; private const int CONNECT_TEMPORARY = 4; private const int NO_ERROR = 0; private const int AF_INET = 2; private const int SOCK_STREAM = 1; private const int IPPROTO_TCP = 6; private const int ERROR_LOGON_FAILURE = 1326; private const int ERROR_ACCESS_DENIED = 5; private const int ERROR_BAD_NETPATH = 53; private const int ERROR_NETWORK_UNREACHABLE = 1231; private const int ERROR_INVALID_PASSWORD = 86; private const int ERROR_SESSION_CREDENTIAL_CONFLICT = 1219; // ── TCP connect check ────────────────────────────────── private static bool TcpConnect(string ip, int port, int timeoutMs = 3000) { try { WSADATA wsa = new WSADATA(); if (WSAStartup(0x202, ref wsa) != 0) return false; IntPtr s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == (IntPtr)(-1)) { WSACleanup(); return false; } // Set non-blocking for timeout var addr = new sockaddr_in { sin_family = AF_INET, sin_port = (ushort)System.Net.IPAddress.HostToNetworkOrder((short)port), sin_addr = BitConverter.ToUInt32( System.Net.IPAddress.Parse(ip).GetAddressBytes(), 0), sin_zero = new byte[8] }; byte[] addrBytes = new byte[16]; Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_family), 0, addrBytes, 0, 2); Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_port), 0, addrBytes, 2, 2); Buffer.BlockCopy(BitConverter.GetBytes(addr.sin_addr), 0, addrBytes, 4, 4); int result = connect(s, addrBytes, 16); closesocket(s); WSACleanup(); return result == 0; } catch { return false; } } // ── Credential validation via IPC$ ──────────────────── private static bool ValidateCredentials(string ip, string username, string password) { string remotePath = string.Format("\\\\{0}\\IPC$", ip); NETRESOURCE nr = new NETRESOURCE { dwScope = 0, dwType = RESOURCETYPE_ANY, dwDisplayType = 0, dwUsage = 0, lpLocalName = null, lpRemoteName = remotePath, lpComment = null, lpProvider = null }; int result = WNetAddConnection2(ref nr, password, username, CONNECT_TEMPORARY); if (result == NO_ERROR) { // Success! Clean up the connection WNetCancelConnection2(remotePath, CONNECT_TEMPORARY, true); return true; } return false; } // ── Main ────────────────────────────────────────────── static int Main(string[] args) { if (args.Length < 4) { Console.Error.WriteLine("Usage: rdpthread.exe "); return 1; } string ip = args[0]; int port = int.Parse(args[1]); string username = args[2]; string password = args[3]; // Step 1: Quick TCP connectivity check if (!TcpConnect(ip, port)) { // Port not reachable - try IPC$ anyway (SMB uses port 445) // The WNetAddConnection2 will fail gracefully if unreachable } // Step 2: Validate credentials via IPC$ (SMB) // This uses Windows built-in authentication against the remote machine bool success = ValidateCredentials(ip, username, password); if (success) { Console.WriteLine("success"); return 0; } return 1; } } }