简体   繁体   English

为什么OpenProcess给了我一个非现有pid的非NULL HANDLE?

[英]Why OpenProcess Gives me a non-NULL HANDLE for a non existing pid?

I reduced the code as much as I could to keep it sscce What this code does: 我尽可能地减少代码以保持代码sscce这段代码的作用:

  • Launch windows notepad with CreateProcess 使用CreateProcess启动Windows记事本
  • Pause. 暂停。 So windows got some time to init notepad 所以Windows有一些时间来初始化记事本
  • Kill notepad with TerminateProcess on the Handle we got from CreateProcess . 使用我们从CreateProcess获得的句柄中的TerminateProcess杀死记事本。
  • Wait for the process handle to terminate, because TerminateProcess is async 等待进程句柄终止,因为TerminateProcess是异步的
  • Call OpenProcess on the dead pid 在死pid上调用OpenProcess
  • Play with my zombie... 玩我的僵尸......

You can easily check (before OpenProcess) in debug that the given Pid is nowhere to be found in either the task manager or processExplorer. 您可以在调试中轻松检查(在OpenProcess之前),在任务管理器或processExplorer中找不到给定的Pid。 But no other windows API functions seems to accept this Pid 但没有其他Windows API函数似乎接受这个Pid

#include "stdafx.h"
#include <windows.h>
#include "Psapi.h"
#include <TlHelp32.h>
#include <iostream>
#include <cassert>

typedef unsigned long PID;
typedef const std::string& P_PATH;

// Launch an executable given by a path and set a few infomatives stuffs passed as ref parameters
bool launch_process(P_PATH path, STARTUPINFO& info, PROCESS_INFORMATION& processInfo, HANDLE& hProcess, PID& pid)
{
    setlocale(LC_ALL, "en_US.utf8");
    std::wstring widestr = std::wstring(path.begin(), path.end());
    const wchar_t* widecstr = widestr.c_str();

    LPTSTR szCmdline = _tcsdup(widecstr);

    if (CreateProcess(szCmdline, NULL, NULL, NULL, false, NULL, NULL, NULL, &info, &processInfo))
    {
        hProcess = processInfo.hProcess;
        pid = processInfo.dwProcessId;
        return true;
    }
    return false;
}

int main(int argc, char* argv[])
{
    // Setup
    // -----
    bool OK = false;

    STARTUPINFO info = { sizeof(info) };
    PROCESS_INFORMATION processInfo;
    HANDLE hProcess;
    PID pid = 0;

    // launch notepad
    // --------------
    OK = launch_process("C:\\WINDOWS\\System32\\notepad.exe", info, processInfo, hProcess, pid);
    assert(OK);

    // wait a bit
    // ----------
    system("PAUSE");
    SetLastError(0);

    // get Handle on notepad
    // ---------------------
    hProcess = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, false, static_cast<DWORD>(pid));
    std::cout << "Error: " <<  GetLastError() << std::endl;

    // Kill notepad
    // ------------
    UINT exitCode = 0;
    DWORD dwWaitResult = 0;

    SetLastError(0);
    OK = TerminateProcess(hProcess, exitCode);
    std::cout << "Error: " << GetLastError() << std::endl;

    // ensure everything went well ( we need to wait because TerminateProcess is asynchronous )
    if (!OK || (dwWaitResult != WaitForSingleObject(hProcess, INFINITE)))
        return -1;

    // Cleanup
    CloseHandle(hProcess);
    CloseHandle(processInfo.hProcess);
    CloseHandle(processInfo.hThread);
    hProcess = nullptr;
    processInfo.hProcess = nullptr;

    // Ya know what let's reopen the pid I just killed 
    // ( you can see that notepad was started then killed, you can check the pid does not exist in either the task manager or processExplorer )
    SetLastError(0);
    hProcess = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, false, static_cast<DWORD>(pid));
    std::cout << "Error: " << GetLastError() << std::endl;

    // Yeah I can speak to the deads! 
    if (hProcess != nullptr)
    {
        LPWSTR szProcessName = L"";
        GetModuleFileNameEx(hProcess, NULL, szProcessName, sizeof szProcessName);

        char c_szText[MAX_PATH];
        wcstombs(c_szText, szProcessName, wcslen(szProcessName) + 1);

        // But the deads have no names
        std::cout << "Error: " << GetLastError() << std::endl;
        std::cout << "Zombie Name: " << c_szText << std::endl;
    }
    return 0;
}
  1. It's not appearing in the list of processes in Task Manager because it has terminated. 它没有出现在任务管理器的进程列表中,因为它已终止。 Task Manager doesn't show zombies (this behaviour differs from eg Linux ps -Af f which does show zombies). 任务管理器不显示僵尸(此行为不同于例如Linux ps -Af f确实显示僵尸)。

  2. It's still there and accessible to OpenProcess because the process object has not been destroyed, ie it is in the zombie state. 它仍然存在并且可以被OpenProcess访问,因为进程对象尚未被破坏,即它处于僵尸状态。 This will be because another process has an open handle to it. 这将是因为另一个进程有一个开放的句柄。 For example, 例如,

    • an antivirus, which may call OpenProcess to get information to assess if the application is malicious, or 一个防病毒软件,它可以调用OpenProcess来获取信息以评估应用程序是否是恶意的,或者
    • Process Monitor, which gets all sorts of information, probably some of which it calls OpenProcess to get, or 进程监视器,它获取各种信息,可能是其中一些调用OpenProcess来获取,或者
    • the DWM, which I imagine, but do not know, calls OpenProcess to get information on whether the application supports eg high DPI mode and so forth, or DWM,我想,但不知道,调用OpenProcess来获取有关应用程序是否支持高DPI模式等的信息,或者
    • any number of other applications may have called OpenProcess previously and have a still open handle. 任何数量的其他应用程序可能先前已调用OpenProcess并且仍具有仍处于打开状态的句柄。

Morals: 德:

  • Don't expect the process object to be destroyed as soon as you close your handle, because yours is not the only handle. 一旦关闭句柄,不要指望过程对象被销毁,因为你的句柄不是唯一的句柄。

  • Zombie state is a perfectly legitimate state for a process to be in. All processes will be in the zombie state if they have terminated, and someone is waiting to read their exit code with GetExitCodeProcess , or have any other interest in the state of the process. 僵尸状态对于进程来说是完全合法的状态。如果它们已经终止,所有进程将处于僵尸状态,并且有人正在等待使用GetExitCodeProcess读取其退出代码,或者对进程状态有任何其他兴趣。 Typically once the exit code has been read, interest in the process wanes, handles are closed, and finally the process will be destroyed. 通常,一旦读取了退出代码,对流程的兴趣就会消失,句柄会被关闭,最后流程将被销毁。

  • The PID can be reused once the process is destroyed. 一旦过程被破坏,PID就可以重复使用。 (Conversely, the PID cannot be reused until all handles are closed). (相反,在关闭所有句柄之前,不能重复使用PID)。 In Windows, the kernel prefers low numbered PIDs and will reuse them quite quickly. 在Windows中,内核更喜欢低编号的PID,并且可以非常快速地重用它们。 This behaviour differs from Linux which prefers incrementing PIDs and won't reuse them until the maximum value is reached. 此行为与Linux不同,后者更喜欢递增PID,并且在达到最大值之前不会重复使用它们。

  • If you want the handle to the process, don't close it. 如果您想要进程的句柄,请不要关闭它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM