Improve process search

This commit is contained in:
世界 2026-09-04 21:32:13 +08:00
parent 7ad8567e67
commit 827f50cd41
No known key found for this signature in database
GPG key ID: CD109927C34A63C4
19 changed files with 140 additions and 78 deletions

View file

@ -112,11 +112,11 @@ type FindConnectionOwnerRequest struct {
}
type ConnectionOwner struct {
ProcessID uint32
UserId int32
UserName string
ProcessPath string
AndroidPackageNames []string
ProcessID uint32
UserId int32
UserName string
ProcessPaths []string
PackageNames []string
}
type Notification struct {

View file

@ -519,8 +519,8 @@ func (d *DefaultDialer) dialAttribution(ctx context.Context, destination M.Socks
ProcessID: metadata.ProcessInfo.ProcessID,
UserID: metadata.ProcessInfo.UserId,
UserName: metadata.ProcessInfo.UserName,
ProcessPath: metadata.ProcessInfo.ProcessPath,
PackageNames: metadata.ProcessInfo.AndroidPackageNames,
ProcessPaths: metadata.ProcessInfo.ProcessPaths,
PackageNames: metadata.ProcessInfo.PackageNames,
}
}
attribution.Rule = metadata.RouteRule

View file

@ -20,26 +20,28 @@ import (
const (
darwinSnapshotTTL = 200 * time.Millisecond
darwinXinpgenSize = 24
darwinXsocketOffset = 104
darwinXinpcbForeignPort = 16
darwinXinpcbLocalPort = 18
darwinXinpcbVFlag = 44
darwinXinpcbForeignAddr = 48
darwinXinpcbLocalAddr = 64
darwinXinpcbIPv4Addr = 12
darwinXsocketUID = 64
darwinXsocketLastPID = 68
darwinTCPExtraStructSize = 208
darwinXinpgenSize = 24
darwinXsocketOffset = 104
darwinXinpcbForeignPort = 16
darwinXinpcbLocalPort = 18
darwinXinpcbVFlag = 44
darwinXinpcbForeignAddr = 48
darwinXinpcbLocalAddr = 64
darwinXinpcbIPv4Addr = 12
darwinXsocketUID = 64
darwinXsocketLastPID = 68
darwinXsocketEffectivePID = 72
darwinTCPExtraStructSize = 208
)
type darwinConnectionEntry struct {
localAddr netip.Addr
remoteAddr netip.Addr
localPort uint16
remotePort uint16
pid uint32
uid int32
localAddr netip.Addr
remoteAddr netip.Addr
localPort uint16
remotePort uint16
pid uint32
effectivePid uint32
uid int32
}
type darwinConnectionMatchKind uint8
@ -105,7 +107,15 @@ func (f *darwinConnectionFinder) find(network string, source netip.AddrPort, des
}
processPath, err := getExecPathFromPID(entry.pid)
if err == nil {
owner.ProcessPath = processPath
owner.ProcessID = entry.pid
owner.ProcessPaths = []string{processPath}
if entry.effectivePid != 0 && entry.effectivePid != entry.pid {
effectivePath, effectiveErr := getExecPathFromPID(entry.effectivePid)
if effectiveErr == nil {
owner.ProcessID = entry.effectivePid
owner.ProcessPaths = []string{effectivePath, processPath}
}
}
return owner, nil
}
if fromCache {
@ -186,10 +196,11 @@ func parseDarwinConnectionEntry(inp []byte, so []byte) (darwinConnectionEntry, b
return darwinConnectionEntry{}, false
}
entry := darwinConnectionEntry{
remotePort: binary.BigEndian.Uint16(inp[darwinXinpcbForeignPort : darwinXinpcbForeignPort+2]),
localPort: binary.BigEndian.Uint16(inp[darwinXinpcbLocalPort : darwinXinpcbLocalPort+2]),
pid: binary.NativeEndian.Uint32(so[darwinXsocketLastPID : darwinXsocketLastPID+4]),
uid: int32(binary.NativeEndian.Uint32(so[darwinXsocketUID : darwinXsocketUID+4])),
remotePort: binary.BigEndian.Uint16(inp[darwinXinpcbForeignPort : darwinXinpcbForeignPort+2]),
localPort: binary.BigEndian.Uint16(inp[darwinXinpcbLocalPort : darwinXinpcbLocalPort+2]),
pid: binary.NativeEndian.Uint32(so[darwinXsocketLastPID : darwinXsocketLastPID+4]),
effectivePid: binary.NativeEndian.Uint32(so[darwinXsocketEffectivePID : darwinXsocketEffectivePID+4]),
uid: int32(binary.NativeEndian.Uint32(so[darwinXsocketUID : darwinXsocketUID+4])),
}
flag := inp[darwinXinpcbVFlag]
switch {

View file

@ -88,7 +88,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
if err != nil {
s.logger.DebugContext(ctx, "find process path: ", err)
} else {
processInfo.ProcessPath = processPath
processInfo.ProcessPaths = []string{processPath}
}
if s.packageManager != nil {
appID := uid % 100000
@ -99,7 +99,7 @@ func (s *linuxSearcher) FindProcessInfo(ctx context.Context, network string, sou
if packages, loaded := s.packageManager.PackagesByID(appID); loaded {
packageNames = append(packageNames, packages...)
}
processInfo.AndroidPackageNames = common.Uniq(packageNames)
processInfo.PackageNames = common.Uniq(packageNames)
}
return processInfo, nil
}

View file

@ -10,6 +10,7 @@ import (
"github.com/sagernet/sing/common/winiphlpapi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
var _ Searcher = (*windowsSearcher)(nil)
@ -36,15 +37,45 @@ func (s *windowsSearcher) Close() error {
}
func (s *windowsSearcher) FindProcessInfo(ctx context.Context, network string, source netip.AddrPort, destination netip.AddrPort) (*adapter.ConnectionOwner, error) {
pid, err := winiphlpapi.FindPid(network, source)
owner, err := winiphlpapi.FindSocketOwner(network, source)
if err != nil {
return nil, err
}
path, err := getProcessPath(pid)
path, err := getProcessPath(owner.Pid)
if err != nil {
return &adapter.ConnectionOwner{ProcessID: pid, UserId: -1}, err
return &adapter.ConnectionOwner{ProcessID: owner.Pid, UserId: -1}, err
}
return &adapter.ConnectionOwner{ProcessID: pid, ProcessPath: path, UserId: -1}, nil
processPaths := []string{path}
if owner.ServiceName != "" {
servicePath, serviceErr := getServiceDLLPath(owner.ServiceName)
if serviceErr == nil {
processPaths = []string{servicePath, path}
}
}
return &adapter.ConnectionOwner{ProcessID: owner.Pid, ProcessPaths: processPaths, UserId: -1}, nil
}
func getServiceDLLPath(serviceName string) (string, error) {
serviceKeyPath := `SYSTEM\CurrentControlSet\Services\` + serviceName
var lastErr error
for _, keyPath := range []string{serviceKeyPath + `\Parameters`, serviceKeyPath} {
key, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE)
if err != nil {
lastErr = err
continue
}
value, valueType, err := key.GetStringValue("ServiceDll")
key.Close()
if err != nil {
lastErr = err
continue
}
if valueType == registry.EXPAND_SZ {
return registry.ExpandString(value)
}
return value, nil
}
return "", lastErr
}
func getProcessPath(pid uint32) (string, error) {

View file

@ -1055,8 +1055,10 @@ func buildConnectionProto(metadata *trafficcontrol.TrackerMetadata) *Connection
ProcessId: metadata.Metadata.ProcessInfo.ProcessID,
UserId: metadata.Metadata.ProcessInfo.UserId,
UserName: metadata.Metadata.ProcessInfo.UserName,
ProcessPath: metadata.Metadata.ProcessInfo.ProcessPath,
PackageNames: metadata.Metadata.ProcessInfo.AndroidPackageNames,
PackageNames: metadata.Metadata.ProcessInfo.PackageNames,
}
if len(metadata.Metadata.ProcessInfo.ProcessPaths) > 0 {
processInfo.ProcessPath = metadata.Metadata.ProcessInfo.ProcessPaths[0]
}
}
return &Connection{

View file

@ -61,10 +61,10 @@ func (c connectionObject) MarshalJSON() ([]byte, error) {
}
var processPath string
if c.Metadata.ProcessInfo != nil {
if c.Metadata.ProcessInfo.ProcessPath != "" {
processPath = c.Metadata.ProcessInfo.ProcessPath
} else if len(c.Metadata.ProcessInfo.AndroidPackageNames) > 0 {
processPath = c.Metadata.ProcessInfo.AndroidPackageNames[0]
if len(c.Metadata.ProcessInfo.ProcessPaths) > 0 {
processPath = c.Metadata.ProcessInfo.ProcessPaths[0]
} else if len(c.Metadata.ProcessInfo.PackageNames) > 0 {
processPath = c.Metadata.ProcessInfo.PackageNames[0]
}
if processPath == "" {
if c.Metadata.ProcessInfo.UserId != -1 {

View file

@ -33,8 +33,11 @@ func FindConnectionOwner(ipProtocol int32, sourceAddress string, sourcePort int3
return nil, err
}
result := &ConnectionOwner{
UserId: owner.UserId,
ProcessPath: owner.ProcessPath,
UserId: owner.UserId,
processPaths: owner.ProcessPaths,
}
if len(owner.ProcessPaths) > 0 {
result.ProcessPath = owner.ProcessPaths[0]
}
if owner.UserId != -1 && owner.UserName == "" {
osUser, _ := user.LookupId(F.ToString(owner.UserId))

View file

@ -160,10 +160,10 @@ func buildOOMConnection(connection *trafficcontrol.TrackerMetadata) oomConnectio
}
var process string
if processInfo := metadata.ProcessInfo; processInfo != nil {
if processInfo.ProcessPath != "" {
process = processInfo.ProcessPath
} else if len(processInfo.AndroidPackageNames) > 0 {
process = processInfo.AndroidPackageNames[0]
if len(processInfo.ProcessPaths) > 0 {
process = processInfo.ProcessPaths[0]
} else if len(processInfo.PackageNames) > 0 {
process = processInfo.PackageNames[0]
}
if process == "" {
if processInfo.UserId != -1 {

View file

@ -90,9 +90,18 @@ type ConnectionOwner struct {
UserId int32
UserName string
ProcessPath string
processPaths []string
androidPackageNames []string
}
func (c *ConnectionOwner) SetProcessPaths(paths StringIterator) {
c.processPaths = iteratorToArray[string](paths)
}
func (c *ConnectionOwner) ProcessPaths() StringIterator {
return newIterator(c.processPaths)
}
func (c *ConnectionOwner) SetAndroidPackageNames(names StringIterator) {
c.androidPackageNames = iteratorToArray[string](names)
}

View file

@ -223,11 +223,15 @@ func (w *platformInterfaceWrapper) FindConnectionOwner(request *adapter.FindConn
if err != nil {
return nil, err
}
processPaths := result.processPaths
if len(processPaths) == 0 && result.ProcessPath != "" {
processPaths = []string{result.ProcessPath}
}
return &adapter.ConnectionOwner{
UserId: result.UserId,
UserName: result.UserName,
ProcessPath: result.ProcessPath,
AndroidPackageNames: result.androidPackageNames,
UserId: result.UserId,
UserName: result.UserName,
ProcessPaths: processPaths,
PackageNames: result.androidPackageNames,
}, nil
}

View file

@ -51,18 +51,19 @@ func (r *Router) searchProcessInfo(ctx context.Context, metadata *adapter.Inboun
return
}
metadata.ProcessInfo = processInfo
if processInfo.ProcessPath != "" {
if len(processInfo.ProcessPaths) > 0 {
processPath := strings.Join(processInfo.ProcessPaths, ", ")
if processInfo.UserName != "" {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath, ", user: ", processInfo.UserName)
r.logger.InfoContext(ctx, "found process path: ", processPath, ", user: ", processInfo.UserName)
} else if processInfo.UserId != -1 {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath, ", user id: ", processInfo.UserId)
r.logger.InfoContext(ctx, "found process path: ", processPath, ", user id: ", processInfo.UserId)
} else {
r.logger.InfoContext(ctx, "found process path: ", processInfo.ProcessPath)
r.logger.InfoContext(ctx, "found process path: ", processPath)
}
return
}
if len(processInfo.AndroidPackageNames) > 0 {
r.logger.InfoContext(ctx, "found package name: ", strings.Join(processInfo.AndroidPackageNames, ", "))
if len(processInfo.PackageNames) > 0 {
r.logger.InfoContext(ctx, "found package name: ", strings.Join(processInfo.PackageNames, ", "))
return
}
if processInfo.UserId != -1 {

View file

@ -25,10 +25,10 @@ func NewPackageNameItem(packageNameList []string) *PackageNameItem {
}
func (r *PackageNameItem) Match(metadata *adapter.InboundContext) bool {
if metadata.ProcessInfo == nil || len(metadata.ProcessInfo.AndroidPackageNames) == 0 {
if metadata.ProcessInfo == nil || len(metadata.ProcessInfo.PackageNames) == 0 {
return false
}
for _, packageName := range metadata.ProcessInfo.AndroidPackageNames {
for _, packageName := range metadata.ProcessInfo.PackageNames {
if r.packageMap[packageName] {
return true
}

View file

@ -39,11 +39,11 @@ func NewPackageNameRegexItem(expressions []string) (*PackageNameRegexItem, error
}
func (r *PackageNameRegexItem) Match(metadata *adapter.InboundContext) bool {
if metadata.ProcessInfo == nil || len(metadata.ProcessInfo.AndroidPackageNames) == 0 {
if metadata.ProcessInfo == nil || len(metadata.ProcessInfo.PackageNames) == 0 {
return false
}
for _, matcher := range r.matchers {
if slices.ContainsFunc(metadata.ProcessInfo.AndroidPackageNames, matcher.MatchString) {
if slices.ContainsFunc(metadata.ProcessInfo.PackageNames, matcher.MatchString) {
return true
}
}

View file

@ -2,6 +2,7 @@ package rule
import (
"path/filepath"
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@ -26,10 +27,12 @@ func NewProcessItem(processNameList []string) *ProcessItem {
}
func (r *ProcessItem) Match(metadata *adapter.InboundContext) bool {
if metadata.ProcessInfo == nil || metadata.ProcessInfo.ProcessPath == "" {
if metadata.ProcessInfo == nil {
return false
}
return r.processMap[filepath.Base(metadata.ProcessInfo.ProcessPath)]
return slices.ContainsFunc(metadata.ProcessInfo.ProcessPaths, func(processPath string) bool {
return r.processMap[filepath.Base(processPath)]
})
}
func (r *ProcessItem) String() string {

View file

@ -1,6 +1,7 @@
package rule
import (
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@ -29,15 +30,11 @@ func (r *ProcessPathItem) Match(metadata *adapter.InboundContext) bool {
if metadata.ProcessInfo == nil {
return false
}
if metadata.ProcessInfo.ProcessPath != "" && r.processMap[metadata.ProcessInfo.ProcessPath] {
if slices.ContainsFunc(metadata.ProcessInfo.ProcessPaths, func(processPath string) bool { return r.processMap[processPath] }) {
return true
}
if C.IsAndroid {
for _, packageName := range metadata.ProcessInfo.AndroidPackageNames {
if r.processMap[packageName] {
return true
}
}
return slices.ContainsFunc(metadata.ProcessInfo.PackageNames, func(packageName string) bool { return r.processMap[packageName] })
}
return false
}

View file

@ -2,6 +2,7 @@ package rule
import (
"regexp"
"slices"
"strings"
"github.com/sagernet/sing-box/adapter"
@ -38,11 +39,11 @@ func NewProcessPathRegexItem(expressions []string) (*ProcessPathRegexItem, error
}
func (r *ProcessPathRegexItem) Match(metadata *adapter.InboundContext) bool {
if metadata.ProcessInfo == nil || metadata.ProcessInfo.ProcessPath == "" {
if metadata.ProcessInfo == nil {
return false
}
for _, matcher := range r.matchers {
if matcher.MatchString(metadata.ProcessInfo.ProcessPath) {
if slices.ContainsFunc(metadata.ProcessInfo.ProcessPaths, matcher.MatchString) {
return true
}
}

View file

@ -38,7 +38,7 @@ type ProcessAttribution struct {
ProcessID uint32 `json:"processId,omitempty"`
UserID int32 `json:"userId,omitempty"`
UserName string `json:"userName,omitempty"`
ProcessPath string `json:"processPath,omitempty"`
ProcessPaths []string `json:"processPaths,omitempty"`
PackageNames []string `json:"packageNames,omitempty"`
}

View file

@ -125,11 +125,11 @@ func (t *resolve1Manager) createMetadata(sender dbus.Sender) adapter.InboundCont
processPath, err := os.Readlink(F.ToString("/proc/", senderPid, "/exe"))
if err == nil {
processInfo.ProcessPath = processPath
processInfo.ProcessPaths = []string{processPath}
} else {
processName, readErr := os.ReadFile(F.ToString("/proc/", senderPid, "/comm"))
if readErr == nil {
processInfo.ProcessPath = strings.TrimSpace(string(processName))
processInfo.ProcessPaths = []string{strings.TrimSpace(string(processName))}
}
}
@ -165,8 +165,8 @@ func (t *resolve1Manager) log(sender dbus.Sender, message ...any) {
metadata := t.createMetadata(sender)
if metadata.ProcessInfo != nil {
var prefix string
if metadata.ProcessInfo.ProcessPath != "" {
prefix = filepath.Base(metadata.ProcessInfo.ProcessPath)
if len(metadata.ProcessInfo.ProcessPaths) > 0 {
prefix = filepath.Base(metadata.ProcessInfo.ProcessPaths[0])
} else if metadata.ProcessInfo.UserName != "" {
prefix = F.ToString("user:", metadata.ProcessInfo.UserName)
} else if metadata.ProcessInfo.UserId != 0 {
@ -183,8 +183,8 @@ func (t *resolve1Manager) logRequest(sender dbus.Sender, message ...any) context
metadata := t.createMetadata(sender)
if metadata.ProcessInfo != nil {
var prefix string
if metadata.ProcessInfo.ProcessPath != "" {
prefix = filepath.Base(metadata.ProcessInfo.ProcessPath)
if len(metadata.ProcessInfo.ProcessPaths) > 0 {
prefix = filepath.Base(metadata.ProcessInfo.ProcessPaths[0])
} else if metadata.ProcessInfo.UserName != "" {
prefix = F.ToString("user:", metadata.ProcessInfo.UserName)
} else if metadata.ProcessInfo.UserId != 0 {