c程序的啓動過程的反彙編分析

0x01  工具準備

1.最簡c代碼一隻,

int main(){

return 0;}

2.ollydbg

3.VC++6.0

4.GCCmingw

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_initcheck_managed_apppre_c_init_tmainCRTStartup_InterlockedCompareExchangePointerduplicate_ppstringsWinMainCRTStartupmainCRTStartup_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後,可以看到GetEnvironmentStringsWGetEnvironmentStrings函數,獲取環境變量的首地址,如圖所示。以Unicode編碼形式返回到寄存器和堆棧中,最後採用WideCharToMultiByte函數將Unicode字符串到一個多字節字符串, 

並且後續有參數分析的一些函數,環境變量信息分析,從而得到main函數所需的參數,然後在00402D4B位置,將參數傳到main函數中,從而執行main函數中的內容。

0x05 後記

最近在閱讀《c++反彙編與逆向分析技術揭祕》,在閱讀到第三章認識啓動函數,找到用戶入口時,得知main函數之前系統要做一些準備工作,再加上上學期學的C語言程序入口函數不是main函數,而是_start函數,這不禁引發了一些思考,到底編譯器在編譯和系統執行程序的時候發生了什麼,因此想以實例進行一定的分析。在思考的過程中,有些涉及到了編譯器的知識,包括它如何工作的,彙編之後又是如何鏈接的,這一部分內容不太熟悉,這一方面得掌握編譯原理的知識,還得學習編譯器的相關內容。那些東西還沒學,因此不免有一些缺憾。瞭解反彙編的一些內容,可以更深層次的理解相對底層的一些東西,包括棧,堆和寄存器的數據交換。另外並未使用到神器IDA,利用IDA會更好地靜態分析一些函數。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章