win32获取进程树,以及命令行参数

jan-bar · · 894 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

### 1.先上代码 ```go package main import ( "bytes" "errors" "flag" "fmt" "io" "os" "sort" "syscall" "unsafe" ) func main() { out := flag.String("o", "-", "write result to file, - eq stdout") flag.Parse() procMap := make(map[uint32]*Process, 32) err := ForEachProcessEntry(func(entry *syscall.ProcessEntry32) error { cmdline, err := GetCmdline(entry.ProcessID) if err != nil { return err } procMap[entry.ProcessID] = &Process{ Pid: entry.ProcessID, Ppid: entry.ParentProcessID, Name: syscall.UTF16ToString(entry.ExeFile[:]), Cmdline: cmdline, Children: make(map[uint32]*Process), } return nil }) if err != nil { fmt.Println("ForEachProcessEntry:", err) return } proc := make(ProcessSlice, 0, 32) for _, v := range procMap { if v.Pid == 0 { proc = append(proc, v) continue // 系统进程 } tmp, ok := procMap[v.Ppid] if ok { tmp.Children[v.Pid] = v } else { proc = append(proc, v) } } sort.Sort(proc) var fmtOut *os.File if *out == "-" { fmtOut = os.Stdout } else { fmtOut, err = os.Create(*out) if err != nil { fmt.Println("os.Create:", *out, ",error:", err) return } defer fmtOut.Close() } str := bytes.NewBufferString("%10d,%10d:") for _, v := range proc { fmt.Fprintf(fmtOut, "%10d,%10d: name:[%s], cmdline:[%s]\n", v.Pid, v.Ppid, v.Name, v.Cmdline) WriteChildren(fmtOut, v.Children, str, 1) } } func WriteChildren(w io.Writer, children map[uint32]*Process, strFmt *bytes.Buffer, layer int) { if len(children) == 0 { return } strFmt.Truncate(10 /* len("%10d,%10d:") */) for i := 0; i < layer*4; i++ { if i > 0 && i%4 == 0 { strFmt.WriteByte('|') } strFmt.WriteByte(' ') } strFmt.WriteString("\\_ name:[%s], cmdline:[%s]\n") fmtStr := strFmt.String() layer++ for _, v := range children { fmt.Fprintf(w, fmtStr, v.Pid, v.Ppid, v.Name, v.Cmdline) WriteChildren(w, v.Children, strFmt, layer) // 递归打印子进程 } } type ( Process struct { Pid, Ppid uint32 Name string Cmdline string Children map[uint32]*Process } ProcessSlice []*Process ) func (d ProcessSlice) Less(i, j int) bool { return d[i].Pid < d[j].Pid } func (d ProcessSlice) Swap(i, j int) { d[i], d[j] = d[j], d[i] } func (d ProcessSlice) Len() int { return len(d) } var ( ntQueryInformationProcess = syscall.MustLoadDLL("ntdll.dll").MustFindProc("NtQueryInformationProcess") readProcessMemory = syscall.MustLoadDLL("kernel32.dll").MustFindProc("ReadProcessMemory") ) func GetCmdline(pid uint32) (string, error) { /* 翻译这个C++代码: https://stackoverflow.com/a/42341811/11844632 */ if pid == 0 { // 系统进程,无法读取 return "", nil } const ( PROCESS_QUERY_INFORMATION = 0x0400 // 定义在winnt.h PROCESS_VM_READ = 0x0010 // 定义在winnt.h ProcessParameters = 32 // ntddk.h的头文件分析的指针偏移 CommandLine = 112 // winternl.h的头文件分析的指针偏移 ) h, err := syscall.OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, false, pid) if err != nil { sysErr, ok := err.(syscall.Errno) if ok && sysErr == syscall.ERROR_ACCESS_DENIED { return "", nil // 没权限,忽略这个进程 } return "", err } defer syscall.CloseHandle(h) var pbi struct { ExitStatus int PebBaseAddress int64 AffinityMask int64 BasePriority int UniqueProcessId int64 InheritedFromUniqueProcessId int64 } r0, _, _ := ntQueryInformationProcess.Call(uintptr(h), 0, uintptr(unsafe.Pointer(&pbi)), unsafe.Sizeof(pbi), 0) if r0 != 0 { return "", errors.New("ntQueryInformationProcess") } var rtlUserProcParamsAddress int64 r0, _, _ = readProcessMemory.Call(uintptr(h), uintptr(pbi.PebBaseAddress+ProcessParameters), uintptr(unsafe.Pointer(&rtlUserProcParamsAddress)), unsafe.Sizeof(rtlUserProcParamsAddress), 0, 0) if r0 == 0 { return "", errors.New("readProcessMemory rtlUserProcParamsAddress") } var commandLine struct { Length uint16 MaximumLength uint16 Buffer int64 } r0, _, _ = readProcessMemory.Call(uintptr(h), uintptr(rtlUserProcParamsAddress+CommandLine), uintptr(unsafe.Pointer(&commandLine)), unsafe.Sizeof(commandLine), 0, 0) if r0 == 0 { return "", errors.New("readProcessMemory commandLine") } commandLineContents := make([]uint16, commandLine.Length/2) r0, _, _ = readProcessMemory.Call(uintptr(h), uintptr(commandLine.Buffer), uintptr(unsafe.Pointer(&commandLineContents[0])), uintptr(commandLine.Length), 0, 0) if r0 == 0 { return "", errors.New("readProcessMemory commandLineContents") } return syscall.UTF16ToString(commandLineContents), nil } func ForEachProcessEntry(f func(*syscall.ProcessEntry32) error) error { snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) if err != nil { return err } defer syscall.CloseHandle(snapshot) var procEntry syscall.ProcessEntry32 procEntry.Size = uint32(unsafe.Sizeof(procEntry)) if err = syscall.Process32First(snapshot, &procEntry); err != nil { return err } for { if err = f(&procEntry); err != nil { return err } if syscall.Process32Next(snapshot, &procEntry) != nil { return nil } } } ``` ### 2.再上结果 ```c 1.下面是输出的部分结果,更下面的进程树有点私密,就不放了 0, 0: name:[[System Process]], cmdline:[] 4, 0: \_ name:[System], cmdline:[] 88, 4: | \_ name:[Registry], cmdline:[] 412, 4: | \_ name:[smss.exe], cmdline:[] 624, 616: name:[csrss.exe], cmdline:[] 716, 708: name:[csrss.exe], cmdline:[] 736, 616: name:[wininit.exe], cmdline:[] 864, 736: \_ name:[services.exe], cmdline:[] 3232, 864: | \_ name:[svchost.exe], cmdline:[] 1328, 864: | \_ name:[svchost.exe], cmdline:[] 3308, 864: | \_ name:[AutoUpdate.exe], cmdline:[] 4184, 864: | \_ name:[svchost.exe], cmdline:[] 1128, 864: | \_ name:[svchost.exe], cmdline:[] 7660, 864: | \_ name:[svchost.exe], cmdline:[C:\WINDOWS\system32\svchost.exe -k UnistackSvcGroup] 2952, 864: | \_ name:[svchost.exe], cmdline:[] 2368, 864: | \_ name:[svchost.exe], cmdline:[] 2172, 864: | \_ name:[svchost.exe], cmdline:[] 2652, 864: | \_ name:[svchost.exe], cmdline:[] 2920, 864: | \_ name:[svchost.exe], cmdline:[] 2700, 864: | \_ name:[svchost.exe], cmdline:[] 1796, 864: | \_ name:[svchost.exe], cmdline:[] 2236, 864: | \_ name:[svchost.exe], cmdline:[] 2328, 864: | \_ name:[svchost.exe], cmdline:[] 3180, 864: | \_ name:[svchost.exe], cmdline:[] 7500, 864: | \_ name:[svchost.exe], cmdline:[] 1924, 864: | \_ name:[svchost.exe], cmdline:[] 3152, 864: | \_ name:[svchost.exe], cmdline:[] 1624, 864: | \_ name:[nvvsvc.exe], cmdline:[] 8228, 864: | \_ name:[svchost.exe], cmdline:[] 13480, 864: | \_ name:[svchost.exe], cmdline:[] 1028, 864: | \_ name:[svchost.exe], cmdline:[] 2052, 864: | \_ name:[svchost.exe], cmdline:[C:\WINDOWS\system32\svchost.exe -k UnistackSvcGroup -s WpnUserService] 3128, 864: | \_ name:[svchost.exe], cmdline:[] 1468, 864: | \_ name:[svchost.exe], cmdline:[] 1680, 864: | \_ name:[svchost.exe], cmdline:[] 1044, 1680: | | \_ name:[sihost.exe], cmdline:[sihost.exe] 3856, 864: | \_ name:[svchost.exe], cmdline:[] 2960, 864: | \_ name:[svchost.exe], cmdline:[] 8136, 864: | \_ name:[SecurityHealthService.exe], cmdline:[] 5668, 864: | \_ name:[PresentationFontCache.exe], cmdline:[] 3008, 864: | \_ name:[svchost.exe], cmdline:[] 3028, 864: | \_ name:[svchost.exe], cmdline:[] 3108, 864: | \_ name:[svchost.exe], cmdline:[] 3400, 864: | \_ name:[svchost.exe], cmdline:[] 1424, 864: | \_ name:[svchost.exe], cmdline:[] 1808, 1424: | | \_ name:[ctfmon.exe], cmdline:[] 6100, 864: | \_ name:[svchost.exe], cmdline:[] 5688, 864: | \_ name:[svchost.exe], cmdline:[] 5372, 864: | \_ name:[svchost.exe], cmdline:[] 3144, 864: | \_ name:[svchost.exe], cmdline:[] 1444, 864: | \_ name:[svchost.exe], cmdline:[] 4036, 864: | \_ name:[svchost.exe], cmdline:[] 7880, 864: | \_ name:[SgrmBroker.exe], cmdline:[] 1528, 864: | \_ name:[svchost.exe], cmdline:[] 4296, 864: | \_ name:[svchost.exe], cmdline:[C:\WINDOWS\system32\svchost.exe -k UnistackSvcGroup -s CDPUserSvc] 3352, 864: | \_ name:[SUService.exe], cmdline:[] 2624, 864: | \_ name:[svchost.exe], cmdline:[] 2308, 864: | \_ name:[svchost.exe], cmdline:[] 5728, 864: | \_ name:[svchost.exe], cmdline:[] 2672, 864: | \_ name:[svchost.exe], cmdline:[] 1672, 864: | \_ name:[svchost.exe], cmdline:[] 2376, 864: | \_ name:[svchost.exe], cmdline:[] 8488, 864: | \_ name:[SunloginClient.exe], cmdline:[] 2224, 8488: | | \_ name:[SunloginClient.exe], cmdline:[] 2228, 864: | \_ name:[svchost.exe], cmdline:[] 2416, 864: | \_ name:[svchost.exe], cmdline:[] 1412, 864: | \_ name:[svchost.exe], cmdline:[] 948, 864: | \_ name:[svchost.exe], cmdline:[] 2804, 864: | \_ name:[svchost.exe], cmdline:[] 9128, 864: | \_ name:[svchost.exe], cmdline:[] 1916, 864: | \_ name:[svchost.exe], cmdline:[] 1484, 864: | \_ name:[svchost.exe], cmdline:[] 1192, 1484: | | \_ name:[taskhostw.exe], cmdline:[taskhostw.exe {222A245B-E637-4AE9-A93F-A59CA119A75E}] 7476, 1484: | | \_ name:[taskhostw.exe], cmdline:[] 968, 864: | \_ name:[svchost.exe], cmdline:[] 6600, 864: | \_ name:[svchost.exe], cmdline:[C:\WINDOWS\system32\svchost.exe -k ClipboardSvcGroup -p -s cbdhsvc] 1648, 864: | \_ name:[nvSCPAPISvr.exe], cmdline:[] 1404, 864: | \_ name:[svchost.exe], cmdline:[] 1724, 864: | \_ name:[svchost.exe], cmdline:[] 1228, 864: | \_ name:[svchost.exe], cmdline:[] 988, 864: | \_ name:[svchost.exe], cmdline:[] 5056, 864: | \_ name:[svchost.exe], cmdline:[] 3640, 864: | \_ name:[svchost.exe], cmdline:[] 2096, 864: | \_ name:[svchost.exe], cmdline:[] 2536, 864: | \_ name:[svchost.exe], cmdline:[] 2332, 864: | \_ name:[igfxCUIService.exe], cmdline:[] 4740, 864: | \_ name:[svchost.exe], cmdline:[] 3880, 864: | \_ name:[svchost.exe], cmdline:[] 3136, 864: | \_ name:[svchost.exe], cmdline:[] 1584, 864: | \_ name:[svchost.exe], cmdline:[] 2692, 864: | \_ name:[svchost.exe], cmdline:[] 1008, 864: | \_ name:[svchost.exe], cmdline:[] 2.为了验证结果正确性,检查了结果进程数量,完全正确,tasklist会把自己算进去,所以会多一个 # tasklist /nh | find /v /c "" 187 # .\proc /nh | find /v /c "" 186 ``` ### 3.做个总结 偶尔看到一个帖子,有人问这个,就研究了一下下。发现其实也不难,不过方法确实是百度搜不到的。但还是被我搜到了。所有就做了个例子供大家参考。 也想过做个Linux的,但是那太简单了,还是不要献丑了。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

894 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传