8 动态链接库的载入分析_第1页
8 动态链接库的载入分析_第2页
8 动态链接库的载入分析_第3页
8 动态链接库的载入分析_第4页
8 动态链接库的载入分析_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第八章 動態連結程式庫的載入分析動態連結程式庫 (DLL) 一直以來都是Windows的重要基礎,Windows CE也不例外。DLL對作業系統十分重要,本節的內容主要是分析loader.c中的程式碼,它負責載入EXE和DLL。這裏要討論的是關於DLL的部分,如無特別說明,本章中所引用的程式碼,都是來自Windows CE .NET原始程式碼樹中的CEROOTPRIVATEWINCEOSCOREOSNKKERNELloader.c。8.1 loader.c中程式碼的組織結構loader.c主要透過以下的API函數,來完成NK核心載入EXE和DLL處理程序的工作,這是使用者程式對DLL和EXE程

2、式操作的進入點。之後再透過一系列的函數呼叫,來完成DLL和EXE的載入和卸載,以及對處理程序和執行緒的其他操作。由於我們現在要討論的是有關DLL的部分,所以只列出了部分函式。l Win32 LoadLibrary callHANDLE SC_LoadLibraryW(LPCWSTR lpszFileName) HINSTANCE SC_LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)HANDLE SC_LoadDriver(LPCWSTR lpszFileName)HANDLE SC_LoadKernelLib

3、rary(LPCWSTR lpszFileName)HANDLE SC_LoadIntChainHandler(LPCWSTR lpszFileName, LPCWSTR lpszFunctionName, BYTE bIRQ)這幾個函數基本上都是負責DLL的載入,它們都呼叫了LoadOneLibraryW這個函數,函數的原型如下:HANDLE LoadOneLibraryW(LPCWSTR lpszFileName, DWORD fLbFlags, WORD wFlags)這個函數在載入DLL的前後,處理一些必要的細節工作。更重要的是呼叫了LoadOneLibraryPart2這個函數,載入

4、的重要工作都在該函數的程式碼中完成,在本章的後面會詳細分析它的內容。l Win32 FreeLibrary callHANDLE SC_FreeIntChainHandler(HANDLE hLib)BOOL SC_FreeLibrary(HANDLE hInst)主要是FreeLibrary函數。這個API函數負責卸載DLL。它呼叫FreeOneLibrary這個函數,函數原型如下:BOOL FreeOneLibrary(PMODULE pMod, BOOL bCallEntry)卸載的過程主要是由FreeOneLibraryPart2負責。在後面的章節會有具體的分析。l Win32 Get

5、ProcAddress callLPVOID SC_GetProcAddressA(HANDLE hInst, LPCSTR lpszProc)LPVOID SC_GetProcAddressW(HANDLE hInst, LPCSTR lpszProc)這兩個函數可以得到處理程序的位址。8.2 module structure這是記錄DLL資訊的重要資料結構之一,每一個程式對應一個module,但一個module可以對應多個處理程序。在系統中維持一條已載入模組的串列,它是一個單向鏈結串列。第一個元素是pModList。程式碼8.1 module structuretypedef struct

6、 Module LPVOID lpSelf; PMODULE pMod; LPWSTR lpszModName; DWORD inuse; DWORD calledfunc; WORD refcntMAX_PROCESSES; LPVOID BasePtr; DWORD DbgFlags; LPDBGPARAM ZonePtr; ulong startip; openexe_t oe; e32_lite e32; o32_lite *o32_ptr; DWORD breadcrumb; DWORD dwNoNotify; WORD wFlags; BYTE bTrustLevel; BYTE

7、bPadding; PMODULE pmodResource; DWORD rwLow; DWORD rwHigh; Module;/* 用於驗證,指向自己的指標 */* 鏈結串列中的下一個模組 */* 模組名字 */* 使用狀況的位元向量 */* 被呼叫的進入點,但不退出 */* 處理程序的引用計數 */* DLL載入基址 */* 偵錯旗標 */* Debug zone 指標 */* 基於0的進入點 */* 執行檔指標 */* e32標頭 */* O32串列指標 */* 每個處理程序對應一個位元,當Notify被禁用時設為1 */* 包含資源的模組 */* ROM DLL中可讀寫段的基底位址

8、 */* ROM DLL中可讀寫段的高位位址 */refcntMAX_PROCESSES是每個處理程序的引用計數。BasePtr是DLL載入的基底位址,這是比較需要注意的部分。oe、e32、*o32_ptr放在後面介紹。8.3 LoadOneLibraryPart2載入DLL的過程減少DLL的引用計數透過檔案名字查找模組,得到pMod模組已經載入到處理程序的位址空間了嗎?增加引用計數,且如果是第一次載入處理程序,則複製欄位為模組分配記憶體初始化 module structure系統能找到指定的DLL檔嗎?增加引用計數使該模組成為pModList的第一個元素細節處理傳回NULL傳回pMod參數與

9、載入已載入的mod符合嗎?傳回NULL否是不符合符合否是呼叫LoadOneLibraryPart2 圖8.1 LoadOneLibraryPart2的基本執行步驟函數原型:PMODULE LoadOneLibraryPart2(LPWSTR lpszFileName, DWORD fLbFlags,WORD wFlags)參數lpszFileName指向DLL名字的字串,該名字確定了模組 (module) 的檔案名稱,而與它儲存在模組庫中的名字無關。函式庫模組以LIBRARY為關鍵字,在模組定義檔 (.def) 中定義。參數hFile是保留做將來使用的,現在必須是NULL。參數wFlags指定

10、載入模組時所要處理的工作。其值可以為0、DONT_RESOLVE_DLL_REFERENCES、LOAD_LIBRARY_AS_DATAFILE或者是LOAD_LIBRARY_IN_KERNEL。DONT_RESOLVE_DLL_REFERENCE:如果使用這個旗標,而且可執行模組本身是一個DLL,則系統並不呼叫DllMain來初始化和結束處理程序和執行緒。此外,一個DLL可能會引入包含在另一個DLL中的函數,而系統映射一個DLL時也會自動載入,當這個旗標被設置之後,系統就不再自動載入額外的DLL。載入過程如圖8.1所示。載入的過程首先設定wFlags的值,然後呼叫pMod = FindMod

11、ByName(dllname),藉由查找pModList串列,來得到DLL的pMod指標。對於已經載入的module,增加其引用次數。由以下的程式碼來完成:if (!(pMod->inuse & (1<<pCurProc->procnum) pMod->inuse |= (1<<pCurProc->procnum);pMod->calledfunc &= (1<<pCurProc->procnum);return pMod->refcntpCurProc->procnum+;呼叫該module的

12、處理程序是目前的處理程序。將目前處理程序對該module的引用計數加一,傳回pMod,便完成了載入的過程。如果module之前並未載入,則需要一系列的工作:1) 為module配置記憶體2) 初始化module,呼叫InitModule3) 增加引用計數4) 將這個新的pMod插入module鏈結串列中,作為第一個元素。程式碼如下:EnterCriticalSection(&ModListcs);pMod->pMod = pModList;pModList = pMod;LeaveCriticalSection(&ModListcs)8.4 DLL載入過程 InitMod

13、ule的執行InitModule處理載入一個新的DLL所要做的大部分工作。函數原型如下:DWORD InitModule (PMODULE pMod, LPWSTR lpszFileName, LPWSTR lpszDllName, DWORD fLbFlags, WORD wFlags)在函數InitModule中,具體執行步驟如下:1) 初始化pMod中的欄位 (field)2) 呼叫函數OpenADll,產生執行檔指標 (openexe_t)3) 載入 module的e32 資訊,產生e32標頭資訊 (e32_lite)4) 檢查bTrust level參數5) 配置記憶體給DLL,取得

14、Module->BasePtr,即載入DLL的基底位址6) 配置記憶體給name 和O32 物件,讀取這個module的O32資訊7) 改變module的名字8) 重定位映射9) 呼叫函數FindEntryPoint ,找到EXE的起始IP (或者DLL的進入點)整體來說,這個函數就是負責設定pMod各個欄位的初始值、取得可執行檔的指標以及EXE或DLL的進入點。在這個過程中,將各個步驟、判斷中的錯誤碼,傳回給LoadOneLibraryPart2,用來作為判斷pMod是否建立成功的資訊,以繼續以後的工作。下面,將對這幾個重要步驟作詳細分析。8.4.1呼叫OpenADll,產生執行檔指標

15、 (openexe_t)module中的oe是可執行檔的指標,每個程式對應一個module,每個module對應一個可執行檔指標。oe的型別openexe_t定義如下:程式碼8.2 openexe_t structuretypedef struct openexe_t union int hppfs; HANDLE hf; TOCentry *tocptr; ; BYTE filetype; BYTE bIsOID; WORD pagemode; DWORD offset; union Name *lpName; CEOID ceOid; ; openexe_t;/ ppfs handle/

16、物件儲存指標/ rom entry pointer/檔案類型/分頁模式/偏移這實際上是與檔案處理等有關的結構,描述可執行程式碼的位址、分頁模式、偏移等資訊。OpenADll呼叫OpenExe,由OpenExe呼叫SafeOpenExe,設定可執行檔指標的工作基本上都在SafeOpenExe中完成。BOOL SafeOpenExe(LPWSTR lpszName, openexe_t *oeptr, BOOL bIsDLL, BOOL bAllowPaging, OEinfo_t *poeinfo)SafeOpenExe還執行以下的工作:1) 尋找EXE檔案所在的目錄2) 按照指定路徑尋找檔案3

17、) 在Windows目錄中尋找檔案4) 在根目錄中尋找檔案在檔案系統中搜尋DLL檔案時,根據搜尋過程中的資訊設定其中各個欄位的值。並藉由下面的程式碼取得檔案的儲存指標:oeptr->hf=CreateFileW(LPWSTR)poeinfo->tmpname, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);即透過CreateFile開啟檔案。如果在ROM中有備份,則設定oeptr->tocptr的值。傳回結果:如果找到,則傳回1,否則傳回0。8.4.2設定module的e32 標頭資訊module的成員e32的型

18、別定義如下:程式碼8.3 e32_lite structuretypedef struct e32_lite unsigned short e32_objcnt; BYTE e32_cevermajor; BYTE e32_ceverminor; unsigned long e32_stackmax; unsigned long e32_vbase; unsigned long e32_vsize; unsigned longe32_sect14rva; unsigned longe32_sect14size; structinfo e32_unitLITE_EXTRA; e32_lite,

19、*LPe32_list;/* PE 32-bit .EXE header */* 記憶體物件個數 */* 版本資訊 */* 版本資訊 */* 堆疊的最大值 */* module的虛擬記憶體基底位址 */* 整個映射的Virtual 大小 */* section 14 rva */* section 14 size */* Array of extra info units */這是與記憶體相關的一組資料,在InitModule中有一段程式碼如下:/ load O32 info for the moduleeptr = &pMod->e32;if (retval = LoadE32

20、(&pMod->oe, eptr, &flags, &entry, (pMod->wFlags & LOAD_LIBRARY_AS_DATAFILE) ? 1 : 0, bAllowPaging, &pMod->bTrustLevel) return retval;它藉由呼叫LoadE32函數讀取DLL檔,設定e32標頭的各部分內容,設定堆疊、虛擬記憶體基底位址、映射等正確的值,使接下來DLL的記憶體配置工作能夠順利進行。8.4.3 DLL的記憶體配置與Module->BasePtr的取得InitModule中的這一部分程式碼的主

21、要作用是設定pMod->BasePtr。為了方便解釋,圖8.2是簡化的流程圖。是 “execute in place” DLL 嗎?是載入核心位址空間嗎?呼叫函數 VirtualAlloc 設定 pMod->BasePtr保留虛擬記憶體成功?由上往下載入修改DLL Load Base取得連續的實體頁面,設定pMod->BasePtr藉由函數 MapPtrProc 設定pMod->BasePtr是否是否否是圖8.2 DLL配置記憶體的過程由可執行檔指標pMod->oe.filetype可得到檔案類型。DLL有兩種映射,一種是普通檔案,需要將其載入到RAM中執行。為了

22、節省時間,還有另一種方式就是XIP (Execute in place) 方式。顧名思義,就是可以立即執行,而不用載入RAM中,所以相應pMod->BasePtr的值也有兩種不同的情況。如果不是XIP (Execute in place) DLL,則保留足夠的虛擬記憶體位址空間以容納整個映射。從已經存在的DLL的底部開始由上往下分配。DLL載入到核心位址空間 (kernel space) 或是使用者空間 (user space) 也有差別。對於載入核心位址空間的DLL,必須為其取得連續的實體頁面。因為核心空間位址和虛擬記憶體位址之間是靜態映射的,這是為了讓核心執行時,不必進行位址的轉換,

23、藉以加快核心的執行。例如在Platform Builder 下產生的platform,啟動時載入的features等就是載入核心的DLL。if (wFlags & LOAD_LIBRARY_IN_KERNEL) PHYSICAL_ADDRESS paRet;/ Loading in the kernel address space paRet = GetContiguousPages(DWORD) (eptr->e32_vsize + PAGE_SIZE - 1) / PAGE_SIZE, 0, 0);if (paRet = INVALID_PHYSICAL_ADDRESS |

24、!(pMod->BasePtr = (PVOID) Phys2Virt(paRet) return ERROR_OUTOFMEMORY;它先向系統要求連續的實體位址頁面,取得實體位址,再將其直接映射到虛擬位址空間,並把虛擬記憶體位址傳給pMod->BasePtr。有關GetContiguousPages及Phys2Virt的細節請參考其他的原始檔案。如果不是載入核心位址空間,則必須為其保留位址空間,以避免其他DLL載入同樣的位址空間,而使該DLL的卸載發生問題。程式碼如下: else / try to honor the Dll's relocation base to p

25、revent relocationif (pTOC->ulKernelFlags & KFLAG_HONOR_DLL_BASE) && (eptr->e32_vbase + eptr->e32_vsize < ROMDllLoadBase) pMod->BasePtr = VirtualAlloc (LPVOID)(ProcArray0.dwVMBase + eptr->e32_vbase), eptr->e32_vsize, MEM_RESERVE | MEM_IMAGE, PAGE_NOACCESS); DEBUGMSG (

26、pMod->BasePtr, (L"Loading DLL '%s' at the preferred loading address %8.8lxn", lpszFileName, ZeroPtr (pMod->BasePtr); 其中,eptr->e32_vbase是module映射的虛擬記憶體基底位址,eptr->e32_vsize是module的虛擬記憶體大小。利用VirtualAlloc在目前處理程序的虛擬記憶體位址空間保留一個區域,基底位址是處理程序位址空間基底位址ProcArray0.dwVMBase + module映

27、射的虛擬記憶體基底位址。大小受模組影響。它使用參數MEM_RESERVE | MEM_IMAGE,只保留了處理程序的一部分虛擬記憶體空間,而沒有實際分配實體記憶體。而且這部分空間不能透過其他記憶體配置的操作如malloc和LocalAlloc使用,也不能被其他DLL佔用。如果配置基底位址時,發現要使用的區域已經被保留,即已經有其他DLL佔用,造成配置虛擬記憶體失敗,則由上往下配置。/ allocate top-down if we can't load it in the dll's preferred load base.if (!pMod->BasePtr &

28、& !(pMod->BasePtr = VirtualAlloc (LPVOID) ProcArray0.dwVMBase,eptr->e32_vsize, MEM_RESERVE | MEM_TOP_DOWN | MEM_IMAGE,PAGE_NOACCESS) return ERROR_OUTOFMEMORY;配置的基底位址是目前處理程序的虛擬記憶體空間基底位址,大小不變,只是由上往下配置。這樣就得到了pMod->BasePtr。下面的程式碼修改DLL的載入基址,這是個全域變數。if (ZeroPtr(pMod->BasePtr) < (DWORD)D

29、llLoadBase)DllLoadBase = ZeroPtr(pMod->BasePtr);如果要載入的DLL是XIP的,即表示它是在ROM中,不需載入到處理程序位址空間,如coredll.dll就是XIP型的DLL。只需在ROM中找到要執行的DLL的基底位址,傳給pMod->BasePtr即可,如以下程式碼所示: else e32_rom *e32rp = (e32_rom *) pMod->oe.tocptr->ulE32Offset;pMod->BasePtr = (LPVOID) MapPtrProc (e32rp->e32_vbase, Pro

30、cArray);if (wFlags & LOAD_LIBRARY_IN_KERNEL) && !IsKernelVa (pMod->BasePtr) return ERROR_BAD_EXE_FORMAT;8.4.4 name和o32物件的記憶體配置o32物件是與存取控制有關的物件。結構定義如下。typedef struct o32_lite unsigned long o32_vsize;unsigned long o32_rva;unsigned long o32_realaddr;unsigned long o32_access;unsigned long

31、 o32_flags;unsigned long o32_psize;unsigned long o32_dataptr; o32_lite, *LPo32_lite;8.4.5 重定位映射非XIP映射需要重新定址 (Relocate)。if (pMod->oe.filetype != FT_ROMIMAGE) / Non-XIP image needs to be relocated./if (pMod->oe.pagemode = PM_NOPAGING) && !(pMod->wFlags & LOAD_LIBRARY_AS_DATAFILE)

32、&& !Relocate (eptr, pMod->o32_ptr, (ulong)pMod->BasePtr,(wFlags & LOAD_LIBRARY_IN_KERNEL) ? 0 : ProcArray0.dwVMBase) return ERROR_OUTOFMEMORY;這裏呼叫Relocate對 DLL重新定址,傳遞的參數為:可執行檔指標eptr,o32標頭資訊pMod->o32_ptr,DLL載入的基底位址pMod->BasePtr,如果DLL載入到核心,則最後一個參數為0,否則傳遞的參數是目前處理程序虛擬記憶體空間的基址。這裏還

33、需要解釋一下重新定址 (Relocate) 的過程。重新定址的過程,是將DLL映射位址定位到目前處理程序的位址空間,即位址0x00000000-0x02000000,從0x01FFFFFF開始由上往下,最頂端是coredll.dll,然後是其他DLL。如果是XIP映射,即DLL在ROM中。如果module被載入記憶體的Slot1 (DLL高位址區域),或者載入到核心中,則需要記錄為這個module而定址的讀寫區。程式碼如下:o32_lite *optr = pMod->o32_ptr;/ If the module is loaded into the Slot 1 (DLL High)

34、 area or into / the kernel we need to record where the read/write section has / been located for this module./if (IsModCodeAddr (pMod->BasePtr) | IsKernelVa(pMod->BasePtr) / find the high/low of RW sectionsfor (loop = 0; loop < eptr->e32_objcnt; loop +, optr +) if (optr->o32_flags &am

35、p; IMAGE_SCN_MEM_WRITE) && !(optr->o32_flags & IMAGE_SCN_MEM_SHARED) if (pMod->rwLow > optr->o32_realaddr)pMod->rwLow = optr->o32_realaddr;if (pMod->rwHigh < optr->o32_realaddr + optr->o32_vsize)pMod->rwHigh = optr->o32_realaddr + optr->o32_vsize;8.

36、4.6 EXE的起始IP這裡是藉由呼叫函數FindEntryPoint ,來設定pMod->startip。if (entry) if (wFlags & LOAD_LIBRARY_IN_KERNEL) | !pMod->e32.e32_sect14rva) pMod->startip = FindEntryPoint(entry,&pMod->e32,pMod->o32_ptr);else HANDLE hLib;if (!(hLib = LoadOneLibraryW (L"mscoree.dll",0,0) | !(pMo

37、d->startip = (DWORD)GetProcAddressA (hLib,"_CorDllMain") return ERROR_DLL_INIT_FAILED;如果是載入到核心,則呼叫FindEntryPoint函數,將pMod的e32資訊和o32標頭當作參數,尋找進入點的記憶體物件,最後取得實際位址。如果不是載入到核心,則是利用GetProcAddressA函數取得DLL的進入點。8.5 實例分析1. 範例環境的建立過程1) 在Platform Builder 4.0下,使用其所提供的emulator作為platform的BSP,建立新的platform

38、 tiny kernel。2) Build後產生新的Platform loader_test,它同時產生debug和release版本。用debug版本偵錯,追蹤loader.c,可以看到這個用作測試的loader_test啟動時載入各個DLL的過程。3) 建立控制台應用程式,編譯產生loader_test上的應用程式 console_test。要注意的是,因為這裏建立起來的是tiny kernel,所以不支援一些C程式庫函數。當然,你也可以建立其他類型的platform。4) 建立空的動態連結程式庫dll_test,用console_test來呼叫dll_test,追蹤DLL載入的過程。主要

39、是看其載入的地址pMod->BasePtr。在Platform Builder的target中看Modules and Symbols視窗,可以看到DLL載入的映射位址範圍和重新定址後的位址範圍。2. 啟動時載入DLL對loader_test的debug版本進行追蹤,在loader.c的程式碼上設置中斷點,觀察其在啟動時,依次載入的幾個DLL的情況。表8.1是loader_test啟動完成時,已載入的幾個DLL和EXE:表8.1載入的DLL和EXEModuleImage Address RangeRelocated Data Address RangeStatuscoredll.dll0x03FC0000-0x03FF6FFF0x01FFF000-0x01FFF780loadedfilesys.exe0x04010000-0x04049FFFloadedfsdmgr.dll0x03F80000-0x03F92FFF0x01FF9000-0x01FF98F0loadedKd.dll0x80293000-0x802A9FFF0x80354000-0x8035A584loadedNk.exe0x80220000-0x802B8FFF0x80330000-0x8034A7E6Loadedrelfsd.dll

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论