Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Windows : comment obtenir les ports écoutés par un programme ?

+5 votes

En fait je cherche à récupérer via un script le(s) port(s) écoutés par un programme, en connaissant le nom de son processus.
Je sais que netstat peut le faire, cependant le programme en question n'apparaît pas dans le retour de la commande, alors qu'il est bien dans le Moniteur de Ressources de Windows.

Jusqu'ici j'ai un bout de code pour récupérer le PID à partir du nom de process, puisqu'il me semblait avoir vu qu'on avait besoin du PID pour récupérer les ports.

import subprocess, sys, os
encoding = sys.stdout.encoding
a = subprocess.check_output("tasklist /fo csv").decode(encoding).split("\r\n")
a = [x.replace('"','').split(",")[0:2] for x in a]
print([x for x in a if x[0]=="firefox.exe"])

Il me manque donc le principal, comment chopper les ports à partir de ce PID ?

demandé 21-Jun-2015 par anonyme

Tu peux le faire sous unix avec ce bout de code:

pid = ton_id_de_process
p1 = subprocess.Popen(['lsof', '-a', '-p' + pid, '-i4'], stdout=subprocess.PIPE)
p2 = subprocess.Popen(["grep", "LISTEN"], stdin=p1.stdout, stdout=subprocess.PIPE)
output = p2.communicate()[0]

tu dois pouvoir trouver l'équivalent de lsof sous windows. étant sous osx je peux pas tester de version valable sous windows mais il y a sûrement une version universelle, à creuser ^^

A priori c'est netstat qui pourrait faire l'affaire, sauf que le programme en question n'apparaît pas, ou pas correctement. Le port qui figure dans le moniteur de ressource n'est pas donné par netstat.

Effectivement la solution de Max est la bonne.
Netstat -ano m'a comblé !

Si la solution te conviens, n'oublie pas d'accepter la réponse en cliquand sur le stick vert.

2 Réponses

+3 votes

La solution étant déjà trouvée, je pose tout de même une réponse n'utilisant pas de Popen ou autres (j'étais curieux de comment faire ça, n'étant pas un Windows user de base, j'ai fouillé un peu sur le net et je suis arrivé une solution), en espérant que ça serve un jour à quelqu'un.

import struct
from ctypes import *
from ctypes.wintypes import *
from socket import inet_aton, inet_ntoa, htons
from socket import AF_INET
from win32com.client import GetObject

TCP_TABLE_BASIC_LISTENER = 0
TCP_TABLE_BASIC_CONNECTIONS = 1
TCP_TABLE_BASIC_ALL = 2
TCP_TABLE_OWNER_PID_LISTENER = 3
TCP_TABLE_OWNER_PID_CONNECTIONS = 4
TCP_TABLE_OWNER_PID_ALL = 5
TCP_TABLE_OWNER_MODULE_LISTENER = 6
TCP_TABLE_OWNER_MODULE_CONNECTIONS = 7
TCP_TABLE_OWNER_MODULE_ALL = 8

NO_ERROR = 0
ERROR_INVALID_PARAMETER = 87
ERROR_INSUFFICIENT_BUFFER = 122

STATES = {
    1: "CLOSED",
    2: "LISTENING",
    3: "SYN_SENT",
    4: "SYN_RCVD",
    5: "ESTABLISHED",
    6: "FIN_WAIT",
    7: "FIN_WAIT_2",
    8: "CLOSE_WAIT",
    9: "CLOSING",
    10: "LAST_ACK",
    11: "TIME_WAIT",
    12: "DELETE_TCB",
}

class socket_info(object):
    State = None
    LocalAddr = None
    LocalPort = None
    RemoteAddr = None
    RemotePort = None

    def __init__ (self, **kwargs):
        for key, word in kwargs.items():
            setattr(self, key, word)


class MIB_TCPROW_OWNER_PID(Structure):
    _fields_ = [
        ("dwState", DWORD),
        ("dwLocalAddr", DWORD),
        ("dwLocalPort", DWORD),
        ("dwRemoteAddr", DWORD),
        ("dwRemotePort", DWORD),
        ("dwOwningPid", DWORD)
    ]


class MIB_TCPTABLE_OWNER_PID(Structure):
    _fields_ = [
        ("dwNumEntries", DWORD),
        ("MIB_TCPROW_OWNER_PID", MIB_TCPROW_OWNER_PID * 0)
    ]


def formatip(row):
    return (inet_ntoa(struct.pack("L", row.dwLocalAddr)),
            inet_ntoa(struct.pack("L", row.dwRemoteAddr)))

_GetExtendedTcpTable = windll.iphlpapi.GetExtendedTcpTable

def GetExtendedTcpTable(vip=AF_INET,
                        table_class=TCP_TABLE_OWNER_PID_ALL):
    if vip == AF_INET:
        table_type = MIB_TCPTABLE_OWNER_PID
        row_type = MIB_TCPROW_OWNER_PID
        format_func = formatip
    else:
        raise ValueError('ip version must be AF_INET')
    table = table_type()
    size = DWORD()
    order = True

    failure = _GetExtendedTcpTable(None,
                                   byref(size),
                                   order,
                                   vip,
                                   table_class,
                                   0)

    if failure == ERROR_INSUFFICIENT_BUFFER:
        resize(table, size.value)
        memset(byref(table), 0, sizeof(table))
        failure = _GetExtendedTcpTable(byref(table),
                                       byref(size),
                                       order,
                                       vip,
                                       table_class,
                                       0)

    if failure != NO_ERROR:
        raise WinError(failure)

    ptr_type = POINTER(row_type * table.dwNumEntries)
    tables = cast(getattr(table, row_type.__name__), ptr_type)[0]

    pytables = []
    for row in tables:
        LocalAddr, RemoteAddr = format_func(row)
        pytables.append(
            socket_info(
                State=STATES.get(row.dwState, "UNKNOWN_STATE_%s" % (str(row.dwState))),
                LocalAddr=LocalAddr,
                LocalPort=htons(row.dwLocalPort),
                RemoteAddr=RemoteAddr,
                RemotePort=htons(row.dwRemotePort),
                OwningPid=int(row.dwOwningPid)
            )
        )
    return pytables

def GetTcpTableForPid(pid,
                      vip=AF_INET,
                      table_class=TCP_TABLE_OWNER_PID_ALL):
    pytables = [p for p in GetExtendedTcpTable(vip, table_class) if p.OwningPid == pid]
    return pytables

def GetPid(name):
    WMI = GetObject('winmgmts:')
    processes = WMI.InstancesOf('Win32_Process')
    pid = [p.Properties_("ProcessID").Value for p in processes if p.Properties_("Name").Value.lower() == name.lower()]
    if not pid:
        raise ValueError("Unable to find pid for %s" % (name,))
    return pid[0]

if __name__ == '__main__':
    argv = sys.argv[1:]
    if not argv:
        tcp_info = GetExtendedTcpTable(table_class=TCP_TABLE_OWNER_PID_LISTENER)
    else:
        tcp_info = GetTcpTableForPid(GetPid(argv[0]), table_class=TCP_TABLE_OWNER_PID_LISTENER)
    print('%-25s%-25s%-30s%s\n' % ('Local', 'Foreign', 'State', 'PID'))
    for item in tcp_info:
        LocalAddr = '{}:{}'.format(item.LocalAddr, item.LocalPort)
        RemoteAddr = '{}:{}'.format(item.RemoteAddr, item.RemotePort)
        print('%-25s%-25s%-30s%s' % (LocalAddr, RemoteAddr,
                                     item.State, item.OwningPid))

refs:
http://stackoverflow.com/questions/18062175/error-using-getextendedtcptable-in-python
http://stackoverflow.com/questions/550653/cross-platform-way-to-get-pids-by-process-name-in-python

répondu 16-Jul-2015 par Arza (726 points)
edité 16-Jul-2015 par Arza

On sent le gars qui vient d'un autre langage ;)

C'est velu... Je prendrai le temps d'étudier ce code ce week-end ou la semaine prochaine, mais je sens que je vais galérer !

Doh, l'API windows, c'est dur à macher. C'est le genre de truc, à trouver par soi-même...

+2 votes

Même si la solution a apparemment été trouvée, voici mes 2 centimes :
1. "cmd.exe /c netstat -nabo" : le "b" nécessite une élévation de privilèges pour associer port et PID.
2. Se pencher sur la fameuse lib Windows "impacket" : https://github.com/CoreSecurity/impacket
WMI peut aussi être ton ami.

répondu 23-Jul-2015 par toto (194 points)

ya moyen d'avoir un petit test en direct ? Genre un petit exemple concret où t'arrives à obtenir le port de tel programme sur ton pc ^^

...