在调用 Main() 之前 Windows 会做什么?

2024-03-16

Windows 必须做一些事情来解析 PE 标头,将可执行文件加载到内存中,并将命令行参数传递给main().

Using OllyDbg I have set the debugger to break on main() so I could view the call stack: http://puu.sh/t5vxB/3d52089d22.png

似乎符号丢失了,所以我们无法获取函数名称,只能获取所看到的内存地址。但是我们可以看到 main 的调用者是kernel32.767262C4,这是被调用者ntdll.77A90FD9。在堆栈底部,我们看到 RETURN tontdll.77A90FA4我认为这是第一个被调用来运行可执行文件的函数。传递给该函数的值得注意的参数似乎是 Windows 的结构化异常处理程序地址和可执行文件的入口点。

那么这些函数到底是如何将程序加载到内存中并为入口点执行做好准备的呢?是调试器显示之前操作系统执行的整个进程main()?


如果你打电话CreateProcess https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx系统内部调用ZwCreateThread[Ex] http://processhacker.sourceforge.net/doc/ntzwapi_8h.html#a8d4778b0d54da088821971642873ef51创建进程中的第一个线程

当你创建线程时 - 你(如果你直接调用ZwCreateThread http://processhacker.sourceforge.net/doc/ntzwapi_8h.html#a8d4778b0d54da088821971642873ef51)或系统初始化CONTEXT https://msdn.microsoft.com/en-us/library/windows/desktop/ms679284(v=vs.85).aspx新线程记录 - 此处Eip(i386) or Rip(amd64)线程的入口点。如果您这样做 - 您可以指定任何地址。但当你打电话说Create[Remote]Thread[Ex] https://msdn.microsoft.com/en-us/library/windows/desktop/dd405484(v=vs.85).aspx- 我怎么说 - 系统填充CONTEXT https://msdn.microsoft.com/en-us/library/windows/desktop/ms679284(v=vs.85).aspx并将自身例程设置为线程入口点。您的原始入口点保存在Eax(i386) or Rcx(amd64)登记。

该例程的名称取决于 Windows 版本。

早些时候这是BaseThreadStartThunk or BaseProcessStartThunk(如果从CreateProcess称为)来自kernel32.dll.

但现在系统指定RtlUserThreadStart from ntdll.dll . the RtlUserThreadStart通常打电话BaseThreadInitThunk from kernel32.dll(本机(启动执行)应用程序除外,例如smss.exe and chkdsk.exe其中没有kernel32.dll完全在自地址空间中)。BaseThreadInitThunk已经调用您的原始线程入口点,并且在(如果)它返回之后 -RtlExitUserThread https://msdn.microsoft.com/en-us/library/windows/desktop/ms682659(v=vs.85).aspx called.

enter image description here enter image description here the main goal of this common thread startup wrapper - set the top level SEH https://msdn.microsoft.com/en-us/library/windows/desktop/ms679270(v=vs.85).aspx filter. only because this we can call SetUnhandledExceptionFilter https://msdn.microsoft.com/en-us/library/windows/desktop/ms680634(v=vs.85).aspx function. if thread start direct from your entry point, without wrapper - the functional of Top level Exception Filter https://msdn.microsoft.com/en-us/library/windows/desktop/ms681401(v=vs.85).aspx become unavailable.

但无论线程入口点是什么——用户空间中的线程——NEVER从此时开始执行!

当用户模式线程开始执行时尽早 - 系统插入APCLdrInitializeThunk作为 Apc 例程 - 这是通过复制(保存)线程完成的CONTEXT到用户堆栈然后调用KiUserApcDispatcher哪个电话LdrInitializeThunk. when LdrInitializeThunk完成 - 我们回到KiUserApcDispatcher这叫NtContinue与保存的线程CONTEXT- 仅在该线程入口点开始执行之后。

但现在系统在这个过程中做了一些优化 - 它复制(保存)线程CONTEXT到用户堆栈并直接调用LdrInitializeThunk。在此函数的末尾NtContinue被调用 - 和正在执行的线程入口点。

so EVERY线程从用户模式开始执行LdrInitializeThunk. (这个函数的名称准确,在从 nt4 到 win10 的所有 Windows 版本中都存在并调用)

enter image description here enter image description here what is this function do ? for what is this ? you may be listen about DLL_THREAD_ATTACH https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx notification ? when new thread in process begin executed (with exception for special system worked threads, like LdrpWorkCallback)- he walk by loaded DLL list, and call DLLs entry points with DLL_THREAD_ATTACH https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx notification (of course if DLL have entry point and DisableThreadLibraryCalls https://msdn.microsoft.com/en-us/library/windows/desktop/ms682579(v=vs.85).aspx not called for this DLL). but how this is implemented ? thanks to LdrInitializeThunk which call LdrpInitialize -> LdrpInitializeThread -> LdrpCallInitRoutine (for DLLs EP)

enter image description here when the first thread in process start - this is special case. need do many extra jobs for process initialization. at this time only two modules loaded in process - EXE and ntdll.dll . LdrInitializeThunk call LdrpInitializeProcess for this job. if very briefly:

  1. 不同的进程结构被初始化

  2. 静态加载 EXE 的所有 DLL(及其依赖项) 链接 - 但不称它们为 EP!

  3. called LdrpDoDebuggerBreak- 这个函数看起来 - 是调试器 附加到流程,如果是 -int 3称为 - 所以调试器 接收异常消息 -STATUS_BREAKPOINT- 大多数调试器可以 UI 调试仅从此时开始。然而存在 调试器,它让调试过程从LdrInitializeThunk- 我所有的屏幕截图都来自这种调试器

  4. 重要的一点 - 直到进程中只执行代码ntdll.dll(并且可能来自kernel32.dll) - 来自另一个的代码 DLL,任何尚未在进程中执行的第三方代码。

  5. 可选加载的 shim dll 来处理 - Shim 引擎已初始化。但 这是可选的

  6. 遍历加载的 DLL 列表并调用其 EPDLL_PROCESS_DETACH https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583(v=vs.85).aspx

  7. TLS https://msdn.microsoft.com/en-us/library/windows/desktop/ms686749(v=vs.85).aspx调用初始化和 TLS 回调(如果存在)

  8. ZwTestAlert被调用 - 此调用检查线程中是否存在 APC 排队,并执行它。这一点在NT4到所有版本中都存在 win 10。例如,以挂起状态创建进程 然后插入 APC 调用(QueueUserAPC https://msdn.microsoft.com/en-us/library/windows/desktop/ms684954(v=vs.85).aspx)到它的线程 (PROCESS_INFORMATION.hThread) - 结果这个调用将是 进程完全初始化后执行,所有DLL_PROCESS_DETACH调用,但在 EXE 入口点之前。在上下文中 第一个进程线程。

  9. 最后调用 NtContinue - 此恢复保存的线程上下文 我们终于跳到线程 EP

enter image description here enter image description here read also Flow of CreateProcess https://web.archive.org/web/20151214014944/https://www.microsoft.com/mspress/books/sampchap/4354a.aspx

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在调用 Main() 之前 Windows 会做什么? 的相关文章

随机推荐