【问题标题】:Should an application calling MessageBoxA() always terminate by ExitProcess() instead of normal function epilogue? [duplicate]调用 MessageBoxA() 的应用程序是否应该总是由 ExitProcess() 而不是正常的函数结尾终止? [复制]
【发布时间】:2016-11-03 18:30:57
【问题描述】:

调用 MessageBoxA() 的应用程序是否应该总是由 ExitProcess() 而不是正常的函数结尾终止?

我无法理解为什么在 Windows 10 x64 上执行 ret 指令后,下面的“MessageBox”程序会挂起。

在 GDB 下调试程序,似乎 Win32 函数NtUserSetCursor 是该行为的原因,即执行ret 0x4 指令后没有任何反应。

0x779c2440 in ?? ()
=> 0x779c2440:  ff 25 18 12 a5 77       jmp    DWORD PTR ds:0x77a51218
(gdb)
0x6c417000 in ?? ()
=> 0x6c417000:  ea 09 70 41 6c 33 00    jmp    0x33:0x6c417009
(gdb)
0x7476262c in win32u!NtUserSetCursor () from C:\WINDOWS\SysWoW64\win32u.dll
=> 0x7476262c <win32u!NtUserSetCursor+12>:      c2 04 00        ret    0x4

但是,如果我使用ExitProcess 退出程序,则没有问题。

使用MessageBoxA 函数的应用程序是否应该总是以ExitProcess 终止?

程序:

BITS 32


extern _MessageBoxA@16

section .data

_szTitle: db "This is a test", 0x00
_szMsg: db "Hello World!", 0x00

section .text

global _start
_start:
    push ebp
    mov ebp, esp

    push 0x00
    push _szTitle
    push _szMsg
    push 0x00

    call _MessageBoxA@16

    leave
    ret

组装和链接:

C:\Users\nlykkei\Desktop>nasm -f win32 test.asm

C:\Users\nlykkei\Desktop>ld -o test.exe test.obj "C:\dev\lib\User32.Lib" --entry _start

【问题讨论】:

  • 请不要转发问题。如果您想更改某些内容,请使用 edit 链接。
  • Hmya,谁曾期望 MessageBox() 创建另一个线程?或者,如果他使用 MessageBox(),即使 Win10 仍然使用线程池加载 DLL,它也能正常工作。这个问题与线程无关。这个问题更令人不快的副作用是它使现有程序无法正常运行。就像我链接到的那个 .NET 程序一样。
  • @Shuzheng,你的程序应该总是直接或间接调用ExitProcess。如果你使用微软的 C 运行时库,你可以通过从 main 函数中退出来间接调用 ExitProcess。在 .NET 中,您可以通过退出所有前台线程来间接调用 ExitProcess。但是如果你是用汇编程序编写的,并且没有在 C 运行时中显式链接,你必须自己做。
  • ExitProcess() 应该 可能会被调用,因为鉴于 Windows 10 中的“新行为”,它是最安全的全方位解决方案。话虽如此,我仍然会争论正如@HansPassant 指出的那样,这种行为确实是一个错误,除非有人可以显示说明本机非 C 应用程序必须使用 ExitProcess() 退出的文档。 Windows 加载程序应该清楚地坚持以前的操作系统版本中存在的“更安全”的行为,允许在两种情况下正确清理进程。
  • @byteptr: this has been happening since at least 2007, Windows 10 只是让它更常见。另见If you return from the main thread, does the process exit?。如果他们的语言不适合他们,程序员肯定会调用 ExitProcess。

标签: windows debugging winapi assembly x86


【解决方案1】:

应用程序在三种情况下终止:

  1. 所有线程结束
  2. 应用程序调用 ExitProcess
  3. 有人打电话给 TerminateProcess

大约 1 个案例 - 经常在您的应用程序中通过自行创建的工作线程打开窗口。这很大程度上取决于你在做什么和 Windows 版本。因此,即使您有单线程应用程序 - 您也无法确定您的进程中没有其他线程启动。在您的情况下,您在 MessageBoxA 之后返回到 kernel32.dll 代码(比如 win 8.1 中的 BaseThreadInitThunk),它调用 ExitThread。但不是退出进程。所以很有可能您的应用程序没有终止。我们总是必须调用 ExitProcess。如果我们使用 c/c++ crt - 当我们从 main 或 WinMain 返回时称为 ExitProcess 的运行时。你不使用 crt - 所以必须自己直接调用 ExitProcess

-------------- 编辑 ----------------------------- --

我做了一些研究 om win 10 (1607)。起初现在ntdll在初始化阶段调用EtwEventRegister(UserDiagnosticGuid, UserDiagnosticProviderCallback);

在内部调用 TpAllocWait 和 2 个工作线程 (TppWorkerThread) 已创建。
所以每一个,即使是单线程设计的应用程序也开始使用 3 个线程(2 个工作线程在 ZwWaitForWorkViaWorkerFactoryWrQueue KWAIT_REASON 中等待)

现在 LoadLibrary 有时也会创建工作线程。 LdrpMapAndSnapDependency -&gt; LdrpQueueWork -&gt; TpPostWork(我猜当存在循环模块依赖时——比如user32 &lt;-&gt; gdi32)。

当 MessageBox 退出时 - ole32.dll 在进程中加载​​并创建工作线程 - 这里调用堆栈:

结果,在 MessageBox 返回后 - 我们有 2 个线程在处理中 - 你和正在工作的线程,它调用 LdrpWorkCallback 然后在退出前在 ZwWaitForWorkViaWorkerFactory 等待近 1 分钟

_Start -ExitThread(但不是ExitProcess)返回时的结果被调用。但进程未终止 - 另一个线程存在于此进程中。 并且晚了约 1 分钟 - 当工作线程退出时 - 进程最终终止

【讨论】:

  • 感谢您的出色回答。您是否有我可以阅读的资料,或者您的知识来自个人经验?
  • @Shuzheng msdn.microsoft.com/en-us/library/windows/desktop/… - 在你的情况下,你假设你的线程 - 单进程 - “进程的最后一个线程终止” - 但这可能不是真的。 kernel32 从您的 EP 返回时调用 ExitThread 但不是 ExitProcess。所以结果取决于 - 你是最后一个线程吗
  • 什么是“EP”? :-)
  • @Shuzheng - EP - 我的意思是入口点。 _从你的代码开始
  • 你能说说为什么 GDB 会在 NtUserSetCursor 中停止吗?
猜你喜欢
  • 1970-01-01
  • 2018-10-03
  • 2021-10-11
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2014-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多