github.com/yaling888/clash@v1.53.0/component/process/process_windows.go (about)

     1  package process
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net/netip"
     7  	"unsafe"
     8  
     9  	"golang.org/x/sys/windows"
    10  
    11  	"github.com/yaling888/clash/common/pool"
    12  )
    13  
    14  var (
    15  	modIphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
    16  
    17  	procGetExtendedTcpTable = modIphlpapi.NewProc("GetExtendedTcpTable")
    18  	procGetExtendedUdpTable = modIphlpapi.NewProc("GetExtendedUdpTable")
    19  )
    20  
    21  func findProcessPath(network string, from netip.AddrPort, to netip.AddrPort) (string, error) {
    22  	family := uint32(windows.AF_INET)
    23  	if from.Addr().Is6() {
    24  		family = windows.AF_INET6
    25  	}
    26  
    27  	var protocol uint32
    28  	switch network {
    29  	case TCP:
    30  		protocol = windows.IPPROTO_TCP
    31  	case UDP:
    32  		protocol = windows.IPPROTO_UDP
    33  	default:
    34  		return "", ErrInvalidNetwork
    35  	}
    36  
    37  	pid, err := findPidByConnectionEndpoint(family, protocol, from, to)
    38  	if err != nil {
    39  		return "", err
    40  	}
    41  
    42  	return getExecPathFromPID(pid)
    43  }
    44  
    45  func findPidByConnectionEndpoint(family uint32, protocol uint32, from netip.AddrPort, to netip.AddrPort) (uint32, error) {
    46  	bufP := pool.GetBufferWriter()
    47  	defer pool.PutBufferWriter(bufP)
    48  
    49  	var buf []byte
    50  	bufSize := uint32(len(buf))
    51  
    52  loop:
    53  	for {
    54  		var ret uintptr
    55  		if bufP.Len() > 0 {
    56  			buf = *bufP
    57  		}
    58  		switch protocol {
    59  		case windows.IPPROTO_TCP:
    60  			ret, _, _ = procGetExtendedTcpTable.Call(
    61  				uintptr(unsafe.Pointer(unsafe.SliceData(buf))),
    62  				uintptr(unsafe.Pointer(&bufSize)),
    63  				0,
    64  				uintptr(family),
    65  				4, // TCP_TABLE_OWNER_PID_CONNECTIONS
    66  				0,
    67  			)
    68  		case windows.IPPROTO_UDP:
    69  			ret, _, _ = procGetExtendedUdpTable.Call(
    70  				uintptr(unsafe.Pointer(unsafe.SliceData(buf))),
    71  				uintptr(unsafe.Pointer(&bufSize)),
    72  				0,
    73  				uintptr(family),
    74  				1, // UDP_TABLE_OWNER_PID
    75  				0,
    76  			)
    77  		default:
    78  			return 0, errors.New("unsupported network")
    79  		}
    80  
    81  		switch ret {
    82  		case 0:
    83  			buf = buf[:bufSize]
    84  
    85  			break loop
    86  		case uintptr(windows.ERROR_INSUFFICIENT_BUFFER):
    87  			bufP.Reset()
    88  			bufP.Grow(int(bufSize))
    89  
    90  			continue loop
    91  		default:
    92  			return 0, fmt.Errorf("syscall error: %d", ret)
    93  		}
    94  	}
    95  
    96  	if len(buf) < int(unsafe.Sizeof(uint32(0))) {
    97  		return 0, fmt.Errorf("invalid table size: %d", len(buf))
    98  	}
    99  
   100  	entriesSize := *(*uint32)(unsafe.Pointer(&buf[0]))
   101  
   102  	switch protocol {
   103  	case windows.IPPROTO_TCP:
   104  		if family == windows.AF_INET {
   105  			type MibTcpRowOwnerPid struct {
   106  				State      uint32
   107  				LocalAddr  [4]byte
   108  				LocalPort  uint32
   109  				RemoteAddr [4]byte
   110  				RemotePort uint32
   111  				OwningPid  uint32
   112  			}
   113  
   114  			if uint32(len(buf))-4 < entriesSize*uint32(unsafe.Sizeof(MibTcpRowOwnerPid{})) {
   115  				return 0, fmt.Errorf("invalid tables size: %d", len(buf))
   116  			}
   117  
   118  			entries := unsafe.Slice((*MibTcpRowOwnerPid)(unsafe.Pointer(&buf[4])), entriesSize)
   119  			for _, entry := range entries {
   120  				localAddr := netip.AddrFrom4(entry.LocalAddr)
   121  				localPort := windows.Ntohs(uint16(entry.LocalPort))
   122  				remoteAddr := netip.AddrFrom4(entry.RemoteAddr)
   123  				remotePort := windows.Ntohs(uint16(entry.RemotePort))
   124  
   125  				if localAddr == from.Addr() && remoteAddr == to.Addr() && localPort == from.Port() && remotePort == to.Port() {
   126  					return entry.OwningPid, nil
   127  				}
   128  			}
   129  		} else {
   130  			type MibTcp6RowOwnerPid struct {
   131  				LocalAddr     [16]byte
   132  				LocalScopeID  uint32
   133  				LocalPort     uint32
   134  				RemoteAddr    [16]byte
   135  				RemoteScopeID uint32
   136  				RemotePort    uint32
   137  				State         uint32
   138  				OwningPid     uint32
   139  			}
   140  
   141  			if uint32(len(buf))-4 < entriesSize*uint32(unsafe.Sizeof(MibTcp6RowOwnerPid{})) {
   142  				return 0, fmt.Errorf("invalid tables size: %d", len(buf))
   143  			}
   144  
   145  			entries := unsafe.Slice((*MibTcp6RowOwnerPid)(unsafe.Pointer(&buf[4])), entriesSize)
   146  			for _, entry := range entries {
   147  				localAddr := netip.AddrFrom16(entry.LocalAddr)
   148  				localPort := windows.Ntohs(uint16(entry.LocalPort))
   149  				remoteAddr := netip.AddrFrom16(entry.RemoteAddr)
   150  				remotePort := windows.Ntohs(uint16(entry.RemotePort))
   151  
   152  				if localAddr == from.Addr() && remoteAddr == to.Addr() && localPort == from.Port() && remotePort == to.Port() {
   153  					return entry.OwningPid, nil
   154  				}
   155  			}
   156  		}
   157  	case windows.IPPROTO_UDP:
   158  		if family == windows.AF_INET {
   159  			type MibUdpRowOwnerPid struct {
   160  				LocalAddr [4]byte
   161  				LocalPort uint32
   162  				OwningPid uint32
   163  			}
   164  
   165  			if uint32(len(buf))-4 < entriesSize*uint32(unsafe.Sizeof(MibUdpRowOwnerPid{})) {
   166  				return 0, fmt.Errorf("invalid tables size: %d", len(buf))
   167  			}
   168  
   169  			entries := unsafe.Slice((*MibUdpRowOwnerPid)(unsafe.Pointer(&buf[4])), entriesSize)
   170  			for _, entry := range entries {
   171  				localAddr := netip.AddrFrom4(entry.LocalAddr)
   172  				localPort := windows.Ntohs(uint16(entry.LocalPort))
   173  
   174  				if (localAddr == from.Addr() || localAddr.IsUnspecified()) && localPort == from.Port() {
   175  					return entry.OwningPid, nil
   176  				}
   177  			}
   178  		} else {
   179  			type MibUdp6RowOwnerPid struct {
   180  				LocalAddr    [16]byte
   181  				LocalScopeId uint32
   182  				LocalPort    uint32
   183  				OwningPid    uint32
   184  			}
   185  
   186  			if uint32(len(buf))-4 < entriesSize*uint32(unsafe.Sizeof(MibUdp6RowOwnerPid{})) {
   187  				return 0, fmt.Errorf("invalid tables size: %d", len(buf))
   188  			}
   189  
   190  			entries := unsafe.Slice((*MibUdp6RowOwnerPid)(unsafe.Pointer(&buf[4])), entriesSize)
   191  			for _, entry := range entries {
   192  				localAddr := netip.AddrFrom16(entry.LocalAddr)
   193  				localPort := windows.Ntohs(uint16(entry.LocalPort))
   194  
   195  				if (localAddr == from.Addr() || localAddr.IsUnspecified()) && localPort == from.Port() {
   196  					return entry.OwningPid, nil
   197  				}
   198  			}
   199  		}
   200  	default:
   201  		return 0, ErrInvalidNetwork
   202  	}
   203  
   204  	return 0, ErrNotFound
   205  }
   206  
   207  func getExecPathFromPID(pid uint32) (string, error) {
   208  	// kernel process starts with a colon in order to distinguish with normal processes
   209  	switch pid {
   210  	case 0:
   211  		// reserved pid for system idle process
   212  		return ":System Idle Process", nil
   213  	case 4:
   214  		// reserved pid for windows kernel image
   215  		return ":System", nil
   216  	}
   217  	h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
   218  	if err != nil {
   219  		return "", err
   220  	}
   221  	defer windows.CloseHandle(h)
   222  
   223  	buf := make([]uint16, windows.MAX_LONG_PATH)
   224  	size := uint32(len(buf))
   225  
   226  	err = windows.QueryFullProcessImageName(h, 0, &buf[0], &size)
   227  	if err != nil {
   228  		return "", err
   229  	}
   230  
   231  	return windows.UTF16ToString(buf[:size]), nil
   232  }