【问题标题】:Process not suspending (NtSuspendProcess) -> What is going on?进程未挂起 (NtSuspendProcess) -> 发生了什么?
【发布时间】:2022-06-10 19:00:43
【问题描述】:

我已将 C# 中的 32 位内存扫描程序改编为 64 位。在扫描程序(阅读:实时程序)之前,应该暂停该进程。我此时正在使用 NtSuspendProcess 来尝试实现这一目标。有趣的是,我有这个程序(无法访问源代码),一旦打开文件,它就拒绝挂起。确定这是我的扫描仪中的一个错误,我尝试暂停程序,再次使用 Process Explorer 打开文件时;它非常短暂地闪烁“暂停”;我在 Suspend Count 下查看了与进程关联的线程,当我告诉 Process Explorer 暂停它时它会增加很好......但是当我告诉 Process Explorer 恢复进程时,它会增加 Suspend Count(你没看错,它会增加它)。

所以是的,使用我的内存扫描仪和 Process Explorer,当这个程序没有打开文件时,它会正常挂起并恢复。当程序打开文件时,它会挂起失败,并在尝试恢复时增加挂起计数。

我怀疑这里有很多事情。不知何故,要挂起的消息被复制,并在调用 resume 时被释放。这解释了暂停计数在应该递减时递增。这可能意味着原始的 Suspend 消息没有被正确使用。

从这一点开始,我什至该如何调试这个问题?我从哪里开始?

以下是我的内存扫描仪中的一些代码 sn-ps:

const uint PROCESS_SUSPEND_RESUME = 0x0800;
const uint PROCESS_QUERY_INFORMATION = 0x0400;
const uint MEM_COMMIT = 0x00001000;
const uint PAGE_READWRITE = 0x04;
const uint PROCESS_WM_READ = 0x0010;

[DllImport("ntdll.dll", EntryPoint = "NtSuspendProcess", SetLastError = true, ExactSpelling = false)]
private static extern UIntPtr NtSuspendProcess(UIntPtr processHandle);

[DllImport("ntdll.dll", EntryPoint = "NtResumeProcess", SetLastError = true, ExactSpelling = false)]
private static extern UIntPtr NtResumeProcess(UIntPtr processHandle);

[DllImport("kernel32.dll")]
public static extern UIntPtr OpenProcess(UIntPtr dwDesiredAccess, bool bInheritHandle, UIntPtr dwProcessId);

[DllImport("kernel32.dll")]
public static extern bool ReadProcessMemory(UIntPtr hProcess, UIntPtr lpBaseAddress, byte[] lpBuffer, UIntPtr dwSize, out UIntPtr lpNumberOfBytesRead);

[DllImport("kernel32.dll")]
static extern void GetSystemInfo(out SYSTEM_INFO lpSystemInfo);

[DllImport("kernel32.dll", SetLastError = true)]
static extern UIntPtr VirtualQueryEx(UIntPtr hProcess, UIntPtr lpAddress, out MEMORY_BASIC_INFORMATION64 lpBuffer, UIntPtr dwLength);

[DllImport("kernel32.dll")]
static extern bool CloseHandle(UIntPtr hObject);

private void Button_Extract_Click(object sender, EventArgs e)
{
    Process process = Process.GetProcessById(int.Parse(DataGridView_Processes.SelectedRows[0].Cells["Process ID"].Value.ToString()));
        UIntPtr processSuspendResumeHandle = OpenProcess(new UIntPtr(PROCESS_SUSPEND_RESUME), false, new UIntPtr((uint)process.Id));
        
        //process.Suspend();
        
        UIntPtr suspendreturnvalue = NtSuspendProcess(processSuspendResumeHandle);
        System.Diagnostics.Debug.WriteLine("Return Value: " + suspendreturnvalue.ToString());

        UIntPtr processHandle = OpenProcess(new UIntPtr(PROCESS_QUERY_INFORMATION | PROCESS_WM_READ), false, new UIntPtr((uint)process.Id));

        //int error = Marshal.GetLastWin32Error();
        //System.Diagnostics.Debug.WriteLine("Last Win32 Error: " + error);

    SYSTEM_INFO sys_info = new SYSTEM_INFO();

    GetSystemInfo(out sys_info);

    UIntPtr proc_min_address = sys_info.minimumApplicationAddress;
    UIntPtr proc_max_address = sys_info.maximumApplicationAddress;

    ulong proc_min_address_l = (ulong)proc_min_address;
    ulong proc_max_address_l = (ulong)proc_max_address;

    //Skip to end

    CloseHandle(processHandle);
    NtResumeProcess(processSuspendResumeHandle);
    CloseHandle(processSuspendResumeHandle);
    MessageBox.Show("Extraction Complete.");
}

【问题讨论】:

  • 检查这些winapi函数的返回值很关键,你不再有友好的异常来提醒你它们失败了。
  • 我添加了代码来获取最后一个 Win32 错误,它的返回值似乎是 0,这表明成功(暂停进程)。然而事实并非如此。
  • 检查函数的返回值,而不是GetLastWin32Error——你处理的是内部用户模式内核API,而不是它上面的Win32层。也检查一下:ntopcode.wordpress.com/tag/ntsuspendprocess
  • 嗯。假设我的方法声明正确,我仍然得到返回值 0。但我偶然发现了一些有趣的东西。到目前为止,我一直在使用 procexp64.exe(我在 64 位 Windows 10 上);但是,如果我使用 procexp.exe,程序会正确挂起。也许我应该补充一点,这个程序是一个 32 位程序;我当时认为这无关紧要。为什么 32 位版本的 Process Explorer 可以正确挂起 32 位程序,而 64 位版本却不能?
  • 需要这个 exe 文件的最低限度。 一旦打开文件 - 这意味着什么?

标签: c# windows ntdll


【解决方案1】:

执行以下操作:

  1. 确保您以管理员身份运行
  2. 检查OpenProcess的返回码应该返回一个非零值,如果不是则继续步骤3。
  3. 查看GetLastError的返回码,在这里搜索它的值https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-(这里只包含错误0-400所以检查侧边栏,有一个链接到包含其他错误代码的其他页面)
  4. 因为看起来你正在访问内存,所以你可能正在使用这个应用程序进行游戏作弊......所以请检查OpenProcessNtOpenProcessNtSuspendProcessNtResumeProcess 使用汇编程序等作为作弊引擎或 x64dbg。
  5. 转到我突出显示的功能的地址。检查前 5 个字节是否有 JMP 指令。如果有,则表示该功能已被防病毒软件或反作弊软件修补。
  6. 如果已打补丁,您可能想禁用防病毒软件,如果仍打补丁,则您反对反作弊。切换到 C++,使用 syswhispers2 使用直接系统调用,使用 Qt for C++,它是 C# Winforms 的更好替代品,它也有一个拖放式表单构建器。
  7. 如果没有任何可疑之处,则这些功能看起来没有打补丁。 GetLastError 调用返回正常状态。那么你可能会反对 rootkit 反作弊。在这种情况下,你有点受不了了。

【讨论】:

    猜你喜欢
    • 2011-05-17
    • 1970-01-01
    • 1970-01-01
    • 2019-03-22
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-26
    相关资源
    最近更新 更多