c程序的启动过程的反汇编分析

0x01  工具准备

1.最简c代码一只,

int main(){

return 0;}

2.ollydbg

3.VC++6.0

4.GCC(mingw)

0x02  代码分析

int main()

{

return 0;

}

在gcc下,添加-nostdlib编译选项,即链接器不链接标准库,会提示以下错误信息:

D:\Backup\我的文档\src>gcc main.c -nostdlib-o main.exe

C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\ccmSU3wr.o:main.c:(.text+0x9): undefined re

ference to `__main‘

collect2.exe: error: ld returned 1 exit status

关于-nostdlib编译选项,只有命令行指定的项才传递给链接器。标准启动文件和库都不会传递给链接器。该选项隐式打开选项-nostartfiles 和-nodefaultlibs。该选项也可以写作--no-standard-libraries。

在gcc执行汇编之后,在链接部分,当只打开选项-nostartfiles时,结果正常,未出现错误信息。而在-nodefaultlibs选项中,提示很多错误信息。

说明main函数,依赖了一些系统标准库文件,在链接的时候,需要到了一些函数,例如pre_cpp_init、check_managed_app、pre_c_init、_tmainCRTStartup、_InterlockedCompareExchangePointer、duplicate_ppstrings、WinMainCRTStartup、mainCRTStartup、_mingw_prepare_except_fr_msvcr80_and_higher….

汇编里面的_main就是C语言里面的main,是因为汇编器和C编译器对符号的命名差一个下划线。

链接器会在系统标准库文件,类似于/lib/crt2.o的文件中,寻找_start符号,然后在_start中执行创建堆对象,栈,打开系统预先提供的设备,将argv,argc参数传入main函数,然后调用main函数。

0x03  vc main函数反汇编分析

1:    int main()

2:    {

00401010   push        ebp   //在堆栈上保存EBP

00401011   mov         ebp,esp //将堆栈当前位置给EBP,以在堆栈结构中存储值时的参考点

00401013   sub         esp,40h //分配空间

00401016   push        ebx  //保存数据段值

00401017   push        esi //源地址指针

00401018   push        edi //目的地址指针

00401019   lea         edi,[ebp-40h] //装入有效地址,用来得到局部变量和函数参数的指针。这里[ebp-40h]就是基地址再向下偏移40h,就是前面说的为本地变量留出的空间的起始地址;将这个值装载入edi寄存器,从而得到局部变量的地址

0040101C   mov         ecx,10h //在ecx寄存器存储10h

00401021   mov         eax,0CCCCCCCCh

00401026   rep stos    dword ptr [edi] //初始化局部变量空间,ds:[edi]

3:        return 0;

00401028   xor         eax,eax

4:    }

0040102A   pop         edi //恢复所有寄存器的值

0040102B   pop         esi

0040102C   pop         ebx

0040102D   mov         esp,ebp //恢复堆栈

0040102F   pop         ebp

00401030   ret //返回到源EIP地址

Vc查看调用栈,可以看到在main函数之前,系统还启动了mainCRTStartup函数,这个函数是控制台环境下多字节编码的启动函数。在kernel32.dll中地址7c816fd7处调用了mainCRTStartup函数。

main() line 2

mainCRTStartup() line 206 + 25 bytes

KERNEL32! 7c816fd7()

0x04 ollydbg反汇编分析

Od载入,如图所示。

堆栈窗口如图所示。

通过堆栈,可以看到kelnel32调用了入口函数(mainCRTStartup),对于od来说,main函数并不是Entry point,而是mainCRTStartup函数。

一直单步,单步到00401146处,od分析为调用GetVersion函数,获取当前运行平台的版本号,因为是控制台程序,所以获取版本号为ms-dos的版本信息。

继续单步,单步到0040119E处,单步进入,可以看到有HeapCreate申请堆空间函数,大小由传递的参数决定,并且该call里有HeapDestroy销毁堆函数。因此0040119E为初始化堆空间,如图所示。

在004011C0处,od分析为GetCommandLineA函数,获取命令行参数信息的首地址。

进入下面的那个call后,可以看到GetEnvironmentStringsW和GetEnvironmentStrings函数,获取环境变量的首地址,如图所示。以Unicode编码形式返回到寄存器和堆栈中,最后采用WideCharToMultiByte函数将Unicode字符串到一个多字节字符串,

并且后续有参数分析的一些函数,环境变量信息分析,从而得到main函数所需的参数,然后在00402D4B位置,将参数传到main函数中,从而执行main函数中的内容。

0x05 后记

最近在阅读《c++反汇编与逆向分析技术揭秘》,在阅读到第三章认识启动函数,找到用户入口时,得知main函数之前系统要做一些准备工作,再加上上学期学的C语言程序入口函数不是main函数,而是_start函数,这不禁引发了一些思考,到底编译器在编译和系统执行程序的时候发生了什么,因此想以实例进行一定的分析。在思考的过程中,有些涉及到了编译器的知识,包括它如何工作的,汇编之后又是如何链接的,这一部分内容不太熟悉,这一方面得掌握编译原理的知识,还得学习编译器的相关内容。那些东西还没学,因此不免有一些缺憾。了解反汇编的一些内容,可以更深层次的理解相对底层的一些东西,包括栈,堆和寄存器的数据交换。另外并未使用到神器IDA,利用IDA会更好地静态分析一些函数。

时间: 2024-10-11 22:33:41

c程序的启动过程的反汇编分析的相关文章

Android应用程序进程启动过程的源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址: http://blog.csdn.net/luoshengyang/article/details/6747696 Android 应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信 机制:这两个特点都是在进程的初始化过程中实现的,本文将详细分析Android应用程序进程创建过程中是如何实现这两个特点的. Android应用程序框架层创建的应用程序进程

Android系统进程Zygote启动过程的源代码分析

原文地址:http://blog.csdn.net/luoshengyang/article/details/6747696 Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实现的,本文将详细分析Android应用程序进程创建过程中是如何实现这两个特点的. Android应用程序框架层创建的应用程序进程的入口函数是ActivityThread.ma

iPhone应用程序的启动过程

Phone的入口函数main,这之后它有是怎样启动应用程序,初始化的呢,这些都是通过 UIApplicationMain 来实现的. 其启动的流程图大致如下图所示: 1 int retVal = UIApplicationMain(argc, argv, nil, nil); 通过上面的语句,创建UIApplication实例.同时,查看应用程序的 Info.plist 文件(该文件记录了一些应用程序的基础信息,比如程序名称,版本,图标等).该文件还包含应用程序资源文件的名称(nib文件,名称用

Info.plist和pch文件的作用,UIApplication,iOS程序的启动过程,AppDelegate 方法解释,UIWindow,生命周期方法

转自:http://blog.csdn.net/dwt1220/article/details/29373817 Info.plist常见的设置 建立一个工程后,会在Supporting files文件夹下看到一个“工程名-Info.plist”的文件,该文件对工程做一些运行期的配置,非常重要,不能删除  注:在旧版本Xcode创建的工程中,这个配置文件的名字就叫“Info.plist”  项目中其他Plist文件不能带有“Info”这个字眼,不然会被错认为是传说中非常重要的“Info.plis

MFC程序的启动过程,很清楚,但仍有待改进

原文出自:http://blog.csdn.net/yuvmen/article/details/5877271 了解MFC程序的启动过程,对于初学者来讲,了学习MFC很有帮助:对于不常用VC的人来说,过一段时间就会忘记.还是来记下来,方便以后查阅. 1.创建Application object对象theApp 程序一开始生产一个(且只有一个)Application object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执行其构造函数,因为并没有定义CMyWinApp构

程序的启动过程

WIN32程序启动过程 第一步:操作系统首先创建响应的进程并分配私有的进程空间,然后操作系统的加载器负责把可执行文件的数据段和代码段映射到进程的虚拟内容空间中. 第二步:加载器读入可执行程序的导入符号表,根据这些符号表可以查找出该可执行程序所有依赖的动态链接库. 第三步:加载器针对该程序依赖的每一个动态链接库调用LoadLibrary. 查找对应的动态库文件,加载器为该动态链接库确定一个合适的基地址.如果该基地址和动态链接库希望加载的基地址不同,加载器还要为该库做rebase,然后把整个动态链接

iOS程序的启动过程-UIWindow

UIApplicationMain main函数中执行了一个UIApplicationMain这个函数 int UIApplicationMain(int argc, char *argv[], NSString *principalClassName, NSString *delegateClassName); argc.argv:直接传递给UIApplicationMain进行相关处理即可 principalClassName:指定应用程序类名(app的象征),该类必须是UIApplicat

跟踪分析Linux内核的启动过程--实验报告 分析 及知识重点

跟踪分析Linux内核的启动过程 攥写人:杨光  学号:20135233 ( *原创作品转载请注明出处*) ( 学习课程:<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ) 知识总结: ****Linux内核中关键目录: arch:不同cpu的支持,我们主要关注的是其中x86文件夹 init:内核启动的相关代码,期中main.c是内核启动的起点,main.c中的start_kernel是内核初始化的起点 ker

UIApplication Delegate和程序的启动过程

在main.m文件中: #import <UIKit/UIKit.h> #import "MJAppDelegate.h" int main(int argc, char * argv[]) { @autoreleasepool { /* argc: 系统或者用户传入的参数个数 argv: 系统或者用户传入的实际参数 1.根据传入的第三个参数创建UIApplication对象 2.根据传入的第四个产生创建UIApplication对象的代理 3.设置刚刚创建出来的代理对象为