通过指定/GS-
编译器标志和/NoDefaultLib
链接器标志,并命名main函数,我使用Microsoft Visual Studio创建了一个最小的,无CRT,依赖关系耗尽的可执行文件mainCRTStartup
。该应用程序不会创建其他线程,并且会mainCRTStartup
在<5秒后返回,但是终止该过程总共需要30秒。
根据我的经验,如果在Windows 10上执行的应用程序仅依赖于默认情况下加载到每个Windows进程中的动态库,则命名为ntdll.dll
,KernelBase.dll
并且kernel32.dll
当主线程从该mainCRTStartup
函数返回时,该进程会正常退出。
如果以静态或动态方式(通过调用LoadLibraryW
)加载了其他库,则从main函数返回将使该进程保持活动状态:正常运行30秒,在调试器下无限期运行。
在创建进程时,Windows 10进程加载器会创建其他线程以更快地加载动态库,请参阅:
Cylance在Windows 10并行加载细分中提到:
工作线程空闲超时设置为30秒。在30秒以内执行的程序由于
ntdll!TppWorkerThreadwaiting
进程终止前的空闲超时而似乎挂起。
请注意,如果进程的主线程返回,则C运行时库(CRT)的某些实现将调用ExitProcess。
另一方面,Microsoft在ExitProcess
:
请注意,从应用程序的主要功能返回会导致对的调用
ExitProcess
。
这是我使用过的最低限度的测试代码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;
}
在mainCRTStartup
函数中注释WinAPI的用法将导致上述WinAPI调用的执行时间。
这是在伪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.
}
}
}
}
}
我希望该过程如果不创建其他线程并从主函数返回,则将终止。
ExitProcess
即使调用WinAPI导致在30秒之前执行,在main函数末尾的调用也会终止该过程。不一定总是可以使用此API,因为有问题的应用程序可能不是我的,而是像这样的第三方应用程序(来自Microsoft):为什么进程会在RtlExitUserProcess / LdrpDrainWorkQueue中挂起?
在我看来,即使Microsoft进程的行为也不正常,Windows 10进程加载器也已损坏。
我希望该过程如果不创建其他线程并从主函数返回,则将终止。
进程可以隐式创建其他线程。例如装载机。需要了解什么意思
从主函数返回
这里是指从标准CRTmainCRTStartup
功能调用的功能。在这个mainCRTStartup
电话之后ExitProcess
。因此,不是任何exe入口真正的入口点函数,而是从入口点调用的某些子函数。但是入口点调用ExitProcess
比。
如果我们不使用CRT,则需要给ExitProcess
自己打电话。如果我们只是从入口点返回-将是RtlExitUserThread
哪个未调用,ExitProcess
除了这是进程(AmILastThread
)中的最后一个线程(如果在并行调用中有2个或更多线程,在这里也可以是竞速ExitThread
)
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句