-
-
[原创]VC++6入口函数分析
-
发表于:
2024-8-15 11:57
2077
-
在写C语言代码时,常常需要从一个main函数开始,main函数可能会有两个参数,也可能有三个:
1 2 3 4 5 6 7 8 9 | int main( int argc, char *argv[])
{
return 0;
}
int main( int argc, char *argv[], char *env[])
{
return 0;
}
|
但程序的真正入口,并非main函数,以VC++6为例,通过debug查看调用堆栈:
发现main函数是由mainCRTStartup调用的,这才是真正的入口函数,将断点打到mainCRTStartup处,观察其过程。
1. 获取系统版本号
刚开始通过GetVersion()获得操作系统版本_osver,后面又通过位运算取得_osver的高16位,得到数值2600,如果按win+r在运行窗口内输入winver
可见系统版本与该数值一致:
2. 初始化堆
代码区和数据区是在程序加载时就已经划分好的,栈区是运行时通过指令指针寄存器esp自动分配的,剩下的就是堆区,需要用时动态申请,这部分空间就是由_heap_init完成的。
3. 设置命令行参数
这里_ioinit是初始化输入输出,即键盘、显示器等设备流。下面通过_setargv设置argc和argv,可以步入该函数观察:
__argc被赋值为1,也就是只有一个命令行参数,__argv被赋值为一个指针,在内存窗口处跳转到该指针指向的位置可见一个字符串,就是可执行文件全路径。
接下来_setenvp是设置环境变量,步入该函数可见:
指针p指向了一个字符串,该字符串包含了很多系统的环境变量。
4. 数据初始化
接下来,调用了一个_cinit函数:
步入该函数,可见调用了两次_initterm:
_initterm是用于初始化数据区的全局变量或者static变量的,第一次调用是初始化C语言的数据区,第二次调用初始化C++的数据区。
可以在一个.cpp源码文件里写这么一段代码:
1 2 3 4 5 6 7 8 9 10 11 | int GetNum()
{
return 999;
}
int x = GetNum();
int main( int argc, char *argv[], char *env[])
{
return 0;
}
|
这里的全局变量x是通过函数调用初始化的。
注意这里源文件后缀名不能是.c,因为.c是按照C标准编译的,C语言不支持这种初始化语法,但C++是支持的,所以后缀名应为.cpp。
将断点打到GetNum内部,观察调用堆栈可见,是第二次调用_initterm时初始化x的:
5. 调用main函数
接下来执行到这步,才开始调用main函数,传入__argc,__argv,_environ,进入main可见:
所传参数符合前面步骤的观察。
6. 退出
接下来,将main函数的返回值0赋值给mainret,并以该值调用exit退出程序。
以上就是VC++6中入口函数的大致过程。
[招生]系统0day安全班,企业级设备固件漏洞挖掘,Linux平台漏洞挖掘!
最后于 2024-8-15 12:07
被米龙·0xFFFE编辑
,原因: