github.com/iDigitalFlame/xmt@v0.5.4/device/winapi/proc_qsi.go (about) 1 //go:build windows && !snap 2 // +build windows,!snap 3 4 // Copyright (C) 2020 - 2023 iDigitalFlame 5 // 6 // This program is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program. If not, see <https://www.gnu.org/licenses/>. 18 // 19 20 package winapi 21 22 import "unsafe" 23 24 const ( 25 procSize = unsafe.Sizeof(procInfo{}) 26 threadSize = unsafe.Sizeof(threadInfo{}) 27 ) 28 29 type procInfo struct { 30 // DO NOT REORDER 31 NextEntryOffset uint32 32 NumberOfThreads uint32 33 _ [6]int64 34 ImageName lsaString 35 _ int32 36 UniqueProcessID uintptr 37 InheritedFromUniqueProcessID uintptr 38 _ uint32 39 SessionID uint32 40 _ [(ptrSize * 13) + 48]byte 41 } 42 type threadInfo struct { 43 // DO NOT REORDER 44 _ [28]byte 45 StartAddress uintptr 46 ClientID clientID 47 _ [12]byte 48 ThreadState uint32 49 WaitReason uint32 50 _ uint32 51 } 52 53 // EnumProcesses attempts to reterive the list of currently running Processes 54 // and will call the supplied function with an entry for each Process. 55 // 56 // The user supplied function can return an error that if non-nil, will stop 57 // Process iteration immediately and will be returned by this function. 58 // 59 // Callers can return the special 'winapi.ErrNoMoreFiles' error that will stop 60 // iteration but will cause this function to return nil. This can be used to 61 // stop iteration without errors if needed. 62 // 63 // This function is affected by the 'snap' buildtag, which if supplied will use 64 // the 'CreateToolhelp32Snapshot' API function instead of the default 65 // 'NtQuerySystemInformation' API function. 66 func EnumProcesses(f func(ProcessEntry) error) error { 67 var ( 68 s uint64 69 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x5, 0, 0, uintptr(unsafe.Pointer(&s))) 70 ) 71 if s == 0 { 72 return formatNtError(r) 73 } 74 // NOTE(dij): Doubling this to ensure we get the correct amount. 75 b := make([]byte, s*2) 76 if r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x5, uintptr(unsafe.Pointer(&b[0])), uintptr(s), uintptr(unsafe.Pointer(&s))); r > 0 { 77 return formatNtError(r) 78 } 79 var err error 80 for x, i := (*procInfo)(unsafe.Pointer(&b[0])), uint32(0); ; x = (*procInfo)(unsafe.Pointer(&b[i])) { 81 if x.UniqueProcessID == 0 { 82 i += x.NextEntryOffset 83 continue 84 } 85 err = f(ProcessEntry{ 86 PID: uint32(x.UniqueProcessID), 87 PPID: uint32(x.InheritedFromUniqueProcessID), 88 Name: UTF16PtrToString(x.ImageName.Buffer), 89 Threads: x.NumberOfThreads, 90 session: int32(x.SessionID), 91 }) 92 if i += x.NextEntryOffset; err != nil { 93 break 94 } 95 if x.NextEntryOffset == 0 { 96 break 97 } 98 } 99 if err == ErrNoMoreFiles { 100 return nil 101 } 102 return err 103 } 104 105 // EnumThreads attempts to reterive the list of currently running Process Threads 106 // and will call the supplied function with an entry for each Thread that matches 107 // the supplied Process ID. 108 // 109 // The user supplied function can return an error that if non-nil, will stop 110 // Thread iteration immediately and will be returned by this function. 111 // 112 // Callers can return the special 'winapi.ErrNoMoreFiles' error that will stop 113 // iteration but will cause this function to return nil. This can be used to 114 // stop iteration without errors if needed. 115 // 116 // This function is affected by the 'snap' buildtag, which if supplied will use 117 // the 'CreateToolhelp32Snapshot' API function instead of the default 118 // 'NtQuerySystemInformation' API function. 119 func EnumThreads(pid uint32, f func(ThreadEntry) error) error { 120 var ( 121 s uint64 122 r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x5, 0, 0, uintptr(unsafe.Pointer(&s))) 123 ) 124 if s == 0 { 125 return formatNtError(r) 126 } 127 // NOTE(dij): Doubling this to ensure we get the correct amount. 128 b := make([]byte, s*2) 129 if r, _, _ = syscallN(funcNtQuerySystemInformation.address(), 0x5, uintptr(unsafe.Pointer(&b[0])), uintptr(s), uintptr(unsafe.Pointer(&s))); r > 0 { 130 return formatNtError(r) 131 } 132 var err error 133 outer: 134 for x, i := (*procInfo)(unsafe.Pointer(&b[0])), uint32(0); ; x = (*procInfo)(unsafe.Pointer(&b[i])) { 135 if uint32(x.UniqueProcessID) != pid { 136 if i += x.NextEntryOffset; x.NextEntryOffset == 0 { 137 break 138 } 139 continue 140 } 141 for z, n := i+uint32(procSize), uint32(0); n < x.NumberOfThreads; n++ { 142 var ( 143 v = (*threadInfo)(unsafe.Pointer(&b[z+(n*uint32(threadSize))])) 144 t = ThreadEntry{TID: uint32(v.ClientID.Thread), PID: uint32(v.ClientID.Process), sus: 1} 145 ) 146 if v.ThreadState == 5 && v.WaitReason == 5 { 147 t.sus = 2 148 } 149 if err = f(t); err != nil { 150 break outer 151 } 152 } 153 break // Should be fine doing this 154 } 155 if err == ErrNoMoreFiles { 156 return nil 157 } 158 return err 159 }