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

时间:2016-12-30 06:55:59

标签: windows assembly x86 portable-executable

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

使用OllyDbg我已将调试器设置为在main()上中断,以便我可以查看调用堆栈: http://puu.sh/t5vxB/3d52089d22.png

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

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

1 个答案:

答案 0 :(得分:8)

如果您在内部致电CreateProcess系统,请致电ZwCreateThread[Ex]以创建正在处理的第一个主题

当你创建线程时 - 你(如果你直接调用ZwCreateThread)或系统初始化新线程的CONTEXT记录 - 这里Eip(i386)Rip(amd64)的入口点线。如果你这样做 - 你可以指定任何地址。但是当你打电话说Create[Remote]Thread[Ex] - 我怎么说 - 系统填充CONTEXT并将自编程设置为线程入口点。您的原始入口点保存在Eax(i386)Rcx(amd64)注册表中。

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

早期BaseThreadStartThunkBaseProcessStartThunk(来自CreateProcess来自kernel32.dll)来自RtlUserThreadStart

但现在系统从ntdll.dll指定RtlUserThreadStartBaseThreadInitThunk通常会从kernel32.dll调用smss.exe(本机(启动执行)应用程序除外,例如chkdsk.exekernel32.dll,其自身没有BaseThreadInitThunk地址空间)。 APC已经调用了原始的线程入口点,并在(if)之后返回 - RtlExitUserThread被调用。

enter image description here enter image description here 这个常见线程启动包装器的主要目标 - 设置顶级SEH过滤器。只是因为我们可以调用SetUnhandledExceptionFilter函数。如果线程从您的入口点直接开始,没有包装器 - Top level Exception Filter的功能变得不可用。

但无论线程入口点 - 用户空间中的线程 - 从不从这一点开始执行!

用户模式线程开始执行时的早期 - 系统插入LdrInitializeThunkCONTEXT作为Apc例程进行线程 - 这是通过将({save)线程KiUserApcDispatcher复制(保存)到用户堆栈来完成的致电LdrInitializeThunk,致电LdrInitializeThunk。当KiUserApcDispatcher完成时 - 我们返回NtContinue,它使用保存的线程CONTEXT调用CONTEXT - 只有在此线程入口点开始执行之后。

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

所以每个主题开始在 LdrpWorkCallback 的用户模式下执行。 (具有确切名称的此函数存在并在从nt4到win10的所有Windows版本中调用

enter image description here enter image description here 这个功能是做什么的?这是什么?你可能会听到DLL_THREAD_ATTACH通知吗?当进程中的新线程开始执行时(特殊系统工作线程除外,如LdrInitializeThunk) - 他按加载的DLL列表行走,并用DLL_THREAD_ATTACH通知调用DLL入口点(当然,如果DLL有条目点和DisableThreadLibraryCalls未调用此DLL)。但是这是如何实现的?感谢致电LdrpInitialize的{​​{1}} - > LdrpInitializeThread - > LdrpCallInitRoutine(对于DLL EP)

enter image description here 当进程中的第一个线程开始时 - 这是特例。需要为流程初始化做许多额外的工作。目前只有两个模块正在加载 - EXEntdll.dllLdrInitializeThunk 给这份工作打电话LdrpInitializeProcess。如果非常简短:

  1. 初始化不同的流程结构
  2. 将所有DLL(及其依赖项)静态加载到EXE 链接 - 但不称他们为EP!
  3. 名为LdrpDoDebuggerBreak - 此函数外观 - 是调试器 附加到进程,如果是 - int 3调用 - 所以调试器 接收异常消息 - STATUS_BREAKPOINT - 大多数调试器都可以 开始UI调试只从这一点开始。然而存在 调试器从LdrInitializeThunk调试进程 - 我从这种调试器中截取的所有截图
  4. 重要的一点 - 直到进程中只执行代码 ntdll.dll(可能来自kernel32.dll) - 来自其他人的代码 DLL,尚未在进程中执行的任何第三方代码。
  5. 可选加载的垫片dll进行处理 - Shim Engine初始化。但     这是可选的
  6. 按加载的DLL列表行走并使用其调用其EP DLL_PROCESS_DETACH
  7. TLS调用初始化和TLS回调(如果存在)

  8. ZwTestAlert被调用 - 此调用检查在线程中存在APC 队列,并执行它。这一点存在于从NT4到。的所有版本中 win 10.这样就可以创建处于暂停状态的进程 然后将APC调用(QueueUserAPC)插入其中 (PROCESS_INFORMATION.hThread) - 结果这个电话会是 全部初始化后,执行完毕 DLL_PROCESS_DETACH已调用,但在EXE入口点之前。在上下文中 第一个流程线程。

  9. 和NtContinue最后调用 - 这个恢复保存的线程上下文 我们终于跳到线程EP
  10. enter image description here enter image description here 另请阅读Flow of CreateProcess