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