Skip to content

Commit e40c16c

Browse files
authored
Merge pull request #1 from secDre4mer/master
feat: rework deadlock handling
2 parents f89599e + a07e82f commit e40c16c

5 files changed

+412
-266
lines changed

handle.go

+21-266
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,12 @@
33
package handle
44

55
import (
6-
"errors"
76
"fmt"
87
"io"
9-
"os"
10-
"reflect"
118
"runtime"
12-
"syscall"
139
"time"
14-
"unicode/utf16"
15-
"unsafe"
16-
17-
"golang.org/x/sys/windows"
18-
)
19-
20-
var (
21-
modntdll = syscall.NewLazyDLL("ntdll.dll")
22-
procNtQuerySystemInformation = modntdll.NewProc("NtQuerySystemInformation")
23-
procNtQueryObject = modntdll.NewProc("NtQueryObject")
2410
)
2511

26-
type unicodeString struct {
27-
Length uint16
28-
MaximumLength uint16
29-
Buffer *uint16
30-
}
31-
32-
type systemHandle struct {
33-
UniqueProcessID uint16
34-
CreatorBackTraceIndex uint16
35-
ObjectTypeIndex uint8
36-
HandleAttributes uint8
37-
HandleValue uint16
38-
Object uint3264
39-
GrantedAccess uint3264
40-
}
41-
42-
type systemHandleInformation struct {
43-
Count uint3264
44-
SystemHandle []systemHandle
45-
}
46-
47-
type objectTypeInformation struct {
48-
TypeName unicodeString
49-
_ [22]uint64 // unused
50-
}
51-
52-
type objectNameInformation struct {
53-
Name unicodeString
54-
}
55-
56-
func NtSuccess(rt uint32) bool {
57-
return rt < 0x8000000
58-
}
59-
6012
type Handle struct {
6113
Process uint16
6214
Handle uint16
@@ -65,248 +17,51 @@ type Handle struct {
6517
}
6618

6719
func QueryHandles(buf []byte, processFilter *uint16, handleTypes []string, queryTimeout time.Duration) (handles []Handle, err error) {
68-
// reset buffer, querying system information seem to require a 0-valued buffer.
69-
// Without this reset, the below sysinfo.Count might be wrong.
70-
for i := 0; i < len(buf); i++ {
71-
buf[i] = 0
72-
}
73-
ownpid := uint16(os.Getpid())
74-
ownprocess, err := windows.GetCurrentProcess()
20+
systemHandles, err := NtQuerySystemHandles(buf)
7521
if err != nil {
76-
return nil, fmt.Errorf("could not get current process: %s", err)
77-
}
78-
defer windows.CloseHandle(ownprocess)
79-
// load all handle information to buffer and convert it to systemHandleInformation
80-
if err := querySystemInformation(buf); err != nil {
81-
return nil, fmt.Errorf("could not query system information: %s", err)
22+
return nil, err
8223
}
83-
sysinfo := (*systemHandleInformation)(unsafe.Pointer(&buf[0]))
84-
sh := (*reflect.SliceHeader)(unsafe.Pointer(&sysinfo.SystemHandle))
85-
sh.Data = uintptr(unsafe.Pointer(&buf[int(unsafe.Sizeof(sysinfo.Count))]))
86-
sh.Len = int(sysinfo.Count)
87-
sh.Cap = int(sysinfo.Count)
88-
var (
89-
typeMapping = make(map[uint8]string) // what objecttypeindex equals which handletype
90-
typeMappingErr = make(map[uint8]int)
91-
typeFilter map[string]struct{}
92-
processErrs = make(map[uint16]struct{})
93-
)
24+
var typeFilter map[string]struct{}
9425
if len(handleTypes) > 0 {
9526
typeFilter = make(map[string]struct{})
9627
for _, handleType := range handleTypes {
9728
typeFilter[handleType] = struct{}{}
9829
}
9930
}
10031
log("type filter: %#v", typeFilter)
101-
log("sysinfo count: %d", sysinfo.Count)
102-
for i := uint3264(0); i < sysinfo.Count; i++ {
103-
handle := sysinfo.SystemHandle[i]
32+
log("handle count: %d", len(systemHandles))
33+
inspector := NewInspector(queryTimeout)
34+
defer inspector.Close()
35+
for _, handle := range systemHandles {
10436
log("handle: %#v", handle)
105-
// some handles cause freeze, skip them
106-
if (handle.GrantedAccess == 0x0012019f) ||
107-
(handle.GrantedAccess == 0x001a019f) ||
108-
(handle.GrantedAccess == 0x00120189) ||
109-
(handle.GrantedAccess == 0x00100000) {
110-
log("skipping handle due to granted access")
111-
continue
112-
}
11337
if processFilter != nil && *processFilter != handle.UniqueProcessID {
11438
log("skipping handle of process %d due to process filter %d", handle.UniqueProcessID, processFilter)
11539
continue
11640
}
117-
if _, ok := processErrs[handle.UniqueProcessID]; ok {
41+
handleType, err := inspector.LookupHandleType(handle)
42+
if err != nil {
43+
log("could not query handle type for handle %d in process %d with access mask %d, error: %v", handle.HandleValue, handle.UniqueProcessID, handle.GrantedAccess, err)
11844
continue
11945
}
120-
// unknown type, query the type information
121-
if _, ok := typeMapping[handle.ObjectTypeIndex]; !ok {
122-
log("handle type %d of handle 0x%X is unknown, querying for type ...", handle.UniqueProcessID, handle.HandleValue)
123-
done := make(chan struct{}, 1)
124-
var (
125-
handleTypeRoutine string
126-
errRoutine error
127-
)
128-
go func() {
129-
handleTypeRoutine, errRoutine = queryTypeInformation(handle, ownprocess, ownpid == handle.UniqueProcessID)
130-
done <- struct{}{}
131-
}()
132-
select {
133-
case <-done:
134-
if errRoutine == errOpenProcess {
135-
log("skipping process %d due to open error", handle.UniqueProcessID)
136-
processErrs[handle.UniqueProcessID] = struct{}{}
137-
continue
138-
}
139-
if errRoutine != nil {
140-
log("handle type %d could not be queried: %s", handle.ObjectTypeIndex, errRoutine)
141-
// to prevent querying tons of types that can't be queries, count errors per
142-
// handle type and ignore this type if more than X tries failed.
143-
typeMappingErr[handle.ObjectTypeIndex]++
144-
if typeMappingErr[handle.ObjectTypeIndex] >= 10 {
145-
typeMapping[handle.ObjectTypeIndex] = "unknown"
146-
}
147-
} else {
148-
log("handle type %d is of type %s", handle.ObjectTypeIndex, handleTypeRoutine)
149-
typeMapping[handle.ObjectTypeIndex] = handleTypeRoutine
150-
}
151-
case <-time.After(queryTimeout):
152-
log("timeout when querying process %d handle 0x%X with granted access 0x%X", handle.ObjectTypeIndex, handle.HandleValue, handle.GrantedAccess)
153-
continue
154-
}
155-
}
156-
handleType := typeMapping[handle.ObjectTypeIndex]
157-
log("handle type: %s", handleType)
15846
if typeFilter != nil {
159-
if _, ok := typeFilter[handleType]; !ok {
160-
// handle type not in filter list, skip
161-
log("skipping handle type %q due to handle filters", handleType)
47+
if _, isTargetType := typeFilter[handleType]; !isTargetType {
16248
continue
16349
}
16450
}
165-
switch handleType {
166-
default:
167-
done := make(chan struct{}, 1)
168-
var (
169-
nameRoutine string
170-
errRoutine error
171-
)
172-
go func() {
173-
nameRoutine, errRoutine = queryNameInformation(handle, ownprocess, ownpid == handle.UniqueProcessID)
174-
done <- struct{}{}
175-
}()
176-
var name string
177-
select {
178-
case <-done:
179-
if errRoutine != nil {
180-
log("could not get handle name for handle 0x%X of type %s: %s", handle.HandleValue, handleType, errRoutine)
181-
} else {
182-
name = nameRoutine
183-
}
184-
case <-time.After(queryTimeout):
185-
log("timeout when querying for handle name of process %d's handle 0x%X (type %s) and granted access 0x%X", handle.UniqueProcessID, handle.HandleValue, handleType, handle.GrantedAccess)
186-
}
187-
handle := Handle{Process: handle.UniqueProcessID, Handle: handle.HandleValue, Name: name, Type: handleType}
188-
log("handle found: process: %d handle: 0x%X name: %10.10s type: %s", handle.Process, handle.Handle, handle.Name, handle.Type)
189-
// add handle to result set
190-
handles = append(handles, handle)
191-
}
192-
}
193-
runtime.KeepAlive(buf)
194-
return handles, nil
195-
}
196-
197-
func querySystemInformation(buf []byte) error {
198-
ret, _, _ := procNtQuerySystemInformation.Call(
199-
16,
200-
uintptr(unsafe.Pointer(&buf[0])),
201-
uintptr(len(buf)),
202-
0,
203-
)
204-
if !NtSuccess(uint32(ret)) {
205-
return fmt.Errorf("NTStatus(0x%X)", ret)
206-
}
207-
return nil
208-
}
209-
210-
var errOpenProcess = errors.New("could not open process")
211-
212-
func queryTypeInformation(handle systemHandle, ownprocess windows.Handle, ownpid bool) (string, error) {
213-
// duplicate handle if it's not from our own process
214-
var h windows.Handle
215-
if !ownpid {
216-
p, err := windows.OpenProcess(
217-
windows.PROCESS_DUP_HANDLE,
218-
true,
219-
uint32(handle.UniqueProcessID),
220-
)
221-
if err != nil {
222-
log("could not open process %d: %s", handle.UniqueProcessID, err)
223-
return "", errOpenProcess
224-
}
225-
defer windows.CloseHandle(p)
226-
if err := windows.DuplicateHandle(
227-
p,
228-
windows.Handle(handle.HandleValue),
229-
ownprocess,
230-
&h,
231-
0,
232-
false,
233-
windows.DUPLICATE_SAME_ACCESS,
234-
); err != nil {
235-
log("could not duplicate process handle 0x%X of process %d: %s", handle.HandleValue, handle.UniqueProcessID, err)
236-
return "", err
237-
}
238-
defer windows.CloseHandle(h)
239-
} else {
240-
h = windows.Handle(handle.HandleValue)
241-
}
242-
buf := make([]byte, 0x1000)
243-
ret, _, _ := procNtQueryObject.Call(
244-
uintptr(h), 2,
245-
uintptr(unsafe.Pointer(&buf[0])),
246-
uintptr(0x1000),
247-
0,
248-
)
249-
if !NtSuccess(uint32(ret)) {
250-
return "", fmt.Errorf("NTStatus(0x%X)", ret)
251-
}
252-
name := (*objectTypeInformation)(unsafe.Pointer(&buf[0])).TypeName.String()
253-
runtime.KeepAlive(buf)
254-
return name, nil
255-
}
256-
257-
func queryNameInformation(handle systemHandle, ownprocess windows.Handle, ownpid bool) (string, error) {
258-
// duplicate handle if it's not from our own process
259-
var h windows.Handle
260-
if !ownpid {
261-
p, err := windows.OpenProcess(
262-
windows.PROCESS_DUP_HANDLE,
263-
true,
264-
uint32(handle.UniqueProcessID),
265-
)
51+
name, err := inspector.LookupHandleName(handle)
26652
if err != nil {
267-
return "", err
268-
}
269-
defer windows.CloseHandle(p)
270-
if err := windows.DuplicateHandle(
271-
p,
272-
windows.Handle(handle.HandleValue),
273-
ownprocess,
274-
&h,
275-
0,
276-
false,
277-
windows.DUPLICATE_SAME_ACCESS,
278-
); err != nil {
279-
return "", err
53+
log("could not query handle name for handle %d in process %d with access mask %d, error: %v", handle.HandleValue, handle.UniqueProcessID, handle.GrantedAccess, err)
54+
continue
28055
}
281-
defer windows.CloseHandle(h)
282-
} else {
283-
h = windows.Handle(handle.HandleValue)
284-
}
285-
log("query (access 0x%X)", handle.GrantedAccess)
286-
buf := make([]byte, 0x1000)
287-
ret, _, _ := procNtQueryObject.Call(
288-
uintptr(h),
289-
1,
290-
uintptr(unsafe.Pointer(&buf[0])),
291-
uintptr(0x1000),
292-
0,
293-
)
294-
if !NtSuccess(uint32(ret)) {
295-
return "", fmt.Errorf("NTStatus(0x%X)", ret)
56+
handles = append(handles, Handle{
57+
Process: handle.UniqueProcessID,
58+
Handle: handle.HandleValue,
59+
Name: name,
60+
Type: handleType,
61+
})
29662
}
297-
name := (*objectNameInformation)(unsafe.Pointer(&buf[0])).Name.String()
29863
runtime.KeepAlive(buf)
299-
return name, nil
300-
}
301-
302-
func (u unicodeString) String() string {
303-
var s []uint16
304-
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
305-
hdr.Data = uintptr(unsafe.Pointer(u.Buffer))
306-
hdr.Len = int(u.Length / 2)
307-
hdr.Cap = int(u.MaximumLength / 2)
308-
log("converting unicode string with length %d and capacity %d", u.Length, u.MaximumLength)
309-
return string(utf16.Decode(s))
64+
return handles, nil
31065
}
31166

31267
var writer io.Writer

0 commit comments

Comments
 (0)