为什么要用”真正”这个词?因为我们从学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

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

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

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

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

以上语法依旧是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 如下图:

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

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

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

C语言程序真正的启动函数的更多相关文章

  1. 一个上午,勉强记住了几种不同语言编译PE的启动函数

    VC:启动函数最乱,三大函数都在后面.前面8个PUSH DELPHI7:启动函数最整洁,2.3.4.2,形式排队 VB:启动函数最好记,12个0.... 汇编:三大函数距离最紧凑,除VB外,启动函数最 ...

  2. c语言程序命名规范:函数、变量、数组、文件名

    函数: //send or recv data task void send_recv_data(void *pvParameters); //get socket error code. retur ...

  3. Android For JNI(一)——JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序,使用C启动JAVA程序

    Android For JNI(一)--JNI的概念以及C语言开发工具dev-c++,编写你的第一个C语言程序 当你的Android之旅一步步的深入的时候,你其实会发现,很多东西都必须去和framew ...

  4. C运行时库(C Run-time Library)详解(提供的另一个最重要的功能是为应用程序添加启动函数。Visual C++对控制台程序默认使用单线程的静态链接库,而MFC中的CFile类已暗藏了多线程)

    一.什么是C运行时库 1)C运行时库就是 C run-time library,是 C 而非 C++ 语言世界的概念:取这个名字就是因为你的 C 程序运行时需要这些库中的函数. 2)C 语言是所谓的“ ...

  5. 在android系统上写C语言程序--开机启动该程序不进入安卓系统

    今天要写的这篇博文意义重大,也是网上很少有的,这是在我工作中学会的一项技术,当然,它也是由简单的问题组合而来的.如何在安卓中写C语言程序,调试安卓驱动,测试程序的的一项重要技能,下面我就不说废话了,直 ...

  6. 【C语言C++编程入门】程序的可读性和函数的调用!

    一个简单程序的结构 你已经看过一个具体的例子,下面可以了解一些 C程序的基本规则了. 程序由一个或多个函数组成,其中一定有一个名为 main()的函数.函数的描述由函数头和函数体组成.函数头包括预处理 ...

  7. 李洪强iOS开发之【零基础学习iOS开发】【02-C语言】02-第一个C语言程序

    前言 前面已经唠叨了这么多理论知识,从这讲开始,就要通过接触代码来学习C语言的语法.学习任何一门语言,首先要掌握的肯定是语法.学习C语言语法的目的:就是能够利用C语言编写程序,然后运行程序跟硬件(计算 ...

  8. 如何把一个c语言程序做成windows服务开机自启动

    原文:如何把一个c语言程序做成windows服务开机自启动 目前写的程序是一个用c语言实现socket侦听的,那么如何把这个程序做成开机自启动呢? 我们是通过vs6.0,编译后生成了.exe文件,然后 ...

  9. C语言程序运行时的一些细节

    本章可以看作是 <Unix 环境高级编程>Ch7 的笔记. C 程序运行的开始和结束 一个可以运行的 C 语言总要有一个 main 函数,main 函数现在的完整定义是 int main( ...

随机推荐

  1. JavaScript的9个陷阱

    来自Nine Javascript Gotchas, 以下是JavaScript容易犯错的九个陷阱.虽然不是什么很高深的技术问题,但注意一下,会使您的编程轻松些,即所谓make life easier ...

  2. IIS自定义404错误不能生效的原因

    通过IIS自定义错误信息的功能,来自定义的404错误页面. 先制作显示错误信息的htm文件404.htm(随便什么名都行),我把文件入在根目录下. 使用Url类型,设置“/404.htm ”,可以成功 ...

  3. C与C++中非常少犯的错误,犯了后却非常难找出的错误

    1.continue,break类的错误(HDU1877): #include<iostream> using namespace std; int main() { int a,b,m, ...

  4. Repository模式(转载)

    近来发现很多ASP.NET MVC的例子中都使用了Repository模式,比如Oxite,ScottGu最近发布的免费的ASP.NET MVC教程都使用了该模式.就简单看了下. 在<企业架构模 ...

  5. 让gcc和gdb支持intel格式的汇编

    Linux下的gdb和gcc默认输出的汇编都是AT&T格式的,但是它们都有方式来转换为Intel格式. -masm=[intel|att] 选择intel或AT&T的汇编语法 gcc ...

  6. c#取得post和get的数据和模拟发送

    可以放在控制器的入口函数中,这样载入就会被调用 asp.net mvc,get+post: public ActionResult Index() { ) { foreach (var item in ...

  7. Java类的实例化的初始化过程

    A a = new A(); new 创建对象过程: 1.类加载     代码验证 2.给对象在内存(堆)中分配空间(给属性赋值): 3.属性赋默认值: byte,short.int,long -&g ...

  8. 记一次有趣的 Netty 源码问题

    背景 起因是一个朋友问我的一个关于 ServerBootstrap 启动的问题. 相关 issue 他的问题我复述一下: ServerBootstrap 的绑定流程如下: ServerBootstra ...

  9. BAT-把当前用户以管理员权限运行

    相关资料: http://jingyan.baidu.com/article/72ee561a5dc24fe16138df95.html 网友求助:联想Y400,Win8系统 怎样获得管理员身份 要求 ...

  10. [I2C]pca9555应用层测试代码

    注意点: 如果在设置I2C_SLAVE的时候,提示device_busy,可以使用I2C_SLAVE_FORCE, 在驱动里面二者对应同一个case语句 应用层可以调用接口:i2c_smbus_wri ...