Created
December 17, 2016 17:00
-
-
Save jordanorelli/3227b67c165ca649ec7eb2a979ef671f to your computer and use it in GitHub Desktop.
watch process creation and termination on windows
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"time" | |
"os" | |
"sort" | |
"io" | |
"syscall" | |
"unicode/utf16" | |
"unsafe" | |
) | |
const ( | |
invalidHandle = ^uintptr(0) // INVALID_HANDLE_VALUE in windows.h | |
maxPath = 260 // MAX_PATH in windows.h | |
FALSE = 0 | |
TRUE = 1 | |
) | |
var ( | |
kernel32 = syscall.NewLazyDLL("kernel32.dll") | |
createSnapshot = kernel32.NewProc("CreateToolhelp32Snapshot") | |
firstProcess = kernel32.NewProc("Process32FirstW") | |
nextProcess = kernel32.NewProc("Process32NextW") | |
closeHandle = kernel32.NewProc("CloseHandle") | |
) | |
type processTable struct { | |
byPid map[uint32]*processEntry32 | |
byName map[string]*processEntry32 | |
entries [1024]processEntry32 | |
numEntries int | |
} | |
func (t *processTable) clear() { | |
*t = processTable{ | |
byPid: make(map[uint32]*processEntry32), | |
byName: make(map[string]*processEntry32), | |
} | |
} | |
func (t *processTable) display(w io.Writer, label string) { | |
fmt.Fprintf(w, "########################################################################################\n"); | |
fmt.Fprintf(w, "\n%s\n\n", label) | |
fmt.Fprintf(w, "Pid\tParent\tName\n"); | |
fmt.Fprintf(w, "----------------------------------------------------------------------------------------\n"); | |
pids := make([]int, 0, t.numEntries) | |
for pid, _ := range t.byPid { | |
pids = append(pids, int(pid)) | |
} | |
sort.Ints(pids) | |
for _, pid := range pids { | |
p := t.byPid[uint32(pid)] | |
fmt.Fprintf(w, "%d\t%d\t%s\n", p.pid, p.parent, p.exe()) | |
} | |
fmt.Fprintf(w, "----------------------------------------------------------------------------------------\n"); | |
fmt.Fprintf(w, "Total Processes: %d\n", t.numEntries) | |
} | |
type processEntry32 struct { | |
size uint32 | |
usage uint32 | |
pid uint32 | |
heapID uintptr | |
moduleID uint32 | |
threads uint32 | |
parent uint32 | |
priority int32 | |
flags uint32 | |
exeFile [260]uint16 | |
} | |
func (p *processEntry32) exe() string { | |
for i := 0; i < 259; i++ { | |
if p.exeFile[i] == 0 && p.exeFile[i+1] == 0 { | |
return string(utf16.Decode(p.exeFile[:i])) | |
} | |
} | |
return string(utf16.Decode(p.exeFile[:260])) | |
} | |
func fillProcessTable(t *processTable) { | |
t.clear() | |
h, _, err := createSnapshot.Call(2, 0) | |
if h == invalidHandle { | |
panic(err) | |
} | |
defer closeHandle.Call(h) | |
entry := &t.entries[t.numEntries] | |
entry.size = uint32(unsafe.Sizeof(*entry)) | |
ok, _, err := firstProcess.Call(h, uintptr(unsafe.Pointer(entry))) | |
if ok == FALSE { | |
panic(err) | |
} | |
t.numEntries++ | |
t.byPid[entry.pid] = entry | |
t.byName[entry.exe()] = entry | |
for { | |
entry := &t.entries[t.numEntries] | |
entry.size = uint32(unsafe.Sizeof(*entry)) | |
ok, _, err = nextProcess.Call(h, uintptr(unsafe.Pointer(entry))) | |
if ok == FALSE { | |
if err == syscall.ERROR_NO_MORE_FILES { | |
break | |
} | |
panic(err) | |
} | |
t.numEntries++ | |
t.byPid[entry.pid] = entry | |
t.byName[entry.exe()] = entry | |
} | |
} | |
func delta(prev, cur *processTable) (*processTable, *processTable) { | |
added, removed := new(processTable), new(processTable) | |
added.clear() | |
removed.clear() | |
for pid, entry := range cur.byPid { | |
if prev.byPid[pid] == nil { | |
dupe := &added.entries[added.numEntries] | |
added.numEntries++ | |
*dupe = *entry | |
added.byPid[pid] = dupe | |
added.byName[entry.exe()] = dupe | |
} | |
} | |
for pid, entry := range prev.byPid { | |
if cur.byPid[pid] == nil { | |
dupe := &removed.entries[removed.numEntries] | |
removed.numEntries++ | |
*dupe = *entry | |
removed.byPid[pid] = dupe | |
removed.byName[entry.exe()] = dupe | |
} | |
} | |
return added, removed | |
} | |
func main() { | |
prev := new(processTable) | |
prev.clear() | |
fillProcessTable(prev) | |
prev.display(os.Stdout, "Initial Process Table") | |
cur := new(processTable) | |
cur.clear() | |
for range time.Tick(time.Second) { | |
fillProcessTable(cur) | |
added, removed := delta(prev, cur) | |
if added.numEntries > 0 { | |
for pid, entry := range added.byPid { | |
fmt.Fprintf(os.Stdout, "+ %d %s\n", pid, entry.exe()) | |
} | |
} | |
if removed.numEntries > 0 { | |
for pid, entry := range removed.byPid { | |
fmt.Fprintf(os.Stdout, "- %d %s\n", pid, entry.exe()) | |
} | |
} | |
prev, cur = cur, prev | |
cur.clear() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment