学习C语言的大家可以可以看看这篇文章可能会对你有帮助哦

为什么要用”真正”这个词?因为我们从学C语言开始,都会先明白这个道理,即C语言有且仅有一个main函数,main函数是C语言的入口点和出口点!(可以参考<<一个C语言程序的基本机构>>)不光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断点调试之窗口监视(内存监视、寄存器和栈回溯)<第五篇>

依次View – Debug Windows-> Call Stack

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

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

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

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

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

//预编译宏
#else /WINMAIN/
#ifdef WPRFLAG
//宽字符版控制台启动函数
void wmainCRTStartup(
#else/WPRFLAG/
//多字节版控制台启动函数
void mainCRTStartup(
#endif /WPRFLAG/
#endif /WINMAIN/
void )
{
//获取系统版本信息
_osver=GetVersion();
_winminor=(_oserver>>8)&0x00FF;
_winmajor=_oserver & 0xFF; //主版本
_winver=(_winmajor<<8)+_winminor; _osver=(_osver>>16) & 0x00FFFF;
//堆空间初始化过程,在这个函数里,指定了程序中堆空间的起始地址
//_MT是多线程标记
#ifdef _MT
if(!_heap_init(1))
#else /_MT/
if(!_heap_intit(0))
#endif /_MT/
fast_error_exit(_RT_HEAPINIT);
//初始化多线程环境
#ifdef _MT
if(!_mtinit())
fast_error_exit(_RT_THREAD);
#endif /_MT/
_try{
//宽字符处理代码略
//多字节版获取命令行
_acmdln=(char)GetCommandLineA();
//多字节版获取环境变量信息
_aenvptr=(char
)_crtGetEnvironmentStringsA();
//多字节版获取命令行信息
_setargv();
//多字节版获取环境变量信息
_setenvp();
#endif /WPRFLAG/
//初始化全局数据和浮点寄存器
_cinit();
//窗口程序处理代码略
//宽字符串里代码略
//获取环境变量信息
_initenv=_environ;
//调用main函数,传递命令行参数信息
mainret=main(_argc,_argv,_environ);
#endif /WPRFLAG/
#endif/WINMAIN/
//检查main函数返回值执行析构函数或atexit注册函数指针,并结束程序
exit(mainret)
}
//退出结束代码略
}

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

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

2._heap_init函数:用于初始化堆空间。在函数实现中使用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之前描述的代码一般,入口函数拥有更多的比如初始化堆空间、浮点数等功能,如果我们这里在多加一些如开辟内存等语句,运行将会报错,大家可以亲自上机尝试。

如果有需要的话请登入网址C语言网

原文地址:http://blog.51cto.com/13860654/2140492

时间: 2024-10-04 11:20:02

学习C语言的大家可以可以看看这篇文章可能会对你有帮助哦的相关文章

轻松学习C语言编程的秘诀:总结+灵感

目前在准备一套C语言的学习教程,所以我这里就以C语言编程的学习来讲.注意,讲的是"轻松学习",那种不注重方法,拼命玩命的方式也有其效果,但不是我提倡的.我讲究的是在方式方法对头.适合你.减轻你学习负担和心里压力的前提下,才适当的抓紧时间. 因此,探索一种很好的学习方法就是我所研究的主要内容. 众所周知,学习C语言并非易事,要学好它更是难上加难.这和你期末考试背会几个题目的答案考上满分没多大关系,也就是说你考试满分也说明不了你学好.学精通了C语言.那么怎么才算学精通C语言?闭着眼睛对自己

第一节 为什么学习C语言

一  c语言的发展 : 1.1C语言的发展过程 C语言是在 70 年代初问世的.一九七八年由美国电话电报公司(AT&T)贝尔实验室正式发表了C语言.目的 改写 UNIX操作系统. 后来由美国国家标准协会(American National Standards Institute)在此基础上制定了一个C 语言标准,于一九八三年发表.通常称之为ANSI C. 1.2C语言的特点 1 C语言简洁.紧凑,使用方便.灵活. 高级语言 : a+b 汇编语言 :ADD AX,BX 机器语言 : 0000 00

C++教程 零基础如何学习C语言!

学习一门技术不是所想的很简单的一回事,学习C语言同样也不是一件简单的事!学好C语言,你想在短时间内学好,肯定是不可能的,但是也不需要你花个十年八年才嫩恶搞精通.今天小编就针对0基础的学员推荐给你们基本书籍以及技巧! 一.要读就读好书,否则不如不读 所有初学者面临的第一个问题便是:如何选择教材.好的开始是成功的一半,选择一本优秀的教材是事半功倍的关键因素.不幸的是,学校通常会帮你指定一本很差劲的C语言课本;而幸运的是,你还可以再次选择. 大名鼎鼎的谭浩强教授出了一本<C语言程序设计>,据说发行量

对大一新生开始学习C语言课程谈几点看法

大家好,首先祝贺大家进入了大学,迈入了大学的校门,也意味着开始了新的征程,希望大家能够有一个美好的大学四年. 先做下自我介绍,我叫李帅阳,(大家可以称呼我 李老师,或是班助,或是...)这是在邹欣老师的倡导下来担任你们学习C语言的助教,本学期将会与李光杰老师合作,非常荣幸能与各位一起度过这美好的一学期. 在本学期,我会竭尽全力为大家服务,希望能够帮助大家在C语言上获得更大的收获,并且能够在编程上找到乐趣. 对于C语言,想必各位都没有听过,但这是你们非常重要的课程,对于咱们专业,如果这门课程学得一

在安卓手机上学习C语言 -- 前言

现在有很多学习C语言的系列教程, 不过这些教程都是在电脑上进行练习的, 这系列文章是给那些手上还没有电脑, 但对于编程有很大的兴趣的朋友看的. 这一系列的文章是写给没有编程基础的朋友的, 在这系列文章的前期, 可能会有一些类似于童话的谎言, 因为没有基础的朋友对于一些非常专业的术语可能会非常迷茫,而想要解释清楚这些术语需要非常多的陌生的概念.而这些概念可能比一个术语更加复杂, 所以在前期, 我会对一些非必要的术语进行片面的阐述. 这系列的文章我也是一时想起, 并没有建立好完善的体系 , 不过我会

学习Golang语言(6):类型--切片

学习Golang语言(1): Hello World 学习Golang语言(2): 变量 学习Golang语言(3):类型--布尔型和数值类型 学习Golang语言(4):类型--字符串 学习Golang语言(5):类型--数组 学习Golang语言(6):类型--切片 在很多应用场景中,数组不能够满足我们的需求.在初始定义数组时,我们并不知道数组所需的长度.因此,我们需要一个大小可以动态变化的数组(动态数组) 在Go语言中,这种"动态数组"成为slice(切片). 但是实际上slic

学习go语言编程系列之定义变量

package main import ( "fmt" "math") func main() { // 1. 定义变量名age,不初始化,使用对应类型的默认值 var age int fmt.Println("My age is", age) // 2. 给变量赋值 age = 29 fmt.Println("My age is", age) age = 50 fmt.Println("My age is"

对学习C语言未来的展望

个人认为C语言对我们学习计算机的学生来说很重要,因为这是我们开始接触计算机的开端,我希望通过这学期的学习,我可以为我的C打下一个良好的基础,在以后学习其他语言可以更容易点.牛老师你的课很棒,结合着课堂练习,让我们跟着你打代码真的可以训练我们的速率和掌握知识点,但是速度对于我们来说有点太快了,毕竟我们才刚刚开始接触C,很多同学在上大学之前对C都没有概念,有些内容我们听起来很吃力.小小建议,讲课速度稍微慢点点,较为难的知识点讲的细致,易懂点. 谢谢老师

学习一门语言

最近在公司的一个论文实现要用torch,torch是用lua写的,所以就要学习lua语言.虽然我之前没学过lua,但看着源代码并不是那么费劲,这其实就是因为语言之间还是相通的.回到如何学习一门语言,我在byr论坛上也发帖求教lua语言学习,好几个回答都说去用菜鸟教程快速入门,其他不懂的再google或百度.我觉得这几个回复很正确,我自己也这样觉得,对于现在的我已经有一定其他语言基础,能够通过菜鸟教程很快入门,然后再去在实践中不断学习语言,即遇到问题取搜索答案,这样的方法高效可用. 在我入门深度学