process_windows.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. package process
  2. import (
  3. "fmt"
  4. "net/netip"
  5. "sync"
  6. "syscall"
  7. "unsafe"
  8. "github.com/metacubex/mihomo/common/nnip"
  9. "github.com/metacubex/mihomo/log"
  10. "golang.org/x/sys/windows"
  11. )
  12. const (
  13. tcpTableFunc = "GetExtendedTcpTable"
  14. tcpTablePidConn = 4
  15. udpTableFunc = "GetExtendedUdpTable"
  16. udpTablePid = 1
  17. queryProcNameFunc = "QueryFullProcessImageNameW"
  18. )
  19. var (
  20. getExTCPTable uintptr
  21. getExUDPTable uintptr
  22. queryProcName uintptr
  23. once sync.Once
  24. )
  25. func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {
  26. return 0, 0, ErrPlatformNotSupport
  27. }
  28. func initWin32API() error {
  29. h, err := windows.LoadLibrary("iphlpapi.dll")
  30. if err != nil {
  31. return fmt.Errorf("LoadLibrary iphlpapi.dll failed: %s", err.Error())
  32. }
  33. getExTCPTable, err = windows.GetProcAddress(h, tcpTableFunc)
  34. if err != nil {
  35. return fmt.Errorf("GetProcAddress of %s failed: %s", tcpTableFunc, err.Error())
  36. }
  37. getExUDPTable, err = windows.GetProcAddress(h, udpTableFunc)
  38. if err != nil {
  39. return fmt.Errorf("GetProcAddress of %s failed: %s", udpTableFunc, err.Error())
  40. }
  41. h, err = windows.LoadLibrary("kernel32.dll")
  42. if err != nil {
  43. return fmt.Errorf("LoadLibrary kernel32.dll failed: %s", err.Error())
  44. }
  45. queryProcName, err = windows.GetProcAddress(h, queryProcNameFunc)
  46. if err != nil {
  47. return fmt.Errorf("GetProcAddress of %s failed: %s", queryProcNameFunc, err.Error())
  48. }
  49. return nil
  50. }
  51. func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {
  52. once.Do(func() {
  53. err := initWin32API()
  54. if err != nil {
  55. log.Errorln("Initialize PROCESS-NAME failed: %s", err.Error())
  56. log.Warnln("All PROCESS-NAMES rules will be skipped")
  57. return
  58. }
  59. })
  60. family := windows.AF_INET
  61. if ip.Is6() {
  62. family = windows.AF_INET6
  63. }
  64. var class int
  65. var fn uintptr
  66. switch network {
  67. case TCP:
  68. fn = getExTCPTable
  69. class = tcpTablePidConn
  70. case UDP:
  71. fn = getExUDPTable
  72. class = udpTablePid
  73. default:
  74. return 0, "", ErrInvalidNetwork
  75. }
  76. buf, err := getTransportTable(fn, family, class)
  77. if err != nil {
  78. return 0, "", err
  79. }
  80. s := newSearcher(family == windows.AF_INET, network == TCP)
  81. pid, err := s.Search(buf, ip, uint16(srcPort))
  82. if err != nil {
  83. return 0, "", err
  84. }
  85. pp, err := getExecPathFromPID(pid)
  86. return 0, pp, err
  87. }
  88. type searcher struct {
  89. itemSize int
  90. port int
  91. ip int
  92. ipSize int
  93. pid int
  94. tcpState int
  95. }
  96. func (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error) {
  97. n := int(readNativeUint32(b[:4]))
  98. itemSize := s.itemSize
  99. for i := 0; i < n; i++ {
  100. row := b[4+itemSize*i : 4+itemSize*(i+1)]
  101. if s.tcpState >= 0 {
  102. tcpState := readNativeUint32(row[s.tcpState : s.tcpState+4])
  103. // MIB_TCP_STATE_ESTAB, only check established connections for TCP
  104. if tcpState != 5 {
  105. continue
  106. }
  107. }
  108. // according to MSDN, only the lower 16 bits of dwLocalPort are used and the port number is in network endian.
  109. // this field can be illustrated as follows depends on different machine endianess:
  110. // little endian: [ MSB LSB 0 0 ] interpret as native uint32 is ((LSB<<8)|MSB)
  111. // big endian: [ 0 0 MSB LSB ] interpret as native uint32 is ((MSB<<8)|LSB)
  112. // so we need an syscall.Ntohs on the lower 16 bits after read the port as native uint32
  113. srcPort := syscall.Ntohs(uint16(readNativeUint32(row[s.port : s.port+4])))
  114. if srcPort != port {
  115. continue
  116. }
  117. srcIP := nnip.IpToAddr(row[s.ip : s.ip+s.ipSize])
  118. // windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto
  119. if ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {
  120. continue
  121. }
  122. pid := readNativeUint32(row[s.pid : s.pid+4])
  123. return pid, nil
  124. }
  125. return 0, ErrNotFound
  126. }
  127. func newSearcher(isV4, isTCP bool) *searcher {
  128. var itemSize, port, ip, ipSize, pid int
  129. tcpState := -1
  130. switch {
  131. case isV4 && isTCP:
  132. // struct MIB_TCPROW_OWNER_PID
  133. itemSize, port, ip, ipSize, pid, tcpState = 24, 8, 4, 4, 20, 0
  134. case isV4 && !isTCP:
  135. // struct MIB_UDPROW_OWNER_PID
  136. itemSize, port, ip, ipSize, pid = 12, 4, 0, 4, 8
  137. case !isV4 && isTCP:
  138. // struct MIB_TCP6ROW_OWNER_PID
  139. itemSize, port, ip, ipSize, pid, tcpState = 56, 20, 0, 16, 52, 48
  140. case !isV4 && !isTCP:
  141. // struct MIB_UDP6ROW_OWNER_PID
  142. itemSize, port, ip, ipSize, pid = 28, 20, 0, 16, 24
  143. }
  144. return &searcher{
  145. itemSize: itemSize,
  146. port: port,
  147. ip: ip,
  148. ipSize: ipSize,
  149. pid: pid,
  150. tcpState: tcpState,
  151. }
  152. }
  153. func getTransportTable(fn uintptr, family int, class int) ([]byte, error) {
  154. for size, buf := uint32(8), make([]byte, 8); ; {
  155. ptr := unsafe.Pointer(&buf[0])
  156. err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0)
  157. switch err {
  158. case 0:
  159. return buf, nil
  160. case uintptr(syscall.ERROR_INSUFFICIENT_BUFFER):
  161. buf = make([]byte, size)
  162. default:
  163. return nil, fmt.Errorf("syscall error: %d", err)
  164. }
  165. }
  166. }
  167. func readNativeUint32(b []byte) uint32 {
  168. return *(*uint32)(unsafe.Pointer(&b[0]))
  169. }
  170. func getExecPathFromPID(pid uint32) (string, error) {
  171. // kernel process starts with a colon in order to distinguish with normal processes
  172. switch pid {
  173. case 0:
  174. // reserved pid for system idle process
  175. return ":System Idle Process", nil
  176. case 4:
  177. // reserved pid for windows kernel image
  178. return ":System", nil
  179. }
  180. h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
  181. if err != nil {
  182. return "", err
  183. }
  184. defer windows.CloseHandle(h)
  185. buf := make([]uint16, syscall.MAX_LONG_PATH)
  186. size := uint32(len(buf))
  187. r1, _, err := syscall.SyscallN(
  188. queryProcName,
  189. uintptr(h),
  190. uintptr(0),
  191. uintptr(unsafe.Pointer(&buf[0])),
  192. uintptr(unsafe.Pointer(&size)),
  193. )
  194. if r1 == 0 {
  195. return "", err
  196. }
  197. return syscall.UTF16ToString(buf[:size]), nil
  198. }