C语言程序真正的启动函数

Wesley13
• 阅读 661

为什么要用”真正”这个词?因为我们从学C语言开始,都会先明白这个道理,即C语言有且仅有一个main函数,main函数是C语言的入口点和出口点!(可以参考>http://www.dotcpp.com/wp/184.htmll)不光C语言如此,C++也如此,甚至无论黑窗口的控制台程序和Windows应用程序,都是从main函数或者WinMain函数开始执行,这当然没错,但事实上main函数仅仅是一个C语言语法规定的入口点,而不是真正的程序入口,因为它也有函数返回值!它也需要被调用!所以,今天我们将带大家去揭秘main函数之前的代码,去看看真正的启动函数是什么!来让大家深入理解C语言程序,方便大家日后的逆向学习!

由于大多数情况下,我们在VC环境下,常常C/C++混编,或控制台程序和windows应用程序都有接触,同时会因为编码方式的区分如ANSI或者Unicode编码启动函数还各不相同,为保持简单、纯粹。我们今天仅仅讨论ANSI编码控制台程序下纯C语言的程序入口分析。

事实上,在VC6编译器下,ANSI编码环境下C语言的真正启动函数名叫做mainCRTStarup,英语好的同学应该可以明白一些,Starup就是初始化、启动的意思,其实也可以根据这点明白这个函数作用就是在C语言启动之前做一些必要的工作,如堆栈初始化、获得主函数的参数等等。

还是本着我们“实践教学”的原则,我们还是以实践、做实验来验证和理解我们的知识,由于关系到函数间调用的关系,我们应该联想到VC6编译器带给我们的栈回溯功能。有兴趣的同学可以参考VC6断点调试之窗口监视(内存监视、寄存器和栈回溯)(http://www.dotcpp.com/wp/545.html)依次View – Debug Windows-> Call Stack

通过编译器提供的栈回溯功能可以看到程序启动后的调用过程,如下:

C语言程序真正的启动函数

通过断点提示,我们看到目前程序位于main函数第四行。可以看到上一次是被mainCRTStartup函数调用,在第206行的25个字节偏移处开始调用,再之前就是KERNEL32了,它是windows系统三大主要文件之一。软件系统层面的调用就到此为止了。

因为大多数逆向分析工具基本都会从这里开始,所以我们也重点研究mainCRTStartup函数的原理。幸运的是,VC6编译器为我们提供了mainCRTStartup函数的源码,但需要大家安装完整版才可以看到,不然只能看到反汇编代码。

这里我们摘录一部分主要的mainCRTStartup代码,供大家参考学习:

C语言程序真正的启动函数

以上语法依旧是C语言,大家可以自行对照注释进行理解,熟悉main函数在调用前的一些准备工作,可以总结如下:

1.GetVersion函数:获取当前运行平台的版本号。控制台下则为MS-DOS的版本信息。

2._heap_inith函数:用于初始化堆空间。在函数实现中使用HeapCreate申请堆空间

3.GetCommandLineA函数:获取命令行参数信息的首地址

4._crtGetEnvironmentStringA函数:获取环境变量信息的首地址

5._setargv函数:此函数根据GetCommandLineA获取命令行参数信息的首地址并进行参数分析 注意主函数的参数就在这里获得!

6._setenvp函数:此函数根据_crtGetEnvironmentStringA函数获取环境变量信息的首地址进行分析。

7._cinit函数:用于全局变量数据和浮点数寄存器的初始化。

大家可以对比代码加注释深入理解main函数启动前的准备工作,来加深程序启动的机制理解。

通过观察,在_cinit()函数之后,我们可以看到有主函数的调用语句mainret = main(_argc,_argv,_environ),现在知道主函数的返回值给谁了吧?

至此,我们最熟悉的main函数就出现了,怎么样,大家连起来了吗?

如果还能理解,我们接下来做一个更改入口函数的实验,来加深大家的学习。如下:

编译器工具栏 Project – Setting – Link – Output 如下图:

C语言程序真正的启动函数

在入口点出输入你想自定义的函数名,比如起名MyDotcpp,将替换掉mainCRTStartup函数,重新被KERNEL32调用,main函数作为C语言语法入口点,被MyDotcpp调用,如图:

C语言程序真正的启动函数

重新打开栈回溯查看调用情况,可以看到入口函数已经被更改掉了:

C语言程序真正的启动函数

当然,这里我们定义的MyDotcpp函数仅仅用来测试更改入口函数,正如mainCRTStartup之前描述的代码一般,入口函数拥有更多的比如初始化堆空间、浮点数等功能,如果我们这里在多加一些如开辟内存等语句,运行将会报错,大家可以亲自上机尝试。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
C语言
主函数程序的入口main函数不可没有,也不可出现多行main函数,main有且仅有一个。intmain()return0main前面的int指的是函数调用后返回一个整型值。int是整形。返回0,0是整数voidmain是过时的函数。库函数C语言本身提供给我们使用的函数。数据类型:char字符型
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
初探 Objective
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言异常处理是许多高级语言都具有的特性,它可以直接中断当前函数并将控制权转交给能够处理异常的函数。不同语言在异常处理的实现上各不相同,本文主要来分析一下ObjectiveC和C这两个语言。为什么要把ObjectiveC和
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这