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 }