揭秘Windows程序启动的神秘之旅

程序的真正入口

winmain或者main不是真正的入口,只是语法规定的入口。通常通过mainCRTStartup、wimmainCRTStartup、WinMainCRTStartup或者wWinMainCRTStartup。

启动函数

默认的

#include<iostream>

intmain() {
std::cout <<
"Hello, World!"<< std::endl;
return0;
}

直接使用msvc,我是用的tools可能不一样,自己的为准。

挂断点,看堆栈

我们的调用流,我反着写

main()àinvoke_main()à__scrt_common_main_seh()à__scrt_common_main()àmainCRTStartup()àkernel32.dllàntdl\windows内核,再次我会在后续补充完整流程,这个不太支持windows的编译工具。

调用的函数

invoke_main

__scrt_common_main_seh()

__scrt_common_main()

mainCRTStartup()

kernel32.dllà

@BaseThreadInitThunk@12()

ntdll.dllà

__RtlUserThreadStart()

__RtlUserThreadStart@8()

详细流程

内核调度(ntdll.dll) __RtlUserThreadStart@8()和__RtlUserThreadStart()

àstdcall 调用约定,初始化teb(Thread Environment Block),设置seh(结构化异常处理) 链,初始化浮点/向量寄存器状态,调用线程入口函数 (BaseThreadInitThunk)

à kernel32.dll @BaseThreadInitThunk@12()

发送 DLL_THREAD_ATTACH 通知给所有已加载的 DLL,初始化线程本地存储(Thread Local Storage),捕获未处理的异常并上报,入口函数返回后,调用 ExitThread 清理stdcall(由被调用者清理栈),调用mainCRTStartup()

à进入exe mainCRTStartup()

(进程专用入口点)连接器是*.exe入口,控制台使用,gui使用winmaincrtstartup

à__scrt_common_main()

你的可执行文件(静态链接到 CRT 库核心主逻辑),负责(运行时库所有的初始化和清理)

à__scrt_common_main_seh()

(she异常保护壳)用—try/--except包裹主流程,兜底捕获未处理的系统异常

à invoke_main

(调用main的封装)统一处理参数,最终进入用户代码

àmain()

详细说明

__RtlUserThreadStart()

// 原型(简化)

void RtlUserThreadStart(

PVOID StartAddress, // 线程要执行的函数地址(通常是 BaseThreadInitThunk)

PVOID Parameter // 传递给线程函数的参数

);

① 初始化 TEB (Thread Environment Block)

② 设置 SEH (结构化异常处理) 链

③ 初始化浮点/向量寄存器状态

④ 调用线程入口函数 (BaseThreadInitThunk)

起始返回后调用__RtlExitUserThread终止线程

BaseThreadInitThunk

参考ReactOS

VOID NTAPI BaseThreadInitThunk(

BOOL InitialThread,

PVOID StartAddress,

PVOID Parameter)

{

// 1. 通知 DLL 新线程创建

LdrInitializeThunk(InitialThread, NULL, NULL);

// 2. 调用实际的线程入口

__try {

((VOID (WINAPI *)(PVOID))StartAddress)(Parameter);

}

__except(UnhandledExceptionFilter(GetExceptionInformation())) {

// 3. 异常处理

ExitProcess(GetExceptionCode());

}

// 4. 线程退出

ExitThread(STATUS_SUCCESS);

}

用户线程最初始入口

设置初始环境(结构化寻常帧)

调用win32线程

mainCRTStartup

// 实际 CRT 启动代码(简化呈现)

void mainCRTStartup()

{

// ① 初始化 CRT 的全局状态

_set_app_type(_crt_console_app);

// ② 设置浮点环境

_fpreset();

// ③ 调用主初始化(含异常保护)

__scrt_common_main_seh();

// ④ 正常情况下不会执行到这里

}

具体分析就好

这里主要功能就是控制台程序的连接器入口点

操作系统加载exe不直接调用main,会调用本函数。作为薄壳,交给__scrt_common_main处理所有初始化。

如果是gui程序就调用winmainCRTStartup,但是再底层同样会触发执行__scrt_common_main。

__scrt_common_main

通用crt(堆、环境变量、安全、cookie、浮点单元)

调用__scrt_common_main_seh去执行受保护的程序的后续流程

cookie是栈保护就是canary

__scrt_common_main_seh

she结构化异常保护边界

结构:

__try{

//调用全局/静态对象的构造函数

调用invoke_main

正常退出exit(result)

}

__except{

//异常终止:显示错误的对话框、生成转储等内部操作

}

调用invoke_main异常处理的最后的防线

invoke_main

给main的参数envp,agrc,agrv传参,调用main封装