【问题标题】:TerminateProcess not suceeding on Windows 10TerminateProcess 在 Windows 10 上不成功
【发布时间】:2016-05-03 23:47:10
【问题描述】:

我们的 C++ 应用程序使用 CreateProcess 启动一个单独的 .exe(它可以启动自己的子进程),如下所示。

BOOL started = ::CreateProcess(NULL,      // application
                p,         // parameters
                NULL,        // process security
                NULL,        // thread security
                TRUE,        // inherit handles flag
                0,           // flags
                NULL,        // inherit environment
                dirLP,      // inherit directory
                &startup,    // STARTUPINFO
                &procinfo);  // PROCESS_INFORMATIO

如果我们需要取消“作业”,我们使用 CreateToolhelp32Snapshot 遍历进程列表以查找我们启动的进程的任何子进程。

static BOOL TerminateProcessTree (HANDLE parentProcess,UINT exitCode) 
{ 
    BOOL            result=TRUE;
    HANDLE         hProcessSnap = NULL; 
    PROCESSENTRY32 pe32      = {0}; 
    //  Take a snapshot of all processes in the system. 

    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

    if (hProcessSnap == INVALID_HANDLE_VALUE) 
        return (FALSE); 
     pe32.dwSize = sizeof(PROCESSENTRY32); 

    //  Walk the snapshot of the processes
    DWORD parentID=GetProcessId(parentProcess);
    if(parentID==0){
        PrintLastError("GetProcessId");
        return FALSE;
    }
   if (Process32First(hProcessSnap, &pe32)) { 
        do{ 
            if(pe32.th32ParentProcessID==parentID){

               HANDLE hProcess = OpenProcess (PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, pe32.th32ProcessID); 
               if(hProcess!=NULL){
                    BOOL terminateChildren=TerminateProcessTree(hProcess,exitCode);
                    BOOL terminatedChild = TerminateProcess(hProcess, exitCode);
                    if (!terminatedChild){
                        PrintLastError("TerminateProcess");
                    }
                    CloseHandle(hProcess);
                    if(!terminatedChild || !terminateChildren){
                        result=FALSE;
                        break;
                    }
               } else{
                   PrintLastError("OpenProcess");
               }
            }

        }while (Process32Next(hProcessSnap, &pe32)); 
    }
    CloseHandle (hProcessSnap); 
    DWORD checkCode=0;
    BOOL terminated;
    if(GetExitCodeProcess(parentProcess,&checkCode) && checkCode==STILL_ACTIVE){
        terminated=TerminateProcess(parentProcess,exitCode);
        if (!terminated){
            PrintLastError("TerminateProcess");
            result= FALSE;
        }
    }
    return result;
}

如前所述,这在 Windows 7 上运行良好。Windows 10 在第一次调用“TerminateProcess”时出现“拒绝访问”失败。显然,Windows 安全模型在进程方面发生了一些变化。

【问题讨论】:

  • 您是否验证OpenProcess 成功,提供有效句柄?
  • 这似乎是一种非常复杂的方式来终止您创建的进程。为什么不直接使用PROCESS_INFORMATION 结构中返回的进程句柄CreateProcess
  • @MrEricSir 他说 CrateProcess 是由终止进程的子进程调用的。
  • 要详细说明 Alf 的评论,请查看 MSDN 上的Job Objects:“进程与作业关联后,默认情况下,它使用 CreateProcess 创建的任何子进程也与作业相关联." 所以你可以使用CreateJobObject()AssignProcessToJobObject() 将初始进程与作业相关联,然后让它创建它想要的任何子进程,然后你可以使用TerminateJobObject() 终止当前关联的所有进程工作。
  • 显然我不是 100% 清楚我的问题。我正在创建一个“可能”也启动一些(子)进程的进程。我掌握了“顶级”流程。稍后,只有顶级进程的句柄,我需要杀死“进程树”。这可以通过枚举进程并定位该树的成员并调用 TerminateProcess 来完成。这在 W7 上运行良好。在 W10 上,TerminateProcess 失败并显示“拒绝访问”。我意识到一个解决方案是采用“作业对象”,但我试图了解 Windows 10 安全模型的哪些部分发生了变化

标签: c++ windows process


【解决方案1】:

处理控制对象的进程树的可靠方法是使用注释线程中所述的 Job 对象。

要记住的一点是,一个进程只能属于一个作业,并且默认情况下,操作系统确定需要 appcompat 帮助的任何 EXE 都会放入由“程序兼容性助手”自动管理的作业对象中。这使得使用 Job 对象来管理任意 3rd 方 EXE 有点复杂(即 AssignProcessToJobObject 这些进程失败)。

如果您使用CreateProcess,您可以使用标志CREATE_BREAKAWAY_FROM_JOB。见this blog post。只要目标 EXE 与调用对象具有相同的权限,这就可以正常工作。

要让标准用户 EXE 运行可能需要管理员权限的 EXE(即它们包含将其标记为 requireAdministrator 的清单),您必须使用 ShellExecuteShellExecuteEx 在此调用 CreateProcess案件将失败。如果您的目标 EXE 都使用正确的 manifest elements,那么它不会被放入 PCA 作业对象中。您可以使用传递 SEE_MASK_FLAG_NO_UI 的技巧,这将产生避免 PCA 作业行为的副作用。如果您要启动任意 3rd 方 EXE,您应该使用 ShellExecuteEx 而不是 CreateProcess

bool SpawnProcess( const WCHAR* szExePath, const WCHAR* szExeArgs )
{
    if( !szExePath )
        return false;

    // NOTE: szExeArgs can be nullptr.

    // Get working directory from executable path.    
    WCHAR szDirectory[MAX_PATH] = {0};
    wcscpy_s( szDirectory, szExePath );
    PathRemoveFileSpec( szDirectory );

    // ShellExecute or ShellExecuteEx must be used instead of CreateProcess
    // to permit the shell to display a UAC prompt asking for consent to
    // elevate when the target executable's manifest specifies a run level
    // of "requireAdministrator".
    //
    // You can only use CreateProcess if you know that the application you
    // are spawning will be at the same run level as the current process.
    // Otherwise, you will receive ERROR_ACCESS_DENIED if the elevation
    // consent could not be obtained.
    SHELLEXECUTEINFO info = {};
    info.cbSize = sizeof( info );
    info.lpVerb = L"open";
    info.fMask = SEE_MASK_FLAG_NO_UI;
    info.lpFile = szExePath;
    info.lpParameters = szExeArgs;
    info.lpDirectory = szDirectory;
    info.nShow = SW_SHOW;
    if( !ShellExecuteEx( &info ) )
        return false;

    return true;
}

注意,如果你想等到这个进程退出,你可以使用:

bool SpawnProcessAndWait( const WCHAR *szExePath, const WCHAR *szExeArgs, DWORD *pdwExitCode )
{
    if( !szExePath )
        return false;

    // NOTE: szExeArgs and pExitCode can be nullptr.

    // Get working directory from executable path.    
    WCHAR szDirectory[MAX_PATH] = {0};
    wcscpy_s( szDirectory, szExePath );
    PathRemoveFileSpec( szDirectory );

    // See SpawnProcess for information why ShellExecute or ShellExecuteEx
    // must be used instead of CreateProcess.
    SHELLEXECUTEINFO info = {};
    info.cbSize = sizeof( info );
    info.lpVerb = L"open";
    info.fMask = SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC | SEE_MASK_NOCLOSEPROCESS;
    info.lpFile = szExePath;
    info.lpParameters = szExeArgs;
    info.lpDirectory = szDirectory;
    info.nShow = SW_SHOW;
    if( !ShellExecuteEx( &info ) )
        return false;

    // Wait for process to finish.
    WaitForSingleObject( info.hProcess, INFINITE );

    // Return exit code from process, if requested by caller.
    if( pdwExitCode )
        GetExitCodeProcess( info.hProcess, pdwExitCode );

    CloseHandle( info.hProcess );
    return true;
}

您没有注意到您的“主”应用程序是使用管理员权限还是标准用户权限。理想情况下,它使用可追溯到 Windows Vista 的 User Account Control 准则的标准用户权限。如果您的应用以标准用户身份运行,那么您的代码很可能由于安全性改进而不再工作,以防止非管理员恶意软件对其他进程执行此类操作。

对于有关 Windows 10 的一般 appcompat 问题,如果您直接从 Windows 7 跳转到 Windows 10,请务必阅读 Windows and Windows Server compatibility cookbook 并回顾 Windows 8.0 和 Windows 8.1 材料。

【讨论】:

  • 一些人提到的解决方案是使用 Jobs API 并将我创建的进程分配给一个作业,然后终止该作业。
猜你喜欢
  • 2011-06-04
  • 2018-09-08
  • 2021-12-26
  • 2021-12-19
  • 2018-08-21
  • 1970-01-01
  • 2018-10-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多