اختبار PowerShell للمنفذ: دليل أداة مخصصة

لنقم بالانتقال إلى كيفية بناء أداة اختبار منفذ PowerShell التي تتيح لك اختبار الأبواب المفتوحة برقم المنفذ والتسمية.

لبناء نص قوي لن يتعثر ويموت على نصف الخوادم الخاصة بك، من المهم التأكد أولاً من أن الشروط الأساسية الموجودة من أجل الحصول على النتيجة النهائية مستوفاة.

ما هي هذه الشروط الأساسية؟ الشروط الأساسية هي الخدمات مثل FTP، HTTP، DCOM، WMI، WSMAN، إلخ. الاتصال الذي تحاول إجراؤه إلى خادم يعتمد عادةً على خدمات مثل هذه.

هناك تسلسل هرمي من عدة طبقات من الفحوصات يمكنك القيام بها على خوادمك قبل محاولة الاتصال اعتمادًا على مدى التحقق الذي تريده.

أولاً وقبل كل شيء، لديك جميع طبقات OSI للانتقال على الجانب الشبكي. هذا لا يشمل الخدمات على نظام الاستضافة الذي تقوم بتنفيذ النص منه وكل شيء آخر على نظام الاستضافة البعيد.

أحد الاختبارات الأولية التي تحتاج إلى إجرائها عند الاستعلام عن خادم بعيد هو التحقق من توافر المنافذ الشبكية المناسبة وقابليتها للوصول. يعتمد ذلك على الخدمات الجاري تشغيلها على الخادم البعيد.

I always used to either not even attempting to test port connections or fumbling around with finding that latest Test-Port script I had scavenged somewhere. Once I found it to figure out what ports I actually needed to a test ahead of time. Today was the final straw.

I needed to query a set of domain controllers before running some CIM queries against them. I went about my normal fumbling around and decided enough was enough and sat down and built my own, fully featured port testing script.

بمساعدة هذا النص من تيكنت تمكنت من إنشاء زوج جيد جداً من الوظائف في PowerShell التي لن تسمح لك فقط بفحص المنافذ TCP و UDP المفتوحة ولكن أيضاً لاختبار مجموعات المنافذ حسب دور الخادم. لا مزيد من البحث في كل مرة عن المنافذ التي تستخدمها أي خدمة!

منحت، خاصةً بالنسبة لـ Active Directory، يمكن أن تختلف المنافذ حسب نظام التشغيل للخادم، والخدمات المختلفة على مراقب النطاق، إلخ. لا تتردد في ضبطها كما تحتاج لبيئتك.

إليك صورة لشاشة مثال على الاستخدام:

Testing ports with PowerShell

كما يمكنك رؤية، يمكنك تحديد عدد غير محدود من الخوادم وسيقوم بإخراج قائمة جميلة من الكائنات مفصولة حسب مجموعة منافذ الخدمة والمنفذ لكل كمبيوتر. حتى الآن، كان ذلك مفيداً جداً! آمل أن تستفيد منه مثلما فعلت!

function Test-ServerRolePortGroup {
	<#
	.ملخص
		تقوم هذه الوظيفة بفحص منافذ TCP/UDP المفتوحة حسب دور الخادم.
	.الوصف
		تقوم هذه الوظيفة بفحص جميع منافذ TCP/UDP المناسبة حسب دور الخادم بحيث لا تحتاج
		إلى حفظ أو البحث عن جميع المنافذ التي يجب اختبارها في كل مرة
		ترغب في التحقق من الاتصال عن بُعد على خادم محدد.
	.ملاحظات
		روابط مراجع المنفذ:
		http://technet.microsoft.com/en-us/library/dd772723(v=ws.10).aspx
		http://en.wikipedia.org/wiki/Server_Message_Block
		http://technet.microsoft.com/en-us/library/cc940063.aspx
	.المعلمة الخادم
		اسم أحد أو أكثر من الخوادم البعيدة، مفصولة بفواصل.
	.المعلمة دور الخادم
		الخدمات على الكمبيوتر التي ترغب في البحث عن المنافذ المفتوحة لها. يمكن أن تكون
		الخدمات الشائعة مثل WinRm، Smb، Dns، Active Directory، و NetBIOS
	.مثال
		PS> Test-ServerRolePortGroup -Computername 'LABDC','LABDC2' -ServerRole NetBIOS,WinRm,Dns
		
		يقوم هذا المثال بفحص المنافذ الشبكية اللازمة لـ NetBIOS، WinRm، و Dns
		للعمل على الخوادم LABDC و LABDC2.
#>
	
	[CmdletBinding()]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[ValidateScript({ Test-Connection -ComputerName $_ -Count 1 -Quiet})]
		[string[]]$Computername,
		[Parameter(Mandatory)]
		[ValidateSet('WinRm','Smb','Dns','ActiveDirectoryGeneral','ActiveDirectoryGlobalCatalog','NetBios')]
		[string[]]$ServerRole
	)
	begin {
		$PortGroups = @{
			'WinRm' = @{ 'TCP' = 5985}
			'Smb' = @{ 'TCP' = 445; 'UDP' = 445 }
			'Dns' = @{ 'TCP' = 53; 'UDP' = 53 }
			'ActiveDirectoryGeneral' = @{ 'TCP' = 25, 88, 389, 464, 636, 5722, 9389; 'UDP' = 88,123,389,464 }
			'ActiveDirectoryGlobalCatalog' = @{ 'TCP' = 3268, 3269 }
			'NetBios' = @{ 'TCP' = 135, 137, 138, 139; 'UDP' = 137,138,139 }
		}
	}
	process {
		foreach ($Computer in $Computername) {
			Write-Verbose "Beginning port tests on computer '$Computer'"
			try {
				$TestPortGroups = $PortGroups.GetEnumerator() | where { $ServerRole -contains $_.Key }
				Write-Verbose "Found '$($TestPortGroups.Count)' port group(s) to test"
				foreach ($PortGroup in $TestPortGroups) {
					$PortGroupName = $PortGroup.Key
					$PortGroupValues = $PortGroup.Value
					foreach ($Value in $PortGroupValues.GetEnumerator()) {
						$Protocol = $Value.Key
						$Ports = $Value.Value
						$TestResult = Test-Port -ComputerName $Computer -Protocol $Protocol -Port $Ports
						$TestResult | Add-Member -MemberType 'NoteProperty' -Name 'PortSet' -Value $PortGroupName
						$TestResult
					}
				}
			} catch {
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Computer: $Computer - Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
				$false
			}
		}
	}
}

function Test-Port {
	<#
	.ملخص
		تقوم هذه الوظيفة بفحص منافذ TCP/UDP المفتوحة.
	.الوصف
		تقوم هذه الوظيفة بفحص أي منفذ TCP/UDP للتحقق مما إذا كان مفتوحًا أم مغلقًا.
	.ملاحظات
		مشكلة معروفة: إذا تم استدعاء هذه الوظيفة بين 10-20 مرة متتالية على نفس المنفذ
			والكمبيوتر، فإن فحص المنفذ UDP سيظهر ناتجًا $false عندما يمكن أن يكون
			$true. لم أفهم لماذا يحدث ذلك.
	.معلمة الخادم
		اسم أحد أو أكثر من الكمبيوترات البعيدة، مفصولة بفواصل.
	.معلمة المنفذ
		أحد أو أكثر من أرقام المنافذ المفصولة بفواصل التي ترغب في اختبارها.
	.معلمة البروتوكول
		البروتوكول (UDP أو TCP) الذي ستقوم بفحصه
	.معلمة مهلة TCP
		عدد ميلي ثانية ستنتظرها الوظيفة حتى تعلن
		إغلاق المنفذ TCP.
	.معلمة مهلة UDP
		عدد ميلي ثانية ستنتظرها الوظيفة حتى تعلن
		إغلاق المنفذ UDP.
	.مثال
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		يقوم هذا المثال بفحص منافذ الشبكة TCP 80 و 443 على كل من خوادم
		LABDC و LABDC2.
#>
	[CmdletBinding(DefaultParameterSetName='TCP')]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[string[]]$ComputerName,
		[Parameter(Mandatory)]
		[int[]]$Port,
		[Parameter(Mandatory)]
		[ValidateSet('TCP', 'UDP')]
		[string]$Protocol,
		[Parameter(ParameterSetName='TCP')]
		[int]$TcpTimeout = 1000,
		[Parameter(ParameterSetName = 'UDP')]
		[int]$UdpTimeout = 1000
	)
	process {
		foreach ($Computer in $ComputerName) {
			foreach ($Portx in $Port) {
				$Output = @{ 'Computername' = $Computer; 'Port' = $Portx; 'Protocol' = $Protocol; 'Result' = '' }
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Beginning port test on '$Computer' on port '$Protocol<code>:$Portx'"
				if ($Protocol -eq 'TCP') {
					$TcpClient = New-Object System.Net.Sockets.TcpClient
					$Connect = $TcpClient.BeginConnect($Computer, $Portx, $null, $null)
					$Wait = $Connect.AsyncWaitHandle.WaitOne($TcpTimeout, $false)
					if (!$Wait) {
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol</code>:$Portx'"
						$Output.Result = $false
					} else {
						$TcpClient.EndConnect($Connect)
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol<code>:$Portx'"
						$Output.Result = $true
					}
					$TcpClient.Close()
					$TcpClient.Dispose()
				} elseif ($Protocol -eq 'UDP') {
					$UdpClient = New-Object System.Net.Sockets.UdpClient
					$UdpClient.Client.ReceiveTimeout = $UdpTimeout
					$UdpClient.Connect($Computer, $Portx)
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Sending UDP message to computer '$Computer' on port '$Portx'"
					$a = new-object system.text.asciiencoding
					$byte = $a.GetBytes("$(Get-Date)")
					[void]$UdpClient.Send($byte, $byte.length)
					#كائن IPEndPoint سيسمح لنا بقراءة البيانات المرسلة من أي مصدر.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#يحظر حتى يعود رسالة على هذا الجهاز من مضيف بعيد.
						Write-Verbose "$($MyInvocation.MyCommand.Name) - Waiting for message return"
						$receivebytes = $UdpClient.Receive([ref]$remoteendpoint)
						[string]$returndata = $a.GetString($receivebytes)
						If ($returndata) {
							Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol</code>:$Portx'"
							$Output.Result = $true
						}
					} catch {
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol`:$Portx' with error '$($_.Exception.Message)'"
						$Output.Result = $false
					}
					$UdpClient.Close()
					$UdpClient.Dispose()
				}
				[pscustomobject]$Output
			}
		}
	}
}

Source:
https://adamtheautomator.com/powershell-test-port/