关于.net:C#中的挂起过程

关于.net:C#中的挂起过程

Suspend Process in C#

如何在C#中挂起整个流程(就像单击"挂起"时的Process Explorer一样)。

我正在使用Process.Start启动Process,并且在某个事件中,我想暂停该进程以便能够对其进行"快照"进行一些调查。


这是我的建议:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
 [Flags]
    public enum ThreadAccess : int
    {
      TERMINATE = (0x0001),
      SUSPEND_RESUME = (0x0002),
      GET_CONTEXT = (0x0008),
      SET_CONTEXT = (0x0010),
      SET_INFORMATION = (0x0020),
      QUERY_INFORMATION = (0x0040),
      SET_THREAD_TOKEN = (0x0080),
      IMPERSONATE = (0x0100),
      DIRECT_IMPERSONATION = (0x0200)
    }

    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
    [DllImport("kernel32.dll")]
    static extern uint SuspendThread(IntPtr hThread);
    [DllImport("kernel32.dll")]
    static extern int ResumeThread(IntPtr hThread);
    [DllImport("kernel32", CharSet = CharSet.Auto,SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);


private static void SuspendProcess(int pid)
{
  var process = Process.GetProcessById(pid); // throws exception if process does not exist

  foreach (ProcessThread pT in process.Threads)
  {
    IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

    if (pOpenThread == IntPtr.Zero)
    {
      continue;
    }

    SuspendThread(pOpenThread);

    CloseHandle(pOpenThread);
  }
}

public static void ResumeProcess(int pid)
{
  var process = Process.GetProcessById(pid);

  if (process.ProcessName == string.Empty)
    return;

  foreach (ProcessThread pT in process.Threads)
  {
    IntPtr pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)pT.Id);

    if (pOpenThread == IntPtr.Zero)
    {
      continue;
    }

    var suspendCount = 0;
    do
    {
      suspendCount = ResumeThread(pOpenThread);
    } while (suspendCount > 0);

    CloseHandle(pOpenThread);
  }
}

感谢马格努斯

包含Flags之后,我对代码进行了一些修改,使其成为项目中的扩展方法。我现在可以使用

1
2
var process = Process.GetProcessById(param.PId);
process.Suspend();

这是可能有兴趣的人的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static class ProcessExtension
{
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
    [DllImport("kernel32.dll")]
    static extern uint SuspendThread(IntPtr hThread);
    [DllImport("kernel32.dll")]
    static extern int ResumeThread(IntPtr hThread);

    public static void Suspend(this Process process)
    {
        foreach (ProcessThread thread in process.Threads)
        {
            var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
            if (pOpenThread == IntPtr.Zero)
            {
                break;
            }
            SuspendThread(pOpenThread);
        }
    }
    public static void Resume(this Process process)
    {
        foreach (ProcessThread thread in process.Threads)
        {
            var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id);
            if (pOpenThread == IntPtr.Zero)
            {
                break;
            }
            ResumeThread(pOpenThread);
        }
    }
}

我完成了一个实用程序,通常用来暂停/杀死/列出进程。完整的源代码在Git上


因此,实际上,其他答案显示的是正在挂起进程中的线程,没有办法真正挂起该进程(即在一个调用中)。

另一个不同的解决方案是实际调试您要启动的目标进程,请参见Mike Stall的博客,以获取有关如何从托管上下文实现此目标的一些建议。

如果实现调试器,则将能够扫描内存或您想要的其他快照。

但是,我想指出的是,从技术上讲,现在确实可以做到这一点。即使您确实调试了目标调试对象进程,系统上的另一个进程也可能会注入一个线程,并且将具有执行代码的能力,而与目标进程的状态无关(即使我们说它是否由于访问冲突而达到了断点) ),如果您已将所有线程的暂停数量都提高到超高的暂停数量,并且当前处于主进程线程的中断点,并且处于任何其他此类假定的冻结状态,则系统仍然有可能向该进程注入另一个线程并执行一些指令。您还可能会遇到修改或替换内核通常调用的所有入口点的麻烦,以此类推,但是现在您已经进入了恶意软件的竞争之列;)...

在任何情况下,使用托管接口进行调试似乎比p /调用许多本地API调用要容易得多,因为这样做会在模拟您可能真正想做的事情上做得很差。使用调试api ;)


有关win32的基础知识,请参见此CodeProject文章:http://www.codeproject.com/KB/threads/pausep.aspx。此示例代码利用了SDK中的ToolHelp32库,因此,我建议您使用一个简单的接口(例如" SuspendProcess(uint processID))"将此示例代码转换为不受管理的C ++ / CLI库。

Process.Start将返回一个Process对象,您可以从该对象获取流程ID,然后根据上述内容将其传递给您的新库。

戴夫


推荐阅读