OT: TCPConnect

Started by stanl, March 27, 2021, 06:27:50 AM

Previous topic - Next topic

stanl


[EDIT]

Didn't want to clog up the 443 thread... but I got curious about using the CLR to obtain TCP Connection. Had initial error about non-static method requires target, but can at least get  connection object open

Anywho???


Code (WINBATCH) Select

established = 0
ObjectClrOption('useany', 'System')
ObjectClrOption('useany', 'System.Net.NetworkInformation')
oIP = ObjectClrNew("System.Net.NetworkInformation.IPGlobalProperties")
oProps = oIP.GetIPGlobalProperties()
oConns = oProps.GetActiveTcpConnections()
oTCP = ObjectClrNew("System.Net.NetworkInformation.TcpConnectionInformation",oConns)


Message("",ObjectTypeGet(oTCP))



just have to figure out enumerating oTCP.

stanl

Forget the CLR stuff... for TCPConnections it is more likely a wild goose chase... I just like playing around with it. However, since Netstat gives the same information then how best to parse Netstat;


The script below [using CLR=>PS] does the basic job very efficiently. Headers would need renaming and ports extracted from Local and remote IP's - but fairly trivial. 
Code (WINBATCH) Select


;Winbatch 2020B - Parsing Netstat
;============================================================================================
IntControl(73,1,0,0,0)
gosub udfs
cFile = "C:\temp\netstat.txt"
If FileExist(cFile) Then FileDelete(cFile)


cScript= $"
$file = "|file|"
$a = netstat -ano
$a[3..$a.count] | ConvertFrom-String | select p2,p3,p4,p5,p6 | where p5 -eq 'established' | Export-Csv -delimiter "`t" -Path $file -NoTypeInformation
$"
cScript = StrReplace(cScript,"|file|",cFile)
BoxOpen("Please Wait","Parsing Netstat Output")
oNoGo = ObjectType("BOOL",@FALSE)
ObjectClrOption("useany", "System.Management.Automation")
objAutoPs = ObjectClrNew("System.Management.Automation.PowerShell")
oPshell = objAutoPs.Create()
oScope = ObjectType("BOOL",@TRUE)
oPshell.AddScript(cScript,oScope)
objAsync = oPshell.BeginInvoke()
ctimeout=100
tries=0
While objAsync.IsCompleted == oNoGo
   TimeDelay(10)
   tries=tries+1
   If tries>ctimeout Then Break
EndWhile


oPShell.EndInvoke(objAsync) 
Boxtext("Script Finished... Starting Excel")


If FileExist(cFile)
cText = FileGet(cFile)
cText = StrReplace(cText,'"','')
cText = StrReplace(cText,',',@TAB)
ClipPut(cText)
oXL = CreateObject("Excel.Application")
oXL.Visible          = @TRUE  ; change this to @FALSE to run hidden
oXL.ScreenUpdating   = @TRUE  ; if running hidden, change this to @FALSE
oXL.UserControl      = @TRUE
oXL.DisplayAlerts    = @FALSE
oXL.WorkBooks.Add()
BoxShut()
oWS = oXL.ActiveWorkBook.Worksheets(1)
oWS.Activate()
oWS.Name = "Netstat"
oWS.Cells(1,1).Select()
oWS.Paste()
oWS.UsedRange.Select()
oXL.Selection.Font.Name = 'Tahoma'
oXL.Selection.Font.Size = 9
oXL.Selection.Font.Bold = @True
oWS.UsedRange.Columns.Autofit()
oWS.ListObjects.Add(:1,oWS.UsedRange, , 1).Name ="Table1"
oWS.Range("Table1[#All]").Select()
oWS.ListObjects("Table1").TableStyle = "TableStyleLight15"
oXL.ActiveWindow.DisplayGridlines = @False
oWS.Cells(1,1).Select()
oWS=0
oXL=0
Pause("Data Loaded Into Excel","Save or Close Workbook")
Else
Pause("Script Has Failed","Cannot Create Excel Output")
Endif
Exit


;======================================================================================================
:WBERRORHANDLER
Terminate(@TRUE,"Error Encountered",err())
;======================================================================================================


:udfs
#DefineSubRoutine err()
If key<>0 Then RegCloseKey(key)
wberroradditionalinfo = wberrorarray[6]
lasterr = wberrorarray[0]
handlerline = wberrorarray[1]
textstring = wberrorarray[5]
linenumber = wberrorarray[8]
errmsg = "Error: ":lasterr:@LF:textstring:@LF:"Line (":linenumber:")":@LF:wberroradditionalinfo
Return(errmsg)
#EndSubRoutine
Return
;===========================================================================================



ChuckC

All other things being equal, if I'm given a choice between learning to use the methods on a class or some other low-level API function(s) vs. parsing the output of an external console program, I will always favor the former over the latter.

https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.tcpconnectioninformation?view=net-5.0

The documentation for the class TcpConnectionInformation states that it is an abstract class.  As such, you cannot instantiate it because there are no constructor methods present.  It's intended purpose is for some other class to inherit from it and to provide one or more c'tor methods for creating class instances.  The abstract base class also permits derived class instances to be referenced via their abstract base type for purposes of exposing methods that are intended to be publicly accessible.

The link referenced above contains a c# example for obtaining and iterating thru an array of TcpConnectionInformation references.  That example is very straight forward.  The only hurdle, if you will, is that it starts with calling a static class method, IPGlobalProperties.GetIPGlobalProperties().  Currently, WinBatch cannot call static class methods on a class type, with a class instance reference being required to invoke a static method.  Given that IPGlobalProperties is also an abstract class with a protected constructor, this precludes directly accessing it with the built-in CLR support in WIL.  What is required is to compose a minimal class in c# that will do that task for you and return the result of IPGlobalProperties.GetIPGlobalProperties(), and then dynamically compile that c# code to an in-memory assembly.  After that is done, the rest of the example code is easily ported to WIL.


stanl

Refrain: parsing Netstat I was just playing around. As for your other comments about abstract classes.... like I said 'wild goose chase'..... Not that many posts about the CLR, but if PS can handle a bit of it... why not?


[EDIT]
P.S. I thought this board was about posting code/with questions and having it examined. Not a philosophy seminar or "can you show me a script to determine if my wife is cheating on me"

ChuckC

In all fairness, the topic starts with 'OT:", so I'd argue that grants a lot more latitude in the responses that you'll get.

The philosophical approach I brought up applies to the choices made when writing any code, period.  I don't like to write code that can easily break, especially if the breakage is in a subtle manner not readily detected by an outright exception that leads to program failure and process termination.  In that light, piping the output of a command line program to a file subjects your code to having little awareness of whether the external program failed, and changes to the format of the output of the program could easily occur w/o your advance knowledge.

I suggested simply doing in-memory compilation of c# code to keep as much of the functionality of the data acquisition and the data manipulation native to WinBatch.  Your approach with utilizing the CLR to invoke PowerShell could have easily deprecated all use of NETSTAT and parsing of a text file by simply directly invoking the .NET classes that I commented on that were in your 1st posting.  PowerShell does not have that static class method invocation problem that WIL's CLR support has, so your PowerShell code could have directly obtained the array of System.Net.NetworkInformation.TcpConnectionInformation references from a call to [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties().  That would have served as an equally good "shim" as compared to the in-memory compilation of c# code and woud still have provided for the possibility of iron-clad exception/error handling every step of the way thru the code's execution.  Those are qualities in code that I find desirable.

Perhaps that discussion serves as a better examination/analysis of 2nd script you posted.


stanl

No worries and I didn't mean to appear defensive but in retrospect was. I admit I was intrigued by the PS pipeline that parsed Netstat and learned more about CLR limits.  As a non-programmer I welcome being schooled on proper coding even if I may not initially appear so. :-[

td

A very quick hack of your original script illustrates how to access the FCL's Connections array using WIL CLR hosting.

Code (winbatch) Select
ObjectClrOption('useany', 'System')
ObjectClrOption('useany', 'System.Net.NetworkInformation')
oIP = ObjectClrNew("System.Net.NetworkInformation.IPGlobalProperties")
oProps = oIP.GetIPGlobalProperties()
aConns = oProps.GetActiveTcpConnections()


Foreach objCon in aConns
   objCon = ObjectClrType('System.Net.NetworkInformation.TcpConnectionInformation',objCon)
   strLocal = objCon.LocalEndPoint.ToString()
   strRmote = objCon.RemoteEndPoint.ToString()
   exit
exit


Note that the FCL/.Net 5/.Net Core(on Windows) and therefore PS (on Windows) rely on the Iphlpapi.dll DLL used in the other tread on this subject.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on March 29, 2021, 08:37:47 AM
A very quick hack of your original script illustrates how to access the FCL's Connections array using WIL CLR hosting.
Note that the FCL/.Net 5/.Net Core(on Windows) and therefore PS (on Windows) rely on the Iphlpapi.dll DLL used in the other tread on this subject.


Yeah. I can see 2 exits [very existential].  So, I initially declared the array wrong. Lesson learned. Of course you should be able to collect .State and determine 'established', but your dll script does so much more so that is a moot point.


Thread should be closed ::)


td

Quote from: stanl on March 29, 2021, 09:15:07 AM


Yeah. I can see 2 exits [very existential].

Just a lazy "programmer trick".  The script didn't need to execute any further to illustrate the point.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

stanl

Quote from: td on March 29, 2021, 09:29:36 AM
Just a lazy "programmer trick".  The script didn't need to execute any further to illustrate the point.


Point taken. added a map for TCP states.
Code (WINBATCH) Select


;Winbatch CLR to obtain TCP Connections
;========================================================================
IntControl(73,1,0,0,0)
gosub udfs
states = $"1,Closed
8,Waiting
9,Closing
12,Deleting
5,Established
6,Waiting
7,Waiting
10,Waiting
2,Listening
4,Waiting
3,Waiting
11,Waiting
0,Unknown$"


TCPStates= MapCreate(states,'',@lf)


ObjectClrOption('useany', 'System')
ObjectClrOption('useany', 'System.Net.NetworkInformation')
oIP = ObjectClrNew("System.Net.NetworkInformation.IPGlobalProperties")
oProps = oIP.GetIPGlobalProperties()
aTCP = oProps.GetActiveTcpConnections()


results="Local":@TAB:"Remote":@TAB:"State":@CRLF
Foreach c in aTCP
   c = ObjectClrType('System.Net.NetworkInformation.TcpConnectionInformation',c)
   l = c.LocalEndPoint.ToString()
   r = c.RemoteEndPoint.ToString()
   s = c.State
   If MapKeyExist(TCPStates,s) Then s=TCPStates[s]
   results = results:l:@TAB:r:@TAB:s:@CRLF
Next


Message("TCPConnectons",results)


:end
oTCP=0
oConns=0
oIP=0
oProps=0
Exit
;======================================================================================================
:WBERRORHANDLER
Terminate(@TRUE,"Error Encountered",err())
;======================================================================================================


:udfs
#DefineSubRoutine err()
wberroradditionalinfo = wberrorarray[6]
lasterr = wberrorarray[0]
handlerline = wberrorarray[1]
textstring = wberrorarray[5]
linenumber = wberrorarray[8]
errmsg = "Error: ":lasterr:@LF:textstring:@LF:"Line (":linenumber:")":@LF:wberroradditionalinfo
Return(errmsg)
#EndSubRoutine
Return
;===========================================================================================