首页
社区
课程
招聘
[原创]VC++6入口函数分析
发表于: 2024-8-15 11:57 1661

[原创]VC++6入口函数分析

2024-8-15 11:57
1661

在写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中入口函数的大致过程。


[课程]FART 脱壳王!加量不加价!FART作者讲授!

最后于 2024-8-15 12:07 被米龙·0xFFFE编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (1)
雪    币: 8599
活跃值: (5060)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
分析的不错。
2024-8-15 13:45
1
游客
登录 | 注册 方可回帖
返回
//