標籤:局部變數 程式碼分析 編譯器 控制台 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會更好地靜態分析一些函數。
c程式的啟動過程的反組譯碼分析