【发布时间】: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 文件的最低限度。 一旦打开文件 - 这意味着什么?