1
2
3 package common
4
5 import (
6 "context"
7 "fmt"
8 "path/filepath"
9 "reflect"
10 "strings"
11 "syscall"
12 "unsafe"
13
14 "github.com/yusufpapurcu/wmi"
15 "golang.org/x/sys/windows"
16 )
17
18
19 type PDH_FMT_COUNTERVALUE_DOUBLE struct {
20 CStatus uint32
21 DoubleValue float64
22 }
23
24
25 type PDH_FMT_COUNTERVALUE_LARGE struct {
26 CStatus uint32
27 LargeValue int64
28 }
29
30
31 type PDH_FMT_COUNTERVALUE_LONG struct {
32 CStatus uint32
33 LongValue int32
34 padding [4]byte
35 }
36
37
38 const (
39 ERROR_SUCCESS = 0
40 ERROR_FILE_NOT_FOUND = 2
41 DRIVE_REMOVABLE = 2
42 DRIVE_FIXED = 3
43 HKEY_LOCAL_MACHINE = 0x80000002
44 RRF_RT_REG_SZ = 0x00000002
45 RRF_RT_REG_DWORD = 0x00000010
46 PDH_FMT_LONG = 0x00000100
47 PDH_FMT_DOUBLE = 0x00000200
48 PDH_FMT_LARGE = 0x00000400
49 PDH_INVALID_DATA = 0xc0000bc6
50 PDH_INVALID_HANDLE = 0xC0000bbc
51 PDH_NO_DATA = 0x800007d5
52
53 STATUS_BUFFER_OVERFLOW = 0x80000005
54 STATUS_BUFFER_TOO_SMALL = 0xC0000023
55 STATUS_INFO_LENGTH_MISMATCH = 0xC0000004
56 )
57
58 const (
59 ProcessBasicInformation = 0
60 ProcessWow64Information = 26
61 ProcessQueryInformation = windows.PROCESS_DUP_HANDLE | windows.PROCESS_QUERY_INFORMATION
62
63 SystemExtendedHandleInformationClass = 64
64 )
65
66 var (
67 Modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
68 ModNt = windows.NewLazySystemDLL("ntdll.dll")
69 ModPdh = windows.NewLazySystemDLL("pdh.dll")
70 ModPsapi = windows.NewLazySystemDLL("psapi.dll")
71
72 ProcGetSystemTimes = Modkernel32.NewProc("GetSystemTimes")
73 ProcNtQuerySystemInformation = ModNt.NewProc("NtQuerySystemInformation")
74 ProcRtlGetNativeSystemInformation = ModNt.NewProc("RtlGetNativeSystemInformation")
75 ProcRtlNtStatusToDosError = ModNt.NewProc("RtlNtStatusToDosError")
76 ProcNtQueryInformationProcess = ModNt.NewProc("NtQueryInformationProcess")
77 ProcNtReadVirtualMemory = ModNt.NewProc("NtReadVirtualMemory")
78 ProcNtWow64QueryInformationProcess64 = ModNt.NewProc("NtWow64QueryInformationProcess64")
79 ProcNtWow64ReadVirtualMemory64 = ModNt.NewProc("NtWow64ReadVirtualMemory64")
80
81 PdhOpenQuery = ModPdh.NewProc("PdhOpenQuery")
82 PdhAddEnglishCounterW = ModPdh.NewProc("PdhAddEnglishCounterW")
83 PdhCollectQueryData = ModPdh.NewProc("PdhCollectQueryData")
84 PdhGetFormattedCounterValue = ModPdh.NewProc("PdhGetFormattedCounterValue")
85 PdhCloseQuery = ModPdh.NewProc("PdhCloseQuery")
86
87 procQueryDosDeviceW = Modkernel32.NewProc("QueryDosDeviceW")
88 )
89
90 type FILETIME struct {
91 DwLowDateTime uint32
92 DwHighDateTime uint32
93 }
94
95
96 func BytePtrToString(p *uint8) string {
97 a := (*[10000]uint8)(unsafe.Pointer(p))
98 i := 0
99 for a[i] != 0 {
100 i++
101 }
102 return string(a[:i])
103 }
104
105
106
107 type CounterInfo struct {
108 PostName string
109 CounterName string
110 Counter windows.Handle
111 }
112
113
114
115 func CreateQuery() (windows.Handle, error) {
116 var query windows.Handle
117 r, _, err := PdhOpenQuery.Call(0, 0, uintptr(unsafe.Pointer(&query)))
118 if r != 0 {
119 return 0, err
120 }
121 return query, nil
122 }
123
124
125 func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, error) {
126 var counter windows.Handle
127 r, _, err := PdhAddEnglishCounterW.Call(
128 uintptr(query),
129 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(cname))),
130 0,
131 uintptr(unsafe.Pointer(&counter)))
132 if r != 0 {
133 return nil, err
134 }
135 return &CounterInfo{
136 PostName: pname,
137 CounterName: cname,
138 Counter: counter,
139 }, nil
140 }
141
142
143
144 func GetCounterValue(counter windows.Handle) (float64, error) {
145 var value PDH_FMT_COUNTERVALUE_DOUBLE
146 r, _, err := PdhGetFormattedCounterValue.Call(uintptr(counter), PDH_FMT_DOUBLE, uintptr(0), uintptr(unsafe.Pointer(&value)))
147 if r != 0 && r != PDH_INVALID_DATA {
148 return 0.0, err
149 }
150 return value.DoubleValue, nil
151 }
152
153 type Win32PerformanceCounter struct {
154 PostName string
155 CounterName string
156 Query windows.Handle
157 Counter windows.Handle
158 }
159
160 func NewWin32PerformanceCounter(postName, counterName string) (*Win32PerformanceCounter, error) {
161 query, err := CreateQuery()
162 if err != nil {
163 return nil, err
164 }
165 var counter = Win32PerformanceCounter{
166 Query: query,
167 PostName: postName,
168 CounterName: counterName,
169 }
170 r, _, err := PdhAddEnglishCounterW.Call(
171 uintptr(counter.Query),
172 uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(counter.CounterName))),
173 0,
174 uintptr(unsafe.Pointer(&counter.Counter)),
175 )
176 if r != 0 {
177 return nil, err
178 }
179 return &counter, nil
180 }
181
182 func (w *Win32PerformanceCounter) GetValue() (float64, error) {
183 r, _, err := PdhCollectQueryData.Call(uintptr(w.Query))
184 if r != 0 && err != nil {
185 if r == PDH_NO_DATA {
186 return 0.0, fmt.Errorf("%w: this counter has not data", err)
187 }
188 return 0.0, err
189 }
190
191 return GetCounterValue(w.Counter)
192 }
193
194 func ProcessorQueueLengthCounter() (*Win32PerformanceCounter, error) {
195 return NewWin32PerformanceCounter("processor_queue_length", `\System\Processor Queue Length`)
196 }
197
198
199 func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error {
200 if _, ok := ctx.Deadline(); !ok {
201 ctxTimeout, cancel := context.WithTimeout(ctx, Timeout)
202 defer cancel()
203 ctx = ctxTimeout
204 }
205
206 errChan := make(chan error, 1)
207 go func() {
208 errChan <- wmi.Query(query, dst, connectServerArgs...)
209 }()
210
211 select {
212 case <-ctx.Done():
213 return ctx.Err()
214 case err := <-errChan:
215 return err
216 }
217 }
218
219
220
221
222
223 func ConvertDOSPath(p string) string {
224 rawDrive := strings.Join(strings.Split(p, `\`)[:3], `\`)
225
226 for d := 'A'; d <= 'Z'; d++ {
227 szDeviceName := string(d) + ":"
228 szTarget := make([]uint16, 512)
229 ret, _, _ := procQueryDosDeviceW.Call(uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(szDeviceName))),
230 uintptr(unsafe.Pointer(&szTarget[0])),
231 uintptr(len(szTarget)))
232 if ret != 0 && windows.UTF16ToString(szTarget[:]) == rawDrive {
233 return filepath.Join(szDeviceName, p[len(rawDrive):])
234 }
235 }
236 return p
237 }
238
239 type NtStatus uint32
240
241 func (s NtStatus) Error() error {
242 if s == 0 {
243 return nil
244 }
245 return fmt.Errorf("NtStatus 0x%08x", uint32(s))
246 }
247
248 func (s NtStatus) IsError() bool {
249 return s>>30 == 3
250 }
251
252 type SystemExtendedHandleTableEntryInformation struct {
253 Object uintptr
254 UniqueProcessId uintptr
255 HandleValue uintptr
256 GrantedAccess uint32
257 CreatorBackTraceIndex uint16
258 ObjectTypeIndex uint16
259 HandleAttributes uint32
260 Reserved uint32
261 }
262
263 type SystemExtendedHandleInformation struct {
264 NumberOfHandles uintptr
265 Reserved uintptr
266 Handles [1]SystemExtendedHandleTableEntryInformation
267 }
268
269
270 func CallWithExpandingBuffer(fn func() NtStatus, buf *[]byte, resultLength *uint32) NtStatus {
271 for {
272 if st := fn(); st == STATUS_BUFFER_OVERFLOW || st == STATUS_BUFFER_TOO_SMALL || st == STATUS_INFO_LENGTH_MISMATCH {
273 if int(*resultLength) <= cap(*buf) {
274 (*reflect.SliceHeader)(unsafe.Pointer(buf)).Len = int(*resultLength)
275 } else {
276 *buf = make([]byte, int(*resultLength))
277 }
278 continue
279 } else {
280 if !st.IsError() {
281 *buf = (*buf)[:int(*resultLength)]
282 }
283 return st
284 }
285 }
286 }
287
288 func NtQuerySystemInformation(
289 SystemInformationClass uint32,
290 SystemInformation *byte,
291 SystemInformationLength uint32,
292 ReturnLength *uint32,
293 ) NtStatus {
294 r0, _, _ := ProcNtQuerySystemInformation.Call(
295 uintptr(SystemInformationClass),
296 uintptr(unsafe.Pointer(SystemInformation)),
297 uintptr(SystemInformationLength),
298 uintptr(unsafe.Pointer(ReturnLength)))
299 return NtStatus(r0)
300 }
301
View as plain text