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

1.先上代码

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]
", 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]
")
	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.再上结果

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:WINDOWSsystem32svchost.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:WINDOWSsystem32svchost.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:WINDOWSsystem32svchost.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:WINDOWSsystem32svchost.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的,但是那太简单了,还是不要献丑了。

原文地址:https://www.cnblogs.com/janbar/p/13698974.html