Windows必须做一些事情来解析PE头,在内存中加载可执行文件,并将命令行参数传递给main()
。
使用OllyDbg我已将调试器设置为在main()上中断,以便我可以查看调用堆栈:
似乎缺少符号,因此我们无法获取函数名称,只能看到它的内存地址。但是我们可以看到main的调用者是kernel32.767262C4
,它是ntdll.77A90FD9
的被调用者。在堆栈的底部,我们看到RETURN到ntdll.77A90FA4
,我认为它是第一个被调用来运行可执行文件的函数。看起来传递给该函数的值得注意的参数是Windows的结构化异常处理程序地址和可执行文件的入口点。
那么这些函数究竟是如何最终将程序加载到内存中并为进入点执行做好准备?调试器是否在main()
之前显示操作系统执行的整个过程?
答案 0 :(得分:8)
如果您在内部致电CreateProcess
系统,请致电ZwCreateThread[Ex]
以创建正在处理的第一个主题
当你创建线程时 - 你(如果你直接调用ZwCreateThread
)或系统初始化新线程的CONTEXT
记录 - 这里Eip(i386)
或Rip(amd64)
的入口点线。如果你这样做 - 你可以指定任何地址。但是当你打电话说Create[Remote]Thread[Ex]
- 我怎么说 - 系统填充CONTEXT
并将自编程设置为线程入口点。您的原始入口点保存在Eax(i386)
或Rcx(amd64)
注册表中。
此例程的名称取决于Windows版本。
早期BaseThreadStartThunk
或BaseProcessStartThunk
(来自CreateProcess
来自kernel32.dll
)来自RtlUserThreadStart
。
但现在系统从ntdll.dll
指定RtlUserThreadStart
。 BaseThreadInitThunk
通常会从kernel32.dll
调用smss.exe
(本机(启动执行)应用程序除外,例如chkdsk.exe
和kernel32.dll
,其自身没有BaseThreadInitThunk
地址空间)。 APC
已经调用了原始的线程入口点,并在(if)之后返回 - RtlExitUserThread
被调用。
这个常见线程启动包装器的主要目标 - 设置顶级
SEH
过滤器。只是因为我们可以调用SetUnhandledExceptionFilter
函数。如果线程从您的入口点直接开始,没有包装器 - Top level Exception Filter的功能变得不可用。
但无论线程入口点 - 用户空间中的线程 - 从不从这一点开始执行!
用户模式线程开始执行时的早期 - 系统插入LdrInitializeThunk
与CONTEXT
作为Apc例程进行线程 - 这是通过将({save)线程KiUserApcDispatcher
复制(保存)到用户堆栈来完成的致电LdrInitializeThunk
,致电LdrInitializeThunk
。当KiUserApcDispatcher
完成时 - 我们返回NtContinue
,它使用保存的线程CONTEXT
调用CONTEXT
- 只有在此线程入口点开始执行之后。
但现在系统在此过程中进行了一些优化 - 它将线程LdrInitializeThunk
复制(保存)到用户堆栈并直接调用NtContinue
。在函数LdrInitializeThunk
的末尾调用 - 并且正在执行线程入口点。
所以每个主题开始在 LdrpWorkCallback
的用户模式下执行。 (具有确切名称的此函数存在并在从nt4到win10的所有Windows版本中调用)
这个功能是做什么的?这是什么?你可能会听到
DLL_THREAD_ATTACH
通知吗?当进程中的新线程开始执行时(特殊系统工作线程除外,如LdrInitializeThunk
) - 他按加载的DLL列表行走,并用DLL_THREAD_ATTACH
通知调用DLL入口点(当然,如果DLL有条目点和DisableThreadLibraryCalls
未调用此DLL)。但是这是如何实现的?感谢致电LdrpInitialize
的{{1}} - > LdrpInitializeThread
- > LdrpCallInitRoutine
(对于DLL EP)
当进程中的第一个线程开始时 - 这是特例。需要为流程初始化做许多额外的工作。目前只有两个模块正在加载 -
EXE
和ntdll.dll
。 LdrInitializeThunk
给这份工作打电话LdrpInitializeProcess
。如果非常简短:
LdrpDoDebuggerBreak
- 此函数外观 - 是调试器
附加到进程,如果是 - int 3
调用 - 所以调试器
接收异常消息 - STATUS_BREAKPOINT
- 大多数调试器都可以
开始UI调试只从这一点开始。然而存在
调试器从LdrInitializeThunk
调试进程 -
我从这种调试器中截取的所有截图ntdll.dll
(可能来自kernel32.dll
) - 来自其他人的代码
DLL,尚未在进程中执行的任何第三方代码。DLL_PROCESS_DETACH
TLS调用初始化和TLS回调(如果存在)
ZwTestAlert
被调用 - 此调用检查在线程中存在APC
队列,并执行它。这一点存在于从NT4到。的所有版本中
win 10.这样就可以创建处于暂停状态的进程
然后将APC调用(QueueUserAPC
)插入其中
(PROCESS_INFORMATION.hThread
) - 结果这个电话会是
全部初始化后,执行完毕
DLL_PROCESS_DETACH
已调用,但在EXE入口点之前。在上下文中
第一个流程线程。