简体   繁体   English

从 exe 入口点返回不会终止 Windows 10 上的进程

[英]Returning from exe entry point does not terminate the process on Windows 10

My attempt我的尝试

I created a minimal, CRT-free, dependency-depleted executable with Microsoft Visual Studio by specifying the /GS- compiler flag and the /NoDefaultLib linker flag, and naming the main function mainCRTStartup .通过指定/GS-编译器标志和/NoDefaultLib linker 标志,并命名主要 function mainCRTStartup ,我使用 Microsoft Visual Studio 创建了一个最小的、无 CRT、依赖项耗尽的可执行文件。 The application does not create additional threads and returns from mainCRTStartup after < 5 seconds, but it takes 30 seconds in total for the process to terminate.应用程序不会创建额外的线程并在 < 5 秒后从mainCRTStartup返回,但进程终止总共需要 30 秒。

Problem description问题描述

From my experience, if an application, executed on Windows 10, only depends on dynamic libraries that are loaded by default into every Windows process, namingly ntdll.dll , KernelBase.dll and kernel32.dll , the process exits normally when the main thread returns from the mainCRTStartup function. From my experience, if an application, executed on Windows 10, only depends on dynamic libraries that are loaded by default into every Windows process, namingly ntdll.dll , KernelBase.dll and kernel32.dll , the process exits normally when the main thread returns从主mainCRTStartup

If other libraries are loaded, statically or dynamically (fe by calling LoadLibraryW ), returning from the main function will leave the process alive: for 30 seconds when run normally and indefinitely when run under a debugger.如果静态或动态加载其他库(通过调用LoadLibraryW进行),从主 function 返回将使进程保持活动状态:正常运行时保持 30 秒,在调试器下运行时无限期运行。

Context语境

On process creation, the Windows 10 process loader creates additional threads to load dynamic libraries faster, see:在进程创建时,Windows 10 进程加载器会创建额外的线程以更快地加载动态库,请参阅:

Cylance mentions in Windows 10 Parallel Loading Breakdown : Cylance 在Windows 10 Parallel Loading Breakdown中提到:

The worker thread idle timeout is set to 30 seconds.工作线程空闲超时设置为 30 秒。 Programs which execute in less than 30 seconds will appear to hang due to ntdll!TppWorkerThreadwaiting for the idle timeout before the process terminates.由于ntdll!TppWorkerThreadwaiting在进程终止前等待空闲超时,在不到 30 秒内执行的程序将出现挂起。

Microsoft mentions in Terminating a Process: How Processes are Terminated :微软在终止进程:如何终止进程中提到:

Note that some implementation of the C run-time library (CRT) call ExitProcess if the primary thread of the process returns.请注意,如果进程的主线程返回,则 C 运行时库 (CRT) 的某些实现会调用 ExitProcess。

On the other hand, Microsoft mentions in ExitProcess :另一方面,微软在ExitProcess中提到:

Note that returning from the main function of an application results in a call to ExitProcess .请注意,从应用程序的主 function 返回会导致调用ExitProcess

Test code测试代码

This is the minimal test code I worked with, I used kernel32!CloseHandle and user32!CloseWindow as examples, the call to them does not actually do anything:这是我使用的最小测试代码,我使用kernel32!CloseHandleuser32!CloseWindow作为示例,对它们的调用实际上并没有做任何事情:

#include <cstdint>

namespace windows {
    typedef const intptr_t Handle;
    typedef const void *   Module;

    constexpr Handle InvalidHandleValue = -1;

    namespace kernel32 {
        extern "C" uint32_t __stdcall CloseHandle(Handle);
        extern "C" uint32_t __stdcall FreeLibrary(Module);
        extern "C" Module   __stdcall LoadLibraryW(const wchar_t *);
    }

    namespace user32 {
        extern "C" uint32_t __stdcall CloseWindow(Handle);
    }
}

int mainCRTStartup() {
    // 0 seconds
    // windows::kernel32::CloseHandle(windows::InvalidHandleValue);

    // 30 seconds
    // windows::user32::CloseWindow(windows::InvalidHandleValue);

    // 0 seconds
    // windows::kernel32::FreeLibrary(windows::kernel32::LoadLibraryW(L"kernel32.dll"));

    // 30 seconds
    // windows::kernel32::FreeLibrary(windows::kernel32::LoadLibraryW(L"user32.dll"));

    // 0 seconds
    // windows::kernel32::FreeLibrary(windows::kernel32::LoadLibraryW(L""));

    return 0;
}

Debugging调试

Commenting in the WinAPI usage in the the mainCRTStartup function results in execution times mentioned above the respective WinAPI call.mainCRTStartup function 中注释 WinAPI 用法会导致上述相应 WinAPI 调用的执行时间。

This is the execution flow of the program traced in a debugger in pseudo C++:这是在伪 C++ 中的调试器中跟踪的程序的执行流程:

ntdll.RtlUserThreadStart() {
    kernel32.BaseThreadInitThunk() {
        const auto return_code = test.mainCRTStartup();

        ntdll.RtlExitUserThread(return_code) {
            if (ntdll.NtQueryInformationThread(CURRENT_THREAD, ThreadAmILastThread) != STATUS_SUCCESS || !AmILastThread) {
                // Bad path - for `30 seconds`.

                ntdll.LdrShutdownThread();
                ntdll.TpCheckTerminateWorker(0);
                ntdll.NtTerminateThread(0, return_code);

                // The thread execution does not return from `NtTerminateThread`, but the process still runs.
            } else {
                // Good path - for `0 seconds`.

                ntdll.RtlExitUserProcess(return_code) {
                    ntdll.EtwpShutdownPrivateLoggers();
                    ntdll.LdrpDrainWorkQueue(0);
                    ntdll.LdrpAcquireLoaderLock();
                    ntdll.RtlEnterCriticalSection(ntdll.FastPebLock);
                    ntdll.RtlLockHeap(peb.ProcessHeap);
                    ntdll.NtTerminateProcess(0, return_code);
                    ntdll.RtlUnlockProcessHeapOnProcessTerminate();
                    ntdll.RtlLeaveCriticalSection(ntdll.FastPebLock);
                    ntdll.RtlReportSilentProcessExit(CURRENT_PROCESS, return_code);
                    ntdll.LdrShutdownProcess();
                    ntdll.NtTerminateProcess(CURRENT_PROCESS, return_code);

                    // The thread execution does not return from `NtTerminateProcess` and the process is terminated.
                }
            }
        }
    }
}

Expected results预期成绩

I expected the process to terminate if it does not create additional threads and returns from the main function.如果它没有创建额外的线程并从主 function 返回,我预计该进程将终止。

Calling ExitProcess at the end of the main function terminates the process, even if WinAPI is called which resulted in 30 seconds execution before.在主 function 结束时调用ExitProcess会终止进程,即使调用 WinAPI 会导致之前执行 30 秒。 Using this API is not always possible, because the problematic application might not be mine, but a 3rd party application (from Microsoft ) like here: Why would a process hang within RtlExitUserProcess/LdrpDrainWorkQueue?使用此 API 并不总是可行的,因为有问题的应用程序可能不是我的,而是第 3 方应用程序(来自Microsoft ),如下所示: Why would a process hang within RtlExitUserProcess/LdrpDrainWorkQueue?

It seems to me that the Windows 10 process loader is broken, if even Microsoft processes behave incorrectly.在我看来,Windows 10 进程加载器已损坏,即使 Microsoft 进程行为不正确。

  1. Is there a clean solution to this problem?这个问题有干净的解决方案吗?
  2. What are those loader threads needed for, if the last user created thread exits?如果最后一个用户创建的线程退出,这些加载器线程需要什么? AFAIK it is impossible at this point to load any other libraries. AFAIK 此时无法加载任何其他库。

I expected the process to terminate if it does not create additional threads and returns from the main function.如果它没有创建额外的线程并从主 function 返回,我预计该进程将终止。

process can implicit create additional threads.进程可以隐式创建额外的线程。 loader for example.以装载机为例。 and need understanding what mean并且需要理解是什么意思

returns from the main functionfunction 返回

here mean function which called from standard CRT mainCRTStartup function.这里的意思是 function 从标准 CRT mainCRTStartup function 调用。 after this mainCRTStartup call ExitProcess .在此mainCRTStartup调用ExitProcess之后。 so not any exe entry real entry point function but some sub-function called from entry point.所以不是任何exe 入口真正的入口点 function 而是从入口点调用的一些子函数。 but entry point call ExitProcess than.但是入口点调用ExitProcess比。

if we not use CRT - we need call ExitProcess yourself.如果我们不使用 CRT - 我们需要自己调用ExitProcess if we simply return from from entry point - will be RtlExitUserThread which not call ExitProcess except this is last thread in process ( AmILastThread ) (and here also can be race if 2 or more threads in parallel call ExitThread )如果我们只是从入口点返回 - 将是RtlExitUserThread ,它不会调用ExitProcess ,除非这是进程中的最后一个线程( AmILastThread )(如果 2 个或更多线程并行调用ExitThread ,这里也可能是比赛)

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

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