[英]Returning from exe entry point does not terminate the process on Windows 10
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 秒。
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 秒,在调试器下运行时无限期运行。
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
。
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!CloseHandle
和user32!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;
}
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.
}
}
}
}
}
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 进程行为不正确。
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 function
从主function 返回
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.