thready
asked on
ports
Hi Experts,
What's the quickest Windows API call to figure out which process (and/or which logged in user) in Windows, has a given open port?
Thanks,
Mike
What's the quickest Windows API call to figure out which process (and/or which logged in user) in Windows, has a given open port?
Thanks,
Mike
ASKER
Anyone?
You are going to hate me for that, but it's going to be much complicated than just a single API call. Actually, it's SNMP that does the job here. Mark Russinovich (now Sysinternals, acquired by MS a couple of years ago) had written a 'netstatp' application that was published along with the full source code back then. While the code was taken down after MS had taken over, I still have it archived. Here we go:
That way, you have a list of the open ports. Now for the hard part, that is getting the associated PID, for which you will need to use Windows' Native API (http://netcode.cz/img/83/nativeapi.html) , in particular get a copy of the global handle table by calling 'ZwQuerySystemInformation( SystemHand leInformat ion,...);' and checking the referenced handles /PIDs. The following code does this, the the structure definitions were taken from Gary Nebbett' book "Native API Reference" (which also contains samples from which this snippet is derived). All you need to do is compiling this and link with ntdll.lib, which comes with the DDK. Alternatively, you could load the Zw* functions dynamically.
(see also https://www.experts-exchange.com/questions/28326769/How-do-I-know-who-is-locking-the-file-in-my-C-codes.html et. al.)
Once you have associated the ports and PIDs, getting the user name is a piece of cake ;o)
Or, in code:
//------------------------------------------------------------
//
// Netstatp
//
// Copyright (C) 1998 Mark Russinovich
// Systems Internals
// http://www.sysinternals.com
//
// This program implements a subset of the Netstat program's
// functionality. Specifically, it enumerates and displays
// information about all UDP and TCP endpoints.
//
//------------------------------------------------------------
#include "windows.h"
#include "stdio.h"
#include "snmp.h"
#include "winsock.h"
#define HOSTNAMELEN 256
#define PORTNAMELEN 256
#define ADDRESSLEN HOSTNAMELEN+PORTNAMELEN
typedef struct _tcpinfo {
struct _tcpinfo *prev;
struct _tcpinfo *next;
UINT state;
UINT localip;
UINT localport;
UINT remoteip;
UINT remoteport;
} TCPINFO, *PTCPINFO;
BOOL (__stdcall *SnmpExtensionInit)(
IN DWORD dwTimeZeroReference,
OUT HANDLE *hPollForTrapEvent,
OUT AsnObjectIdentifier *supportedView);
BOOL (__stdcall *SnmpExtensionQuery)(
IN BYTE requestType,
IN OUT RFC1157VarBindList *variableBindings,
OUT AsnInteger *errorStatus,
OUT AsnInteger *errorIndex);
//
// Possible TCP endpoint states
//
static char TcpState[][32] = {
"???",
"CLOSED",
"LISTENING",
"SYN_SENT",
"SEN_RECEIVED",
"ESTABLISHED",
"FIN_WAIT",
"FIN_WAIT2",
"CLOSE_WAIT",
"CLOSING",
"LAST_ACK",
"TIME_WAIT"
};
//
// Lists of endpoints
//
TCPINFO TcpInfoTable;
TCPINFO UdpInfoTable;
//------------------------------------------------------------
//
// GetPortName
//
// Translate port numbers into their text equivalent if
// there is one
//
//------------------------------------------------------------
char *GetPortName( UINT port, char *proto, char *name, int namelen )
{
struct servent *psrvent;
if( psrvent = getservbyport( htons( (USHORT) port ), proto )) {
strcpy( name, psrvent->s_name );
} else {
sprintf(name, "%d", port);
}
return name;
}
//------------------------------------------------------------
//
// GetIpHostName
//
// Translate IP addresses into their name-resolved form
// if possible.
//
//------------------------------------------------------------
char *GetIpHostName( BOOL local, UINT ipaddr, char *name, int namelen )
{
struct hostent *phostent;
UINT nipaddr;
nipaddr = htonl( ipaddr );
if( !ipaddr ) {
if( !local ) {
sprintf( name, "%d.%d.%d.%d",
(nipaddr >> 24) & 0xFF,
(nipaddr >> 16) & 0xFF,
(nipaddr >> 8) & 0xFF,
(nipaddr) & 0xFF);
} else {
gethostname(name, namelen);
}
} else if( ipaddr == 0x0100007f ) {
if( local ) {
gethostname(name, namelen);
} else {
strcpy( name, "localhost" );
}
} else if( phostent = gethostbyaddr( (char *) &ipaddr,
sizeof( nipaddr ), PF_INET )) {
strcpy( name, phostent->h_name );
} else {
sprintf( name, "%d.%d.%d.%d",
(nipaddr >> 24) & 0xFF,
(nipaddr >> 16) & 0xFF,
(nipaddr >> 8) & 0xFF,
(nipaddr) & 0xFF);
}
return name;
}
//------------------------------------------------------------
//
// LoadInetMibEntryPoints
//
// Load the TCP/IP SNMP extension DLL and locate the entry
// points we will use.
//
//------------------------------------------------------------
BOOLEAN LoadInetMibEntryPoints()
{
HINSTANCE hInetLib;
if( !(hInetLib = LoadLibrary( "inetmib1.dll" ))) {
return FALSE;
}
if( !(SnmpExtensionInit = (void *) GetProcAddress( hInetLib,
"SnmpExtensionInit" )) ) {
return FALSE;
}
if( !(SnmpExtensionQuery = (void *) GetProcAddress( hInetLib,
"SnmpExtensionQuery" )) ) {
return FALSE;
}
return TRUE;
}
//------------------------------------------------------------
//
// Main
//
// Do it all. Load and initialize the SNMP extension DLL and
// then build a table of TCP endpoints and UDP endpoints. After
// each table is built resolve addresses to names and print
// out the information
//
//------------------------------------------------------------
int main( int argc, char *argv[] )
{
HANDLE hTrapEvent;
AsnObjectIdentifier hIdentifier;
RFC1157VarBindList bindList;
RFC1157VarBind bindEntry;
UINT tcpidentifiers[] = { 1,3,6,1,2,1,6,13,1,1};
UINT udpidentifiers[] = { 1,3,6,1,2,1,7,5,1,1};
AsnInteger errorStatus, errorIndex;
TCPINFO *currentEntry, *newEntry;
UINT currentIndex;
WORD wVersionRequested;
WSADATA wsaData;
char localname[HOSTNAMELEN], remotename[HOSTNAMELEN];
char remoteport[PORTNAMELEN], localport[PORTNAMELEN];
char localaddr[ADDRESSLEN], remoteaddr[ADDRESSLEN];
//
// Initialize winsock
//
wVersionRequested = MAKEWORD( 1, 1 );
if( WSAStartup( wVersionRequested, &wsaData ) ) {
printf("Could not initialize Winsock.\n");
return 1;
}
//
// Locate and initialize INETMIB1
//
if( !LoadInetMibEntryPoints()) {
printf("Could not load extension DLL.\n");
return 1;
}
if( !SnmpExtensionInit( GetCurrentTime(), &hTrapEvent, &hIdentifier )) {
printf("Could not initialize extension DLL.\n");
return 1;
}
//
// Initialize the query structure once
//
bindEntry.name.idLength = 0xA;
bindEntry.name.ids = tcpidentifiers;
bindList.list = &bindEntry;
bindList.len = 1;
TcpInfoTable.prev = &TcpInfoTable;
TcpInfoTable.next = &TcpInfoTable;
//
// Roll through TCP connections
//
currentIndex = 1;
currentEntry = &TcpInfoTable;
while(1) {
if( !SnmpExtensionQuery( ASN_RFC1157_GETNEXTREQUEST,
&bindList, &errorStatus, &errorIndex )) {
return 1;
}
//
// Terminate when we're no longer seeing TCP information
//
if( bindEntry.name.idLength < 0xA ) break;
//
// Go back to start of table if we're reading info
// about the next byte
//
if( currentIndex != bindEntry.name.ids[9] ) {
currentEntry = TcpInfoTable.next;
currentIndex = bindEntry.name.ids[9];
}
//
// Build our TCP information table
//
switch( bindEntry.name.ids[9] ) {
case 1:
//
// Always allocate a new structure
//
newEntry = (TCPINFO *) malloc( sizeof(TCPINFO ));
newEntry->prev = currentEntry;
newEntry->next = &TcpInfoTable;
currentEntry->next = newEntry;
currentEntry = newEntry;
currentEntry->state = bindEntry.value.asnValue.number;
break;
case 2:
currentEntry->localip =
*(UINT *) bindEntry.value.asnValue.address.stream;
currentEntry = currentEntry->next;
break;
case 3:
currentEntry->localport =
bindEntry.value.asnValue.number;
currentEntry = currentEntry->next;
break;
case 4:
currentEntry->remoteip =
*(UINT *) bindEntry.value.asnValue.address.stream;
currentEntry = currentEntry->next;
break;
case 5:
currentEntry->remoteport =
bindEntry.value.asnValue.number;
currentEntry = currentEntry->next;
break;
}
}
//
// Now print the connection information
//
printf("%7s %-30s %-30s %s\n", "Proto", "Local", "Remote", "State" );
currentEntry = TcpInfoTable.next;
while( currentEntry != &TcpInfoTable ) {
sprintf( localaddr, "%s:%s",
GetIpHostName( TRUE, currentEntry->localip, localname, HOSTNAMELEN),
GetPortName( currentEntry->localport, "tcp", localport, PORTNAMELEN ));
sprintf( remoteaddr, "%s:%s",
GetIpHostName( FALSE, currentEntry->remoteip, remotename, HOSTNAMELEN),
currentEntry->remoteip ?
GetPortName( currentEntry->remoteport, "tcp", remoteport, PORTNAMELEN ):
"0" );
printf("%7s %-30s %-30s %s\n", "TCP",
localaddr, remoteaddr,
TcpState[currentEntry->state]);
currentEntry = currentEntry->next;
}
printf("\n");
//
// Initialize the query structure once
//
bindEntry.name.idLength = 0xA;
bindEntry.name.ids = udpidentifiers;
bindList.list = &bindEntry;
bindList.len = 1;
UdpInfoTable.prev = &UdpInfoTable;
UdpInfoTable.next = &UdpInfoTable;
//
// Roll through UDP endpoints
//
currentIndex = 1;
currentEntry = &UdpInfoTable;
while(1) {
if( !SnmpExtensionQuery( ASN_RFC1157_GETNEXTREQUEST,
&bindList, &errorStatus, &errorIndex )) {
return 1;
}
//
// Terminate when we're no longer seeing TCP information
//
if( bindEntry.name.idLength < 0xA ) break;
//
// Go back to start of table if we're reading info
// about the next byte
//
if( currentIndex != bindEntry.name.ids[9] ) {
currentEntry = UdpInfoTable.next;
currentIndex = bindEntry.name.ids[9];
}
//
// Build our TCP information table
//
switch( bindEntry.name.ids[9] ) {
case 1:
//
// Always allocate a new structure
//
newEntry = (TCPINFO *) malloc( sizeof(TCPINFO ));
newEntry->prev = currentEntry;
newEntry->next = &UdpInfoTable;
currentEntry->next = newEntry;
currentEntry = newEntry;
currentEntry->localip =
*(UINT *) bindEntry.value.asnValue.address.stream;
break;
case 2:
currentEntry->localport =
bindEntry.value.asnValue.number;
currentEntry = currentEntry->next;
break;
}
}
//
// Now print the connection information
//
currentEntry = UdpInfoTable.next;
while( currentEntry != &UdpInfoTable ) {
printf("%7s %s:%s\n", "UDP",
GetIpHostName( TRUE, currentEntry->localip, localname, HOSTNAMELEN),
GetPortName( currentEntry->localport, "udp", localport, PORTNAMELEN ) );
currentEntry = currentEntry->next;
}
printf("\n");
}
That way, you have a list of the open ports. Now for the hard part, that is getting the associated PID, for which you will need to use Windows' Native API (http://netcode.cz/img/83/nativeapi.html) , in particular get a copy of the global handle table by calling 'ZwQuerySystemInformation(
#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <stdio.h>
#pragma warning ( disable: 4768)
#include <map>
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemHandleInformation = 16,
} SYSTEM_INFORMATION_CLASS;
typedef struct _SYSTEM_HANDLE_INFORMATION
{
ULONG ProcessId;
UCHAR ObjectTypeNumber;
UCHAR Flags;
USHORT Handle;
PVOID Object;
ACCESS_MASK GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
typedef enum _OBJECT_INFORMATION_CLASS
{
ObjectBasicInformation,
ObjectNameInformation,
ObjectTypeInformation,
ObjectAllTypesInformation,
ObjectHandleInformation
} OBJECT_INFORMATION_CLASS;
typedef struct _OBJECT_BASIC_INFORMATION
{
ULONG Attributes;
ACCESS_MASK GrantedAccess;
ULONG HandleCount;
ULONG PointerCount;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
ULONG Reserved [ 3];
ULONG NameInformationLength;
ULONG TypeInformationLength;
ULONG SecurityDescriptorLength;
LARGE_INTEGER CreateTime;
} OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION;
typedef struct _OBJECT_TYPE_INFORMATION
{
UNICODE_STRING Name;
ULONG ObjectCount;
ULONG HandleCount;
ULONG Reserved1 [ 4];
ULONG PeakObjectCount;
ULONG PeakHandleCount;
ULONG Reserved2 [ 4];
ULONG InvalidAttributes;
GENERIC_MAPPING GenericMapping;
ULONG ValidAccess;
UCHAR Unknown;
BOOLEAN MaintainHandleDatabase;
NT::POOL_TYPE PoolType;
ULONG PagedPoolUsage;
ULONG NonPagedPoolUsage;
} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
typedef struct _OBJECT_NAME_INFORMATION
{
UNICODE_STRING Name;
} OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION;
typedef std::map<ULONG, ULONG> pid_map;
extern "C"
NTSTATUS
__stdcall
ZwQuerySystemInformation ( SYSTEM_INFORMATION_CLASS,
PVOID,
ULONG,
PULONG
);
extern "C"
NTSTATUS
__stdcall
ZwQueryObject ( HANDLE,
OBJECT_INFORMATION_CLASS,
PVOID,
ULONG,
PULONG
);
extern "C"
NTSTATUS
__stdcall
ZwDuplicateObject ( HANDLE,
HANDLE,
HANDLE,
PHANDLE,
ACCESS_MASK,
ULONG,
ULONG
);
POBJECT_NAME_INFORMATION
GetObjectNameInformation ( HANDLE __hObject, wchar_t* __pwszTypeFilter)
{
NTSTATUS _ntStatus;
OBJECT_BASIC_INFORMATION _obi;
POBJECT_TYPE_INFORMATION _poti;
POBJECT_NAME_INFORMATION _poni;
ULONG _ul;
ZwQueryObject ( __hObject,
ObjectBasicInformation,
&_obi,
sizeof ( _obi),
&_ul
);
_ul = _obi.TypeInformationLength + 2;
_poti = ( POBJECT_TYPE_INFORMATION) new char [ _ul];
_ntStatus = ZwQueryObject ( __hObject,
ObjectTypeInformation,
_poti,
_ul,
&_ul
);
if ( __pwszTypeFilter && !wcscmp ( _poti->Name.Buffer, __pwszTypeFilter))
{
delete [] _poti;
return NULL;
}
_ul = !_obi.NameInformationLength
? MAX_PATH * sizeof ( WCHAR)
: _obi.NameInformationLength;
_poni = ( POBJECT_NAME_INFORMATION) new char [ _ul];
_ntStatus = ZwQueryObject ( __hObject,
ObjectNameInformation,
_poni,
_ul,
&_ul
);
/*
wprintf ( L"Obj 0x%8.8x %.*s %.*s\n",
__hObject,
_poni->Name.Length / 2,
_poni->Name.Buffer,
_poti->Name.Length / 2,
_poti->Name.Buffer
);
*/
delete [] _poti;
return ( _poni);
}
void PrintObjectUsers ( wchar_t* __pwszObj, wchar_t* __pwszTypeFilter)
{
pid_map _pid_map;
ULONG _ulPID;
NTSTATUS _ntStatus;
HANDLE _hObject;
HANDLE _hProcess;
PSYSTEM_HANDLE_INFORMATION _pshi;
POBJECT_NAME_INFORMATION _poni;
ULONG _ul = 0x1000;
PULONG _pul = new ULONG [ _ul];
// hProcess = OpenProcess ( PROCESS_DUP_HANDLE, FALSE, dwPID);
while ( STATUS_INFO_LENGTH_MISMATCH == ZwQuerySystemInformation ( SystemHandleInformation,
_pul,
_ul * sizeof ( ULONG),
0
)
) delete [] _pul, _pul = new ULONG [ _ul *= 2];
_pshi = ( PSYSTEM_HANDLE_INFORMATION) ( _pul + 1);
for ( ULONG _i = 0; _i < *_pul; _i++)
{
_ulPID = _pshi [ _i].ProcessId;
_hObject = NULL;
_hProcess = OpenProcess ( PROCESS_DUP_HANDLE,
FALSE,
_ulPID
);
if ( !_hProcess)
{
//wprintf ( L"FAILED to 'OpenProcess()' for PID %d\n", _ulPID);
continue;
}
ZwDuplicateObject ( _hProcess,
( HANDLE) _pshi [ _i].Handle,
NtCurrentProcess (),
&_hObject,
0,
0,
DUPLICATE_SAME_ATTRIBUTES
);
if ( !_hObject)
{
wprintf ( L"FAILED to 'ZwDuplicateObject()' for PID %d\n", _ulPID);
continue;
}
if ( _hObject)
{
_poni = GetObjectNameInformation ( _hObject, __pwszTypeFilter);
if ( !_poni) continue;
if ( !_poni->Name.Length) continue;
if ( wcsstr ( _poni->Name.Buffer, __pwszObj))
{
pid_map::iterator _i_map = _pid_map.find ( _ulPID);
// if ( _i_map == _pid_map.end ())
{
wprintf ( L"PID: %d\tNAME: %s\n",
_ulPID,
_poni->Name.Buffer
);
// _pid_map.insert ( pid_map::value_type ( _ulPID, _ulPID));
}
}
delete [] _poni;
}
CloseHandle ( _hObject);
CloseHandle ( _hProcess);
}
delete [] _pul;
}
int wmain ( int __wargc,
wchar_t** __wargv
)
{
wchar_t* __pwc;
if ( 2 != __wargc)
return ( -1);
PrintObjectUsers ( *( __wargv + 1), NULL);
return ( 0);
}
(see also https://www.experts-exchange.com/questions/28326769/How-do-I-know-who-is-locking-the-file-in-my-C-codes.html et. al.)
Once you have associated the ports and PIDs, getting the user name is a piece of cake ;o)
Or, in code:
#include <windows.h>
#include <lmcons.h>
#include <stdio.h>
#pragma comment ( lib, "advapi32.lib")
#pragma comment ( lib, "user32.lib")
int GetProcessOwner ( HANDLE hProcess, char* acBuffer, ULONG ulSize)
{
PSECURITY_DESCRIPTOR psd;
PSID psid;
DWORD dwSDLength;
DWORD dwNeeded;
DWORD dwErr;
DWORD dwNameSize;
char acReferencedDomain [ LM20_DNLEN + 1];
DWORD dwDomainBufSize = sizeof ( acReferencedDomain);
SID_NAME_USE eUse;
BOOL bDefaulted;
SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION;
dwNameSize = ulSize;
if ( !GetKernelObjectSecurity ( hProcess,
si,
NULL,
0,
&dwNeeded
)
)
{
dwErr = GetLastError ();
if ( ERROR_INSUFFICIENT_BUFFER != dwErr)
{
return ( dwErr);
}
dwSDLength = dwNeeded;
psd = ( PSECURITY_DESCRIPTOR) LocalAlloc ( LPTR,
dwSDLength
);
__try
{
if ( !GetKernelObjectSecurity ( hProcess,
si,
psd,
dwSDLength,
&dwNeeded
)
)
{
dwErr = GetLastError ();
__leave;
}
if ( !GetSecurityDescriptorOwner ( psd,
&psid,
&bDefaulted
)
)
{
dwErr = GetLastError ();
__leave;
}
// lookup clear text name of the owner
if ( !LookupAccountSid ( NULL,
psid,
acBuffer,
&dwNameSize,
acReferencedDomain,
&dwDomainBufSize,
&eUse
)
)
{
dwErr = GetLastError ();
__leave;
}
dwErr = 0;
}
__finally
{
LocalFree ( psd);
}
}
return ( dwErr);
}
BOOL EnableDebugPriv ( BOOL bEnable)
{
HANDLE hToken;
TOKEN_PRIVILEGES tp;
if ( !OpenProcessToken ( GetCurrentProcess (),
TOKEN_ADJUST_PRIVILEGES,
&hToken
)
) return ( FALSE);
tp.PrivilegeCount = 1;
LookupPrivilegeValue ( NULL,
SE_DEBUG_NAME,
&tp.Privileges [ 0].Luid
);
tp.Privileges [ 0].Attributes = bEnable
? SE_PRIVILEGE_ENABLED
: 0;
AdjustTokenPrivileges ( hToken,
FALSE,
&tp,
sizeof ( tp),
NULL,
NULL
);
return ( GetLastError() == ERROR_SUCCESS);
}
void main ( int argc, char** argv)
{
HANDLE hProcess;
char acBuf [ 256];
DWORD dwErr;
if ( 2 != argc)
return;
DWORD dwPID = atoi(*( argv + 1));
printf ( "\nPID:\t%d\n", dwPID);
EnableDebugPriv ( TRUE);
hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, dwPID);
dwErr = GetProcessOwner ( hProcess, acBuf, 256);
if ( !dwErr)
printf ( "\nowner:\t%s\n", acBuf);
else
printf ( "\nerror:\t%d\n", dwErr);
CloseHandle ( hProcess);
}
ASKER
Holy crap! That's a lot of code! Thanks jkr!
Although, I just found something that looks smaller - a call to GetExtendedTcpTable, followed by a call to GetProcessOwner (int processId), found here:
http://stackoverflow.com/questions/777548/how-do-i-determine-the-owner-of-a-process-in-c
what do you think?
Although, I just found something that looks smaller - a call to GetExtendedTcpTable, followed by a call to GetProcessOwner (int processId), found here:
http://stackoverflow.com/questions/777548/how-do-i-determine-the-owner-of-a-process-in-c
what do you think?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
GetExtendedTcpTable is awesome!
ASKER