Build rdpthread.exe - RDP credential validator (C# via WNetAddConnection2)
This commit is contained in:
212
rdpthread.cs
Normal file
212
rdpthread.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
// 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 <ip> <port> <username> <password>
|
||||
// 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 <ip> <port> <username> <password>");
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user