漫談相容核心之十:Windows的進程建立和映像裝入

來源:互聯網
上載者:User

   關於Windows的進程建立和映像裝入的過程,“Microsoft Windows Internals 4e”一書的第六章中有頗為詳細的說明。本文就以此為依據,夾譯、夾敘、夾議地作一介紹。書中說,建立進程的過程分成六個階段,發生於作業系統的三個部分中,那就是:Windows用戶端即某個應用進程的包括Kernel32.dll在內的動態串連庫,Windows的“執行體”、即核心(確切地說是核心的上層),以及Windows子系統的服務進程Csrss中。這六個階段是:
    1. 開啟目標映像檔案。
    2. 建立Windows的“執行體進程對象”,也就是核心中的“進程式控制制塊”資料結構。
    3. 建立該進程的初始(第一個)線程,包括其堆棧、上下文、以及“執行體線程對象”,即核心中的“線程式控制制塊”資料結構。
    4. 將建立進程通知Windows子系統。
    5. 啟動初始線程地運行(除非因為參數中的CREATE_SUSPENDED標誌位為1而一建立便被掛起)。
    6. 在新進程和線程的上下文中完成使用者空間的初始化,包括裝入所需的DLL,然後開始目標程式的運行。
下面分階段敘述。

第一階段:開啟目標映像檔案
    在Win32位API中,建立進程是由CreateProcess()完成的。這實際上是個宏定義,根據不同的情況定義成CreateProcessA()或CreateProcessW()之一,這兩個函數都在kernel32.dll中(可以用工具depends觀察)。兩個函數的區別僅在於字串的表達,前者採用ASCII字元,而後者採用“寬字元”、即Unicode。實際上Windows的內部都採用寬字元,所以前者只是把字串轉換成寬字元格式,然後調用後者。
    可以在Windows上啟動並執行可執行軟體有好幾類,處理的方法自然就不一樣:
    ● Windows的32位.exe映像,直接運行。
    ● Windows的16位.exe映像,啟動ntvdm.exe,以原有命令列作為參數。
    ● DOS的.exe、.com、或.pif映像,啟動ntvdm.exe,以原有命令列作為參數。
    ● DOS的.bat或.cmd批命令檔案(指令碼),啟動cmd.exe,以原有命令列作為參數。
    ● POSIX可執行映像,啟動posix.exe,以原有命令列作為參數。
    ● OS/2可執行映像,啟動os2.exe,以原有命令列作為參數。
    這裡面最重要的當然是32位的.exe映像,而最後兩類現在已經很少見了。從對於除32位.exe以外的各種映像的處理,讀者不妨對比一下Wine對.exe映像的處理,看看這裡有著什麼樣的相似性。
    當然,我們在這裡只關心32位.exe映像。對於這一類映像,CreateProcess()首先開啟映像檔案,再為其(分配)建立一個“Section”、即記憶體區間。建立記憶體區間的目的當然是要把映像檔案影射到這個區間,不過此時還不忙著映射,還要看看。看什麼呢?首先是看已經開啟的目標檔案是否一個合格的.exe映像(萬一是DLL映像?)。還要看的事就有點出乎讀者意外了,看的是在“註冊表”中的這個路徑:
    HKLM/SOFTWARE/Microsoft/Windows NT/CurrentVersion/Image File Execution Options

    用depends可以看到,ntdll.dll中有個函數LdrQueryImageFileExecutionOption s(),就是專門幹這個事的。
    如果上述路徑下有以目標映像檔案的檔案名稱和副檔名為“鍵”的表項,例如“image.exe”,而表項中又有名為“Debugger”的值,那麼這個值(一個字串)就替換了原來的目標檔案名,變成新的目標映像名,並重新執行上述的第一階段操作。這樣做的目的當然是為偵錯工具提供方便,但是我們不妨設想:如果駭客或某個木馬程式設法在註冊表中加上了一個表項?這時候使用者以為是啟動了程式A,而實際啟動的卻是B!。

第二階段:建立核心中的進程對象
    我們知道,Linux上的每個進程(線程)都有一個“進程式控制制塊”、即task_struct資料結構,與具體進程/線程有關的絕大部分資訊都集中儲存在這個資料結構中。而Windows則有所不同。首先,Windows的進程和線程各有不同的“對象”、即資料結構,從概念上把線程和進程分離開來。線程是具體的(執行)上下文,是CPU調度的單位和目標,而進程只是若干共用地址空間和特性(如調度優先順序)的線程的集合。於是,進程有進程的資料結構,線程有線程的資料結構。這就好像是對一組task_struct資料結構“提取公因子”所形成的結果,這個舉措是很好理解的。進一步,Windows又把本可集中儲存的的進程資料結構也拆分成好幾個對象,有的在核心中,有的則在使用者空間。
    核心中與進程有關的對象有:
    ● EPROCESS。即struct _EPROCESS,在“Internals”書中也稱為“Process Block”。它代表著Windows的一個進程,‘E’表示“Executive”,微軟把Windows核心中的上層稱為“Executive”、以區別於下層的裝置驅動和記憶體管理等成分、一般翻譯成“執行體”。“Executive”也有“管理”、“運行”的意思(所以CEO就是“總裁”)。
    ● KPROCESS。這是EPROCESS內部的一個成分,其名稱就叫“Pcb”。
    ● W32PROCESS。下面將要講到,在使用者空間有個“Windows子系統”的服務進程csrss。這個服務進程為系統中的每個Windows應用進程都維持著一個資料結構,其中包含了一些與視窗和圖形介面有關的資訊。而對於視窗和圖形介面的操作原來也是由csrss在“客戶”進程的請求下實現的。但是,為了提高效率,後來把這部分功能移到了核心中。與此相應,有關資料結構的一部分也需要移到核心中,就成了W32PROCESS。
    既然KPROCESS是EPROCESS一部分,實際上核心中與進程有關的對象實際上只有兩種,就是EPROCESS和W32PROCESS。不過這裡沒有包括“開啟對象表”,那也是每個進程都有的(Linux核心中的“開啟檔案表”也在進程式控制制塊的外面)。
    使用者空間與進程有關的對象有:
    ● 如上所述,把W32PROCESS資料結構移入核心以後,csrss仍需要為每個Windows進程保持一些別的資訊,所以csrss內部仍有按進程的相應資料結構。
    ● PEB(Process Environment Block)、即“進程環境塊”。PEB中記錄著進程的運行參數、映像裝入地址等等資訊。PEB在使用者空間中的位置是固定的,總是在0x7ffdf000。在Windows中,使用者空間和系統空間的分界線是2GB、即0x80000000,所以PEB在靠近使用者空間頂端的地方。

    “Internals”書中並未給出有關資料結構的定義,但是通過Debug手段給出了EPROCESS的內部結構:

CODE:

   +0x000  Pcb               : _KPROCESS
   +0x06c  ProcessLock       : _EX_PUSH_LOCK
   +0x070  CreateTime        : _LARGE_INTEGER
   +0x078  ExitTime          : _LARGE_INTEGER
   +0x080  RundownProtect    : _EX_RUNDOWN_REF
   +0x084  UniqueProcessId    : Ptr32Void
   +0x088  ActiveProcessLinks   : _LIST_ENTRY
   +0x090  QuotaUsage        : [3]  Uint4B
   +0x09c  QuotaPeak         : [3]  Uint4B
   +0x0a8  CommitCharge      : Uint4B
   +0x0ac  PeakVirtualSize    : Uint4B
   +0x0b0  VirtualSize        : Uint4B
   +0x0b4  SessionProcessLinks  : _LIST_ENTRY
   +0x0bc  DebugPort         : Ptr32Void
   +0x0c0  ExceptionPort      : Ptr32Void
   +0x0c4  ObjectTable        : Ptr32_HANDLE_TABLE
   +0x0c8  Token             : _EX_FAST_REF
   +0x0cc  WorkingSetLock    : _FAST_MUTEX
   +0x0ec  WorkingSetPage    : Uint4B
   +0x0f0  AddressCreationLock  : _FAST_MUTEX
   +0x110  HyperSpaceLock    : Uint4B
   +0x114  ForkInProgress     : Ptr32_ETHREAD
   +0x118  HardwareTrigger    : Uint4B
可見,EPROCESS的第一個成分是Pcb,其類型是_KPROCESS、即KPROCESS,這是一個大小為0x6c的資料結構。書中也給出了它的內部結構。
    “Undocumented Windows 2000 Secrets”一書也以Debug手段給出了這個資料結構的內部結構,但是列出的結構與此有很大的不同,也許是因為版本的關係。從所列的內容看,似乎“Secrets”一書倒是正確的,因為那裡所列的EPROCESS結構中有關於虛存的成分Vm,是一個大小為0x50的資料結構,而這裡沒有,但是虛存(地址空間)顯然是進程的主要資源,所以EPROCESS資料結構中理應有它的位置。由此看來,“Secrets”一書所述更接近於案頭和伺服器系統的現實,而“Internals”書中所列可能更接近於不帶MMU的嵌入式系統。而且,“Secrets”一書還在附錄C中給出了通過逆向工程手段得到的EPROCESS和PEB等資料結構的定義(代碼),這當然是很有價值的。
    那麼,如果確有不同版本的EPROCESS,這會有什麼影響呢?首先,使用者空間的應用程式不能直接存取核心中的EPROCESS資料結構,所以具體的EPROCESS資料結構屬於核心的內部實現,只要核心中的各種成分、各個環節都配套成龍,“自圓其說”,就沒有什麼問題,這跟Linux核心中一些條件編譯和裁剪的效果是類似的。可是,另一方面,對於可以動態裝入的.sys模組,如果在模組中需要訪問這些資料結構,那就可能有問題了,因為.sys模組都是以二進位映像的形式提供的,不像在Linux中那樣可以由原始碼重新編譯。怎麼辦呢?我們可以到Windows的DDK中去找找答案。
    在DDK的.h檔案中,有函數IoGetCurrentProcess()的申明:

CODE:

NTKERNELAPI
PEPROCESS
IoGetCurrentProcess(
    VOID
);
這個函數是核心為.sys模組提供的支撐函數,相當於由Linux核心匯出的函數。其傳回值類型是PEPROCESS,就是指向EPROCESS資料結構的指標。顯然,這跟Linux核心中的current相似,調用的目的是擷取當前進程的EPROCESS資料結構(指標)。但是,DDK的.h檔案中卻並未給出EPROCESS資料結構的定義,所以調用這個函數所得到的僅僅是個指標,實際上與void*並無區別。這意味著在.sys模組中是不允許直接存取其內部成分的。那麼,.sys模組如何使用這個指標呢?下面就是一個例子,還是在DDK中:

CODE:

NTKERNELAPI
VOID
MmProbeAndLockProcessPages (
    IN OUT PMDL MemoryDescriptorList,
    IN PEPROCESS Process,
    IN KPROCESSOR_MODE AccessMode,
    IN LOCK_OPERATION Operation
    );
這個函數的作用是鎖定某個進程的某些儲存頁面(不讓換出),其輸入參數之一就是指向該進程的EPROCESS結構的指標。當然,這個函數也是由核心提供的(屬於我們所說的裝置驅動介面)。所以指標的提供者和使用者都是核心,只要這二者配套即可,.sys模組在這裡只不過是傳遞了一下,所以也不會有問題。
    假定proc是指向進程式控制制塊的指標,並且進程式控制制塊中有個成份X,是個整數,那麼在Linux的動態安裝模組中可以直接用“proc->X”訪問這個成分,但是在Windows的.sys模組中則只能通過類似於get_X()、set_X()一類的支撐函數訪問這個成分。將資料結構的內容跟對於這些內容的操作(method)相分離,正是“對象”與“資料結構”的區別所在。而將資料結構的內容“封裝”起來,也正是微軟所需要的,因為它不願意公開這些資料結構。
    對於相容核心的開發,這意味著我們不必拘泥於採用與Windows完全一致的EPROCESS資料結構(儘管“Secrets”的附錄C已經給出了它的定義),一些內部的操作和處理也不必完全與之相同,而只要與DDK所規定的介面相符就可以了。

    瞭解了有關的進程對象,我們可以言歸正傳了。
    所謂建立核心中的進程對象,實際上就是建立以EPROCESS為核心、為基礎的相關資料結構,這就是系統調用NtCreateProcess()要做的事情,主要包括:
    ● 分配並設定EPROCESS資料結構。
    ● 其他相關的資料結構的設定,例如“開啟對象表”。
    ● 為目標進程建立初始的地址空間。
    ● 對目標進程的“核心進程塊”KPROCESS進行初始化。
    ● 將系統DLL的映像映射到目標進程的(使用者)地址空間。
    ● 將目標進程的映像映射到其自身的使用者空間。
    ● 設定好目標進程的“進程環境塊”PEB。
    ● 映射其他需要映射到使用者空間的資料結構,例如與“當地語言支援”、即NLS有關的資料結構。
    ● 完成EPROCESS建立,將其掛入進程隊列(注意受調度的是線程隊列而不是進程隊列)。
    這裡將系統DLL、實際上是ntdll.dll、映射到目標進程的使用者空間是很關鍵的。這是因為,除別的、主流的功能和作用外,ntdll.dll同時也起著相當於Linux中ELF“解譯器”的作用,也擔負著為目標映像建立動態串連的任務。

    值得注意的是,NtCreateProcess()與CreateProcess()不同。CreateProcess()建立一個進程並使其(初始線程)運行,除非在建立時就指定要將其掛起。而NtCreateProcess(),則只是在核心中建立該進程的EPROCESS資料結構並為其建立起地址空間。這隻是個空殼架子,因為沒有線程就談不上運行,調度的目標是線程而不是線程。而且,對NtCreateProcess()的調用還有個條件,那就是目標映像已經被映射到一個儲存區間(Section)中。

第三階段:建立初始線程
    如上所述,進程只是個空架子,實際的運行實體是裡面的線程。所以下一步就是建立目標進程的初始線程,即其第一個線程。
與EPROCESS相對應,線程的資料結構是ETHREAD,並且其第一個成分是資料結構KTHREAD,稱為TCB。同樣,“Internals”和“Secrets”兩本書中所列的ETHREAD內部結構有所不同,後者的附錄C中給出了通過逆向工程得到的ETHREAD資料結構定義。
    同樣,從Windows DDK中申明的一些函數也可以看出,.sys模組只是傳遞ETHREAD指標或KTHREAD指標(由於KTHREAD是ETHREAD中的第一個成分,二者實際上是一回事),而不會直接存取它的具體成分。

CODE:

PKTHREAD NTAPI KeGetCurrentThread();

NTKERNELAPI  KPRIORITY
KeQueryPriorityThread (IN PKTHREAD Thread);

NTKERNELAPI  LONG
KeSetBasePriorityThread (IN PKTHREAD Thread, IN LONG Increment);

NTKERNELAPI  PDEVICE_OBJECT
IoGetDeviceToVerify(IN PETHREAD Thread);
此外,就像進程有“進程環境塊”PEB一樣,線程也有“線程環境塊”TEB,KTHREAD結構中有個指標指向其存在於使用者空間的TEB。前面講過,PEB在使用者空間的位置是固定的,PEB下方就是TEB,進程中有幾個線程就有幾個TEB,每個TEB佔一個4KB的頁面。

    這個階段的操作是通過系統調用NtCreateThread()完成的,主要包括:
    ● 建立和設定目標線程的ETHREAD資料結構,並處理好與EPROCESS的關係(例如進程塊中的執行緒計數等等)。
    ● 在目標進程的使用者空間建立並設定目標線程的TEB。
    ● 將目標線程在使用者空間的起始地址設定成指向Kernel32.dll中的BaseProcessStart()或BaseThreadStart(),前者用於進程中的第一個線程,後者用於隨後的線程。使用者程式在調用NtCreateThread()時也要提供一個使用者級的起始函數(地址),BaseProcessStart()和BaseThreadStart()在完成初始化時會調用這個起始函數。ETHREAD資料結構中有兩個成份,分別用來存放這兩個地址。
    ● 設定目標線程的KTHREAD資料結構並為其分配堆棧。特別地,將其上下文中的斷點(返回點)設定成指向核心中的一段程式KiThreadStartup,使得該線程一旦被調度運行時就從這裡開始執行。
    ● 系統中可能登記了一些每當建立線程時就應加以調用的“通知”函數,調用這些函數。

第四階段:通知Windows子系統
    Windows、確切地說是Windows NT、當初的設計目標是支援三種不同系統的應用軟體。第一種是Windows本身的應用軟體,即所謂“Native”Windows軟體,這是微軟開發Windows NT的真正目的。第二種是OS/2的應用軟體,這是因為當時微軟與IBM還有合作關係。第三種是與Unix應用軟體相似、符合POSIX標準的軟體,那是因為當時美國的軍方採購有這樣的要求。不過實際上微軟對後兩種應用的支援從一開始就是半心半意的,後來翅膀長硬了,就更不必勉為其難了。但是,儘管如此,當初在設計的時候還是考慮了對不同“平台”的支援,即在同一個核心的基礎上配以不同的外圍軟體,形成不同的應用軟體運行環境,微軟稱之為“子系統(Subsystem)”。於是,在Windows核心上就有了所謂“Windows子系統”、“OS/2子系統”、和“POSIX子系統”。當然,時至今日,實際上只剩下Windows子系統了。
    那麼,所謂子系統是怎樣構成的呢?“Internals”書中闡明了Windows子系統的構成,說這是由下列幾個要素構成的。
    一、 子系統進程csrss.exe。包括了對下列成分和功能的支援:
        ● 控制台(字元型)視窗的操作。面向控制台/終端的應用本身不支援視窗操作(例如視窗的移動、大化/小化、遮蓋等等),但是又需要在視窗中運行,所以需要有額外的支援。
        ● 進程和線程的管理。例如彈出一個對話窗,說某個進程沒有響應,讓使用者選擇是否結束該進程的運行,等等。每個Windows進程/線程再建立/退出時都要向csrss.exe進程發出通知。
        ● DOS軟體和16位Windows軟體在(32位)Windows上的運行。
        ● 其它。包括對當地語言(IME)的支援。
這個進程之所以叫csrss,是“C/S Run-time SubSystem”的意思,csrss是Windows子系統的服務進程。其實三個子系統都是C/S結構,但是OS/2子系統的服務進程稱為os2ss,POSIX子系統的服務進程稱為Psxss。之所以如此,據“Internals”說,是因為最初時三個子系統的服務進程是合在一起的,就叫csrss,後來才把那兩個子系統移了出來另立門戶,但剩下的還繼續叫csrss。
    二、 核心中的圖形裝置驅動、即Win32k.sys模組。其功能包括:
        ● 視窗管理,控制著視窗的顯示和各種螢幕輸出(例如游標),還擔負著從鍵盤、滑鼠等裝置接收輸入並將它們分發給各個具體應用的任務。
        ● 為應用軟體提供一個圖形函數庫。
    三、 若干“系統DLL”,如Kernel32.dll、Advapi32.dll、User32.dll、以及Gdi32.dll。

    上述的第二個要素Win32k.sys原先也是和csrss.exe合在一起的,這部分功能也由服務進程在使用者空間提供。應用進程通過處理序間通訊向csrss發出圖形操作請求,由csrss完成有關的圖形操作。但是後來發現頻繁的處理序間通訊和調度成了瓶頸,所以就把這一部分功能剝離出來,移進了核心,這就是Win32k.sys。這一來,對於一般的32位Windows應用而言,留給csrss、或者說必須要通過csrss辦的事就很少了。但是,儘管如此,在建立WIndows進程時還是要通知csrss,因為它擔負著對所有WIndows進程的管理。另一方面,csrss在接到通知以後就會在螢幕上顯示那個沙漏狀的游標,如果這是個有視窗的進程的話。
    注意這裡向csrss發出通知的是父進程、即調用CreateProcess()的進程,而不是新建立出來的進程,它還沒有開始運行。

    至此CreateProcess()的操作已經完成,從CreateProcess()返回就退出了kernel32.dll,回到了應用程式或更高層的DLL中。這四個階段都是立足於父進程的使用者空間,在整個過程中進行了多次系統調用,每次系統調用完成後都回到使用者空間中。例如,在第二階段中就調用了NtCreateProcess(),第三階段中就調用了NtCreateThread(),而整個建立進程的過程包括了許多次系統調用(有些系統調用屬於細節,所以上面並未提及)。
    其實Linux的進程建立也不是一次系統調用就可完成的,典型的過程就包括fock()、execve()等系統調用,但是在Windows上就更多了。這跟Windows的整個系統調用介面的設計有關。以使用者空間的記憶體配置為例,Linux的系統調用brk()只有一個參數,那就是區間的長度,但是Windows的系統調用NtAllocateVirtualMemory()卻有6個參數,其第一個參數是ProcessHandle,這是標誌著一個已開啟進程對象的Handle。這說明什麼呢?這說明Linux進程只能為自己分配空間,而Windows進程卻可以為別的進程分配空間。或者說,在儲存空間的分配上Linux進程是“自力更生”的,而Windows進程卻可以“包辦代替”。
    這對於系統設計的影響可能遠超讀者的想像。就拿為子進程的第一個線程分配使用者空間堆棧而言,既然Linux進程(線程)只能為自己分配空間,而使用者空間堆棧又必須在進入使用者空間運行之前就已存在,那就只好在核心中完成使用者空間堆棧的分配。相比之下,Windows進程可以為別的進程分配空間,於是就可以由父進程在使用者空間中為子進程完成這些操作。這樣,有些事情Linux只能在核心中做,而Windows可以在使用者空間做。有些人稱Windows為“微核心”,這或許也是個原因。而Windows的CreateProcess()中包含著更多的系統調用,也就很好理解了。
    現在,雖然父進程已經從庫函數CreateProcess()中返回了,子進程的運行卻還未開始,它的運行還要經曆下面的第五和第六兩個階段。

第五階段:啟動初始線程
    新建立的線程未必是可以被立即調度啟動並執行,因為使用者可能在建立時把標誌位CREATE_ SUSPENDED設成了1。如果那樣的話,就需要等待別的進程通過系統調用恢複其運行資格以後才可以被調度運行。否則現在已經可以被調度運行了。至於什麼時候才會被調度運行,則就要看優先順序等等條件了。而一旦受調度運行,那就是以建立進程的身份在運行、與CreateProcess()的調用者無關了。
    如前所述,當進程的第一個線程首次受調度運行時,由於線程(系統空間)堆棧的設定,首先執行的是KiThreadStartup。這段程式把目標線程的IRQL從DPC級降低到APC級,然後調用核心功能PspUserThreadStartup()。
    最後,PspUserThreadStartup()將使用者空間ntdll.dll中的函數LdrInitializeThunk()作為APC函數掛入APC隊列,再企圖“返回到”使用者空間。Windows的APC跟Linux的signal機制頗為相似,相當於使用者空間的“中斷服務”。所以,在返回使用者空間的前夕,就會檢查APC函數的存在並加以執行(如果存在的話)。
    於是,此時的CPU將兩次進入使用者空間。第一次是因為APC請求的存在而進入使用者空間,執行APC函數LdrInitializeThunk(),執行完畢以後仍回到系統空間。然後,第二次進入使用者空間才是“返回”使用者空間。返回到使用者空間的什麼地方呢?前面已經講到,返回到Kernel32.dll中的BaseProcessStart()或BaseThreadStart(),對於進程中的第一個線程是BaseProcessStart()。至於使用者程式所提供的(線程)入口,則是作為參數(函數指標)提供給BaseProcessStart()或BaseThreadStart()的,這兩個函數都會使用這指標調用由使用者提供的入口函數。

第六階段:使用者空間的初始化和DLL的串連
    使用者空間的初始化和DLL的串連是由LdrInitializeThunk()作為APC函數的執行來完成的。
    在應用軟體與動態串連庫的串連這一點上,我們已經看到,不管是Linux、Windows、還是Wine,都是一致的,那就是在使用者空間完成:
    ● Linux的.so模組串連由“解譯器”在使用者空間完成。“解譯器”相當於一個不需要事先串連的動態庫,因為它的入口是固定的。“解譯器”的映像是由核心裝入使用者空間的。
    ● Windows的DLL串連由ntdll.dll中的LdrInitializeThunk()在使用者空間完成。在此之前ntdll.dll與應用軟體尚未串連,但是已經被映射到了使用者空間。函數LdrInitializeThunk()在映像中的位置是系統初始化時就預先確定並記錄在案的,所以在進入這個函數之前也不需要串連。
    ● Wine的動態庫串連分兩種情況。一種是ELF格式的.so模組,另一種是PE格式的DLL。二者的串連都是在使用者空間完成的,前者仍由ELF解譯器ld-linux.so.2完成,後者則由工具軟體wine-kthread完成。後者的具體調用路徑是:
main() > wine_init() > __wine_process_init() > __wine_kernel_init() >
wine_switch_to_stack() > start_process() > LdrInitializeThunk()
    這在“Wine的二進位映像裝入和啟動”那篇漫談中已經講到過了。注意這裡最終完成DLL串連的函數也叫LdrInitializeThunk(),顯然Wine的作者對於Windows的這一套是清楚的。

    通過以上的敘述,我們可以看到Windows的進程建立過程與Linux有較大的不同,但是裝入PE映像和實現DLL串連的過程卻與Linux的對應過程相似,只是把“解譯器”整合到了“系統DLL”裡面,並且是作為APC函數執行的,其他就沒有太大的區別了。但是,如果跟Wine的PE映像裝入過程相比,則顯然Wine的過程(見“Wine的二進位映像裝入和啟動”)是比較複雜、效率也比較低的。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.