程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> 關於C >> Hello World程序背後的故事解密(二)——程序之生

Hello World程序背後的故事解密(二)——程序之生

編輯:關於C

 

近幾個月實在是太忙了,偶然想起來博客上一看,離上次寫文章居然過了兩個月有余,於是手癢癢想加把勁,再碼點兒技術文上來^_^

 

這個系列是為了挖掘出一個簡單的類似Hello World程序隱藏在CRT之下的復雜性,因此在上次分析了“編譯器選項和CRT”之後,今天我想再來簡單分析一下從程序進程建立直到程序運行到C/C++入口函數處發生的那點兒事兒。

 

我們知道,在Windows下一個進程是使用CreateProcess函數創建的,這個函數調用的成功,標志著有一個程序被加載至內存並准備開始運行了。系統在建立進程時,會完成給進程分配資源,初始化進程的內存空間,初始化進程內核對象,初始化進程環境快(PEB),加載PE映像文件,初始化全局堆,載入DLL等等工作,但是這些工作完成後卻並不代表我們的程序已經開始執行了。這是因為進程本身是有“惰性”的,它不會主動執行代碼,所以操作系統這時候會建立我們程序的第一個線程,而我們的代碼將從這個線程來開始執行。

 

既然我們討論“開始執行”,那麼本文將不會涉及CreateProcess的原理以及系統建立進程的細節。我們將從線程的建立開始,來討論我們的主題。

 

我們且不談系統提供的給我們使用的CreateThread等函數,首先我們需要知道的是實際上一個線程的建立最終是由NtCreateThreadEx函數實現的,而這個函數則是將58作為調用號傳入EAX然後直接使用sysenter指令陷入內核,從而使得線程能夠最終建立。NtCreateThreadEx是一個沒有被微軟公布的函數,由ntdll.dll文件導出。如果你有興趣可以去網上搜索該函數的原型,但注意由於它並是不公開的函數,所以網上所述的原型不一定正確,並且微軟也不會保證今後這個接口不會改變。

 

內核在接收到新建線程的請求後就會替我們著手新建一個線程,並且為該線程初始化一些參數(包括TEB等),最終將線程的入口設置為RtlUserThreadStart函數,該函數是由ntdll.dll導出的。RtlUserThreadStart的主要工作就是建立SHE的異常處理函數鏈,它有兩個參數,其中一個是用戶之前指定的線程入口函數,另一個是傳給該入口的參數。注意雖然RtlUserThreadStart有兩個參數,但這並不意味著曾經有人調用過它並且給它傳參了,這兩個參數實際上這是操作系統硬性寫入的兩個值。我們必須知道,實際上RtlUserThreadStart的執行只是因為操作系統把該線程EIP硬性設置到了RtlUserThreadStart的入口處而已。

 

RtlUserThreadStart會調用BaseThreadInitThunk函數,這個函數是由kernel.dll導出的,它主要將用戶指定的參數壓棧,接著就直接調用用戶指定的線程入口函數了。

 

至此,一個線程全面建立!

 

當然,如果這個線程是主線程的話,情況有些不同。因為這時候BaseThreadInitThunk調用的可還不會是你的main或WinMain函數,它將調用PE文件中指定的入口函數,如果是VC編譯的程序這個入口將是mainCRTStartup函數

 

作者 死亡的飛翔

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved