Wine中PE格式檔案的載入(一):Wine初始化過程
首先了解下Wine初始化過程。
我們執行”wine WeChat.exe”命令,發生的過程是怎麼樣的?接下來從wine原始碼一步步分析函式呼叫過程。
在loader/目錄下的原始碼編譯,由main.c生成了“wine”Linux可執行檔案;preloader.c生成了“pre-loader”Linux可執行檔案。
假設在終端通過命令“wine WeChat.exe”啟動微信;該過程涉及preloader,但是最終會執行到“wine”中的main函式。所以這裡從wine的main函式呼叫wine_init開始分析。
wine_init函式如下圖所示:
主要做了以下三件事:
1. 呼叫了mmap_init()函式,這個函式與預留地址空間相關。
2. 通過wine_dlopen()裝入由Wine提供的動態連線庫ntdll.dll。由於Wine特殊的系統結構,它不能使用微軟的ntdll.dll,而只能使用Wine自己的DLL,這樣的DLL稱為“內建(built-in)”DLL。
3. 執行內建ntdll.dll中的函式__wine_process_init()。先通過wine_dlsym()取得這個函式的入口地址,然後通過指標init_func進行呼叫。之所以要以這樣的方式呼叫,是因為這個函式在ntdll.dll中,而wine_dlopen()只是裝入了這個DLL、卻並未完成與此模組的動態連結。
一般而言,Wine既可以安裝使用本身的“內建”動態庫,也可以安裝使用Windows上相應的“原裝(native)”動態庫。但是,其中有幾個動態庫是特殊的,因而只能使用Wine自己的版本,ntdll就是其一。實際上Windows上的ntdll.dll中根本不會有__wine_process_init()這麼個函式。Wine自己的“內建”DLL實際上就是Linux的.so模組,是在Linux環境下由gcc產生的。GNU的C庫glibc中提供了一組用來處理.so模組的函式,上面的wine_dlopen()和wine_dlsym()最終都是呼叫這些庫函式(例如dlopen()和dlsym())來完成操作。
說明:
linux提供了載入和處理動態連結庫的系統呼叫,dlopen以指定模式開啟指定的動態連線庫檔案,並返回一個控制代碼給呼叫程序,dlsym通過控制代碼和連線符名稱獲取函式名或者變數名。__wine_process_init函式如下圖所示:
主要做了以下四件事情:
1. thread_init函式的呼叫
2. 由load_builtin_dll("kernel32.dll")裝入Wine的另一個“內建”動態連線庫kernel32.dll。可見kernel32.dll也必須是個“built-in(內建)”DLL。
3. 由LdrGetProcedureAddress()從裝入的kernel32.dll映像中取得函式__wine_kernel_init()的入口。